Begin refactor of how registers and values are handled in the VM
This commit is contained in:
parent
7cfd60d281
commit
e0579c6536
674
.vscode/launch.json
vendored
Normal file
674
.vscode/launch.json
vendored
Normal file
@ -0,0 +1,674 @@
|
|||||||
|
{
|
||||||
|
// Use IntelliSense to learn about possible attributes.
|
||||||
|
// Hover to view descriptions of existing attributes.
|
||||||
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"type": "lldb",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Debug unit tests in library 'dust_lang'",
|
||||||
|
"cargo": {
|
||||||
|
"args": [
|
||||||
|
"test",
|
||||||
|
"--no-run",
|
||||||
|
"--lib",
|
||||||
|
"--package=dust-lang"
|
||||||
|
],
|
||||||
|
"filter": {
|
||||||
|
"name": "dust_lang",
|
||||||
|
"kind": "lib"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"args": [],
|
||||||
|
"cwd": "${workspaceFolder}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "lldb",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Debug integration test 'assignment_errors'",
|
||||||
|
"cargo": {
|
||||||
|
"args": [
|
||||||
|
"test",
|
||||||
|
"--no-run",
|
||||||
|
"--test=assignment_errors",
|
||||||
|
"--package=dust-lang"
|
||||||
|
],
|
||||||
|
"filter": {
|
||||||
|
"name": "assignment_errors",
|
||||||
|
"kind": "test"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"args": [],
|
||||||
|
"cwd": "${workspaceFolder}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "lldb",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Debug integration test 'basic'",
|
||||||
|
"cargo": {
|
||||||
|
"args": [
|
||||||
|
"test",
|
||||||
|
"--no-run",
|
||||||
|
"--test=basic",
|
||||||
|
"--package=dust-lang"
|
||||||
|
],
|
||||||
|
"filter": {
|
||||||
|
"name": "basic",
|
||||||
|
"kind": "test"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"args": [],
|
||||||
|
"cwd": "${workspaceFolder}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "lldb",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Debug integration test 'comparison'",
|
||||||
|
"cargo": {
|
||||||
|
"args": [
|
||||||
|
"test",
|
||||||
|
"--no-run",
|
||||||
|
"--test=comparison",
|
||||||
|
"--package=dust-lang"
|
||||||
|
],
|
||||||
|
"filter": {
|
||||||
|
"name": "comparison",
|
||||||
|
"kind": "test"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"args": [],
|
||||||
|
"cwd": "${workspaceFolder}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "lldb",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Debug integration test 'functions'",
|
||||||
|
"cargo": {
|
||||||
|
"args": [
|
||||||
|
"test",
|
||||||
|
"--no-run",
|
||||||
|
"--test=functions",
|
||||||
|
"--package=dust-lang"
|
||||||
|
],
|
||||||
|
"filter": {
|
||||||
|
"name": "functions",
|
||||||
|
"kind": "test"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"args": [],
|
||||||
|
"cwd": "${workspaceFolder}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "lldb",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Debug integration test 'lists'",
|
||||||
|
"cargo": {
|
||||||
|
"args": [
|
||||||
|
"test",
|
||||||
|
"--no-run",
|
||||||
|
"--test=lists",
|
||||||
|
"--package=dust-lang"
|
||||||
|
],
|
||||||
|
"filter": {
|
||||||
|
"name": "lists",
|
||||||
|
"kind": "test"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"args": [],
|
||||||
|
"cwd": "${workspaceFolder}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "lldb",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Debug integration test 'logic_and'",
|
||||||
|
"cargo": {
|
||||||
|
"args": [
|
||||||
|
"test",
|
||||||
|
"--no-run",
|
||||||
|
"--test=logic_and",
|
||||||
|
"--package=dust-lang"
|
||||||
|
],
|
||||||
|
"filter": {
|
||||||
|
"name": "logic_and",
|
||||||
|
"kind": "test"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"args": [],
|
||||||
|
"cwd": "${workspaceFolder}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "lldb",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Debug integration test 'logic_and_and'",
|
||||||
|
"cargo": {
|
||||||
|
"args": [
|
||||||
|
"test",
|
||||||
|
"--no-run",
|
||||||
|
"--test=logic_and_and",
|
||||||
|
"--package=dust-lang"
|
||||||
|
],
|
||||||
|
"filter": {
|
||||||
|
"name": "logic_and_and",
|
||||||
|
"kind": "test"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"args": [],
|
||||||
|
"cwd": "${workspaceFolder}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "lldb",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Debug integration test 'logic_or'",
|
||||||
|
"cargo": {
|
||||||
|
"args": [
|
||||||
|
"test",
|
||||||
|
"--no-run",
|
||||||
|
"--test=logic_or",
|
||||||
|
"--package=dust-lang"
|
||||||
|
],
|
||||||
|
"filter": {
|
||||||
|
"name": "logic_or",
|
||||||
|
"kind": "test"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"args": [],
|
||||||
|
"cwd": "${workspaceFolder}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "lldb",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Debug integration test 'logic_variables'",
|
||||||
|
"cargo": {
|
||||||
|
"args": [
|
||||||
|
"test",
|
||||||
|
"--no-run",
|
||||||
|
"--test=logic_variables",
|
||||||
|
"--package=dust-lang"
|
||||||
|
],
|
||||||
|
"filter": {
|
||||||
|
"name": "logic_variables",
|
||||||
|
"kind": "test"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"args": [],
|
||||||
|
"cwd": "${workspaceFolder}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "lldb",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Debug integration test 'loops'",
|
||||||
|
"cargo": {
|
||||||
|
"args": [
|
||||||
|
"test",
|
||||||
|
"--no-run",
|
||||||
|
"--test=loops",
|
||||||
|
"--package=dust-lang"
|
||||||
|
],
|
||||||
|
"filter": {
|
||||||
|
"name": "loops",
|
||||||
|
"kind": "test"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"args": [],
|
||||||
|
"cwd": "${workspaceFolder}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "lldb",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Debug integration test 'math_add'",
|
||||||
|
"cargo": {
|
||||||
|
"args": [
|
||||||
|
"test",
|
||||||
|
"--no-run",
|
||||||
|
"--test=math_add",
|
||||||
|
"--package=dust-lang"
|
||||||
|
],
|
||||||
|
"filter": {
|
||||||
|
"name": "math_add",
|
||||||
|
"kind": "test"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"args": [],
|
||||||
|
"cwd": "${workspaceFolder}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "lldb",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Debug integration test 'math_add_assign'",
|
||||||
|
"cargo": {
|
||||||
|
"args": [
|
||||||
|
"test",
|
||||||
|
"--no-run",
|
||||||
|
"--test=math_add_assign",
|
||||||
|
"--package=dust-lang"
|
||||||
|
],
|
||||||
|
"filter": {
|
||||||
|
"name": "math_add_assign",
|
||||||
|
"kind": "test"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"args": [],
|
||||||
|
"cwd": "${workspaceFolder}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "lldb",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Debug integration test 'math_add_errors'",
|
||||||
|
"cargo": {
|
||||||
|
"args": [
|
||||||
|
"test",
|
||||||
|
"--no-run",
|
||||||
|
"--test=math_add_errors",
|
||||||
|
"--package=dust-lang"
|
||||||
|
],
|
||||||
|
"filter": {
|
||||||
|
"name": "math_add_errors",
|
||||||
|
"kind": "test"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"args": [],
|
||||||
|
"cwd": "${workspaceFolder}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "lldb",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Debug integration test 'math_divide'",
|
||||||
|
"cargo": {
|
||||||
|
"args": [
|
||||||
|
"test",
|
||||||
|
"--no-run",
|
||||||
|
"--test=math_divide",
|
||||||
|
"--package=dust-lang"
|
||||||
|
],
|
||||||
|
"filter": {
|
||||||
|
"name": "math_divide",
|
||||||
|
"kind": "test"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"args": [],
|
||||||
|
"cwd": "${workspaceFolder}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "lldb",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Debug integration test 'math_divide_assign'",
|
||||||
|
"cargo": {
|
||||||
|
"args": [
|
||||||
|
"test",
|
||||||
|
"--no-run",
|
||||||
|
"--test=math_divide_assign",
|
||||||
|
"--package=dust-lang"
|
||||||
|
],
|
||||||
|
"filter": {
|
||||||
|
"name": "math_divide_assign",
|
||||||
|
"kind": "test"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"args": [],
|
||||||
|
"cwd": "${workspaceFolder}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "lldb",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Debug integration test 'math_divide_erros'",
|
||||||
|
"cargo": {
|
||||||
|
"args": [
|
||||||
|
"test",
|
||||||
|
"--no-run",
|
||||||
|
"--test=math_divide_erros",
|
||||||
|
"--package=dust-lang"
|
||||||
|
],
|
||||||
|
"filter": {
|
||||||
|
"name": "math_divide_erros",
|
||||||
|
"kind": "test"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"args": [],
|
||||||
|
"cwd": "${workspaceFolder}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "lldb",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Debug integration test 'math_modulo'",
|
||||||
|
"cargo": {
|
||||||
|
"args": [
|
||||||
|
"test",
|
||||||
|
"--no-run",
|
||||||
|
"--test=math_modulo",
|
||||||
|
"--package=dust-lang"
|
||||||
|
],
|
||||||
|
"filter": {
|
||||||
|
"name": "math_modulo",
|
||||||
|
"kind": "test"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"args": [],
|
||||||
|
"cwd": "${workspaceFolder}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "lldb",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Debug integration test 'math_modulo_assign'",
|
||||||
|
"cargo": {
|
||||||
|
"args": [
|
||||||
|
"test",
|
||||||
|
"--no-run",
|
||||||
|
"--test=math_modulo_assign",
|
||||||
|
"--package=dust-lang"
|
||||||
|
],
|
||||||
|
"filter": {
|
||||||
|
"name": "math_modulo_assign",
|
||||||
|
"kind": "test"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"args": [],
|
||||||
|
"cwd": "${workspaceFolder}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "lldb",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Debug integration test 'math_modulo_errors'",
|
||||||
|
"cargo": {
|
||||||
|
"args": [
|
||||||
|
"test",
|
||||||
|
"--no-run",
|
||||||
|
"--test=math_modulo_errors",
|
||||||
|
"--package=dust-lang"
|
||||||
|
],
|
||||||
|
"filter": {
|
||||||
|
"name": "math_modulo_errors",
|
||||||
|
"kind": "test"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"args": [],
|
||||||
|
"cwd": "${workspaceFolder}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "lldb",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Debug integration test 'math_multiply'",
|
||||||
|
"cargo": {
|
||||||
|
"args": [
|
||||||
|
"test",
|
||||||
|
"--no-run",
|
||||||
|
"--test=math_multiply",
|
||||||
|
"--package=dust-lang"
|
||||||
|
],
|
||||||
|
"filter": {
|
||||||
|
"name": "math_multiply",
|
||||||
|
"kind": "test"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"args": [],
|
||||||
|
"cwd": "${workspaceFolder}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "lldb",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Debug integration test 'math_multiply_assign'",
|
||||||
|
"cargo": {
|
||||||
|
"args": [
|
||||||
|
"test",
|
||||||
|
"--no-run",
|
||||||
|
"--test=math_multiply_assign",
|
||||||
|
"--package=dust-lang"
|
||||||
|
],
|
||||||
|
"filter": {
|
||||||
|
"name": "math_multiply_assign",
|
||||||
|
"kind": "test"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"args": [],
|
||||||
|
"cwd": "${workspaceFolder}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "lldb",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Debug integration test 'math_multiply_errors'",
|
||||||
|
"cargo": {
|
||||||
|
"args": [
|
||||||
|
"test",
|
||||||
|
"--no-run",
|
||||||
|
"--test=math_multiply_errors",
|
||||||
|
"--package=dust-lang"
|
||||||
|
],
|
||||||
|
"filter": {
|
||||||
|
"name": "math_multiply_errors",
|
||||||
|
"kind": "test"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"args": [],
|
||||||
|
"cwd": "${workspaceFolder}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "lldb",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Debug integration test 'math_subtract'",
|
||||||
|
"cargo": {
|
||||||
|
"args": [
|
||||||
|
"test",
|
||||||
|
"--no-run",
|
||||||
|
"--test=math_subtract",
|
||||||
|
"--package=dust-lang"
|
||||||
|
],
|
||||||
|
"filter": {
|
||||||
|
"name": "math_subtract",
|
||||||
|
"kind": "test"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"args": [],
|
||||||
|
"cwd": "${workspaceFolder}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "lldb",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Debug integration test 'math_subtract_assign'",
|
||||||
|
"cargo": {
|
||||||
|
"args": [
|
||||||
|
"test",
|
||||||
|
"--no-run",
|
||||||
|
"--test=math_subtract_assign",
|
||||||
|
"--package=dust-lang"
|
||||||
|
],
|
||||||
|
"filter": {
|
||||||
|
"name": "math_subtract_assign",
|
||||||
|
"kind": "test"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"args": [],
|
||||||
|
"cwd": "${workspaceFolder}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "lldb",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Debug integration test 'math_subtract_errors'",
|
||||||
|
"cargo": {
|
||||||
|
"args": [
|
||||||
|
"test",
|
||||||
|
"--no-run",
|
||||||
|
"--test=math_subtract_errors",
|
||||||
|
"--package=dust-lang"
|
||||||
|
],
|
||||||
|
"filter": {
|
||||||
|
"name": "math_subtract_errors",
|
||||||
|
"kind": "test"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"args": [],
|
||||||
|
"cwd": "${workspaceFolder}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "lldb",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Debug integration test 'native_functions'",
|
||||||
|
"cargo": {
|
||||||
|
"args": [
|
||||||
|
"test",
|
||||||
|
"--no-run",
|
||||||
|
"--test=native_functions",
|
||||||
|
"--package=dust-lang"
|
||||||
|
],
|
||||||
|
"filter": {
|
||||||
|
"name": "native_functions",
|
||||||
|
"kind": "test"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"args": [],
|
||||||
|
"cwd": "${workspaceFolder}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "lldb",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Debug integration test 'scopes'",
|
||||||
|
"cargo": {
|
||||||
|
"args": [
|
||||||
|
"test",
|
||||||
|
"--no-run",
|
||||||
|
"--test=scopes",
|
||||||
|
"--package=dust-lang"
|
||||||
|
],
|
||||||
|
"filter": {
|
||||||
|
"name": "scopes",
|
||||||
|
"kind": "test"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"args": [],
|
||||||
|
"cwd": "${workspaceFolder}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "lldb",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Debug integration test 'unary_operations'",
|
||||||
|
"cargo": {
|
||||||
|
"args": [
|
||||||
|
"test",
|
||||||
|
"--no-run",
|
||||||
|
"--test=unary_operations",
|
||||||
|
"--package=dust-lang"
|
||||||
|
],
|
||||||
|
"filter": {
|
||||||
|
"name": "unary_operations",
|
||||||
|
"kind": "test"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"args": [],
|
||||||
|
"cwd": "${workspaceFolder}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "lldb",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Debug integration test 'variables'",
|
||||||
|
"cargo": {
|
||||||
|
"args": [
|
||||||
|
"test",
|
||||||
|
"--no-run",
|
||||||
|
"--test=variables",
|
||||||
|
"--package=dust-lang"
|
||||||
|
],
|
||||||
|
"filter": {
|
||||||
|
"name": "variables",
|
||||||
|
"kind": "test"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"args": [],
|
||||||
|
"cwd": "${workspaceFolder}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "lldb",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Debug benchmark 'addictive_addition'",
|
||||||
|
"cargo": {
|
||||||
|
"args": [
|
||||||
|
"test",
|
||||||
|
"--no-run",
|
||||||
|
"--bench=addictive_addition",
|
||||||
|
"--package=dust-lang"
|
||||||
|
],
|
||||||
|
"filter": {
|
||||||
|
"name": "addictive_addition",
|
||||||
|
"kind": "bench"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"args": [],
|
||||||
|
"cwd": "${workspaceFolder}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "lldb",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Debug benchmark 'fibonacci'",
|
||||||
|
"cargo": {
|
||||||
|
"args": [
|
||||||
|
"test",
|
||||||
|
"--no-run",
|
||||||
|
"--bench=fibonacci",
|
||||||
|
"--package=dust-lang"
|
||||||
|
],
|
||||||
|
"filter": {
|
||||||
|
"name": "fibonacci",
|
||||||
|
"kind": "bench"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"args": [],
|
||||||
|
"cwd": "${workspaceFolder}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "lldb",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Debug benchmark 'threads'",
|
||||||
|
"cargo": {
|
||||||
|
"args": [
|
||||||
|
"test",
|
||||||
|
"--no-run",
|
||||||
|
"--bench=threads",
|
||||||
|
"--package=dust-lang"
|
||||||
|
],
|
||||||
|
"filter": {
|
||||||
|
"name": "threads",
|
||||||
|
"kind": "bench"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"args": [],
|
||||||
|
"cwd": "${workspaceFolder}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "lldb",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Debug executable 'dust'",
|
||||||
|
"cargo": {
|
||||||
|
"args": [
|
||||||
|
"build",
|
||||||
|
"--bin=dust",
|
||||||
|
"--package=dust-cli"
|
||||||
|
],
|
||||||
|
"filter": {
|
||||||
|
"name": "dust",
|
||||||
|
"kind": "bin"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"args": [
|
||||||
|
"bench/addictive_addition/addictive_addition.ds"
|
||||||
|
],
|
||||||
|
"cwd": "${workspaceFolder}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "lldb",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Debug unit tests in executable 'dust'",
|
||||||
|
"cargo": {
|
||||||
|
"args": [
|
||||||
|
"test",
|
||||||
|
"--no-run",
|
||||||
|
"--bin=dust",
|
||||||
|
"--package=dust-cli"
|
||||||
|
],
|
||||||
|
"filter": {
|
||||||
|
"name": "dust",
|
||||||
|
"kind": "bin"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"args": [],
|
||||||
|
"cwd": "${workspaceFolder}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
4
Cargo.lock
generated
4
Cargo.lock
generated
@ -349,6 +349,7 @@ dependencies = [
|
|||||||
"rand",
|
"rand",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"smallvec",
|
||||||
"smartstring",
|
"smartstring",
|
||||||
"tracing",
|
"tracing",
|
||||||
]
|
]
|
||||||
@ -805,6 +806,9 @@ name = "smallvec"
|
|||||||
version = "1.13.2"
|
version = "1.13.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
|
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "smartstring"
|
name = "smartstring"
|
||||||
|
@ -22,6 +22,7 @@ smartstring = { version = "1.0.1", features = [
|
|||||||
], default-features = false }
|
], default-features = false }
|
||||||
tracing = "0.1.41"
|
tracing = "0.1.41"
|
||||||
crossbeam-channel = "0.5.14"
|
crossbeam-channel = "0.5.14"
|
||||||
|
smallvec = { version = "1.13.2", features = ["serde"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
criterion = { version = "0.3.4", features = ["html_reports"] }
|
criterion = { version = "0.3.4", features = ["html_reports"] }
|
||||||
|
137
dust-lang/src/chunk/constant_table.rs
Normal file
137
dust-lang/src/chunk/constant_table.rs
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::DustString;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, PartialOrd, Serialize, Deserialize)]
|
||||||
|
pub struct ConstantTable {
|
||||||
|
pub bytes: Vec<u8>,
|
||||||
|
pub characters: Vec<char>,
|
||||||
|
pub floats: Vec<f64>,
|
||||||
|
pub integers: Vec<i64>,
|
||||||
|
pub strings: Vec<DustString>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ConstantTable {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
bytes: Vec::with_capacity(0),
|
||||||
|
characters: Vec::with_capacity(0),
|
||||||
|
floats: Vec::with_capacity(0),
|
||||||
|
integers: Vec::with_capacity(0),
|
||||||
|
strings: Vec::with_capacity(0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
pub fn with_data(
|
||||||
|
bytes: impl Into<Vec<u8>>,
|
||||||
|
characters: impl Into<Vec<char>>,
|
||||||
|
floats: impl Into<Vec<f64>>,
|
||||||
|
integers: impl Into<Vec<i64>>,
|
||||||
|
strings: impl Into<Vec<DustString>>,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
bytes: bytes.into(),
|
||||||
|
characters: characters.into(),
|
||||||
|
floats: floats.into(),
|
||||||
|
integers: integers.into(),
|
||||||
|
strings: strings.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.bytes.len()
|
||||||
|
+ self.characters.len()
|
||||||
|
+ self.floats.len()
|
||||||
|
+ self.integers.len()
|
||||||
|
+ self.strings.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.bytes.is_empty()
|
||||||
|
&& self.characters.is_empty()
|
||||||
|
&& self.floats.is_empty()
|
||||||
|
&& self.integers.is_empty()
|
||||||
|
&& self.strings.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert_byte(&mut self, byte: u8) -> u16 {
|
||||||
|
if let Some(index) = self.bytes.iter().position(|&probe| probe == byte) {
|
||||||
|
index as u16
|
||||||
|
} else {
|
||||||
|
let index = self.bytes.len() as u16;
|
||||||
|
|
||||||
|
self.bytes.push(byte);
|
||||||
|
|
||||||
|
index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert_character(&mut self, character: char) -> u16 {
|
||||||
|
if let Some(index) = self.characters.iter().position(|&probe| probe == character) {
|
||||||
|
index as u16
|
||||||
|
} else {
|
||||||
|
let index = self.characters.len() as u16;
|
||||||
|
|
||||||
|
self.characters.push(character);
|
||||||
|
|
||||||
|
index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert_float(&mut self, float: f64) -> u16 {
|
||||||
|
if let Some(index) = self.floats.iter().position(|&probe| probe == float) {
|
||||||
|
index as u16
|
||||||
|
} else {
|
||||||
|
let index = self.floats.len() as u16;
|
||||||
|
|
||||||
|
self.floats.push(float);
|
||||||
|
|
||||||
|
index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert_integer(&mut self, integer: i64) -> u16 {
|
||||||
|
if let Some(index) = self.integers.iter().position(|&probe| probe == integer) {
|
||||||
|
index as u16
|
||||||
|
} else {
|
||||||
|
let index = self.integers.len() as u16;
|
||||||
|
|
||||||
|
self.integers.push(integer);
|
||||||
|
|
||||||
|
index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert_string(&mut self, string: DustString) -> u16 {
|
||||||
|
if let Some(index) = self.strings.iter().position(|probe| probe == &string) {
|
||||||
|
index as u16
|
||||||
|
} else {
|
||||||
|
let index = self.strings.len() as u16;
|
||||||
|
|
||||||
|
self.strings.push(string);
|
||||||
|
|
||||||
|
index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_byte(&self, index: u16) -> Option<u8> {
|
||||||
|
self.bytes.get(index as usize).copied()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_character(&self, index: u16) -> Option<char> {
|
||||||
|
self.characters.get(index as usize).copied()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_float(&self, index: u16) -> Option<f64> {
|
||||||
|
self.floats.get(index as usize).copied()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_integer(&self, index: u16) -> Option<i64> {
|
||||||
|
self.integers.get(index as usize).copied()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_string(&self, index: u16) -> Option<&DustString> {
|
||||||
|
self.strings.get(index as usize)
|
||||||
|
}
|
||||||
|
}
|
@ -43,7 +43,7 @@ use std::io::{self, Write};
|
|||||||
|
|
||||||
use colored::{ColoredString, Colorize};
|
use colored::{ColoredString, Colorize};
|
||||||
|
|
||||||
use crate::{Chunk, Local};
|
use crate::{Chunk, Local, Type};
|
||||||
|
|
||||||
const INSTRUCTION_COLUMNS: [(&str, usize); 4] =
|
const INSTRUCTION_COLUMNS: [(&str, usize); 4] =
|
||||||
[("i", 5), ("POSITION", 12), ("OPERATION", 17), ("INFO", 41)];
|
[("i", 5), ("POSITION", 12), ("OPERATION", 17), ("INFO", 41)];
|
||||||
@ -66,11 +66,11 @@ const LOCAL_BORDERS: [&str; 3] = [
|
|||||||
"╰─────┴────────────────┴──────────┴───────┴───────╯",
|
"╰─────┴────────────────┴──────────┴───────┴───────╯",
|
||||||
];
|
];
|
||||||
|
|
||||||
const CONSTANT_COLUMNS: [(&str, usize); 3] = [("i", 5), ("TYPE", 26), ("VALUE", 26)];
|
const CONSTANT_COLUMNS: [(&str, usize); 3] = [("i", 8), ("TYPE", 26), ("VALUE", 26)];
|
||||||
const CONSTANT_BORDERS: [&str; 3] = [
|
const CONSTANT_BORDERS: [&str; 3] = [
|
||||||
"╭─────┬──────────────────────────┬──────────────────────────╮",
|
"╭────────┬──────────────────────────┬──────────────────────────╮",
|
||||||
"├─────┼──────────────────────────┼──────────────────────────┤",
|
"├────────┼──────────────────────────┼──────────────────────────┤",
|
||||||
"╰─────┴──────────────────────────┴──────────────────────────╯",
|
"╰────────┴──────────────────────────┴──────────────────────────╯",
|
||||||
];
|
];
|
||||||
|
|
||||||
const INDENTATION: &str = "│ ";
|
const INDENTATION: &str = "│ ";
|
||||||
@ -322,6 +322,7 @@ impl<'a, W: Write> Disassembler<'a, W> {
|
|||||||
let identifier_display = self
|
let identifier_display = self
|
||||||
.chunk
|
.chunk
|
||||||
.constants
|
.constants
|
||||||
|
.strings
|
||||||
.get(*identifier_index as usize)
|
.get(*identifier_index as usize)
|
||||||
.map(|value| value.to_string())
|
.map(|value| value.to_string())
|
||||||
.unwrap_or_else(|| "unknown".to_string());
|
.unwrap_or_else(|| "unknown".to_string());
|
||||||
@ -352,8 +353,74 @@ impl<'a, W: Write> Disassembler<'a, W> {
|
|||||||
self.write_center_border(&column_name_line)?;
|
self.write_center_border(&column_name_line)?;
|
||||||
self.write_center_border(CONSTANT_BORDERS[1])?;
|
self.write_center_border(CONSTANT_BORDERS[1])?;
|
||||||
|
|
||||||
for (index, value) in self.chunk.constants.iter().enumerate() {
|
for (index, value) in self.chunk.constants.bytes.iter().enumerate() {
|
||||||
let type_display = value.r#type().to_string();
|
let type_display = Type::Byte.to_string();
|
||||||
|
let value_display = {
|
||||||
|
let mut value_string = value.to_string();
|
||||||
|
|
||||||
|
if value_string.len() > 26 {
|
||||||
|
value_string = format!("{value_string:.23}...");
|
||||||
|
}
|
||||||
|
|
||||||
|
value_string
|
||||||
|
};
|
||||||
|
let constant_display = format!("│{index:^5}│{type_display:^26}│{value_display:^26}│");
|
||||||
|
|
||||||
|
self.write_center_border(&constant_display)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (index, value) in self.chunk.constants.floats.iter().enumerate() {
|
||||||
|
let type_display = Type::Float.to_string();
|
||||||
|
let value_display = {
|
||||||
|
let mut value_string = value.to_string();
|
||||||
|
|
||||||
|
if value_string.len() > 26 {
|
||||||
|
value_string = format!("{value_string:.23}...");
|
||||||
|
}
|
||||||
|
|
||||||
|
value_string
|
||||||
|
};
|
||||||
|
let constant_display = format!("│{index:^5}│{type_display:^26}│{value_display:^26}│");
|
||||||
|
|
||||||
|
self.write_center_border(&constant_display)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (index, value) in self.chunk.constants.characters.iter().enumerate() {
|
||||||
|
let type_display = Type::Character.to_string();
|
||||||
|
let value_display = {
|
||||||
|
let mut value_string = value.to_string();
|
||||||
|
|
||||||
|
if value_string.len() > 26 {
|
||||||
|
value_string = format!("{value_string:.23}...");
|
||||||
|
}
|
||||||
|
|
||||||
|
value_string
|
||||||
|
};
|
||||||
|
let constant_display = format!("│{index:^5}│{type_display:^26}│{value_display:^26}│");
|
||||||
|
|
||||||
|
self.write_center_border(&constant_display)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (index, value) in self.chunk.constants.integers.iter().enumerate() {
|
||||||
|
let index_display = format!("INT_{index}");
|
||||||
|
let type_display = Type::Integer.to_string();
|
||||||
|
let value_display = {
|
||||||
|
let mut value_string = value.to_string();
|
||||||
|
|
||||||
|
if value_string.len() > 26 {
|
||||||
|
value_string = format!("{value_string:.23}...");
|
||||||
|
}
|
||||||
|
|
||||||
|
value_string
|
||||||
|
};
|
||||||
|
let constant_display =
|
||||||
|
format!("│{index_display:^8}│{type_display:^26}│{value_display:^26}│");
|
||||||
|
|
||||||
|
self.write_center_border(&constant_display)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (index, value) in self.chunk.constants.strings.iter().enumerate() {
|
||||||
|
let type_display = Type::String.to_string();
|
||||||
let value_display = {
|
let value_display = {
|
||||||
let mut value_string = value.to_string();
|
let mut value_string = value.to_string();
|
||||||
|
|
||||||
|
@ -13,10 +13,12 @@
|
|||||||
//! [`Chunk::with_data`] can be used to create a chunk for comparison to the compiler output. Do not
|
//! [`Chunk::with_data`] can be used to create a chunk for comparison to the compiler output. Do not
|
||||||
//! try to run these chunks in a virtual machine. Due to their missing stack size and record index,
|
//! try to run these chunks in a virtual machine. Due to their missing stack size and record index,
|
||||||
//! they will cause a panic or undefined behavior.
|
//! they will cause a panic or undefined behavior.
|
||||||
|
mod constant_table;
|
||||||
mod disassembler;
|
mod disassembler;
|
||||||
mod local;
|
mod local;
|
||||||
mod scope;
|
mod scope;
|
||||||
|
|
||||||
|
pub use constant_table::ConstantTable;
|
||||||
pub use disassembler::Disassembler;
|
pub use disassembler::Disassembler;
|
||||||
pub use local::Local;
|
pub use local::Local;
|
||||||
pub use scope::Scope;
|
pub use scope::Scope;
|
||||||
@ -27,7 +29,7 @@ use std::sync::Arc;
|
|||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{DustString, Function, FunctionType, Instruction, Span, Value};
|
use crate::{DustString, Function, FunctionType, Instruction, Span};
|
||||||
|
|
||||||
/// Representation of a Dust program or function.
|
/// Representation of a Dust program or function.
|
||||||
///
|
///
|
||||||
@ -39,7 +41,7 @@ pub struct Chunk {
|
|||||||
|
|
||||||
pub(crate) instructions: Vec<Instruction>,
|
pub(crate) instructions: Vec<Instruction>,
|
||||||
pub(crate) positions: Vec<Span>,
|
pub(crate) positions: Vec<Span>,
|
||||||
pub(crate) constants: Vec<Value>,
|
pub(crate) constants: ConstantTable,
|
||||||
pub(crate) locals: Vec<Local>,
|
pub(crate) locals: Vec<Local>,
|
||||||
pub(crate) prototypes: Vec<Arc<Chunk>>,
|
pub(crate) prototypes: Vec<Arc<Chunk>>,
|
||||||
|
|
||||||
@ -54,7 +56,7 @@ impl Chunk {
|
|||||||
r#type: FunctionType,
|
r#type: FunctionType,
|
||||||
instructions: impl Into<Vec<Instruction>>,
|
instructions: impl Into<Vec<Instruction>>,
|
||||||
positions: impl Into<Vec<Span>>,
|
positions: impl Into<Vec<Span>>,
|
||||||
constants: impl Into<Vec<Value>>,
|
constants: ConstantTable,
|
||||||
locals: impl Into<Vec<Local>>,
|
locals: impl Into<Vec<Local>>,
|
||||||
prototypes: impl IntoIterator<Item = Chunk>,
|
prototypes: impl IntoIterator<Item = Chunk>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
@ -63,7 +65,7 @@ impl Chunk {
|
|||||||
r#type,
|
r#type,
|
||||||
instructions: instructions.into(),
|
instructions: instructions.into(),
|
||||||
positions: positions.into(),
|
positions: positions.into(),
|
||||||
constants: constants.into(),
|
constants,
|
||||||
locals: locals.into(),
|
locals: locals.into(),
|
||||||
prototypes: prototypes.into_iter().map(Arc::new).collect(),
|
prototypes: prototypes.into_iter().map(Arc::new).collect(),
|
||||||
register_count: 0,
|
register_count: 0,
|
||||||
|
@ -33,8 +33,9 @@ use std::{mem::replace, sync::Arc};
|
|||||||
use optimize::control_flow_register_consolidation;
|
use optimize::control_flow_register_consolidation;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Chunk, ConcreteValue, DustError, DustString, FunctionType, Instruction, Lexer, Local,
|
Chunk, DustError, DustString, FunctionType, Instruction, Lexer, Local, NativeFunction, Operand,
|
||||||
NativeFunction, Operand, Operation, Scope, Span, Token, TokenKind, Type, Value,
|
Operation, Scope, Span, Token, TokenKind, Type,
|
||||||
|
chunk::ConstantTable,
|
||||||
instruction::{CallNative, Close, GetLocal, Jump, LoadList, Return, SetLocal, TypeCode},
|
instruction::{CallNative, Close, GetLocal, Jump, LoadList, Return, SetLocal, TypeCode},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -88,7 +89,7 @@ pub struct Compiler<'src> {
|
|||||||
|
|
||||||
/// Constants that have been compiled. These are assigned to the chunk when [`Compiler::finish`]
|
/// Constants that have been compiled. These are assigned to the chunk when [`Compiler::finish`]
|
||||||
/// is called.
|
/// is called.
|
||||||
constants: Vec<Value>,
|
constants: ConstantTable,
|
||||||
|
|
||||||
/// Block-local variables and their types. The locals are assigned to the chunk when
|
/// Block-local variables and their types. The locals are assigned to the chunk when
|
||||||
/// [`Compiler::finish`] is called. The types are discarded after compilation.
|
/// [`Compiler::finish`] is called. The types are discarded after compilation.
|
||||||
@ -144,7 +145,7 @@ impl<'src> Compiler<'src> {
|
|||||||
return_type: Type::None,
|
return_type: Type::None,
|
||||||
},
|
},
|
||||||
instructions: Vec::new(),
|
instructions: Vec::new(),
|
||||||
constants: Vec::new(),
|
constants: ConstantTable::new(),
|
||||||
locals: Vec::new(),
|
locals: Vec::new(),
|
||||||
prototypes: Vec::new(),
|
prototypes: Vec::new(),
|
||||||
stack_size: 0,
|
stack_size: 0,
|
||||||
@ -215,7 +216,7 @@ impl<'src> Compiler<'src> {
|
|||||||
r#type: self.r#type,
|
r#type: self.r#type,
|
||||||
instructions,
|
instructions,
|
||||||
positions,
|
positions,
|
||||||
constants: self.constants.to_vec(),
|
constants: self.constants,
|
||||||
locals,
|
locals,
|
||||||
prototypes: self.prototypes,
|
prototypes: self.prototypes,
|
||||||
register_count: self.stack_size,
|
register_count: self.stack_size,
|
||||||
@ -275,13 +276,10 @@ impl<'src> Compiler<'src> {
|
|||||||
.enumerate()
|
.enumerate()
|
||||||
.rev()
|
.rev()
|
||||||
.find_map(|(index, (local, _))| {
|
.find_map(|(index, (local, _))| {
|
||||||
let constant = self.constants.get(local.identifier_index as usize)?;
|
let identifier = self
|
||||||
let identifier =
|
.constants
|
||||||
if let Value::Concrete(ConcreteValue::String(identifier)) = constant {
|
.strings
|
||||||
identifier
|
.get(local.identifier_index as usize)?;
|
||||||
} else {
|
|
||||||
return None;
|
|
||||||
};
|
|
||||||
|
|
||||||
if identifier == identifier_text {
|
if identifier == identifier_text {
|
||||||
Some(index as u16)
|
Some(index as u16)
|
||||||
@ -305,8 +303,8 @@ impl<'src> Compiler<'src> {
|
|||||||
) -> (u16, u16) {
|
) -> (u16, u16) {
|
||||||
info!("Declaring local {identifier}");
|
info!("Declaring local {identifier}");
|
||||||
|
|
||||||
let identifier = Value::Concrete(ConcreteValue::string(identifier));
|
let identifier = DustString::from(identifier);
|
||||||
let identifier_index = self.push_or_get_constant(identifier);
|
let identifier_index = self.push_or_get_constant_string(identifier);
|
||||||
let local_index = self.locals.len() as u16;
|
let local_index = self.locals.len() as u16;
|
||||||
|
|
||||||
self.locals.push((
|
self.locals.push((
|
||||||
@ -322,22 +320,24 @@ impl<'src> Compiler<'src> {
|
|||||||
.get(local_index as usize)
|
.get(local_index as usize)
|
||||||
.and_then(|(local, _)| {
|
.and_then(|(local, _)| {
|
||||||
self.constants
|
self.constants
|
||||||
|
.strings
|
||||||
.get(local.identifier_index as usize)
|
.get(local.identifier_index as usize)
|
||||||
.map(|value| value.to_string())
|
.map(|value| value.to_string())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn push_or_get_constant(&mut self, value: Value) -> u16 {
|
fn push_or_get_constant_string(&mut self, string: DustString) -> u16 {
|
||||||
if let Some(index) = self
|
if let Some(index) = self
|
||||||
.constants
|
.constants
|
||||||
|
.strings
|
||||||
.iter()
|
.iter()
|
||||||
.position(|constant| constant == &value)
|
.position(|constant| constant == &string)
|
||||||
{
|
{
|
||||||
index as u16
|
index as u16
|
||||||
} else {
|
} else {
|
||||||
let index = self.constants.len() as u16;
|
let index = self.constants.len() as u16;
|
||||||
|
|
||||||
self.constants.push(value);
|
self.constants.strings.push(string);
|
||||||
|
|
||||||
index
|
index
|
||||||
}
|
}
|
||||||
@ -466,17 +466,65 @@ impl<'src> Compiler<'src> {
|
|||||||
self.instructions.push((instruction, r#type, position));
|
self.instructions.push((instruction, r#type, position));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn emit_constant(
|
fn emit_byte_constant(&mut self, byte: u8, position: Span) -> Result<(), CompileError> {
|
||||||
|
let constant_index = self.constants.insert_byte(byte);
|
||||||
|
let destination = self.next_register();
|
||||||
|
let load_constant =
|
||||||
|
Instruction::load_constant(destination, TypeCode::BYTE, constant_index, false);
|
||||||
|
|
||||||
|
self.emit_instruction(load_constant, Type::Byte, position);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn emit_character_constant(
|
||||||
&mut self,
|
&mut self,
|
||||||
constant: ConcreteValue,
|
character: char,
|
||||||
position: Span,
|
position: Span,
|
||||||
) -> Result<(), CompileError> {
|
) -> Result<(), CompileError> {
|
||||||
let r#type = constant.r#type();
|
let constant_index = self.constants.insert_character(character);
|
||||||
let constant_index = self.push_or_get_constant(Value::Concrete(constant));
|
|
||||||
let destination = self.next_register();
|
let destination = self.next_register();
|
||||||
let load_constant = Instruction::load_constant(destination, constant_index, false);
|
let load_constant =
|
||||||
|
Instruction::load_constant(destination, TypeCode::CHARACTER, constant_index, false);
|
||||||
|
|
||||||
self.emit_instruction(load_constant, r#type, position);
|
self.emit_instruction(load_constant, Type::Character, position);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn emit_float_constant(&mut self, float: f64, position: Span) -> Result<(), CompileError> {
|
||||||
|
let constant_index = self.constants.insert_float(float);
|
||||||
|
let destination = self.next_register();
|
||||||
|
let load_constant =
|
||||||
|
Instruction::load_constant(destination, TypeCode::FLOAT, constant_index, false);
|
||||||
|
|
||||||
|
self.emit_instruction(load_constant, Type::Float, position);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn emit_integer_constant(&mut self, integer: i64, position: Span) -> Result<(), CompileError> {
|
||||||
|
let constant_index = self.constants.insert_integer(integer);
|
||||||
|
let destination = self.next_register();
|
||||||
|
let load_constant =
|
||||||
|
Instruction::load_constant(destination, TypeCode::INTEGER, constant_index, false);
|
||||||
|
|
||||||
|
self.emit_instruction(load_constant, Type::Integer, position);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn emit_string_constant(
|
||||||
|
&mut self,
|
||||||
|
string: DustString,
|
||||||
|
position: Span,
|
||||||
|
) -> Result<(), CompileError> {
|
||||||
|
let constant_index = self.constants.insert_string(string);
|
||||||
|
let destination = self.next_register();
|
||||||
|
let load_constant =
|
||||||
|
Instruction::load_constant(destination, TypeCode::STRING, constant_index, false);
|
||||||
|
|
||||||
|
self.emit_instruction(load_constant, Type::String, position);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -511,9 +559,8 @@ impl<'src> Compiler<'src> {
|
|||||||
|
|
||||||
let byte = u8::from_str_radix(&text[2..], 16)
|
let byte = u8::from_str_radix(&text[2..], 16)
|
||||||
.map_err(|error| CompileError::ParseIntError { error, position })?;
|
.map_err(|error| CompileError::ParseIntError { error, position })?;
|
||||||
let value = ConcreteValue::Byte(byte);
|
|
||||||
|
|
||||||
self.emit_constant(value, position)?;
|
self.emit_byte_constant(byte, position)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
@ -530,10 +577,7 @@ impl<'src> Compiler<'src> {
|
|||||||
|
|
||||||
if let Token::Character(character) = self.current_token {
|
if let Token::Character(character) = self.current_token {
|
||||||
self.advance()?;
|
self.advance()?;
|
||||||
|
self.emit_character_constant(character, position)?;
|
||||||
let value = ConcreteValue::Character(character);
|
|
||||||
|
|
||||||
self.emit_constant(value, position)?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
@ -557,9 +601,8 @@ impl<'src> Compiler<'src> {
|
|||||||
error,
|
error,
|
||||||
position: self.previous_position,
|
position: self.previous_position,
|
||||||
})?;
|
})?;
|
||||||
let value = ConcreteValue::Float(float);
|
|
||||||
|
|
||||||
self.emit_constant(value, position)?;
|
self.emit_float_constant(float, position)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
@ -589,9 +632,7 @@ impl<'src> Compiler<'src> {
|
|||||||
integer_value = integer_value * 10 + digit;
|
integer_value = integer_value * 10 + digit;
|
||||||
}
|
}
|
||||||
|
|
||||||
let value = ConcreteValue::Integer(integer_value);
|
self.emit_integer_constant(integer_value, position)?;
|
||||||
|
|
||||||
self.emit_constant(value, position)?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
@ -609,9 +650,9 @@ impl<'src> Compiler<'src> {
|
|||||||
if let Token::String(text) = self.current_token {
|
if let Token::String(text) = self.current_token {
|
||||||
self.advance()?;
|
self.advance()?;
|
||||||
|
|
||||||
let value = ConcreteValue::string(text);
|
let string = DustString::from(text);
|
||||||
|
|
||||||
self.emit_constant(value, position)?;
|
self.emit_string_constant(string, position)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
@ -1478,24 +1519,34 @@ impl<'src> Compiler<'src> {
|
|||||||
|
|
||||||
self.advance()?;
|
self.advance()?;
|
||||||
|
|
||||||
let should_return_value =
|
let (should_return_value, return_type) =
|
||||||
if matches!(self.current_token, Token::Semicolon | Token::RightBrace) {
|
if matches!(self.current_token, Token::Semicolon | Token::RightBrace) {
|
||||||
self.update_return_type(Type::None)?;
|
self.update_return_type(Type::None)?;
|
||||||
|
|
||||||
false
|
(false, TypeCode(0))
|
||||||
} else {
|
} else {
|
||||||
self.parse_expression()?;
|
self.parse_expression()?;
|
||||||
|
|
||||||
let expression_type = self.get_last_instruction_type();
|
let expression_type = self.get_last_instruction_type();
|
||||||
|
let type_code = match expression_type {
|
||||||
|
Type::Boolean => TypeCode::BOOLEAN,
|
||||||
|
Type::Byte => TypeCode::BYTE,
|
||||||
|
Type::Character => TypeCode::CHARACTER,
|
||||||
|
Type::Float => TypeCode::FLOAT,
|
||||||
|
Type::Integer => TypeCode::INTEGER,
|
||||||
|
Type::String => TypeCode::STRING,
|
||||||
|
_ => TypeCode(0),
|
||||||
|
};
|
||||||
|
|
||||||
self.update_return_type(expression_type)?;
|
self.update_return_type(expression_type)?;
|
||||||
|
|
||||||
true
|
(true, type_code)
|
||||||
};
|
};
|
||||||
let end = self.current_position.1;
|
let end = self.current_position.1;
|
||||||
let return_register = self.next_register() - 1;
|
let return_register = self.next_register() - 1;
|
||||||
let r#return = Instruction::from(Return {
|
let r#return = Instruction::from(Return {
|
||||||
should_return_value,
|
should_return_value,
|
||||||
|
return_type,
|
||||||
return_register,
|
return_register,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1529,7 +1580,7 @@ impl<'src> Compiler<'src> {
|
|||||||
{
|
{
|
||||||
// Do nothing if the last instruction is a return or a return followed by a jump
|
// Do nothing if the last instruction is a return or a return followed by a jump
|
||||||
} else if self.allow(Token::Semicolon)? {
|
} else if self.allow(Token::Semicolon)? {
|
||||||
let r#return = Instruction::r#return(false, 0);
|
let r#return = Instruction::r#return(false, TypeCode(0), 0);
|
||||||
|
|
||||||
self.emit_instruction(r#return, Type::None, self.current_position);
|
self.emit_instruction(r#return, Type::None, self.current_position);
|
||||||
} else {
|
} else {
|
||||||
@ -1549,7 +1600,16 @@ impl<'src> Compiler<'src> {
|
|||||||
})?;
|
})?;
|
||||||
|
|
||||||
let should_return_value = previous_expression_type != Type::None;
|
let should_return_value = previous_expression_type != Type::None;
|
||||||
let r#return = Instruction::r#return(should_return_value, previous_register);
|
let type_code = match previous_expression_type {
|
||||||
|
Type::Boolean => TypeCode::BOOLEAN,
|
||||||
|
Type::Byte => TypeCode::BYTE,
|
||||||
|
Type::Character => TypeCode::CHARACTER,
|
||||||
|
Type::Float => TypeCode::FLOAT,
|
||||||
|
Type::Integer => TypeCode::INTEGER,
|
||||||
|
Type::String => TypeCode::STRING,
|
||||||
|
_ => TypeCode(0),
|
||||||
|
};
|
||||||
|
let r#return = Instruction::r#return(should_return_value, type_code, previous_register);
|
||||||
|
|
||||||
self.update_return_type(previous_expression_type.clone())?;
|
self.update_return_type(previous_expression_type.clone())?;
|
||||||
self.emit_instruction(r#return, Type::None, self.current_position);
|
self.emit_instruction(r#return, Type::None, self.current_position);
|
||||||
|
@ -2,10 +2,11 @@ use std::fmt::{self, Display, Formatter};
|
|||||||
|
|
||||||
use crate::{Instruction, Operation};
|
use crate::{Instruction, Operation};
|
||||||
|
|
||||||
use super::InstructionBuilder;
|
use super::{InstructionBuilder, TypeCode};
|
||||||
|
|
||||||
pub struct LoadConstant {
|
pub struct LoadConstant {
|
||||||
pub destination: u16,
|
pub destination: u16,
|
||||||
|
pub type_code: TypeCode,
|
||||||
pub constant_index: u16,
|
pub constant_index: u16,
|
||||||
pub jump_next: bool,
|
pub jump_next: bool,
|
||||||
}
|
}
|
||||||
@ -13,11 +14,13 @@ pub struct LoadConstant {
|
|||||||
impl From<Instruction> for LoadConstant {
|
impl From<Instruction> for LoadConstant {
|
||||||
fn from(instruction: Instruction) -> Self {
|
fn from(instruction: Instruction) -> Self {
|
||||||
let destination = instruction.a_field();
|
let destination = instruction.a_field();
|
||||||
|
let type_code = instruction.b_type();
|
||||||
let constant_index = instruction.b_field();
|
let constant_index = instruction.b_field();
|
||||||
let jump_next = instruction.c_field() != 0;
|
let jump_next = instruction.c_field() != 0;
|
||||||
|
|
||||||
LoadConstant {
|
LoadConstant {
|
||||||
destination,
|
destination,
|
||||||
|
type_code,
|
||||||
constant_index,
|
constant_index,
|
||||||
jump_next,
|
jump_next,
|
||||||
}
|
}
|
||||||
@ -29,6 +32,7 @@ impl From<LoadConstant> for Instruction {
|
|||||||
InstructionBuilder {
|
InstructionBuilder {
|
||||||
operation: Operation::LOAD_CONSTANT,
|
operation: Operation::LOAD_CONSTANT,
|
||||||
a_field: load_constant.destination,
|
a_field: load_constant.destination,
|
||||||
|
b_type: load_constant.type_code,
|
||||||
b_field: load_constant.constant_index,
|
b_field: load_constant.constant_index,
|
||||||
c_field: load_constant.jump_next as u16,
|
c_field: load_constant.jump_next as u16,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
@ -41,11 +45,20 @@ impl Display for LoadConstant {
|
|||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
let LoadConstant {
|
let LoadConstant {
|
||||||
destination,
|
destination,
|
||||||
|
type_code,
|
||||||
constant_index,
|
constant_index,
|
||||||
jump_next,
|
jump_next,
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
write!(f, "R{destination} = C{constant_index}")?;
|
match *type_code {
|
||||||
|
TypeCode::BOOLEAN => write!(f, "R_BOOL_{destination} = C_BOOL_{constant_index}")?,
|
||||||
|
TypeCode::BYTE => write!(f, "R_BYTE_{destination} = C_BYTE_{constant_index}")?,
|
||||||
|
TypeCode::CHARACTER => write!(f, "R_CHAR_{destination} = C_CHAR_{constant_index}")?,
|
||||||
|
TypeCode::FLOAT => write!(f, "R_FLOAT_{destination} = C_FLOAT_{constant_index}")?,
|
||||||
|
TypeCode::INTEGER => write!(f, "R_INT_{destination} = C_INT_{constant_index}")?,
|
||||||
|
TypeCode::STRING => write!(f, "R_STR_{destination} = C_STR_{constant_index}")?,
|
||||||
|
unknown => unknown.panic_from_unknown_code(),
|
||||||
|
}
|
||||||
|
|
||||||
if *jump_next {
|
if *jump_next {
|
||||||
write!(f, " JUMP +1")?;
|
write!(f, " JUMP +1")?;
|
||||||
|
@ -154,6 +154,7 @@ pub use type_code::TypeCode;
|
|||||||
|
|
||||||
use crate::NativeFunction;
|
use crate::NativeFunction;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq, PartialOrd)]
|
||||||
pub struct InstructionBuilder {
|
pub struct InstructionBuilder {
|
||||||
pub operation: Operation,
|
pub operation: Operation,
|
||||||
pub a_field: u16,
|
pub a_field: u16,
|
||||||
@ -198,6 +199,18 @@ impl Default for InstructionBuilder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Debug for InstructionBuilder {
|
||||||
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
|
write!(f, "{self}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for InstructionBuilder {
|
||||||
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
|
write!(f, "{}", self.build())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// An instruction for the Dust virtual machine.
|
/// An instruction for the Dust virtual machine.
|
||||||
///
|
///
|
||||||
/// See the [module-level documentation](index.html) for more information.
|
/// See the [module-level documentation](index.html) for more information.
|
||||||
@ -275,9 +288,15 @@ impl Instruction {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_constant(destination: u16, constant_index: u16, jump_next: bool) -> Instruction {
|
pub fn load_constant(
|
||||||
|
destination: u16,
|
||||||
|
type_code: TypeCode,
|
||||||
|
constant_index: u16,
|
||||||
|
jump_next: bool,
|
||||||
|
) -> Instruction {
|
||||||
Instruction::from(LoadConstant {
|
Instruction::from(LoadConstant {
|
||||||
destination,
|
destination,
|
||||||
|
type_code,
|
||||||
constant_index,
|
constant_index,
|
||||||
jump_next,
|
jump_next,
|
||||||
})
|
})
|
||||||
@ -511,9 +530,14 @@ impl Instruction {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn r#return(should_return_value: bool, return_register: u16) -> Instruction {
|
pub fn r#return(
|
||||||
|
should_return_value: bool,
|
||||||
|
return_type: TypeCode,
|
||||||
|
return_register: u16,
|
||||||
|
) -> Instruction {
|
||||||
Instruction::from(Return {
|
Instruction::from(Return {
|
||||||
should_return_value,
|
should_return_value,
|
||||||
|
return_type,
|
||||||
return_register,
|
return_register,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -2,20 +2,23 @@ use std::fmt::{self, Display, Formatter};
|
|||||||
|
|
||||||
use crate::{Instruction, Operation};
|
use crate::{Instruction, Operation};
|
||||||
|
|
||||||
use super::InstructionBuilder;
|
use super::{InstructionBuilder, TypeCode};
|
||||||
|
|
||||||
pub struct Return {
|
pub struct Return {
|
||||||
pub should_return_value: bool,
|
pub should_return_value: bool,
|
||||||
|
pub return_type: TypeCode,
|
||||||
pub return_register: u16,
|
pub return_register: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Instruction> for Return {
|
impl From<Instruction> for Return {
|
||||||
fn from(instruction: Instruction) -> Self {
|
fn from(instruction: Instruction) -> Self {
|
||||||
let should_return_value = instruction.b_field() != 0;
|
let should_return_value = instruction.b_field() != 0;
|
||||||
|
let return_type = instruction.b_type();
|
||||||
let return_register = instruction.c_field();
|
let return_register = instruction.c_field();
|
||||||
|
|
||||||
Return {
|
Return {
|
||||||
should_return_value,
|
should_return_value,
|
||||||
|
return_type,
|
||||||
return_register,
|
return_register,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -25,11 +28,13 @@ impl From<Return> for Instruction {
|
|||||||
fn from(r#return: Return) -> Self {
|
fn from(r#return: Return) -> Self {
|
||||||
let operation = Operation::RETURN;
|
let operation = Operation::RETURN;
|
||||||
let b_field = r#return.should_return_value as u16;
|
let b_field = r#return.should_return_value as u16;
|
||||||
|
let b_type = r#return.return_type;
|
||||||
let c_field = r#return.return_register;
|
let c_field = r#return.return_register;
|
||||||
|
|
||||||
InstructionBuilder {
|
InstructionBuilder {
|
||||||
operation,
|
operation,
|
||||||
b_field,
|
b_field,
|
||||||
|
b_type,
|
||||||
c_field,
|
c_field,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
@ -41,11 +46,18 @@ impl Display for Return {
|
|||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
let Return {
|
let Return {
|
||||||
should_return_value,
|
should_return_value,
|
||||||
|
return_type,
|
||||||
return_register,
|
return_register,
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
if *should_return_value {
|
if *should_return_value {
|
||||||
write!(f, "RETURN R{return_register}")
|
match *return_type {
|
||||||
|
TypeCode::INTEGER => write!(f, "RETURN R_INT_{}", return_register),
|
||||||
|
TypeCode::FLOAT => write!(f, "RETURN R_FLOAT_{}", return_register),
|
||||||
|
TypeCode::BOOLEAN => write!(f, "RETURN R_BOOL_{}", return_register),
|
||||||
|
TypeCode::STRING => write!(f, "RETURN R_STRING_{}", return_register),
|
||||||
|
unknown => unknown.panic_from_unknown_code(),
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
write!(f, "RETURN")
|
write!(f, "RETURN")
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
|
||||||
pub struct TypeCode(pub u8);
|
pub struct TypeCode(pub u8);
|
||||||
|
|
||||||
impl TypeCode {
|
impl TypeCode {
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
//!
|
//!
|
||||||
//! println!("{}", report);
|
//! println!("{}", report);
|
||||||
//! ```
|
//! ```
|
||||||
|
#![feature(get_many_mut)]
|
||||||
|
|
||||||
pub mod chunk;
|
pub mod chunk;
|
||||||
pub mod compiler;
|
pub mod compiler;
|
||||||
@ -42,15 +43,13 @@ pub mod vm;
|
|||||||
pub use crate::chunk::{Chunk, Disassembler, Local, Scope};
|
pub use crate::chunk::{Chunk, Disassembler, Local, Scope};
|
||||||
pub use crate::compiler::{CompileError, Compiler, compile};
|
pub use crate::compiler::{CompileError, Compiler, compile};
|
||||||
pub use crate::dust_error::{AnnotatedError, DustError};
|
pub use crate::dust_error::{AnnotatedError, DustError};
|
||||||
pub use crate::instruction::{Operand, Instruction, Operation};
|
pub use crate::instruction::{Instruction, Operand, Operation};
|
||||||
pub use crate::lexer::{LexError, Lexer, lex};
|
pub use crate::lexer::{LexError, Lexer, lex};
|
||||||
pub use crate::native_function::{NativeFunction, NativeFunctionError};
|
pub use crate::native_function::{NativeFunction, NativeFunctionError};
|
||||||
pub use crate::token::{Token, TokenKind, TokenOwned};
|
pub use crate::token::{Token, TokenKind, TokenOwned};
|
||||||
pub use crate::r#type::{EnumType, FunctionType, StructType, Type, TypeConflict};
|
pub use crate::r#type::{EnumType, FunctionType, StructType, Type, TypeConflict};
|
||||||
pub use crate::value::{
|
pub use crate::value::{AbstractList, DustString, Function, RangeValue, Value};
|
||||||
AbstractList, ConcreteValue, DustString, Function, RangeValue, Value, ValueError,
|
pub use crate::vm::{Vm, run};
|
||||||
};
|
|
||||||
pub use crate::vm::{Pointer, Vm, run};
|
|
||||||
|
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
@ -3,20 +3,5 @@ use std::{ops::Range, panic};
|
|||||||
use crate::vm::ThreadData;
|
use crate::vm::ThreadData;
|
||||||
|
|
||||||
pub fn panic(data: &mut ThreadData, _: u16, argument_range: Range<u16>) -> bool {
|
pub fn panic(data: &mut ThreadData, _: u16, argument_range: Range<u16>) -> bool {
|
||||||
let position = data.current_position();
|
todo!()
|
||||||
let mut message = format!("Dust panic at {position}!");
|
|
||||||
|
|
||||||
for register_index in argument_range {
|
|
||||||
let value_option = data.open_register_allow_empty_unchecked(register_index);
|
|
||||||
let value = match value_option {
|
|
||||||
Some(value) => value,
|
|
||||||
None => continue,
|
|
||||||
};
|
|
||||||
let string = value.display(data);
|
|
||||||
|
|
||||||
message.push_str(&string);
|
|
||||||
message.push('\n');
|
|
||||||
}
|
|
||||||
|
|
||||||
panic!("{}", message)
|
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
use std::io::{Write, stdin, stdout};
|
use std::io::{Write, stdin, stdout};
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
|
|
||||||
|
use crate::DustString;
|
||||||
use crate::{
|
use crate::{
|
||||||
ConcreteValue, Value,
|
Value,
|
||||||
vm::{Register, ThreadData, get_next_action},
|
vm::{Register, ThreadData},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn read_line(data: &mut ThreadData, destination: u16, _argument_range: Range<u16>) -> bool {
|
pub fn read_line(data: &mut ThreadData, destination: u16, _argument_range: Range<u16>) -> bool {
|
||||||
|
let current_frame = data.current_frame_mut();
|
||||||
let mut buffer = String::new();
|
let mut buffer = String::new();
|
||||||
|
|
||||||
if stdin().read_line(&mut buffer).is_ok() {
|
if stdin().read_line(&mut buffer).is_ok() {
|
||||||
@ -14,45 +16,18 @@ pub fn read_line(data: &mut ThreadData, destination: u16, _argument_range: Range
|
|||||||
|
|
||||||
buffer.truncate(length.saturating_sub(1));
|
buffer.truncate(length.saturating_sub(1));
|
||||||
|
|
||||||
let register = Register::Value(Value::Concrete(ConcreteValue::string(buffer)));
|
let register = current_frame.registers.get_string_mut(destination);
|
||||||
|
|
||||||
data.set_register(destination, register);
|
*register = Register::Value(DustString::from(buffer));
|
||||||
}
|
}
|
||||||
|
|
||||||
data.next_action = get_next_action(data);
|
|
||||||
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write(data: &mut ThreadData, _: u16, argument_range: Range<u16>) -> bool {
|
pub fn write(data: &mut ThreadData, _: u16, argument_range: Range<u16>) -> bool {
|
||||||
let mut stdout = stdout();
|
todo!()
|
||||||
|
|
||||||
for register_index in argument_range {
|
|
||||||
if let Some(value) = data.open_register_allow_empty_unchecked(register_index) {
|
|
||||||
let string = value.display(data);
|
|
||||||
let _ = stdout.write(string.as_bytes());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let _ = stdout.flush();
|
|
||||||
data.next_action = get_next_action(data);
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_line(data: &mut ThreadData, _: u16, argument_range: Range<u16>) -> bool {
|
pub fn write_line(data: &mut ThreadData, _: u16, argument_range: Range<u16>) -> bool {
|
||||||
let mut stdout = stdout().lock();
|
todo!()
|
||||||
|
|
||||||
for register_index in argument_range {
|
|
||||||
if let Some(value) = data.open_register_allow_empty_unchecked(register_index) {
|
|
||||||
let string = value.display(data);
|
|
||||||
let _ = stdout.write(string.as_bytes());
|
|
||||||
let _ = stdout.write(b"\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let _ = stdout.flush();
|
|
||||||
data.next_action = get_next_action(data);
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
@ -2,39 +2,38 @@ use std::ops::Range;
|
|||||||
|
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
|
|
||||||
use crate::{
|
use crate::vm::ThreadData;
|
||||||
Value,
|
|
||||||
vm::{Register, ThreadData, get_next_action},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn random_int(data: &mut ThreadData, destination: u16, argument_range: Range<u16>) -> bool {
|
pub fn random_int(data: &mut ThreadData, destination: u16, argument_range: Range<u16>) -> bool {
|
||||||
let mut argument_range_iter = argument_range.into_iter();
|
todo!()
|
||||||
let (min, max) = {
|
|
||||||
let mut min = None;
|
|
||||||
|
|
||||||
loop {
|
// let mut argument_range_iter = argument_range.into_iter();
|
||||||
let register_index = argument_range_iter
|
// let (min, max) = {
|
||||||
.next()
|
// let mut min = None;
|
||||||
.unwrap_or_else(|| panic!("No argument was passed to \"random_int\""));
|
|
||||||
let value_option = data.open_register_allow_empty_unchecked(register_index);
|
|
||||||
|
|
||||||
if let Some(argument) = value_option {
|
// loop {
|
||||||
if let Some(integer) = argument.as_integer() {
|
// let register_index = argument_range_iter
|
||||||
if min.is_none() {
|
// .next()
|
||||||
min = Some(integer);
|
// .unwrap_or_else(|| panic!("No argument was passed to \"random_int\""));
|
||||||
} else {
|
// let value_option = data.open_register_allow_empty_unchecked(register_index);
|
||||||
break (min, integer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let random_integer = rand::thread_rng().gen_range(min.unwrap()..max);
|
// if let Some(argument) = value_option {
|
||||||
|
// if let Some(integer) = argument.as_integer() {
|
||||||
|
// if min.is_none() {
|
||||||
|
// min = Some(integer);
|
||||||
|
// } else {
|
||||||
|
// break (min, integer);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
|
||||||
data.set_register(destination, Register::Value(Value::integer(random_integer)));
|
// let random_integer = rand::thread_rng().gen_range(min.unwrap()..max);
|
||||||
|
|
||||||
data.next_action = get_next_action(data);
|
// data.set_register(destination, Register::Value(Value::integer(random_integer)));
|
||||||
|
|
||||||
false
|
// data.next_action = get_next_action(data);
|
||||||
|
|
||||||
|
// false
|
||||||
}
|
}
|
||||||
|
@ -1,18 +1,20 @@
|
|||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ConcreteValue, Value,
|
Value,
|
||||||
vm::{Register, ThreadData, get_next_action},
|
vm::{Register, ThreadData},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn to_string(data: &mut ThreadData, destination: u16, argument_range: Range<u16>) -> bool {
|
pub fn to_string(data: &mut ThreadData, destination: u16, argument_range: Range<u16>) -> bool {
|
||||||
let argument_value = data.open_register_unchecked(argument_range.start);
|
todo!()
|
||||||
let argument_string = argument_value.display(data);
|
|
||||||
let register = Register::Value(Value::Concrete(ConcreteValue::string(argument_string)));
|
|
||||||
|
|
||||||
data.set_register(destination, register);
|
// let argument_value = data.open_register_unchecked(argument_range.start);
|
||||||
|
// let argument_string = argument_value.display(data);
|
||||||
|
// let register = Register::Value(Value::Concrete(ConcreteValue::string(argument_string)));
|
||||||
|
|
||||||
data.next_action = get_next_action(data);
|
// data.set_register(destination, register);
|
||||||
|
|
||||||
false
|
// data.next_action = get_next_action(data);
|
||||||
|
|
||||||
|
// false
|
||||||
}
|
}
|
||||||
|
@ -7,58 +7,62 @@ use tracing::{Level, info, span};
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
DustString,
|
DustString,
|
||||||
vm::{Thread, ThreadData, get_next_action},
|
vm::{Thread, ThreadData},
|
||||||
};
|
};
|
||||||
|
|
||||||
fn start_thread(data: &mut ThreadData, argument_range: Range<u16>) -> JoinHandle<()> {
|
fn start_thread(data: &mut ThreadData, argument_range: Range<u16>) -> JoinHandle<()> {
|
||||||
let mut argument_range_iter = argument_range.into_iter();
|
todo!()
|
||||||
let function_argument = {
|
|
||||||
loop {
|
|
||||||
let register_index = argument_range_iter
|
|
||||||
.next()
|
|
||||||
.unwrap_or_else(|| panic!("No argument was passed to \"spawn\""));
|
|
||||||
let value_option = data.open_register_allow_empty_unchecked(register_index);
|
|
||||||
|
|
||||||
if let Some(argument) = value_option {
|
// let mut argument_range_iter = argument_range.into_iter();
|
||||||
break argument;
|
// let function_argument = {
|
||||||
}
|
// loop {
|
||||||
}
|
// let register_index = argument_range_iter
|
||||||
};
|
// .next()
|
||||||
let function = function_argument.as_function().unwrap();
|
// .unwrap_or_else(|| panic!("No argument was passed to \"spawn\""));
|
||||||
let prototype_index = function.prototype_index as usize;
|
// let value_option = data.open_register_allow_empty_unchecked(register_index);
|
||||||
let current_call = data.call_stack.last_unchecked();
|
|
||||||
let prototype = current_call.chunk.prototypes[prototype_index].clone();
|
|
||||||
|
|
||||||
info!(
|
// if let Some(argument) = value_option {
|
||||||
"Spawning thread for \"{}\"",
|
// break argument;
|
||||||
function
|
// }
|
||||||
.name
|
// }
|
||||||
.as_ref()
|
// };
|
||||||
.cloned()
|
// let function = function_argument.as_function().unwrap();
|
||||||
.unwrap_or_else(|| DustString::from("anonymous"))
|
// let prototype_index = function.prototype_index as usize;
|
||||||
);
|
// let current_call = data.stack.last_unchecked();
|
||||||
|
// let prototype = current_call.chunk.prototypes[prototype_index].clone();
|
||||||
|
|
||||||
let thread_name = prototype
|
// info!(
|
||||||
.name
|
// "Spawning thread for \"{}\"",
|
||||||
.as_ref()
|
// function
|
||||||
.map(|name| name.to_string())
|
// .name
|
||||||
.unwrap_or_else(|| "anonymous".to_string());
|
// .as_ref()
|
||||||
let mut thread = Thread::new(prototype);
|
// .cloned()
|
||||||
|
// .unwrap_or_else(|| DustString::from("anonymous"))
|
||||||
|
// );
|
||||||
|
|
||||||
Builder::new()
|
// let thread_name = prototype
|
||||||
.name(thread_name)
|
// .name
|
||||||
.spawn(move || {
|
// .as_ref()
|
||||||
let span = span!(Level::INFO, "Spawned thread");
|
// .map(|name| name.to_string())
|
||||||
let _enter = span.enter();
|
// .unwrap_or_else(|| "anonymous".to_string());
|
||||||
|
// let mut thread = Thread::new(prototype);
|
||||||
|
|
||||||
thread.run();
|
// Builder::new()
|
||||||
})
|
// .name(thread_name)
|
||||||
.expect("Critical VM Error: Failed to spawn thread")
|
// .spawn(move || {
|
||||||
|
// let span = span!(Level::INFO, "Spawned thread");
|
||||||
|
// let _enter = span.enter();
|
||||||
|
|
||||||
|
// thread.run();
|
||||||
|
// })
|
||||||
|
// .expect("Critical VM Error: Failed to spawn thread")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn spawn(data: &mut ThreadData, _: u16, argument_range: Range<u16>) -> bool {
|
pub fn spawn(data: &mut ThreadData, _: u16, argument_range: Range<u16>) -> bool {
|
||||||
let _ = start_thread(data, argument_range);
|
todo!()
|
||||||
data.next_action = get_next_action(data);
|
|
||||||
|
|
||||||
false
|
// let _ = start_thread(data, argument_range);
|
||||||
|
// data.next_action = get_next_action(data);
|
||||||
|
|
||||||
|
// false
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
use std::fmt::{self, Display, Formatter};
|
use std::fmt::{self, Display, Formatter};
|
||||||
|
|
||||||
use crate::{vm::ThreadData, Pointer, Type};
|
use crate::{
|
||||||
|
Type,
|
||||||
|
vm::{Pointer, ThreadData},
|
||||||
|
};
|
||||||
|
|
||||||
use super::DustString;
|
use super::DustString;
|
||||||
|
|
||||||
@ -12,23 +15,7 @@ pub struct AbstractList {
|
|||||||
|
|
||||||
impl AbstractList {
|
impl AbstractList {
|
||||||
pub fn display(&self, data: &ThreadData) -> DustString {
|
pub fn display(&self, data: &ThreadData) -> DustString {
|
||||||
let mut display = DustString::new();
|
todo!()
|
||||||
|
|
||||||
display.push('[');
|
|
||||||
|
|
||||||
for (i, item) in self.item_pointers.iter().enumerate() {
|
|
||||||
if i > 0 {
|
|
||||||
display.push_str(", ");
|
|
||||||
}
|
|
||||||
|
|
||||||
let item_display = data.follow_pointer_unchecked(*item).display(data);
|
|
||||||
|
|
||||||
display.push_str(&item_display);
|
|
||||||
}
|
|
||||||
|
|
||||||
display.push(']');
|
|
||||||
|
|
||||||
display
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,341 +0,0 @@
|
|||||||
use std::fmt::{self, Display, Formatter};
|
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use smartstring::{LazyCompact, SmartString};
|
|
||||||
use tracing::trace;
|
|
||||||
|
|
||||||
use crate::{Type, Value, ValueError};
|
|
||||||
|
|
||||||
use super::RangeValue;
|
|
||||||
|
|
||||||
pub type DustString = SmartString<LazyCompact>;
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
|
|
||||||
pub enum ConcreteValue {
|
|
||||||
Boolean(bool),
|
|
||||||
Byte(u8),
|
|
||||||
Character(char),
|
|
||||||
Float(f64),
|
|
||||||
Integer(i64),
|
|
||||||
List(Vec<ConcreteValue>),
|
|
||||||
Range(RangeValue),
|
|
||||||
String(DustString),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ConcreteValue {
|
|
||||||
pub fn to_value(self) -> Value {
|
|
||||||
Value::Concrete(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn list<T: Into<Vec<ConcreteValue>>>(into_list: T) -> Self {
|
|
||||||
ConcreteValue::List(into_list.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn string<T: Into<SmartString<LazyCompact>>>(to_string: T) -> Self {
|
|
||||||
ConcreteValue::String(to_string.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn as_string(&self) -> Option<&DustString> {
|
|
||||||
if let ConcreteValue::String(string) = self {
|
|
||||||
Some(string)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn display(&self) -> DustString {
|
|
||||||
DustString::from(self.to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn r#type(&self) -> Type {
|
|
||||||
match self {
|
|
||||||
ConcreteValue::Boolean(_) => Type::Boolean,
|
|
||||||
ConcreteValue::Byte(_) => Type::Byte,
|
|
||||||
ConcreteValue::Character(_) => Type::Character,
|
|
||||||
ConcreteValue::Float(_) => Type::Float,
|
|
||||||
ConcreteValue::Integer(_) => Type::Integer,
|
|
||||||
ConcreteValue::List(list) => {
|
|
||||||
let item_type = list.first().map_or(Type::Any, |item| item.r#type());
|
|
||||||
|
|
||||||
Type::List(Box::new(item_type))
|
|
||||||
}
|
|
||||||
ConcreteValue::Range(range) => range.r#type(),
|
|
||||||
ConcreteValue::String(_) => Type::String,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add(&self, other: &Self) -> ConcreteValue {
|
|
||||||
use ConcreteValue::*;
|
|
||||||
|
|
||||||
match (self, other) {
|
|
||||||
(Byte(left), Byte(right)) => {
|
|
||||||
let sum = left.saturating_add(*right);
|
|
||||||
|
|
||||||
Byte(sum)
|
|
||||||
}
|
|
||||||
(Character(left), Character(right)) => {
|
|
||||||
let mut concatenated = DustString::new();
|
|
||||||
|
|
||||||
concatenated.push(*left);
|
|
||||||
concatenated.push(*right);
|
|
||||||
|
|
||||||
String(concatenated)
|
|
||||||
}
|
|
||||||
(Character(left), String(right)) => {
|
|
||||||
let mut concatenated = DustString::new();
|
|
||||||
|
|
||||||
concatenated.push(*left);
|
|
||||||
concatenated.push_str(right);
|
|
||||||
|
|
||||||
String(concatenated)
|
|
||||||
}
|
|
||||||
(Float(left), Float(right)) => {
|
|
||||||
let sum = left + right;
|
|
||||||
|
|
||||||
Float(sum)
|
|
||||||
}
|
|
||||||
(Integer(left), Integer(right)) => {
|
|
||||||
let sum = left.saturating_add(*right);
|
|
||||||
|
|
||||||
Integer(sum)
|
|
||||||
}
|
|
||||||
(String(left), Character(right)) => {
|
|
||||||
let concatenated = format!("{}{}", left, right);
|
|
||||||
|
|
||||||
String(DustString::from(concatenated))
|
|
||||||
}
|
|
||||||
(String(left), String(right)) => {
|
|
||||||
let concatenated = format!("{}{}", left, right);
|
|
||||||
|
|
||||||
String(DustString::from(concatenated))
|
|
||||||
}
|
|
||||||
_ => panic!(
|
|
||||||
"{}",
|
|
||||||
ValueError::CannotAdd(
|
|
||||||
Value::Concrete(self.clone()),
|
|
||||||
Value::Concrete(other.clone())
|
|
||||||
)
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn subtract(&self, other: &Self) -> ConcreteValue {
|
|
||||||
use ConcreteValue::*;
|
|
||||||
|
|
||||||
match (self, other) {
|
|
||||||
(Byte(left), Byte(right)) => ConcreteValue::Byte(left.saturating_sub(*right)),
|
|
||||||
(Float(left), Float(right)) => ConcreteValue::Float(left - right),
|
|
||||||
(Integer(left), Integer(right)) => ConcreteValue::Integer(left.saturating_sub(*right)),
|
|
||||||
_ => panic!(
|
|
||||||
"{}",
|
|
||||||
ValueError::CannotSubtract(
|
|
||||||
Value::Concrete(self.clone()),
|
|
||||||
Value::Concrete(other.clone())
|
|
||||||
)
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn multiply(&self, other: &Self) -> Result<ConcreteValue, ValueError> {
|
|
||||||
use ConcreteValue::*;
|
|
||||||
|
|
||||||
let product = match (self, other) {
|
|
||||||
(Byte(left), Byte(right)) => ConcreteValue::Byte(left.saturating_mul(*right)),
|
|
||||||
(Float(left), Float(right)) => ConcreteValue::Float(left * right),
|
|
||||||
(Integer(left), Integer(right)) => ConcreteValue::Integer(left.saturating_mul(*right)),
|
|
||||||
_ => {
|
|
||||||
return Err(ValueError::CannotMultiply(
|
|
||||||
self.clone().to_value(),
|
|
||||||
other.clone().to_value(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(product)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn divide(&self, other: &Self) -> Result<ConcreteValue, ValueError> {
|
|
||||||
use ConcreteValue::*;
|
|
||||||
|
|
||||||
let quotient = match (self, other) {
|
|
||||||
(Byte(left), Byte(right)) => ConcreteValue::Byte(left.saturating_div(*right)),
|
|
||||||
(Float(left), Float(right)) => ConcreteValue::Float(left / right),
|
|
||||||
(Integer(left), Integer(right)) => ConcreteValue::Integer(left.saturating_div(*right)),
|
|
||||||
_ => {
|
|
||||||
return Err(ValueError::CannotMultiply(
|
|
||||||
self.clone().to_value(),
|
|
||||||
other.clone().to_value(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(quotient)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn modulo(&self, other: &Self) -> Result<ConcreteValue, ValueError> {
|
|
||||||
use ConcreteValue::*;
|
|
||||||
|
|
||||||
let product = match (self, other) {
|
|
||||||
(Byte(left), Byte(right)) => ConcreteValue::Byte(left.wrapping_rem(*right)),
|
|
||||||
(Float(left), Float(right)) => ConcreteValue::Float(left % right),
|
|
||||||
(Integer(left), Integer(right)) => {
|
|
||||||
ConcreteValue::Integer(left.wrapping_rem_euclid(*right))
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
return Err(ValueError::CannotMultiply(
|
|
||||||
self.clone().to_value(),
|
|
||||||
other.clone().to_value(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(product)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn negate(&self) -> ConcreteValue {
|
|
||||||
use ConcreteValue::*;
|
|
||||||
|
|
||||||
match self {
|
|
||||||
Boolean(value) => ConcreteValue::Boolean(!value),
|
|
||||||
Byte(value) => ConcreteValue::Byte(value.wrapping_neg()),
|
|
||||||
Float(value) => ConcreteValue::Float(-value),
|
|
||||||
Integer(value) => ConcreteValue::Integer(value.wrapping_neg()),
|
|
||||||
_ => panic!("{}", ValueError::CannotNegate(self.clone().to_value())),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn not(&self) -> Result<ConcreteValue, ValueError> {
|
|
||||||
use ConcreteValue::*;
|
|
||||||
|
|
||||||
let not = match self {
|
|
||||||
Boolean(value) => ConcreteValue::Boolean(!value),
|
|
||||||
_ => return Err(ValueError::CannotNot(self.clone().to_value())),
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(not)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn equals(&self, other: &ConcreteValue) -> bool {
|
|
||||||
use ConcreteValue::*;
|
|
||||||
|
|
||||||
match (self, other) {
|
|
||||||
(Boolean(left), Boolean(right)) => left == right,
|
|
||||||
(Byte(left), Byte(right)) => left == right,
|
|
||||||
(Character(left), Character(right)) => left == right,
|
|
||||||
(Float(left), Float(right)) => left == right,
|
|
||||||
(Integer(left), Integer(right)) => left == right,
|
|
||||||
(List(left), List(right)) => left == right,
|
|
||||||
(Range(left), Range(right)) => left == right,
|
|
||||||
(String(left), String(right)) => left == right,
|
|
||||||
_ => {
|
|
||||||
panic!(
|
|
||||||
"{}",
|
|
||||||
ValueError::CannotCompare(
|
|
||||||
Value::Concrete(self.clone()),
|
|
||||||
Value::Concrete(other.clone())
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn less_than(&self, other: &ConcreteValue) -> Result<ConcreteValue, ValueError> {
|
|
||||||
use ConcreteValue::*;
|
|
||||||
|
|
||||||
let less_than = match (self, other) {
|
|
||||||
(Boolean(left), Boolean(right)) => ConcreteValue::Boolean(left < right),
|
|
||||||
(Byte(left), Byte(right)) => ConcreteValue::Boolean(left < right),
|
|
||||||
(Character(left), Character(right)) => ConcreteValue::Boolean(left < right),
|
|
||||||
(Float(left), Float(right)) => ConcreteValue::Boolean(left < right),
|
|
||||||
(Integer(left), Integer(right)) => ConcreteValue::Boolean(left < right),
|
|
||||||
(List(left), List(right)) => ConcreteValue::Boolean(left < right),
|
|
||||||
(Range(left), Range(right)) => ConcreteValue::Boolean(left < right),
|
|
||||||
(String(left), String(right)) => ConcreteValue::Boolean(left < right),
|
|
||||||
_ => {
|
|
||||||
return Err(ValueError::CannotCompare(
|
|
||||||
self.clone().to_value(),
|
|
||||||
other.clone().to_value(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(less_than)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn less_than_or_equals(&self, other: &ConcreteValue) -> Result<ConcreteValue, ValueError> {
|
|
||||||
use ConcreteValue::*;
|
|
||||||
|
|
||||||
let less_than_or_equal = match (self, other) {
|
|
||||||
(Boolean(left), Boolean(right)) => ConcreteValue::Boolean(left <= right),
|
|
||||||
(Byte(left), Byte(right)) => ConcreteValue::Boolean(left <= right),
|
|
||||||
(Character(left), Character(right)) => ConcreteValue::Boolean(left <= right),
|
|
||||||
(Float(left), Float(right)) => ConcreteValue::Boolean(left <= right),
|
|
||||||
(Integer(left), Integer(right)) => ConcreteValue::Boolean(left <= right),
|
|
||||||
(List(left), List(right)) => ConcreteValue::Boolean(left <= right),
|
|
||||||
(Range(left), Range(right)) => ConcreteValue::Boolean(left <= right),
|
|
||||||
(String(left), String(right)) => ConcreteValue::Boolean(left <= right),
|
|
||||||
_ => {
|
|
||||||
return Err(ValueError::CannotCompare(
|
|
||||||
self.clone().to_value(),
|
|
||||||
other.clone().to_value(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(less_than_or_equal)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Clone for ConcreteValue {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
trace!("Cloning concrete value {}", self);
|
|
||||||
|
|
||||||
match self {
|
|
||||||
ConcreteValue::Boolean(boolean) => ConcreteValue::Boolean(*boolean),
|
|
||||||
ConcreteValue::Byte(byte) => ConcreteValue::Byte(*byte),
|
|
||||||
ConcreteValue::Character(character) => ConcreteValue::Character(*character),
|
|
||||||
ConcreteValue::Float(float) => ConcreteValue::Float(*float),
|
|
||||||
ConcreteValue::Integer(integer) => ConcreteValue::Integer(*integer),
|
|
||||||
ConcreteValue::List(list) => ConcreteValue::List(list.clone()),
|
|
||||||
ConcreteValue::Range(range) => ConcreteValue::Range(*range),
|
|
||||||
ConcreteValue::String(string) => ConcreteValue::String(string.clone()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for ConcreteValue {
|
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
ConcreteValue::Boolean(boolean) => write!(f, "{boolean}"),
|
|
||||||
ConcreteValue::Byte(byte) => write!(f, "0x{byte:02x}"),
|
|
||||||
ConcreteValue::Character(character) => write!(f, "{character}"),
|
|
||||||
ConcreteValue::Float(float) => {
|
|
||||||
write!(f, "{float}")?;
|
|
||||||
|
|
||||||
if float.fract() == 0.0 {
|
|
||||||
write!(f, ".0")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
ConcreteValue::Integer(integer) => write!(f, "{integer}"),
|
|
||||||
ConcreteValue::List(list) => {
|
|
||||||
write!(f, "[")?;
|
|
||||||
|
|
||||||
for (index, item) in list.iter().enumerate() {
|
|
||||||
if index > 0 {
|
|
||||||
write!(f, ", ")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
write!(f, "{item}")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
write!(f, "]")
|
|
||||||
}
|
|
||||||
ConcreteValue::Range(range_value) => {
|
|
||||||
write!(f, "{range_value}")
|
|
||||||
}
|
|
||||||
ConcreteValue::String(string) => write!(f, "{string}"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,57 +1,40 @@
|
|||||||
//! Runtime values used by the VM.
|
//! Runtime values used by the VM.
|
||||||
mod abstract_list;
|
mod abstract_list;
|
||||||
mod concrete_value;
|
|
||||||
mod function;
|
mod function;
|
||||||
mod range_value;
|
mod range_value;
|
||||||
|
|
||||||
pub use abstract_list::AbstractList;
|
pub use abstract_list::AbstractList;
|
||||||
pub use concrete_value::{ConcreteValue, DustString};
|
|
||||||
pub use function::Function;
|
pub use function::Function;
|
||||||
pub use range_value::RangeValue;
|
pub use range_value::RangeValue;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use smartstring::{LazyCompact, SmartString};
|
||||||
|
|
||||||
use std::fmt::{self, Debug, Display, Formatter};
|
use std::fmt::{self, Debug, Display, Formatter};
|
||||||
|
|
||||||
use crate::{Type, vm::ThreadData};
|
use crate::{Type, vm::ThreadData};
|
||||||
|
|
||||||
|
pub type DustString = SmartString<LazyCompact>;
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
|
||||||
pub enum Value {
|
pub enum Value {
|
||||||
Concrete(ConcreteValue),
|
Boolean(bool),
|
||||||
|
Byte(u8),
|
||||||
|
Character(char),
|
||||||
|
Float(f64),
|
||||||
|
Integer(i64),
|
||||||
|
String(DustString),
|
||||||
|
|
||||||
|
List(Vec<Value>),
|
||||||
|
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
AbstractList(AbstractList),
|
AbstractList(AbstractList),
|
||||||
|
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
Function(Function),
|
Function(Function),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Value {
|
impl Value {
|
||||||
pub fn boolean(boolean: bool) -> Self {
|
|
||||||
Value::Concrete(ConcreteValue::Boolean(boolean))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn byte(byte: u8) -> Self {
|
|
||||||
Value::Concrete(ConcreteValue::Byte(byte))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn character(character: char) -> Self {
|
|
||||||
Value::Concrete(ConcreteValue::Character(character))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn float(float: f64) -> Self {
|
|
||||||
Value::Concrete(ConcreteValue::Float(float))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn integer(integer: i64) -> Self {
|
|
||||||
Value::Concrete(ConcreteValue::Integer(integer))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn string(string: impl Into<DustString>) -> Self {
|
|
||||||
Value::Concrete(ConcreteValue::String(string.into()))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn as_boolean(&self) -> Option<bool> {
|
pub fn as_boolean(&self) -> Option<bool> {
|
||||||
if let Value::Concrete(ConcreteValue::Boolean(boolean)) = self {
|
if let Value::Boolean(boolean) = self {
|
||||||
Some(*boolean)
|
Some(*boolean)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
@ -59,7 +42,7 @@ impl Value {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_byte(&self) -> Option<u8> {
|
pub fn as_byte(&self) -> Option<u8> {
|
||||||
if let Value::Concrete(ConcreteValue::Byte(byte)) = self {
|
if let Value::Byte(byte) = self {
|
||||||
Some(*byte)
|
Some(*byte)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
@ -67,7 +50,7 @@ impl Value {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_character(&self) -> Option<char> {
|
pub fn as_character(&self) -> Option<char> {
|
||||||
if let Value::Concrete(ConcreteValue::Character(character)) = self {
|
if let Value::Character(character) = self {
|
||||||
Some(*character)
|
Some(*character)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
@ -75,7 +58,7 @@ impl Value {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_float(&self) -> Option<f64> {
|
pub fn as_float(&self) -> Option<f64> {
|
||||||
if let Value::Concrete(ConcreteValue::Float(float)) = self {
|
if let Value::Float(float) = self {
|
||||||
Some(*float)
|
Some(*float)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
@ -91,7 +74,7 @@ impl Value {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_integer(&self) -> Option<i64> {
|
pub fn as_integer(&self) -> Option<i64> {
|
||||||
if let Value::Concrete(ConcreteValue::Integer(integer)) = self {
|
if let Value::Integer(integer) = self {
|
||||||
Some(*integer)
|
Some(*integer)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
@ -99,15 +82,15 @@ impl Value {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_string(&self) -> Option<&DustString> {
|
pub fn as_string(&self) -> Option<&DustString> {
|
||||||
if let Value::Concrete(ConcreteValue::String(value)) = self {
|
if let Value::String(string) = self {
|
||||||
Some(value)
|
Some(string)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_string(&self) -> bool {
|
pub fn is_string(&self) -> bool {
|
||||||
matches!(self, Value::Concrete(ConcreteValue::String(_)))
|
matches!(self, Value::String(_))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_function(&self) -> bool {
|
pub fn is_function(&self) -> bool {
|
||||||
@ -116,7 +99,13 @@ impl Value {
|
|||||||
|
|
||||||
pub fn r#type(&self) -> Type {
|
pub fn r#type(&self) -> Type {
|
||||||
match self {
|
match self {
|
||||||
Value::Concrete(concrete_value) => concrete_value.r#type(),
|
Value::Boolean(_) => Type::Boolean,
|
||||||
|
Value::Byte(_) => Type::Byte,
|
||||||
|
Value::Character(_) => Type::Character,
|
||||||
|
Value::Float(_) => Type::Float,
|
||||||
|
Value::Integer(_) => Type::Integer,
|
||||||
|
Value::String(_) => Type::String,
|
||||||
|
Value::List(_) => Type::List(Box::new(Type::Any)),
|
||||||
Value::AbstractList(AbstractList { item_type, .. }) => {
|
Value::AbstractList(AbstractList { item_type, .. }) => {
|
||||||
Type::List(Box::new(item_type.clone()))
|
Type::List(Box::new(item_type.clone()))
|
||||||
}
|
}
|
||||||
@ -124,105 +113,32 @@ impl Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add(&self, other: &Value) -> Value {
|
|
||||||
let sum = match (self, other) {
|
|
||||||
(Value::Concrete(left), Value::Concrete(right)) => left.add(right),
|
|
||||||
_ => panic!("{}", ValueError::CannotAdd(self.clone(), other.clone())),
|
|
||||||
};
|
|
||||||
|
|
||||||
Value::Concrete(sum)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn subtract(&self, other: &Value) -> Value {
|
|
||||||
let difference = match (self, other) {
|
|
||||||
(Value::Concrete(left), Value::Concrete(right)) => left.subtract(right),
|
|
||||||
_ => panic!(
|
|
||||||
"{}",
|
|
||||||
ValueError::CannotSubtract(self.clone(), other.clone())
|
|
||||||
),
|
|
||||||
};
|
|
||||||
|
|
||||||
Value::Concrete(difference)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn multiply(&self, other: &Value) -> Result<Value, ValueError> {
|
|
||||||
match (self, other) {
|
|
||||||
(Value::Concrete(left), Value::Concrete(right)) => {
|
|
||||||
left.multiply(right).map(Value::Concrete)
|
|
||||||
}
|
|
||||||
_ => Err(ValueError::CannotMultiply(
|
|
||||||
self.to_owned(),
|
|
||||||
other.to_owned(),
|
|
||||||
)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn divide(&self, other: &Value) -> Result<Value, ValueError> {
|
|
||||||
match (self, other) {
|
|
||||||
(Value::Concrete(left), Value::Concrete(right)) => {
|
|
||||||
left.divide(right).map(Value::Concrete)
|
|
||||||
}
|
|
||||||
_ => Err(ValueError::CannotDivide(self.to_owned(), other.to_owned())),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn modulo(&self, other: &Value) -> Result<Value, ValueError> {
|
|
||||||
match (self, other) {
|
|
||||||
(Value::Concrete(left), Value::Concrete(right)) => {
|
|
||||||
left.modulo(right).map(Value::Concrete)
|
|
||||||
}
|
|
||||||
_ => Err(ValueError::CannotModulo(self.to_owned(), other.to_owned())),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn negate(&self) -> Value {
|
|
||||||
let concrete = match self {
|
|
||||||
Value::Concrete(concrete_value) => concrete_value.negate(),
|
|
||||||
_ => panic!("{}", ValueError::CannotNegate(self.clone())),
|
|
||||||
};
|
|
||||||
|
|
||||||
Value::Concrete(concrete)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn not(&self) -> Result<Value, ValueError> {
|
|
||||||
match self {
|
|
||||||
Value::Concrete(concrete_value) => concrete_value.not().map(Value::Concrete),
|
|
||||||
_ => Err(ValueError::CannotNot(self.to_owned())),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn equals(&self, other: &Value) -> bool {
|
|
||||||
match (self, other) {
|
|
||||||
(Value::Concrete(left), Value::Concrete(right)) => left.equals(right),
|
|
||||||
_ => panic!(
|
|
||||||
"{}",
|
|
||||||
ValueError::CannotCompare(self.to_owned(), other.to_owned())
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn less(&self, other: &Value) -> Result<Value, ValueError> {
|
|
||||||
match (self, other) {
|
|
||||||
(Value::Concrete(left), Value::Concrete(right)) => {
|
|
||||||
left.less_than(right).map(Value::Concrete)
|
|
||||||
}
|
|
||||||
_ => Err(ValueError::CannotCompare(self.to_owned(), other.to_owned())),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn less_than_or_equals(&self, other: &Value) -> Result<Value, ValueError> {
|
|
||||||
match (self, other) {
|
|
||||||
(Value::Concrete(left), Value::Concrete(right)) => {
|
|
||||||
left.less_than_or_equals(right).map(Value::Concrete)
|
|
||||||
}
|
|
||||||
_ => Err(ValueError::CannotCompare(self.to_owned(), other.to_owned())),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn display(&self, data: &ThreadData) -> DustString {
|
pub fn display(&self, data: &ThreadData) -> DustString {
|
||||||
match self {
|
match self {
|
||||||
|
Value::Boolean(boolean) => DustString::from(boolean.to_string()),
|
||||||
|
Value::Byte(byte) => DustString::from(byte.to_string()),
|
||||||
|
Value::Character(character) => DustString::from(character.to_string()),
|
||||||
|
Value::Float(float) => DustString::from(float.to_string()),
|
||||||
|
Value::Integer(integer) => DustString::from(integer.to_string()),
|
||||||
|
Value::List(list) => {
|
||||||
|
let mut display = DustString::new();
|
||||||
|
|
||||||
|
display.push_str("[");
|
||||||
|
|
||||||
|
for (index, value) in list.iter().enumerate() {
|
||||||
|
if index > 0 {
|
||||||
|
display.push_str(", ");
|
||||||
|
}
|
||||||
|
|
||||||
|
display.push_str(&value.display(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
display.push_str("]");
|
||||||
|
|
||||||
|
display
|
||||||
|
}
|
||||||
|
Value::String(string) => string.clone(),
|
||||||
Value::AbstractList(list) => list.display(data),
|
Value::AbstractList(list) => list.display(data),
|
||||||
Value::Concrete(concrete_value) => concrete_value.display(),
|
|
||||||
Value::Function(function) => DustString::from(function.to_string()),
|
Value::Function(function) => DustString::from(function.to_string()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -231,60 +147,27 @@ impl Value {
|
|||||||
impl Display for Value {
|
impl Display for Value {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Value::Concrete(concrete_value) => write!(f, "{concrete_value}"),
|
Value::Boolean(boolean) => write!(f, "{boolean}"),
|
||||||
|
Value::Byte(byte) => write!(f, "{byte}"),
|
||||||
|
Value::Character(character) => write!(f, "{character}"),
|
||||||
|
Value::Float(float) => write!(f, "{float}"),
|
||||||
|
Value::Integer(integer) => write!(f, "{integer}"),
|
||||||
|
Value::String(string) => write!(f, "{string}"),
|
||||||
|
Value::List(list) => {
|
||||||
|
write!(f, "[")?;
|
||||||
|
|
||||||
|
for (index, value) in list.iter().enumerate() {
|
||||||
|
if index > 0 {
|
||||||
|
write!(f, ", ")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
write!(f, "{}", value)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
write!(f, "]")
|
||||||
|
}
|
||||||
Value::AbstractList(list) => write!(f, "{list}"),
|
Value::AbstractList(list) => write!(f, "{list}"),
|
||||||
Value::Function(function) => write!(f, "{function}"),
|
Value::Function(function) => write!(f, "{function}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
|
||||||
pub enum ValueError {
|
|
||||||
CannotAdd(Value, Value),
|
|
||||||
CannotAnd(Value, Value),
|
|
||||||
CannotCompare(Value, Value),
|
|
||||||
CannotDivide(Value, Value),
|
|
||||||
CannotModulo(Value, Value),
|
|
||||||
CannotMultiply(Value, Value),
|
|
||||||
CannotNegate(Value),
|
|
||||||
CannotNot(Value),
|
|
||||||
CannotSubtract(Value, Value),
|
|
||||||
CannotOr(Value, Value),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for ValueError {
|
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
ValueError::CannotAdd(left, right) => {
|
|
||||||
write!(f, "Cannot add {left} and {right}")
|
|
||||||
}
|
|
||||||
ValueError::CannotAnd(left, right) => {
|
|
||||||
write!(f, "Cannot use logical AND operation on {left} and {right}")
|
|
||||||
}
|
|
||||||
ValueError::CannotCompare(left, right) => {
|
|
||||||
write!(f, "Cannot compare {left} and {right}")
|
|
||||||
}
|
|
||||||
ValueError::CannotDivide(left, right) => {
|
|
||||||
write!(f, "Cannot divide {left} by {right}")
|
|
||||||
}
|
|
||||||
ValueError::CannotModulo(left, right) => {
|
|
||||||
write!(f, "Cannot use modulo operation on {left} and {right}")
|
|
||||||
}
|
|
||||||
ValueError::CannotMultiply(left, right) => {
|
|
||||||
write!(f, "Cannot multiply {left} by {right}")
|
|
||||||
}
|
|
||||||
ValueError::CannotNegate(value) => {
|
|
||||||
write!(f, "Cannot negate {value}")
|
|
||||||
}
|
|
||||||
ValueError::CannotNot(value) => {
|
|
||||||
write!(f, "Cannot use logical NOT operation on {value}")
|
|
||||||
}
|
|
||||||
ValueError::CannotSubtract(left, right) => {
|
|
||||||
write!(f, "Cannot subtract {right} from {left}")
|
|
||||||
}
|
|
||||||
ValueError::CannotOr(left, right) => {
|
|
||||||
write!(f, "Cannot use logical OR operation on {left} and {right}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
254
dust-lang/src/vm/action.rs
Normal file
254
dust-lang/src/vm/action.rs
Normal file
@ -0,0 +1,254 @@
|
|||||||
|
use tracing::trace;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
AbstractList, Instruction, Operation, Type, Value,
|
||||||
|
instruction::{InstructionBuilder, LoadBoolean, TypeCode},
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::{Pointer, Register, thread::ThreadData};
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
|
pub struct Action {
|
||||||
|
pub logic: ActionLogic,
|
||||||
|
pub fields: InstructionBuilder,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&Instruction> for Action {
|
||||||
|
fn from(instruction: &Instruction) -> Self {
|
||||||
|
let logic = match instruction.operation() {
|
||||||
|
Operation::POINT => point,
|
||||||
|
Operation::CLOSE => close,
|
||||||
|
Operation::LOAD_BOOLEAN => load_boolean,
|
||||||
|
Operation::LOAD_CONSTANT => load_constant,
|
||||||
|
Operation::LOAD_LIST => load_list,
|
||||||
|
Operation::LOAD_FUNCTION => load_function,
|
||||||
|
Operation::LOAD_SELF => load_self,
|
||||||
|
Operation::GET_LOCAL => get_local,
|
||||||
|
Operation::SET_LOCAL => set_local,
|
||||||
|
Operation::ADD => add,
|
||||||
|
Operation::JUMP => jump,
|
||||||
|
Operation::RETURN => r#return,
|
||||||
|
unknown => unknown.panic_from_unknown_code(),
|
||||||
|
};
|
||||||
|
let fields = InstructionBuilder {
|
||||||
|
operation: instruction.operation(),
|
||||||
|
b_is_constant: instruction.b_is_constant(),
|
||||||
|
c_is_constant: instruction.c_is_constant(),
|
||||||
|
d_field: instruction.d_field(),
|
||||||
|
b_type: instruction.b_type(),
|
||||||
|
c_type: instruction.c_type(),
|
||||||
|
a_field: instruction.a_field(),
|
||||||
|
b_field: instruction.b_field(),
|
||||||
|
c_field: instruction.c_field(),
|
||||||
|
};
|
||||||
|
|
||||||
|
Action { logic, fields }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type ActionLogic = fn(InstructionBuilder, &mut ThreadData);
|
||||||
|
|
||||||
|
pub fn point(fields: InstructionBuilder, data: &mut ThreadData) {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn close(fields: InstructionBuilder, data: &mut ThreadData) {
|
||||||
|
let from = fields.b_field;
|
||||||
|
let to = fields.c_field;
|
||||||
|
let r#type = fields.b_type;
|
||||||
|
let current_frame = data.current_frame_mut();
|
||||||
|
|
||||||
|
match r#type {
|
||||||
|
TypeCode::INTEGER => {
|
||||||
|
let registers = current_frame.registers.get_many_integer_mut(from, to);
|
||||||
|
|
||||||
|
for register in registers {
|
||||||
|
*register = Register::Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => todo!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load_boolean(fields: InstructionBuilder, data: &mut ThreadData) {
|
||||||
|
let destination = fields.a_field;
|
||||||
|
let boolean = fields.b_field != 0;
|
||||||
|
let new_register = Register::Value(boolean);
|
||||||
|
let current_frame = data.current_frame_mut();
|
||||||
|
let old_register = current_frame.registers.get_boolean_mut(destination);
|
||||||
|
|
||||||
|
*old_register = new_register;
|
||||||
|
|
||||||
|
if fields.c_field != 0 {
|
||||||
|
current_frame.instruction_pointer += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load_constant(fields: InstructionBuilder, data: &mut ThreadData) {
|
||||||
|
let destination = fields.a_field;
|
||||||
|
let constant_index = fields.b_field;
|
||||||
|
let r#type = fields.b_type;
|
||||||
|
let current_frame = data.current_frame_mut();
|
||||||
|
|
||||||
|
match r#type {
|
||||||
|
TypeCode::INTEGER => {
|
||||||
|
let value = current_frame
|
||||||
|
.prototype
|
||||||
|
.constants
|
||||||
|
.get_integer(constant_index)
|
||||||
|
.unwrap();
|
||||||
|
let new_register = Register::Value(value);
|
||||||
|
let old_register = current_frame.registers.get_integer_mut(destination);
|
||||||
|
|
||||||
|
*old_register = new_register;
|
||||||
|
}
|
||||||
|
unknown => todo!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
if fields.c_field != 0 {
|
||||||
|
current_frame.instruction_pointer += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load_list(fields: InstructionBuilder, data: &mut ThreadData) {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load_function(fields: InstructionBuilder, data: &mut ThreadData) {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load_self(fields: InstructionBuilder, data: &mut ThreadData) {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_local(fields: InstructionBuilder, data: &mut ThreadData) {
|
||||||
|
let destination = fields.a_field;
|
||||||
|
let local_index = fields.b_field;
|
||||||
|
let r#type = fields.b_type;
|
||||||
|
let current_frame = data.current_frame_mut();
|
||||||
|
|
||||||
|
match r#type {
|
||||||
|
TypeCode::INTEGER => {
|
||||||
|
let new_register = Register::Pointer::<i64>(Pointer::ConstantInteger(local_index));
|
||||||
|
let old_register = current_frame.registers.get_integer_mut(destination);
|
||||||
|
|
||||||
|
*old_register = new_register;
|
||||||
|
}
|
||||||
|
unknown => todo!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_local(fields: InstructionBuilder, data: &mut ThreadData) {
|
||||||
|
let register_index = fields.b_field;
|
||||||
|
let local_index = fields.c_field;
|
||||||
|
let r#type = fields.b_type;
|
||||||
|
let current_frame = data.current_frame_mut();
|
||||||
|
|
||||||
|
match r#type {
|
||||||
|
TypeCode::INTEGER => {
|
||||||
|
let new_register = Register::Pointer::<i64>(Pointer::ConstantInteger(local_index));
|
||||||
|
let old_register = current_frame.registers.get_integer_mut(register_index);
|
||||||
|
|
||||||
|
*old_register = new_register;
|
||||||
|
}
|
||||||
|
unknown => todo!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add(fields: InstructionBuilder, data: &mut ThreadData) {
|
||||||
|
let destination = fields.a_field;
|
||||||
|
let left_index = fields.b_field;
|
||||||
|
let left_is_constant = fields.b_is_constant;
|
||||||
|
let right_index = fields.c_field;
|
||||||
|
let right_is_constant = fields.c_is_constant;
|
||||||
|
let left_type = fields.b_type;
|
||||||
|
let right_type = fields.c_type;
|
||||||
|
let current_frame = data.current_frame_mut();
|
||||||
|
|
||||||
|
match (left_type, right_type) {
|
||||||
|
(TypeCode::INTEGER, TypeCode::INTEGER) => {
|
||||||
|
let left = if left_is_constant {
|
||||||
|
current_frame
|
||||||
|
.prototype
|
||||||
|
.constants
|
||||||
|
.get_integer(left_index)
|
||||||
|
.unwrap()
|
||||||
|
} else {
|
||||||
|
*current_frame
|
||||||
|
.registers
|
||||||
|
.get_integer(left_index)
|
||||||
|
.expect_value()
|
||||||
|
};
|
||||||
|
let right = if right_is_constant {
|
||||||
|
current_frame
|
||||||
|
.prototype
|
||||||
|
.constants
|
||||||
|
.get_integer(right_index)
|
||||||
|
.unwrap()
|
||||||
|
} else {
|
||||||
|
*current_frame
|
||||||
|
.registers
|
||||||
|
.get_integer(right_index)
|
||||||
|
.expect_value()
|
||||||
|
};
|
||||||
|
let new_register = Register::Value(left + right);
|
||||||
|
let old_register = current_frame.registers.get_integer_mut(destination);
|
||||||
|
|
||||||
|
*old_register = new_register;
|
||||||
|
}
|
||||||
|
unknown => todo!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn jump(fields: InstructionBuilder, data: &mut ThreadData) {
|
||||||
|
let offset = fields.b_field as usize;
|
||||||
|
let is_positive = fields.c_field != 0;
|
||||||
|
let current_frame = data.current_frame_mut();
|
||||||
|
|
||||||
|
if is_positive {
|
||||||
|
current_frame.instruction_pointer += offset;
|
||||||
|
} else {
|
||||||
|
current_frame.instruction_pointer -= offset + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn r#return(fields: InstructionBuilder, data: &mut ThreadData) {
|
||||||
|
trace!("Returning. Stack size = {}", data.stack.len());
|
||||||
|
|
||||||
|
let should_return_value = fields.b_field != 0;
|
||||||
|
let r#type = fields.b_type;
|
||||||
|
let return_register = fields.c_field;
|
||||||
|
let current_frame = data.stack.pop().unwrap();
|
||||||
|
|
||||||
|
match r#type {
|
||||||
|
TypeCode::INTEGER => {
|
||||||
|
if data.stack.is_empty() {
|
||||||
|
if should_return_value {
|
||||||
|
let return_value = current_frame
|
||||||
|
.registers
|
||||||
|
.get_integer(return_register)
|
||||||
|
.expect_value();
|
||||||
|
|
||||||
|
data.return_value = Some(Some(Value::Integer(*return_value)));
|
||||||
|
} else {
|
||||||
|
data.return_value = Some(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if should_return_value {
|
||||||
|
let return_value = current_frame
|
||||||
|
.registers
|
||||||
|
.get_integer(return_register)
|
||||||
|
.expect_value();
|
||||||
|
let outer_frame = data.current_frame_mut();
|
||||||
|
let register = outer_frame.registers.get_integer_mut(return_register);
|
||||||
|
|
||||||
|
*register = Register::Value(*return_value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => todo!(),
|
||||||
|
}
|
||||||
|
}
|
41
dust-lang/src/vm/call_frame.rs
Normal file
41
dust-lang/src/vm/call_frame.rs
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
use std::{
|
||||||
|
fmt::{self, Debug, Display, Formatter},
|
||||||
|
sync::Arc,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{Chunk, DustString};
|
||||||
|
|
||||||
|
use super::register_table::RegisterTable;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct CallFrame {
|
||||||
|
pub prototype: Arc<Chunk>,
|
||||||
|
pub registers: RegisterTable,
|
||||||
|
pub return_register: u16,
|
||||||
|
pub instruction_pointer: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CallFrame {
|
||||||
|
pub fn new(prototype: Arc<Chunk>, return_register: u16) -> Self {
|
||||||
|
Self {
|
||||||
|
prototype,
|
||||||
|
return_register,
|
||||||
|
instruction_pointer: 0,
|
||||||
|
registers: RegisterTable::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for CallFrame {
|
||||||
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"{} IP = {}",
|
||||||
|
self.prototype
|
||||||
|
.name
|
||||||
|
.as_ref()
|
||||||
|
.unwrap_or(&DustString::from("anonymous")),
|
||||||
|
self.instruction_pointer,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -1,44 +0,0 @@
|
|||||||
use std::{
|
|
||||||
fmt::{self, Debug, Display, Formatter},
|
|
||||||
sync::Arc,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{Chunk, DustString};
|
|
||||||
|
|
||||||
use super::Register;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct FunctionCall {
|
|
||||||
pub chunk: Arc<Chunk>,
|
|
||||||
pub ip: usize,
|
|
||||||
pub return_register: u16,
|
|
||||||
pub registers: Vec<Register>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FunctionCall {
|
|
||||||
pub fn new(chunk: Arc<Chunk>, return_register: u16) -> Self {
|
|
||||||
let register_count = chunk.register_count;
|
|
||||||
|
|
||||||
Self {
|
|
||||||
chunk,
|
|
||||||
ip: 0,
|
|
||||||
return_register,
|
|
||||||
registers: vec![Register::Empty; register_count],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for FunctionCall {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"FunctionCall: {} | IP: {} | Registers: {}",
|
|
||||||
self.chunk
|
|
||||||
.name
|
|
||||||
.as_ref()
|
|
||||||
.unwrap_or(&DustString::from("anonymous")),
|
|
||||||
self.ip,
|
|
||||||
self.registers.len()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,19 +1,16 @@
|
|||||||
//! Virtual machine and errors
|
//! Virtual machine and errors
|
||||||
mod function_call;
|
mod action;
|
||||||
mod run_action;
|
mod call_frame;
|
||||||
mod stack;
|
mod pointer;
|
||||||
|
mod register_table;
|
||||||
mod thread;
|
mod thread;
|
||||||
|
|
||||||
use std::{
|
use std::{sync::Arc, thread::Builder};
|
||||||
fmt::{self, Debug, Display, Formatter},
|
|
||||||
sync::Arc,
|
|
||||||
thread::Builder,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub use function_call::FunctionCall;
|
pub use action::Action;
|
||||||
pub use run_action::RunAction;
|
pub use call_frame::CallFrame;
|
||||||
pub(crate) use run_action::get_next_action;
|
pub use pointer::Pointer;
|
||||||
pub use stack::Stack;
|
pub use register_table::{Register, RegisterTable};
|
||||||
pub use thread::{Thread, ThreadData};
|
pub use thread::{Thread, ThreadData};
|
||||||
|
|
||||||
use crossbeam_channel::bounded;
|
use crossbeam_channel::bounded;
|
||||||
@ -62,39 +59,3 @@ impl Vm {
|
|||||||
rx.recv().unwrap_or(None)
|
rx.recv().unwrap_or(None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
|
||||||
pub enum Register {
|
|
||||||
Empty,
|
|
||||||
Value(Value),
|
|
||||||
Pointer(Pointer),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Register {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
Self::Empty => write!(f, "empty"),
|
|
||||||
Self::Value(value) => write!(f, "{}", value),
|
|
||||||
Self::Pointer(pointer) => write!(f, "{}", pointer),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)]
|
|
||||||
pub enum Pointer {
|
|
||||||
Register(u16),
|
|
||||||
Constant(u16),
|
|
||||||
Stack(usize, u16),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Pointer {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
Self::Register(index) => write!(f, "PR{}", index),
|
|
||||||
Self::Constant(index) => write!(f, "PC{}", index),
|
|
||||||
Self::Stack(call_index, register_index) => {
|
|
||||||
write!(f, "PS{}R{}", call_index, register_index)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
69
dust-lang/src/vm/pointer.rs
Normal file
69
dust-lang/src/vm/pointer.rs
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
use std::fmt::{self, Debug, Display, Formatter};
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, PartialEq, PartialOrd)]
|
||||||
|
pub enum Pointer {
|
||||||
|
ConstantBoolean(u16),
|
||||||
|
ConstantByte(u16),
|
||||||
|
ConstantCharacter(u16),
|
||||||
|
ConstantFloat(u16),
|
||||||
|
ConstantInteger(u16),
|
||||||
|
ConstantString(u16),
|
||||||
|
|
||||||
|
RegisterBoolean(u16),
|
||||||
|
RegisterByte(u16),
|
||||||
|
RegisterCharacter(u16),
|
||||||
|
RegisterFloat(u16),
|
||||||
|
RegisterInteger(u16),
|
||||||
|
RegisterString(u16),
|
||||||
|
|
||||||
|
ForeignConstantBoolean(u16, u16),
|
||||||
|
ForeignConstantByte(u16, u16),
|
||||||
|
ForeignConstantCharacter(u16, u16),
|
||||||
|
ForeignConstantFloat(u16, u16),
|
||||||
|
ForeignConstantInteger(u16, u16),
|
||||||
|
ForeignConstantString(u16, u16),
|
||||||
|
|
||||||
|
ForeignRegisterBoolean(u16, u16),
|
||||||
|
ForeignRegisterByte(u16, u16),
|
||||||
|
ForeignRegisterCharacter(u16, u16),
|
||||||
|
ForeignRegisterFloat(u16, u16),
|
||||||
|
ForeignRegisterInteger(u16, u16),
|
||||||
|
ForeignRegisterString(u16, u16),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for Pointer {
|
||||||
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
|
write!(f, "{self}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Pointer {
|
||||||
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Pointer::ConstantBoolean(index) => write!(f, "pc_bool({index})"),
|
||||||
|
Pointer::ConstantByte(index) => write!(f, "pc_byte({index})"),
|
||||||
|
Pointer::ConstantCharacter(index) => write!(f, "pc_char({index})"),
|
||||||
|
Pointer::ConstantFloat(index) => write!(f, "pc_float({index})"),
|
||||||
|
Pointer::ConstantInteger(index) => write!(f, "pc_int({index})"),
|
||||||
|
Pointer::ConstantString(index) => write!(f, "pc_str({index})"),
|
||||||
|
Pointer::RegisterBoolean(index) => write!(f, "pr_bool({index})"),
|
||||||
|
Pointer::RegisterByte(index) => write!(f, "pr_byte({index})"),
|
||||||
|
Pointer::RegisterCharacter(index) => write!(f, "pr_char({index})"),
|
||||||
|
Pointer::RegisterFloat(index) => write!(f, "przfloat({index})"),
|
||||||
|
Pointer::RegisterInteger(index) => write!(f, "pr_int({index})"),
|
||||||
|
Pointer::RegisterString(index) => write!(f, "pr_str({index})"),
|
||||||
|
Pointer::ForeignConstantBoolean(index, _) => write!(f, "pfc_bool({index})"),
|
||||||
|
Pointer::ForeignConstantByte(index, _) => write!(f, "pfc_byte({index})"),
|
||||||
|
Pointer::ForeignConstantCharacter(index, _) => write!(f, "pfc_char({index})"),
|
||||||
|
Pointer::ForeignConstantFloat(index, _) => write!(f, "pfc_float({index})"),
|
||||||
|
Pointer::ForeignConstantInteger(index, _) => write!(f, "pfc_int({index})"),
|
||||||
|
Pointer::ForeignConstantString(index, _) => write!(f, "pfc_str({index})"),
|
||||||
|
Pointer::ForeignRegisterBoolean(index, _) => write!(f, "pfr_bool({index})"),
|
||||||
|
Pointer::ForeignRegisterByte(index, _) => write!(f, "pfr_byte({index})"),
|
||||||
|
Pointer::ForeignRegisterCharacter(index, _) => write!(f, "pfr_char({index})"),
|
||||||
|
Pointer::ForeignRegisterFloat(index, _) => write!(f, "pfr_float({index})"),
|
||||||
|
Pointer::ForeignRegisterInteger(index, _) => write!(f, "pfr_int({index})"),
|
||||||
|
Pointer::ForeignRegisterString(index, _) => write!(f, "pfr_str({index})"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
185
dust-lang/src/vm/register_table.rs
Normal file
185
dust-lang/src/vm/register_table.rs
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
use smallvec::{SmallVec, smallvec};
|
||||||
|
use tracing::trace;
|
||||||
|
|
||||||
|
use crate::DustString;
|
||||||
|
|
||||||
|
use super::Pointer;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum Register<T: Clone> {
|
||||||
|
Empty,
|
||||||
|
Value(T),
|
||||||
|
Pointer(Pointer),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Clone> Register<T> {
|
||||||
|
pub fn expect_value(&self) -> &T {
|
||||||
|
if let Self::Value(value) = self {
|
||||||
|
value
|
||||||
|
} else {
|
||||||
|
panic!("Expected a value")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct RegisterTable {
|
||||||
|
booleans: SmallVec<[Register<bool>; 64]>,
|
||||||
|
bytes: SmallVec<[Register<u8>; 64]>,
|
||||||
|
characters: SmallVec<[Register<char>; 64]>,
|
||||||
|
floats: SmallVec<[Register<f64>; 64]>,
|
||||||
|
integers: SmallVec<[Register<i64>; 64]>,
|
||||||
|
strings: SmallVec<[Register<DustString>; 64]>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RegisterTable {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
booleans: smallvec![Register::Empty; 64],
|
||||||
|
bytes: smallvec![Register::Empty; 64],
|
||||||
|
characters: smallvec![Register::Empty; 64],
|
||||||
|
floats: smallvec![Register::Empty; 64],
|
||||||
|
integers: smallvec![Register::Empty; 64],
|
||||||
|
strings: smallvec![Register::Empty; 64],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_boolean(&self, index: u16) -> &Register<bool> {
|
||||||
|
let index = index as usize;
|
||||||
|
|
||||||
|
if cfg!(debug_assertions) {
|
||||||
|
self.booleans.get(index).unwrap()
|
||||||
|
} else {
|
||||||
|
unsafe { self.booleans.get(index).unwrap_unchecked() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_boolean_mut(&mut self, index: u16) -> &mut Register<bool> {
|
||||||
|
let index = index as usize;
|
||||||
|
|
||||||
|
if cfg!(debug_assertions) {
|
||||||
|
self.booleans.get_mut(index).unwrap()
|
||||||
|
} else {
|
||||||
|
unsafe { self.booleans.get_mut(index).unwrap_unchecked() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_byte(&self, index: u16) -> &Register<u8> {
|
||||||
|
let index = index as usize;
|
||||||
|
|
||||||
|
if cfg!(debug_assertions) {
|
||||||
|
self.bytes.get(index).unwrap()
|
||||||
|
} else {
|
||||||
|
unsafe { self.bytes.get(index).unwrap_unchecked() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_byte_mut(&mut self, index: u16) -> &mut Register<u8> {
|
||||||
|
let index = index as usize;
|
||||||
|
|
||||||
|
if cfg!(debug_assertions) {
|
||||||
|
self.bytes.get_mut(index).unwrap()
|
||||||
|
} else {
|
||||||
|
unsafe { self.bytes.get_mut(index).unwrap_unchecked() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_character(&self, index: u16) -> &Register<char> {
|
||||||
|
let index = index as usize;
|
||||||
|
|
||||||
|
if cfg!(debug_assertions) {
|
||||||
|
self.characters.get(index).unwrap()
|
||||||
|
} else {
|
||||||
|
unsafe { self.characters.get(index).unwrap_unchecked() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_character_mut(&mut self, index: u16) -> &mut Register<char> {
|
||||||
|
let index = index as usize;
|
||||||
|
|
||||||
|
if cfg!(debug_assertions) {
|
||||||
|
self.characters.get_mut(index).unwrap()
|
||||||
|
} else {
|
||||||
|
unsafe { self.characters.get_mut(index).unwrap_unchecked() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_float(&self, index: u16) -> &Register<f64> {
|
||||||
|
let index = index as usize;
|
||||||
|
|
||||||
|
if cfg!(debug_assertions) {
|
||||||
|
self.floats.get(index).unwrap()
|
||||||
|
} else {
|
||||||
|
unsafe { self.floats.get(index).unwrap_unchecked() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_float_mut(&mut self, index: u16) -> &mut Register<f64> {
|
||||||
|
let index = index as usize;
|
||||||
|
|
||||||
|
if cfg!(debug_assertions) {
|
||||||
|
self.floats.get_mut(index).unwrap()
|
||||||
|
} else {
|
||||||
|
unsafe { self.floats.get_mut(index).unwrap_unchecked() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_integer(&self, index: u16) -> &Register<i64> {
|
||||||
|
let index = index as usize;
|
||||||
|
|
||||||
|
if cfg!(debug_assertions) {
|
||||||
|
self.integers.get(index).unwrap()
|
||||||
|
} else {
|
||||||
|
unsafe { self.integers.get(index).unwrap_unchecked() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_integer_mut(&mut self, index: u16) -> &mut Register<i64> {
|
||||||
|
let index = index as usize;
|
||||||
|
|
||||||
|
if cfg!(debug_assertions) {
|
||||||
|
self.integers.get_mut(index).unwrap()
|
||||||
|
} else {
|
||||||
|
unsafe { self.integers.get_mut(index).unwrap_unchecked() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_integer(&mut self, index: u16, value: i64) {
|
||||||
|
trace!("Set R_INT_{index} to value {value}");
|
||||||
|
|
||||||
|
let index = index as usize;
|
||||||
|
|
||||||
|
self.integers[index] = Register::Value(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_many_integer_mut(&mut self, from: u16, to: u16) -> &mut [Register<i64>] {
|
||||||
|
let from = from as usize;
|
||||||
|
let to = to as usize;
|
||||||
|
|
||||||
|
if cfg!(debug_assertions) {
|
||||||
|
self.integers.get_many_mut([from..to]).unwrap()[0]
|
||||||
|
} else {
|
||||||
|
unsafe { self.integers.get_many_mut([from..to]).unwrap_unchecked()[0] }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_string(&self, index: u16) -> &Register<DustString> {
|
||||||
|
let index = index as usize;
|
||||||
|
|
||||||
|
if cfg!(debug_assertions) {
|
||||||
|
self.strings.get(index).unwrap()
|
||||||
|
} else {
|
||||||
|
unsafe { self.strings.get(index).unwrap_unchecked() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_string_mut(&mut self, index: u16) -> &mut Register<DustString> {
|
||||||
|
let index = index as usize;
|
||||||
|
|
||||||
|
if cfg!(debug_assertions) {
|
||||||
|
self.strings.get_mut(index).unwrap()
|
||||||
|
} else {
|
||||||
|
unsafe { self.strings.get_mut(index).unwrap_unchecked() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,893 +0,0 @@
|
|||||||
use tracing::trace;
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
AbstractList, ConcreteValue, Instruction, Operand, Type, Value,
|
|
||||||
instruction::{
|
|
||||||
Add, Call, CallNative, Close, Divide, Equal, GetLocal, Jump, Less, LessEqual, LoadBoolean,
|
|
||||||
LoadConstant, LoadFunction, LoadList, LoadSelf, Modulo, Multiply, Negate, Not, Point,
|
|
||||||
Return, SetLocal, Subtract, Test, TestSet, TypeCode,
|
|
||||||
},
|
|
||||||
vm::FunctionCall,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::{Pointer, Register, thread::ThreadData};
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
|
||||||
pub struct RunAction {
|
|
||||||
pub logic: RunnerLogic,
|
|
||||||
pub instruction: Instruction,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Instruction> for RunAction {
|
|
||||||
fn from(instruction: Instruction) -> Self {
|
|
||||||
let operation = instruction.operation();
|
|
||||||
let logic = RUNNER_LOGIC_TABLE[operation.0 as usize];
|
|
||||||
|
|
||||||
RunAction { logic, instruction }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type RunnerLogic = fn(Instruction, &mut ThreadData) -> bool;
|
|
||||||
|
|
||||||
pub const RUNNER_LOGIC_TABLE: [RunnerLogic; 25] = [
|
|
||||||
point,
|
|
||||||
close,
|
|
||||||
load_boolean,
|
|
||||||
load_constant,
|
|
||||||
load_function,
|
|
||||||
load_list,
|
|
||||||
load_self,
|
|
||||||
get_local,
|
|
||||||
set_local,
|
|
||||||
add,
|
|
||||||
subtract,
|
|
||||||
multiply,
|
|
||||||
divide,
|
|
||||||
modulo,
|
|
||||||
equal,
|
|
||||||
less,
|
|
||||||
less_equal,
|
|
||||||
negate,
|
|
||||||
not,
|
|
||||||
test,
|
|
||||||
test_set,
|
|
||||||
call,
|
|
||||||
call_native,
|
|
||||||
jump,
|
|
||||||
r#return,
|
|
||||||
];
|
|
||||||
|
|
||||||
pub(crate) fn get_next_action(data: &mut ThreadData) -> RunAction {
|
|
||||||
let current_call = data.call_stack.last_mut_unchecked();
|
|
||||||
let instruction = current_call.chunk.instructions[current_call.ip];
|
|
||||||
let operation = instruction.operation();
|
|
||||||
let logic = RUNNER_LOGIC_TABLE[operation.0 as usize];
|
|
||||||
|
|
||||||
current_call.ip += 1;
|
|
||||||
|
|
||||||
RunAction { logic, instruction }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn point(instruction: Instruction, data: &mut ThreadData) -> bool {
|
|
||||||
let Point { from, to } = instruction.into();
|
|
||||||
let from_register = data.get_register_unchecked(from);
|
|
||||||
let from_register_is_empty = matches!(from_register, Register::Empty);
|
|
||||||
|
|
||||||
if !from_register_is_empty {
|
|
||||||
let register = Register::Pointer(Pointer::Register(to));
|
|
||||||
|
|
||||||
data.set_register(from, register);
|
|
||||||
}
|
|
||||||
|
|
||||||
data.next_action = get_next_action(data);
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn close(instruction: Instruction, data: &mut ThreadData) -> bool {
|
|
||||||
let Close { from, to } = instruction.into();
|
|
||||||
|
|
||||||
for register_index in from..to {
|
|
||||||
data.set_register(register_index, Register::Empty);
|
|
||||||
}
|
|
||||||
|
|
||||||
data.next_action = get_next_action(data);
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn load_boolean(instruction: Instruction, data: &mut ThreadData) -> bool {
|
|
||||||
let LoadBoolean {
|
|
||||||
destination,
|
|
||||||
value,
|
|
||||||
jump_next,
|
|
||||||
} = instruction.into();
|
|
||||||
let boolean = Value::Concrete(ConcreteValue::Boolean(value));
|
|
||||||
let register = Register::Value(boolean);
|
|
||||||
|
|
||||||
data.set_register(destination, register);
|
|
||||||
|
|
||||||
if jump_next {
|
|
||||||
let current_call = data.call_stack.last_mut_unchecked();
|
|
||||||
|
|
||||||
current_call.ip += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
data.next_action = get_next_action(data);
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn load_constant(instruction: Instruction, data: &mut ThreadData) -> bool {
|
|
||||||
let LoadConstant {
|
|
||||||
destination,
|
|
||||||
constant_index,
|
|
||||||
jump_next,
|
|
||||||
} = instruction.into();
|
|
||||||
let register = Register::Pointer(Pointer::Constant(constant_index));
|
|
||||||
|
|
||||||
trace!("Load constant {constant_index} into R{destination}");
|
|
||||||
|
|
||||||
data.set_register(destination, register);
|
|
||||||
|
|
||||||
if jump_next {
|
|
||||||
let current_call = data.call_stack.last_mut_unchecked();
|
|
||||||
|
|
||||||
current_call.ip += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
data.next_action = get_next_action(data);
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn load_list(instruction: Instruction, data: &mut ThreadData) -> bool {
|
|
||||||
let LoadList {
|
|
||||||
destination,
|
|
||||||
start_register,
|
|
||||||
jump_next,
|
|
||||||
} = instruction.into();
|
|
||||||
let mut item_pointers = Vec::with_capacity((destination - start_register) as usize);
|
|
||||||
let mut item_type = Type::Any;
|
|
||||||
|
|
||||||
for register_index in start_register..destination {
|
|
||||||
match data.get_register_unchecked(register_index) {
|
|
||||||
Register::Empty => continue,
|
|
||||||
Register::Value(value) => {
|
|
||||||
if item_type == Type::Any {
|
|
||||||
item_type = value.r#type();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Register::Pointer(pointer) => {
|
|
||||||
if item_type == Type::Any {
|
|
||||||
item_type = data.follow_pointer_unchecked(*pointer).r#type();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let pointer = Pointer::Register(register_index);
|
|
||||||
|
|
||||||
item_pointers.push(pointer);
|
|
||||||
}
|
|
||||||
|
|
||||||
let list_value = Value::AbstractList(AbstractList {
|
|
||||||
item_type,
|
|
||||||
item_pointers,
|
|
||||||
});
|
|
||||||
let register = Register::Value(list_value);
|
|
||||||
|
|
||||||
data.set_register(destination, register);
|
|
||||||
|
|
||||||
data.next_action = get_next_action(data);
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn load_function(instruction: Instruction, data: &mut ThreadData) -> bool {
|
|
||||||
let LoadFunction {
|
|
||||||
destination,
|
|
||||||
prototype_index,
|
|
||||||
jump_next,
|
|
||||||
} = instruction.into();
|
|
||||||
let prototype_index = prototype_index as usize;
|
|
||||||
let current_call = data.call_stack.last_mut_unchecked();
|
|
||||||
let prototype = ¤t_call.chunk.prototypes[prototype_index];
|
|
||||||
let function = prototype.as_function();
|
|
||||||
let register = Register::Value(Value::Function(function));
|
|
||||||
|
|
||||||
data.set_register(destination, register);
|
|
||||||
|
|
||||||
data.next_action = get_next_action(data);
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn load_self(instruction: Instruction, data: &mut ThreadData) -> bool {
|
|
||||||
let LoadSelf {
|
|
||||||
destination,
|
|
||||||
jump_next,
|
|
||||||
} = instruction.into();
|
|
||||||
let current_call = data.call_stack.last_mut_unchecked();
|
|
||||||
let prototype = ¤t_call.chunk;
|
|
||||||
let function = prototype.as_function();
|
|
||||||
let register = Register::Value(Value::Function(function));
|
|
||||||
|
|
||||||
data.set_register(destination, register);
|
|
||||||
|
|
||||||
data.next_action = get_next_action(data);
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_local(instruction: Instruction, data: &mut ThreadData) -> bool {
|
|
||||||
let GetLocal {
|
|
||||||
destination,
|
|
||||||
local_index,
|
|
||||||
} = instruction.into();
|
|
||||||
let local_register_index = data.get_local_register(local_index);
|
|
||||||
let register = Register::Pointer(Pointer::Register(local_register_index));
|
|
||||||
|
|
||||||
data.set_register(destination, register);
|
|
||||||
|
|
||||||
data.next_action = get_next_action(data);
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_local(instruction: Instruction, data: &mut ThreadData) -> bool {
|
|
||||||
let SetLocal {
|
|
||||||
register_index,
|
|
||||||
local_index,
|
|
||||||
} = instruction.into();
|
|
||||||
let local_register_index = data.get_local_register(local_index);
|
|
||||||
let register = Register::Pointer(Pointer::Register(register_index));
|
|
||||||
|
|
||||||
data.set_register(local_register_index, register);
|
|
||||||
|
|
||||||
data.next_action = get_next_action(data);
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add(instruction: Instruction, data: &mut ThreadData) -> bool {
|
|
||||||
let Add {
|
|
||||||
destination,
|
|
||||||
left,
|
|
||||||
left_type,
|
|
||||||
right,
|
|
||||||
right_type,
|
|
||||||
} = instruction.into();
|
|
||||||
let sum = match (left_type, right_type) {
|
|
||||||
(TypeCode::INTEGER, TypeCode::INTEGER) => {
|
|
||||||
let left = unsafe {
|
|
||||||
data.get_argument_unchecked(left)
|
|
||||||
.as_integer()
|
|
||||||
.unwrap_unchecked()
|
|
||||||
};
|
|
||||||
let right = unsafe {
|
|
||||||
data.get_argument_unchecked(right)
|
|
||||||
.as_integer()
|
|
||||||
.unwrap_unchecked()
|
|
||||||
};
|
|
||||||
|
|
||||||
ConcreteValue::Integer(left + right)
|
|
||||||
}
|
|
||||||
(TypeCode::FLOAT, TypeCode::FLOAT) => {
|
|
||||||
let left = unsafe {
|
|
||||||
data.get_argument_unchecked(left)
|
|
||||||
.as_float()
|
|
||||||
.unwrap_unchecked()
|
|
||||||
};
|
|
||||||
let right = unsafe {
|
|
||||||
data.get_argument_unchecked(right)
|
|
||||||
.as_float()
|
|
||||||
.unwrap_unchecked()
|
|
||||||
};
|
|
||||||
|
|
||||||
ConcreteValue::Float(left + right)
|
|
||||||
}
|
|
||||||
_ => panic!("VM Error: Cannot add values"),
|
|
||||||
};
|
|
||||||
let register = Register::Value(Value::Concrete(sum));
|
|
||||||
|
|
||||||
data.set_register(destination, register);
|
|
||||||
|
|
||||||
data.next_action = get_next_action(data);
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn subtract(instruction: Instruction, data: &mut ThreadData) -> bool {
|
|
||||||
let Subtract {
|
|
||||||
destination,
|
|
||||||
left,
|
|
||||||
left_type,
|
|
||||||
right,
|
|
||||||
right_type,
|
|
||||||
} = instruction.into();
|
|
||||||
let difference = match (left_type, right_type) {
|
|
||||||
(TypeCode::INTEGER, TypeCode::INTEGER) => {
|
|
||||||
let left = unsafe {
|
|
||||||
data.get_argument_unchecked(left)
|
|
||||||
.as_integer()
|
|
||||||
.unwrap_unchecked()
|
|
||||||
};
|
|
||||||
let right = unsafe {
|
|
||||||
data.get_argument_unchecked(right)
|
|
||||||
.as_integer()
|
|
||||||
.unwrap_unchecked()
|
|
||||||
};
|
|
||||||
|
|
||||||
ConcreteValue::Integer(left - right)
|
|
||||||
}
|
|
||||||
(TypeCode::FLOAT, TypeCode::FLOAT) => {
|
|
||||||
let left = unsafe {
|
|
||||||
data.get_argument_unchecked(left)
|
|
||||||
.as_float()
|
|
||||||
.unwrap_unchecked()
|
|
||||||
};
|
|
||||||
let right = unsafe {
|
|
||||||
data.get_argument_unchecked(right)
|
|
||||||
.as_float()
|
|
||||||
.unwrap_unchecked()
|
|
||||||
};
|
|
||||||
|
|
||||||
ConcreteValue::Float(left - right)
|
|
||||||
}
|
|
||||||
_ => panic!("VM Error: Cannot subtract values"),
|
|
||||||
};
|
|
||||||
let register = Register::Value(Value::Concrete(difference));
|
|
||||||
|
|
||||||
data.set_register(destination, register);
|
|
||||||
|
|
||||||
data.next_action = get_next_action(data);
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn multiply(instruction: Instruction, data: &mut ThreadData) -> bool {
|
|
||||||
let Multiply {
|
|
||||||
destination,
|
|
||||||
left,
|
|
||||||
left_type,
|
|
||||||
right,
|
|
||||||
right_type,
|
|
||||||
} = instruction.into();
|
|
||||||
let product = match (left_type, right_type) {
|
|
||||||
(TypeCode::INTEGER, TypeCode::INTEGER) => {
|
|
||||||
let left = unsafe {
|
|
||||||
data.get_argument_unchecked(left)
|
|
||||||
.as_integer()
|
|
||||||
.unwrap_unchecked()
|
|
||||||
};
|
|
||||||
let right = unsafe {
|
|
||||||
data.get_argument_unchecked(right)
|
|
||||||
.as_integer()
|
|
||||||
.unwrap_unchecked()
|
|
||||||
};
|
|
||||||
|
|
||||||
ConcreteValue::Integer(left * right)
|
|
||||||
}
|
|
||||||
(TypeCode::FLOAT, TypeCode::FLOAT) => {
|
|
||||||
let left = unsafe {
|
|
||||||
data.get_argument_unchecked(left)
|
|
||||||
.as_float()
|
|
||||||
.unwrap_unchecked()
|
|
||||||
};
|
|
||||||
let right = unsafe {
|
|
||||||
data.get_argument_unchecked(right)
|
|
||||||
.as_float()
|
|
||||||
.unwrap_unchecked()
|
|
||||||
};
|
|
||||||
|
|
||||||
ConcreteValue::Float(left * right)
|
|
||||||
}
|
|
||||||
_ => panic!("VM Error: Cannot multiply values"),
|
|
||||||
};
|
|
||||||
let register = Register::Value(Value::Concrete(product));
|
|
||||||
|
|
||||||
data.set_register(destination, register);
|
|
||||||
|
|
||||||
data.next_action = get_next_action(data);
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn divide(instruction: Instruction, data: &mut ThreadData) -> bool {
|
|
||||||
let Divide {
|
|
||||||
destination,
|
|
||||||
left,
|
|
||||||
left_type,
|
|
||||||
right,
|
|
||||||
right_type,
|
|
||||||
} = instruction.into();
|
|
||||||
let quotient = match (left_type, right_type) {
|
|
||||||
(TypeCode::INTEGER, TypeCode::INTEGER) => {
|
|
||||||
let left = unsafe {
|
|
||||||
data.get_argument_unchecked(left)
|
|
||||||
.as_integer()
|
|
||||||
.unwrap_unchecked()
|
|
||||||
};
|
|
||||||
let right = unsafe {
|
|
||||||
data.get_argument_unchecked(right)
|
|
||||||
.as_integer()
|
|
||||||
.unwrap_unchecked()
|
|
||||||
};
|
|
||||||
|
|
||||||
ConcreteValue::Integer(left / right)
|
|
||||||
}
|
|
||||||
(TypeCode::FLOAT, TypeCode::FLOAT) => {
|
|
||||||
let left = unsafe {
|
|
||||||
data.get_argument_unchecked(left)
|
|
||||||
.as_float()
|
|
||||||
.unwrap_unchecked()
|
|
||||||
};
|
|
||||||
let right = unsafe {
|
|
||||||
data.get_argument_unchecked(right)
|
|
||||||
.as_float()
|
|
||||||
.unwrap_unchecked()
|
|
||||||
};
|
|
||||||
|
|
||||||
ConcreteValue::Float(left / right)
|
|
||||||
}
|
|
||||||
_ => panic!("VM Error: Cannot divide values"),
|
|
||||||
};
|
|
||||||
let register = Register::Value(Value::Concrete(quotient));
|
|
||||||
|
|
||||||
data.set_register(destination, register);
|
|
||||||
|
|
||||||
data.next_action = get_next_action(data);
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn modulo(instruction: Instruction, data: &mut ThreadData) -> bool {
|
|
||||||
let Modulo {
|
|
||||||
destination,
|
|
||||||
left,
|
|
||||||
left_type,
|
|
||||||
right,
|
|
||||||
right_type,
|
|
||||||
} = instruction.into();
|
|
||||||
let remainder = match (left_type, right_type) {
|
|
||||||
(TypeCode::INTEGER, TypeCode::INTEGER) => {
|
|
||||||
let left = unsafe {
|
|
||||||
data.get_argument_unchecked(left)
|
|
||||||
.as_integer()
|
|
||||||
.unwrap_unchecked()
|
|
||||||
};
|
|
||||||
let right = unsafe {
|
|
||||||
data.get_argument_unchecked(right)
|
|
||||||
.as_integer()
|
|
||||||
.unwrap_unchecked()
|
|
||||||
};
|
|
||||||
|
|
||||||
ConcreteValue::Integer(left % right)
|
|
||||||
}
|
|
||||||
_ => panic!("VM Error: Cannot modulo values"),
|
|
||||||
};
|
|
||||||
let register = Register::Value(Value::Concrete(remainder));
|
|
||||||
|
|
||||||
data.set_register(destination, register);
|
|
||||||
|
|
||||||
data.next_action = get_next_action(data);
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn test(instruction: Instruction, data: &mut ThreadData) -> bool {
|
|
||||||
let Test {
|
|
||||||
operand_register,
|
|
||||||
test_value,
|
|
||||||
} = instruction.into();
|
|
||||||
let value = data.open_register_unchecked(operand_register);
|
|
||||||
let boolean = if let Value::Concrete(ConcreteValue::Boolean(boolean)) = value {
|
|
||||||
*boolean
|
|
||||||
} else {
|
|
||||||
panic!("VM Error: Expected boolean value for TEST operation",);
|
|
||||||
};
|
|
||||||
|
|
||||||
if boolean == test_value {
|
|
||||||
let current_call = data.call_stack.last_mut_unchecked();
|
|
||||||
|
|
||||||
current_call.ip += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
data.next_action = get_next_action(data);
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn test_set(instruction: Instruction, data: &mut ThreadData) -> bool {
|
|
||||||
let TestSet {
|
|
||||||
destination,
|
|
||||||
argument,
|
|
||||||
test_value,
|
|
||||||
} = instruction.into();
|
|
||||||
let value = data.get_argument_unchecked(argument);
|
|
||||||
let boolean = if let Value::Concrete(ConcreteValue::Boolean(boolean)) = value {
|
|
||||||
*boolean
|
|
||||||
} else {
|
|
||||||
panic!("VM Error: Expected boolean value for TEST_SET operation",);
|
|
||||||
};
|
|
||||||
|
|
||||||
if boolean == test_value {
|
|
||||||
} else {
|
|
||||||
let pointer = match argument {
|
|
||||||
Operand::Constant(constant_index) => Pointer::Constant(constant_index),
|
|
||||||
Operand::Register(register_index) => Pointer::Register(register_index),
|
|
||||||
};
|
|
||||||
let register = Register::Pointer(pointer);
|
|
||||||
|
|
||||||
data.set_register(destination, register);
|
|
||||||
}
|
|
||||||
|
|
||||||
data.next_action = get_next_action(data);
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn equal(instruction: Instruction, data: &mut ThreadData) -> bool {
|
|
||||||
let Equal {
|
|
||||||
comparator,
|
|
||||||
left,
|
|
||||||
left_type,
|
|
||||||
right,
|
|
||||||
right_type,
|
|
||||||
} = instruction.into();
|
|
||||||
let is_equal = match (left_type, right_type) {
|
|
||||||
(TypeCode::INTEGER, TypeCode::INTEGER) => {
|
|
||||||
let left = unsafe {
|
|
||||||
data.get_argument_unchecked(left)
|
|
||||||
.as_integer()
|
|
||||||
.unwrap_unchecked()
|
|
||||||
};
|
|
||||||
let right = unsafe {
|
|
||||||
data.get_argument_unchecked(right)
|
|
||||||
.as_integer()
|
|
||||||
.unwrap_unchecked()
|
|
||||||
};
|
|
||||||
|
|
||||||
left == right
|
|
||||||
}
|
|
||||||
(TypeCode::FLOAT, TypeCode::FLOAT) => {
|
|
||||||
let left = unsafe {
|
|
||||||
data.get_argument_unchecked(left)
|
|
||||||
.as_float()
|
|
||||||
.unwrap_unchecked()
|
|
||||||
};
|
|
||||||
let right = unsafe {
|
|
||||||
data.get_argument_unchecked(right)
|
|
||||||
.as_float()
|
|
||||||
.unwrap_unchecked()
|
|
||||||
};
|
|
||||||
|
|
||||||
left == right
|
|
||||||
}
|
|
||||||
(TypeCode::BOOLEAN, TypeCode::BOOLEAN) => {
|
|
||||||
let left = unsafe {
|
|
||||||
data.get_argument_unchecked(left)
|
|
||||||
.as_boolean()
|
|
||||||
.unwrap_unchecked()
|
|
||||||
};
|
|
||||||
let right = unsafe {
|
|
||||||
data.get_argument_unchecked(right)
|
|
||||||
.as_boolean()
|
|
||||||
.unwrap_unchecked()
|
|
||||||
};
|
|
||||||
|
|
||||||
left == right
|
|
||||||
}
|
|
||||||
(TypeCode::STRING, TypeCode::STRING) => {
|
|
||||||
let left = unsafe {
|
|
||||||
data.get_argument_unchecked(left)
|
|
||||||
.as_string()
|
|
||||||
.unwrap_unchecked()
|
|
||||||
};
|
|
||||||
let right = unsafe {
|
|
||||||
data.get_argument_unchecked(right)
|
|
||||||
.as_string()
|
|
||||||
.unwrap_unchecked()
|
|
||||||
};
|
|
||||||
|
|
||||||
left == right
|
|
||||||
}
|
|
||||||
_ => panic!("VM Error: Cannot compare values"),
|
|
||||||
};
|
|
||||||
|
|
||||||
if is_equal == comparator {
|
|
||||||
let current_call = data.call_stack.last_mut_unchecked();
|
|
||||||
|
|
||||||
current_call.ip += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
data.next_action = get_next_action(data);
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn less(instruction: Instruction, data: &mut ThreadData) -> bool {
|
|
||||||
let Less {
|
|
||||||
comparator,
|
|
||||||
left,
|
|
||||||
left_type,
|
|
||||||
right,
|
|
||||||
right_type,
|
|
||||||
} = instruction.into();
|
|
||||||
let is_less = match (left_type, right_type) {
|
|
||||||
(TypeCode::INTEGER, TypeCode::INTEGER) => {
|
|
||||||
let left = unsafe {
|
|
||||||
data.get_argument_unchecked(left)
|
|
||||||
.as_integer()
|
|
||||||
.unwrap_unchecked()
|
|
||||||
};
|
|
||||||
let right = unsafe {
|
|
||||||
data.get_argument_unchecked(right)
|
|
||||||
.as_integer()
|
|
||||||
.unwrap_unchecked()
|
|
||||||
};
|
|
||||||
|
|
||||||
left < right
|
|
||||||
}
|
|
||||||
(TypeCode::FLOAT, TypeCode::FLOAT) => {
|
|
||||||
let left = unsafe {
|
|
||||||
data.get_argument_unchecked(left)
|
|
||||||
.as_float()
|
|
||||||
.unwrap_unchecked()
|
|
||||||
};
|
|
||||||
let right = unsafe {
|
|
||||||
data.get_argument_unchecked(right)
|
|
||||||
.as_float()
|
|
||||||
.unwrap_unchecked()
|
|
||||||
};
|
|
||||||
|
|
||||||
left < right
|
|
||||||
}
|
|
||||||
_ => panic!("VM Error: Cannot compare values"),
|
|
||||||
};
|
|
||||||
|
|
||||||
if is_less == comparator {
|
|
||||||
let current_call = data.call_stack.last_mut_unchecked();
|
|
||||||
|
|
||||||
current_call.ip += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
data.next_action = get_next_action(data);
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn less_equal(instruction: Instruction, data: &mut ThreadData) -> bool {
|
|
||||||
let LessEqual {
|
|
||||||
comparator,
|
|
||||||
left,
|
|
||||||
left_type,
|
|
||||||
right,
|
|
||||||
right_type,
|
|
||||||
} = instruction.into();
|
|
||||||
let is_less_or_equal = match (left_type, right_type) {
|
|
||||||
(TypeCode::INTEGER, TypeCode::INTEGER) => {
|
|
||||||
let left = unsafe {
|
|
||||||
data.get_argument_unchecked(left)
|
|
||||||
.as_integer()
|
|
||||||
.unwrap_unchecked()
|
|
||||||
};
|
|
||||||
let right = unsafe {
|
|
||||||
data.get_argument_unchecked(right)
|
|
||||||
.as_integer()
|
|
||||||
.unwrap_unchecked()
|
|
||||||
};
|
|
||||||
|
|
||||||
left <= right
|
|
||||||
}
|
|
||||||
(TypeCode::FLOAT, TypeCode::FLOAT) => {
|
|
||||||
let left = unsafe {
|
|
||||||
data.get_argument_unchecked(left)
|
|
||||||
.as_float()
|
|
||||||
.unwrap_unchecked()
|
|
||||||
};
|
|
||||||
let right = unsafe {
|
|
||||||
data.get_argument_unchecked(right)
|
|
||||||
.as_float()
|
|
||||||
.unwrap_unchecked()
|
|
||||||
};
|
|
||||||
|
|
||||||
left <= right
|
|
||||||
}
|
|
||||||
_ => panic!("VM Error: Cannot compare values"),
|
|
||||||
};
|
|
||||||
|
|
||||||
if is_less_or_equal == comparator {
|
|
||||||
let current_call = data.call_stack.last_mut_unchecked();
|
|
||||||
|
|
||||||
current_call.ip += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
data.next_action = get_next_action(data);
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn negate(instruction: Instruction, data: &mut ThreadData) -> bool {
|
|
||||||
let Negate {
|
|
||||||
destination,
|
|
||||||
argument,
|
|
||||||
argument_type,
|
|
||||||
} = instruction.into();
|
|
||||||
let argument = data.get_argument_unchecked(argument);
|
|
||||||
let negated = argument.negate();
|
|
||||||
let register = Register::Value(negated);
|
|
||||||
|
|
||||||
data.set_register(destination, register);
|
|
||||||
|
|
||||||
data.next_action = get_next_action(data);
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn not(instruction: Instruction, data: &mut ThreadData) -> bool {
|
|
||||||
let Not {
|
|
||||||
destination,
|
|
||||||
argument,
|
|
||||||
} = instruction.into();
|
|
||||||
let argument = data.get_argument_unchecked(argument);
|
|
||||||
let not = match argument {
|
|
||||||
Value::Concrete(ConcreteValue::Boolean(boolean)) => ConcreteValue::Boolean(!boolean),
|
|
||||||
_ => panic!("VM Error: Expected boolean value for NOT operation"),
|
|
||||||
};
|
|
||||||
let register = Register::Value(Value::Concrete(not));
|
|
||||||
|
|
||||||
data.set_register(destination, register);
|
|
||||||
|
|
||||||
data.next_action = get_next_action(data);
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn jump(instruction: Instruction, data: &mut ThreadData) -> bool {
|
|
||||||
let Jump {
|
|
||||||
offset,
|
|
||||||
is_positive,
|
|
||||||
} = instruction.into();
|
|
||||||
let offset = offset as usize;
|
|
||||||
let current_call = data.call_stack.last_mut_unchecked();
|
|
||||||
|
|
||||||
if is_positive {
|
|
||||||
current_call.ip += offset;
|
|
||||||
} else {
|
|
||||||
current_call.ip -= offset + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
data.next_action = get_next_action(data);
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn call(instruction: Instruction, data: &mut ThreadData) -> bool {
|
|
||||||
let Call {
|
|
||||||
destination: return_register,
|
|
||||||
function_register,
|
|
||||||
argument_count,
|
|
||||||
is_recursive,
|
|
||||||
} = instruction.into();
|
|
||||||
let current_call = data.call_stack.last_unchecked();
|
|
||||||
let first_argument_register = return_register - argument_count;
|
|
||||||
let prototype = if is_recursive {
|
|
||||||
current_call.chunk.clone()
|
|
||||||
} else {
|
|
||||||
let function = data
|
|
||||||
.open_register_unchecked(function_register)
|
|
||||||
.as_function()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
current_call.chunk.prototypes[function.prototype_index as usize].clone()
|
|
||||||
};
|
|
||||||
let mut next_call = FunctionCall::new(prototype, return_register);
|
|
||||||
let mut argument_index = 0;
|
|
||||||
|
|
||||||
for register_index in first_argument_register..return_register {
|
|
||||||
let value_option = data.open_register_allow_empty_unchecked(register_index);
|
|
||||||
let argument = if let Some(value) = value_option {
|
|
||||||
value.clone()
|
|
||||||
} else {
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
|
|
||||||
next_call.registers[argument_index] = Register::Value(argument);
|
|
||||||
argument_index += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
data.call_stack.push(next_call);
|
|
||||||
|
|
||||||
data.next_action = get_next_action(data);
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn call_native(instruction: Instruction, data: &mut ThreadData) -> bool {
|
|
||||||
let CallNative {
|
|
||||||
destination,
|
|
||||||
function,
|
|
||||||
argument_count,
|
|
||||||
} = instruction.into();
|
|
||||||
let first_argument_index = destination - argument_count;
|
|
||||||
let argument_range = first_argument_index..destination;
|
|
||||||
|
|
||||||
function.call(data, destination, argument_range)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn r#return(instruction: Instruction, data: &mut ThreadData) -> bool {
|
|
||||||
trace!("Returning with call stack:\n{}", data.call_stack);
|
|
||||||
|
|
||||||
let Return {
|
|
||||||
should_return_value,
|
|
||||||
return_register,
|
|
||||||
} = instruction.into();
|
|
||||||
let (destination, return_value) = if data.call_stack.len() == 1 {
|
|
||||||
if should_return_value {
|
|
||||||
data.return_value_index = Some(return_register);
|
|
||||||
};
|
|
||||||
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
let return_value = data.empty_register_or_clone_constant_unchecked(return_register);
|
|
||||||
let destination = data.call_stack.pop_unchecked().return_register;
|
|
||||||
|
|
||||||
(destination, return_value)
|
|
||||||
};
|
|
||||||
|
|
||||||
if should_return_value {
|
|
||||||
data.set_register(destination, Register::Value(return_value));
|
|
||||||
}
|
|
||||||
|
|
||||||
data.next_action = get_next_action(data);
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
|
|
||||||
use crate::Operation;
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
const ALL_OPERATIONS: [(Operation, RunnerLogic); 24] = [
|
|
||||||
(Operation::POINT, point),
|
|
||||||
(Operation::CLOSE, close),
|
|
||||||
(Operation::LOAD_BOOLEAN, load_boolean),
|
|
||||||
(Operation::LOAD_CONSTANT, load_constant),
|
|
||||||
(Operation::LOAD_LIST, load_list),
|
|
||||||
(Operation::LOAD_SELF, load_self),
|
|
||||||
(Operation::GET_LOCAL, get_local),
|
|
||||||
(Operation::SET_LOCAL, set_local),
|
|
||||||
(Operation::ADD, add),
|
|
||||||
(Operation::SUBTRACT, subtract),
|
|
||||||
(Operation::MULTIPLY, multiply),
|
|
||||||
(Operation::DIVIDE, divide),
|
|
||||||
(Operation::MODULO, modulo),
|
|
||||||
(Operation::TEST, test),
|
|
||||||
(Operation::TEST_SET, test_set),
|
|
||||||
(Operation::EQUAL, equal),
|
|
||||||
(Operation::LESS, less),
|
|
||||||
(Operation::LESS_EQUAL, less_equal),
|
|
||||||
(Operation::NEGATE, negate),
|
|
||||||
(Operation::NOT, not),
|
|
||||||
(Operation::CALL, call),
|
|
||||||
(Operation::CALL_NATIVE, call_native),
|
|
||||||
(Operation::JUMP, jump),
|
|
||||||
(Operation::RETURN, r#return),
|
|
||||||
];
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn operations_map_to_the_correct_runner() {
|
|
||||||
for (operation, expected_runner) in ALL_OPERATIONS {
|
|
||||||
let actual_runner = RUNNER_LOGIC_TABLE[operation.0 as usize];
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
expected_runner, actual_runner,
|
|
||||||
"{operation} runner is incorrect"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,137 +0,0 @@
|
|||||||
use std::{
|
|
||||||
fmt::{self, Debug, Display, Formatter},
|
|
||||||
ops::{Index, IndexMut, Range},
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::FunctionCall;
|
|
||||||
|
|
||||||
#[derive(Clone, PartialEq)]
|
|
||||||
pub struct Stack<T> {
|
|
||||||
items: Vec<T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Stack<T> {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Stack {
|
|
||||||
items: Vec::with_capacity(1),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_capacity(capacity: usize) -> Self {
|
|
||||||
Stack {
|
|
||||||
items: Vec::with_capacity(capacity),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_empty(&self) -> bool {
|
|
||||||
self.items.is_empty()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn len(&self) -> usize {
|
|
||||||
self.items.len()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_unchecked(&self, index: usize) -> &T {
|
|
||||||
if cfg!(debug_assertions) {
|
|
||||||
assert!(index < self.len(), "Stack underflow");
|
|
||||||
|
|
||||||
&self.items[index]
|
|
||||||
} else {
|
|
||||||
unsafe { self.items.get_unchecked(index) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_unchecked_mut(&mut self, index: usize) -> &mut T {
|
|
||||||
if cfg!(debug_assertions) {
|
|
||||||
assert!(index < self.len(), "Stack underflow");
|
|
||||||
|
|
||||||
&mut self.items[index]
|
|
||||||
} else {
|
|
||||||
unsafe { self.items.get_unchecked_mut(index) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn push(&mut self, item: T) {
|
|
||||||
self.items.push(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn pop(&mut self) -> Option<T> {
|
|
||||||
self.items.pop()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn last(&self) -> Option<&T> {
|
|
||||||
self.items.last()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn last_mut(&mut self) -> Option<&mut T> {
|
|
||||||
self.items.last_mut()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn pop_unchecked(&mut self) -> T {
|
|
||||||
if cfg!(debug_assertions) {
|
|
||||||
assert!(!self.is_empty(), "Stack underflow");
|
|
||||||
|
|
||||||
self.items.pop().unwrap()
|
|
||||||
} else {
|
|
||||||
unsafe { self.items.pop().unwrap_unchecked() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn last_unchecked(&self) -> &T {
|
|
||||||
if cfg!(debug_assertions) {
|
|
||||||
assert!(!self.is_empty(), "Stack underflow");
|
|
||||||
|
|
||||||
self.items.last().unwrap()
|
|
||||||
} else {
|
|
||||||
unsafe { self.items.last().unwrap_unchecked() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn last_mut_unchecked(&mut self) -> &mut T {
|
|
||||||
if cfg!(debug_assertions) {
|
|
||||||
assert!(!self.is_empty(), "Stack underflow");
|
|
||||||
|
|
||||||
self.items.last_mut().unwrap()
|
|
||||||
} else {
|
|
||||||
unsafe { self.items.last_mut().unwrap_unchecked() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Default for Stack<T> {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Index<Range<usize>> for Stack<T> {
|
|
||||||
type Output = [T];
|
|
||||||
|
|
||||||
fn index(&self, index: Range<usize>) -> &Self::Output {
|
|
||||||
&self.items[index]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> IndexMut<Range<usize>> for Stack<T> {
|
|
||||||
fn index_mut(&mut self, index: Range<usize>) -> &mut Self::Output {
|
|
||||||
&mut self.items[index]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Debug> Debug for Stack<T> {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
write!(f, "{:?}", self.items)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Stack<FunctionCall> {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
writeln!(f, "----- DUST CALL STACK -----")?;
|
|
||||||
|
|
||||||
for (index, function_call) in self.items.iter().enumerate().rev() {
|
|
||||||
writeln!(f, "{index:02} | {function_call}")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
write!(f, "---------------------------")
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,10 +1,10 @@
|
|||||||
use std::{mem::replace, sync::Arc, thread::JoinHandle};
|
use std::{sync::Arc, thread::JoinHandle};
|
||||||
|
|
||||||
use tracing::{info, trace};
|
use tracing::{info, trace};
|
||||||
|
|
||||||
use crate::{Chunk, DustString, Operand, Span, Value, vm::FunctionCall};
|
use crate::{Chunk, DustString, Value};
|
||||||
|
|
||||||
use super::{Pointer, Register, RunAction, Stack};
|
use super::{Action, CallFrame};
|
||||||
|
|
||||||
pub struct Thread {
|
pub struct Thread {
|
||||||
chunk: Arc<Chunk>,
|
chunk: Arc<Chunk>,
|
||||||
@ -24,45 +24,42 @@ impl Thread {
|
|||||||
.unwrap_or_else(|| DustString::from("anonymous"))
|
.unwrap_or_else(|| DustString::from("anonymous"))
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut call_stack = Stack::with_capacity(self.chunk.prototypes.len() + 1);
|
let mut call_stack = Vec::with_capacity(self.chunk.prototypes.len() + 1);
|
||||||
let mut main_call = FunctionCall::new(self.chunk.clone(), 0);
|
let mut main_call = CallFrame::new(self.chunk.clone(), 0);
|
||||||
main_call.ip = 1; // The first action is already known
|
|
||||||
|
|
||||||
call_stack.push(main_call);
|
call_stack.push(main_call);
|
||||||
|
|
||||||
let first_action = RunAction::from(*self.chunk.instructions.first().unwrap());
|
|
||||||
let mut thread_data = ThreadData {
|
let mut thread_data = ThreadData {
|
||||||
call_stack,
|
stack: call_stack,
|
||||||
next_action: first_action,
|
return_value: None,
|
||||||
return_value_index: None,
|
spawned_threads: Vec::with_capacity(0),
|
||||||
spawned_threads: Vec::new(),
|
|
||||||
};
|
};
|
||||||
|
let mut action_sequence = Vec::with_capacity(self.chunk.instructions.len());
|
||||||
|
|
||||||
|
for instruction in self.chunk.instructions.iter() {
|
||||||
|
action_sequence.push(Action::from(instruction));
|
||||||
|
}
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
trace!("Instruction: {}", thread_data.next_action.instruction);
|
let current_frame = thread_data.current_frame_mut();
|
||||||
|
let current_action = if cfg!(debug_assertions) {
|
||||||
|
action_sequence
|
||||||
|
.get(current_frame.instruction_pointer)
|
||||||
|
.unwrap()
|
||||||
|
} else {
|
||||||
|
unsafe {
|
||||||
|
action_sequence
|
||||||
|
.get(current_frame.instruction_pointer)
|
||||||
|
.unwrap_unchecked()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
current_frame.instruction_pointer += 1;
|
||||||
|
|
||||||
let should_end = (thread_data.next_action.logic)(
|
trace!("Instruction: {}", current_action.fields);
|
||||||
thread_data.next_action.instruction,
|
|
||||||
&mut thread_data,
|
|
||||||
);
|
|
||||||
|
|
||||||
if should_end {
|
(current_action.logic)(current_action.fields, &mut thread_data);
|
||||||
let value_option = if let Some(register_index) = thread_data.return_value_index {
|
|
||||||
let value =
|
|
||||||
thread_data.empty_register_or_clone_constant_unchecked(register_index);
|
|
||||||
|
|
||||||
Some(value)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
thread_data
|
|
||||||
.spawned_threads
|
|
||||||
.into_iter()
|
|
||||||
.for_each(|join_handle| {
|
|
||||||
let _ = join_handle.join();
|
|
||||||
});
|
|
||||||
|
|
||||||
|
if let Some(value_option) = thread_data.return_value {
|
||||||
return value_option;
|
return value_option;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -71,200 +68,25 @@ impl Thread {
|
|||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ThreadData {
|
pub struct ThreadData {
|
||||||
pub call_stack: Stack<FunctionCall>,
|
pub stack: Vec<CallFrame>,
|
||||||
pub next_action: RunAction,
|
pub return_value: Option<Option<Value>>,
|
||||||
pub return_value_index: Option<u16>,
|
|
||||||
pub spawned_threads: Vec<JoinHandle<()>>,
|
pub spawned_threads: Vec<JoinHandle<()>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ThreadData {
|
impl ThreadData {
|
||||||
pub fn current_position(&self) -> Span {
|
pub fn current_frame(&self) -> &CallFrame {
|
||||||
let current_call = self.call_stack.last_unchecked();
|
|
||||||
|
|
||||||
current_call.chunk.positions[current_call.ip]
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn follow_pointer_unchecked(&self, pointer: Pointer) -> &Value {
|
|
||||||
trace!("Follow {pointer}");
|
|
||||||
|
|
||||||
match pointer {
|
|
||||||
Pointer::Register(register_index) => self.open_register_unchecked(register_index),
|
|
||||||
Pointer::Constant(constant_index) => self.get_constant_unchecked(constant_index),
|
|
||||||
Pointer::Stack(stack_index, register_index) => unsafe {
|
|
||||||
let register = self
|
|
||||||
.call_stack
|
|
||||||
.get_unchecked(stack_index)
|
|
||||||
.registers
|
|
||||||
.get_unchecked(register_index as usize);
|
|
||||||
|
|
||||||
match register {
|
|
||||||
Register::Value(value) => value,
|
|
||||||
Register::Pointer(pointer) => self.follow_pointer_unchecked(*pointer),
|
|
||||||
Register::Empty => panic!("VM Error: Register {register_index} is empty"),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_register_unchecked(&self, register_index: u16) -> &Register {
|
|
||||||
trace!("Get R{register_index}");
|
|
||||||
|
|
||||||
let register_index = register_index as usize;
|
|
||||||
|
|
||||||
if cfg!(debug_assertions) {
|
if cfg!(debug_assertions) {
|
||||||
&self.call_stack.last_unchecked().registers[register_index]
|
self.stack.last().unwrap()
|
||||||
} else {
|
} else {
|
||||||
unsafe {
|
unsafe { self.stack.last().unwrap_unchecked() }
|
||||||
self.call_stack
|
|
||||||
.last_unchecked()
|
|
||||||
.registers
|
|
||||||
.get_unchecked(register_index)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_register(&mut self, to_register: u16, register: Register) {
|
pub fn current_frame_mut(&mut self) -> &mut CallFrame {
|
||||||
let to_register = to_register as usize;
|
|
||||||
|
|
||||||
self.call_stack.last_mut_unchecked().registers[to_register] = register;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn open_register_unchecked(&self, register_index: u16) -> &Value {
|
|
||||||
let register_index = register_index as usize;
|
|
||||||
|
|
||||||
let register = if cfg!(debug_assertions) {
|
|
||||||
&self.call_stack.last_unchecked().registers[register_index]
|
|
||||||
} else {
|
|
||||||
unsafe {
|
|
||||||
self.call_stack
|
|
||||||
.last_unchecked()
|
|
||||||
.registers
|
|
||||||
.get_unchecked(register_index)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
trace!("Open R{register_index} to {register}");
|
|
||||||
|
|
||||||
match register {
|
|
||||||
Register::Value(value) => value,
|
|
||||||
Register::Pointer(pointer) => self.follow_pointer_unchecked(*pointer),
|
|
||||||
Register::Empty => panic!("VM Error: Register {register_index} is empty"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn open_register_allow_empty_unchecked(&self, register_index: u16) -> Option<&Value> {
|
|
||||||
trace!("Open R{register_index}");
|
|
||||||
|
|
||||||
let register = self.get_register_unchecked(register_index);
|
|
||||||
|
|
||||||
trace!("Open R{register_index} to {register}");
|
|
||||||
|
|
||||||
match register {
|
|
||||||
Register::Value(value) => Some(value),
|
|
||||||
Register::Pointer(pointer) => Some(self.follow_pointer_unchecked(*pointer)),
|
|
||||||
Register::Empty => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn empty_register_or_clone_constant_unchecked(&mut self, register_index: u16) -> Value {
|
|
||||||
let register_index = register_index as usize;
|
|
||||||
let old_register = replace(
|
|
||||||
&mut self.call_stack.last_mut_unchecked().registers[register_index],
|
|
||||||
Register::Empty,
|
|
||||||
);
|
|
||||||
|
|
||||||
match old_register {
|
|
||||||
Register::Value(value) => value,
|
|
||||||
Register::Pointer(pointer) => match pointer {
|
|
||||||
Pointer::Register(register_index) => {
|
|
||||||
self.empty_register_or_clone_constant_unchecked(register_index)
|
|
||||||
}
|
|
||||||
Pointer::Constant(constant_index) => {
|
|
||||||
self.get_constant_unchecked(constant_index).clone()
|
|
||||||
}
|
|
||||||
Pointer::Stack(stack_index, register_index) => {
|
|
||||||
let call = self.call_stack.get_unchecked_mut(stack_index);
|
|
||||||
|
|
||||||
let old_register = replace(
|
|
||||||
&mut call.registers[register_index as usize],
|
|
||||||
Register::Empty,
|
|
||||||
);
|
|
||||||
|
|
||||||
match old_register {
|
|
||||||
Register::Value(value) => value,
|
|
||||||
Register::Pointer(pointer) => {
|
|
||||||
self.follow_pointer_unchecked(pointer).clone()
|
|
||||||
}
|
|
||||||
Register::Empty => panic!("VM Error: Register {register_index} is empty"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Register::Empty => panic!("VM Error: Register {register_index} is empty"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn clone_register_value_or_constant_unchecked(&self, register_index: u16) -> Value {
|
|
||||||
let register = self.get_register_unchecked(register_index);
|
|
||||||
|
|
||||||
match register {
|
|
||||||
Register::Value(value) => value.clone(),
|
|
||||||
Register::Pointer(pointer) => match pointer {
|
|
||||||
Pointer::Register(register_index) => {
|
|
||||||
self.open_register_unchecked(*register_index).clone()
|
|
||||||
}
|
|
||||||
Pointer::Constant(constant_index) => {
|
|
||||||
self.get_constant_unchecked(*constant_index).clone()
|
|
||||||
}
|
|
||||||
Pointer::Stack(stack_index, register_index) => {
|
|
||||||
let call = self.call_stack.get_unchecked(*stack_index);
|
|
||||||
let register = &call.registers[*register_index as usize];
|
|
||||||
|
|
||||||
match register {
|
|
||||||
Register::Value(value) => value.clone(),
|
|
||||||
Register::Pointer(pointer) => {
|
|
||||||
self.follow_pointer_unchecked(*pointer).clone()
|
|
||||||
}
|
|
||||||
Register::Empty => panic!("VM Error: Register {register_index} is empty"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Register::Empty => panic!("VM Error: Register {register_index} is empty"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// DRY helper to get a value from an Argument
|
|
||||||
pub fn get_argument_unchecked(&self, argument: Operand) -> &Value {
|
|
||||||
match argument {
|
|
||||||
Operand::Constant(constant_index) => self.get_constant_unchecked(constant_index),
|
|
||||||
Operand::Register(register_index) => self.open_register_unchecked(register_index),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_constant_unchecked(&self, constant_index: u16) -> &Value {
|
|
||||||
let constant_index = constant_index as usize;
|
|
||||||
|
|
||||||
if cfg!(debug_assertions) {
|
if cfg!(debug_assertions) {
|
||||||
&self.call_stack.last().unwrap().chunk.constants[constant_index]
|
self.stack.last_mut().unwrap()
|
||||||
} else {
|
} else {
|
||||||
unsafe {
|
unsafe { self.stack.last_mut().unwrap_unchecked() }
|
||||||
self.call_stack
|
|
||||||
.last_unchecked()
|
|
||||||
.chunk
|
|
||||||
.constants
|
|
||||||
.get_unchecked(constant_index)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_local_register(&self, local_index: u16) -> u16 {
|
|
||||||
let local_index = local_index as usize;
|
|
||||||
let chunk = &self.call_stack.last_unchecked().chunk;
|
|
||||||
|
|
||||||
assert!(
|
|
||||||
local_index < chunk.locals.len(),
|
|
||||||
"VM Error: Local index out of bounds"
|
|
||||||
);
|
|
||||||
|
|
||||||
chunk.locals[local_index].register_index
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user