Compare commits
30 Commits
8cf98d8a9e
...
45ce76aef7
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
45ce76aef7 | ||
|
|
3621b1ef33 | ||
|
|
836227c8d3 | ||
|
|
0ae59705d4 | ||
|
|
8e2607b6ca | ||
|
|
dc73e86d8c | ||
|
|
555cceb9d6 | ||
|
|
fbb7933eb6 | ||
|
|
0287d6ada4 | ||
|
|
73cd6a255d | ||
|
|
83ea67c01b | ||
|
|
16059cca4e | ||
|
|
9ffe60ebef | ||
|
|
2beafec5d9 | ||
|
|
aba8eb66bd | ||
|
|
1abcaa92c7 | ||
|
|
168f7c71d5 | ||
|
|
56ed895b6e | ||
|
|
1e4646999d | ||
|
|
68d6c907fe | ||
|
|
8150c64c7d | ||
|
|
024d796ca4 | ||
|
|
ea185dbffd | ||
|
|
6571262af0 | ||
|
|
77ae133747 | ||
|
|
142a2d518b | ||
|
|
5b65c64fe5 | ||
|
|
e985fa5fe1 | ||
|
|
160ade2410 | ||
|
|
e2bc5948c1 |
287
docs/pitcode.md
Normal file
287
docs/pitcode.md
Normal file
@@ -0,0 +1,287 @@
|
||||
# Pitmachine
|
||||
A pitmachine is an abstract register machine for executing pitcode.
|
||||
|
||||
The pitmachine assembly language is JSON. The instructions field contains an array of labels and instructions. Labels are the targets of branch instructions. They are simple text. Slots are elements in an activation frame. They are designated by a small positive integer. Slots are the general registers of the pitmachine. Slots hold the arguments, variables, and temporaries of a function invocation.
|
||||
|
||||
{
|
||||
"name": "program_name",
|
||||
"data": {⸳⸳⸳},
|
||||
"source": "...",
|
||||
The labels record associates labels with tokens for use by debuggers.
|
||||
|
||||
"labels": {
|
||||
"entry": token,
|
||||
"beyond": token,
|
||||
...
|
||||
},
|
||||
The instructions array is a list of instructions and labels.
|
||||
|
||||
instructions: [
|
||||
A statement label:
|
||||
|
||||
"entry",
|
||||
go to beyond:
|
||||
|
||||
["jump", "beyond"],
|
||||
assign slot 8: pi / 2
|
||||
|
||||
["access", 13, {"kind": "name", "name": "pi", "make": "intrinsic", ⸳⸳⸳}],
|
||||
["int", 14, 2],
|
||||
["divide", 8, 13, 14],
|
||||
⸳⸳⸳
|
||||
"beyond"
|
||||
⸳⸳⸳
|
||||
]
|
||||
}
|
||||
|
||||
## Pitcode instructions
|
||||
This is a register based machine.
|
||||
|
||||
### Arithmetic
|
||||
Arithmetic instructions perform operations on numbers. All other cases disrupt (except add, which is polymorphic).
|
||||
|
||||
"add", dest, left, right // dest, left, and right are numbers designating slots in the current activation frame. Also works on texts (concatenation).
|
||||
|
||||
"subtract", dest, left, right
|
||||
|
||||
"multiply", dest, left, right
|
||||
|
||||
"divide", dest, left, right
|
||||
|
||||
"integer_divide", dest, left, right
|
||||
|
||||
"modulo", dest, left, right
|
||||
|
||||
"remainder", dest, left, right
|
||||
|
||||
"max", dest, left, right
|
||||
|
||||
"min", dest, left, right
|
||||
|
||||
"abs", dest, right
|
||||
|
||||
"neg", dest, right
|
||||
|
||||
"sign", dest, right
|
||||
|
||||
"fraction", dest, right
|
||||
|
||||
"integer", dest, right
|
||||
|
||||
"ceiling", dest, right, place
|
||||
|
||||
"floor", dest, right, place
|
||||
|
||||
"round", dest, right, place
|
||||
|
||||
"trunc", dest, right, place
|
||||
|
||||
### Text
|
||||
Text instructions perform operations on texts. All other cases disrupt.
|
||||
|
||||
"concat", dest, left, right // concat two texts
|
||||
|
||||
"concat_space", dest, left, right
|
||||
|
||||
"character", dest, right
|
||||
|
||||
"codepoint", dest, right
|
||||
|
||||
"length", dest, right
|
||||
|
||||
"lower", dest, right
|
||||
|
||||
"upper", dest, right
|
||||
|
||||
"append", pretext, right
|
||||
|
||||
Append the right text to the pretext, forwarding and growing its capacity if necessary.
|
||||
|
||||
### Comparison
|
||||
Comparison instructions perform operations on texts or numbers. All other cases disrupt.
|
||||
|
||||
"eq", dest, left, right
|
||||
|
||||
"ne", dest, left, right
|
||||
|
||||
"lt", dest, left, right
|
||||
|
||||
"le", dest, left, right
|
||||
|
||||
"gt", dest, left, right
|
||||
|
||||
"ge", dest, left, right
|
||||
|
||||
### Logical
|
||||
|
||||
"not", dest, right
|
||||
|
||||
### Bitwise
|
||||
Bitwise instructions convert operands to 32-bit integers. Non-numbers disrupt.
|
||||
|
||||
"bitand", dest, left, right
|
||||
|
||||
"bitor", dest, left, right
|
||||
|
||||
"bitxor", dest, left, right
|
||||
|
||||
"bitnot", dest, right
|
||||
|
||||
"shl", dest, left, right
|
||||
|
||||
"shr", dest, left, right
|
||||
|
||||
"ushr", dest, left, right
|
||||
|
||||
### Function
|
||||
|
||||
"frame", dest, func, nr_args
|
||||
|
||||
Prepare to invoke the func object. If the nr_args is too large, disrupt. Allocate the new activation frame. Put the current frame pointer into it.
|
||||
|
||||
"goframe", dest, func, nr_args
|
||||
|
||||
Same as frame, except that the current frame is reused if it is large enough.
|
||||
|
||||
"invoke", frame
|
||||
|
||||
Store the next instruction address in the current frame. Make the new frame the current frame. Jump to the entry point.
|
||||
|
||||
"goinvoke", frame
|
||||
|
||||
"apply", func, array
|
||||
|
||||
"return", value
|
||||
|
||||
"return_value", dest
|
||||
|
||||
"setarg", frame, slot, value // set the slot of frame to value
|
||||
|
||||
### Branching
|
||||
|
||||
"jump", label
|
||||
|
||||
"jump_true", slot, label
|
||||
|
||||
If the value in the slot is true, jump to the label. Otherwise, continue with the next instruction.
|
||||
|
||||
"jump_false": slot, label
|
||||
|
||||
If the value in the slot is false, jump to the label. Otherwise, continue with the next instruction.
|
||||
|
||||
"jump_null": slot, label
|
||||
|
||||
"jump_empty": slot, label
|
||||
|
||||
"wary_true", slot, label
|
||||
|
||||
If the value in the slot is true, jump to the label. If the value is false, continue with the next instruction. Otherwise disrupt because of a type error.
|
||||
|
||||
"wary_false": slot, label
|
||||
|
||||
If the value in the slot is false, jump to the label. If the value is true, continue with the next instruction. Otherwise disrupt because of a type error.
|
||||
|
||||
### Sensory
|
||||
|
||||
Does the right slot contain a value of the indicated type?
|
||||
|
||||
"array?", dest, right
|
||||
|
||||
"blob?", dest, right
|
||||
|
||||
"character?", dest, right
|
||||
|
||||
"data?", dest, right
|
||||
|
||||
"digit?", dest, right
|
||||
|
||||
"false?", dest, right
|
||||
|
||||
"fit?", dest, right
|
||||
|
||||
"function?", dest, right
|
||||
|
||||
"integer?", dest, right
|
||||
|
||||
"letter?", dest, right
|
||||
|
||||
"logical?", dest, right
|
||||
|
||||
"null?", dest, right
|
||||
|
||||
"pattern?", dest, right
|
||||
|
||||
"record?", dest, right
|
||||
|
||||
"stone?", dest, right
|
||||
|
||||
"text?", dest, right
|
||||
|
||||
"true?", dest, right
|
||||
|
||||
"upper?", dest, right
|
||||
|
||||
"whitespace?", dest, right
|
||||
|
||||
### Potpourri
|
||||
|
||||
"stone", dest, right // stone an object
|
||||
|
||||
"true", dest
|
||||
|
||||
"false", dest
|
||||
|
||||
"null", dest
|
||||
|
||||
"move", dest, right
|
||||
|
||||
"int", dest, small_int
|
||||
|
||||
"access", dest, literal
|
||||
|
||||
This is used to access values (numbers, texts) from the program's immutable memory. The literal is a number or text.
|
||||
|
||||
"load", dest, object, subscript
|
||||
|
||||
This is used to load values from records and arrays.
|
||||
|
||||
"store", dest, object, subscript
|
||||
|
||||
This is used to store values into records and arrays.
|
||||
|
||||
"delete", object, subscript
|
||||
|
||||
used to delete a field from a record.
|
||||
|
||||
"get", dest, slot, level
|
||||
|
||||
This is used to get values from slots in outer frames.
|
||||
|
||||
"put", value, slot, level
|
||||
|
||||
This is used to store values into slots in outer frames.
|
||||
|
||||
"push", array, slot
|
||||
|
||||
Append to a mutable array.
|
||||
|
||||
"pop", dest, array
|
||||
|
||||
Remove the last element of a mutable array, putting the element into dest.
|
||||
|
||||
"disrupt"
|
||||
|
||||
### Make
|
||||
|
||||
"array", dest, nr_elements
|
||||
|
||||
"blob", dest, nr_bits
|
||||
|
||||
"function", dest, code_name_text
|
||||
|
||||
"pretext", dest, nr_characters
|
||||
|
||||
A pretext must be converted to text by stone before it can leave the function scope.
|
||||
|
||||
"record", dest, nr_elements
|
||||
|
||||
@@ -574,7 +574,21 @@ int cell_init(int argc, char **argv)
|
||||
if (JS_IsException(result)) {
|
||||
JSValue exc = JS_GetException(ctx);
|
||||
const char *str = JS_ToCString(ctx, exc);
|
||||
if (str) { printf("Exception: %s\n", str); JS_FreeCString(ctx, str); }
|
||||
if (str) { printf("Error: %s\n", str); JS_FreeCString(ctx, str); }
|
||||
cJSON *stack = JS_GetStack(ctx);
|
||||
if (stack) {
|
||||
int n = cJSON_GetArraySize(stack);
|
||||
for (int i = 0; i < n; i++) {
|
||||
cJSON *fr = cJSON_GetArrayItem(stack, i);
|
||||
const char *fn = cJSON_GetStringValue(cJSON_GetObjectItem(fr, "function"));
|
||||
const char *file = cJSON_GetStringValue(cJSON_GetObjectItem(fr, "file"));
|
||||
int line = (int)cJSON_GetNumberValue(cJSON_GetObjectItem(fr, "line"));
|
||||
int col = (int)cJSON_GetNumberValue(cJSON_GetObjectItem(fr, "column"));
|
||||
printf(" at %s (%s:%d:%d)\n", fn ? fn : "<anonymous>", file ? file : "<unknown>", line, col);
|
||||
}
|
||||
cJSON_Delete(stack);
|
||||
}
|
||||
JS_FreeValue(ctx, exc);
|
||||
} else if (!JS_IsNull(result)) {
|
||||
const char *str = JS_ToCString(ctx, result);
|
||||
if (str) { printf("%s\n", str); JS_FreeCString(ctx, str); }
|
||||
@@ -716,6 +730,20 @@ int cell_init(int argc, char **argv)
|
||||
printf("Error: %s\n", err_str);
|
||||
JS_FreeCString(ctx, err_str);
|
||||
}
|
||||
cJSON *stack = JS_GetStack(ctx);
|
||||
if (stack) {
|
||||
int n = cJSON_GetArraySize(stack);
|
||||
for (int i = 0; i < n; i++) {
|
||||
cJSON *fr = cJSON_GetArrayItem(stack, i);
|
||||
const char *fn = cJSON_GetStringValue(cJSON_GetObjectItem(fr, "function"));
|
||||
const char *file = cJSON_GetStringValue(cJSON_GetObjectItem(fr, "file"));
|
||||
int line = (int)cJSON_GetNumberValue(cJSON_GetObjectItem(fr, "line"));
|
||||
int col = (int)cJSON_GetNumberValue(cJSON_GetObjectItem(fr, "column"));
|
||||
printf(" at %s (%s:%d:%d)\n", fn ? fn : "<anonymous>", file ? file : "<unknown>", line, col);
|
||||
}
|
||||
cJSON_Delete(stack);
|
||||
}
|
||||
JS_FreeValue(ctx, exc);
|
||||
exit_code = 1;
|
||||
} else if (!JS_IsNull(result)) {
|
||||
const char *str = JS_ToCString(ctx, result);
|
||||
|
||||
4037
source/quickjs.c
4037
source/quickjs.c
File diff suppressed because it is too large
Load Diff
@@ -1260,6 +1260,11 @@ char *JS_Mcode (const char *ast_json);
|
||||
Returns result of execution, or JS_EXCEPTION on error. */
|
||||
JSValue JS_CallMcode (JSContext *ctx, const char *mcode_json);
|
||||
|
||||
/* Get stack trace as cJSON array of frame objects.
|
||||
Returns NULL if no register VM frame is active.
|
||||
Caller must call cJSON_Delete() on the result. */
|
||||
struct cJSON *JS_GetStack (JSContext *ctx);
|
||||
|
||||
/* Integrate a CellModule with an environment and execute.
|
||||
Returns callable function value, or JS_EXCEPTION on error. */
|
||||
JSValue cell_module_integrate (JSContext *ctx, CellModule *mod, JSValue env);
|
||||
|
||||
39
tests/demo.ce
Normal file
39
tests/demo.ce
Normal file
@@ -0,0 +1,39 @@
|
||||
function safe_add(a, b) {
|
||||
return a + b
|
||||
} disruption {
|
||||
print("disruption caught in safe_add")
|
||||
}
|
||||
|
||||
function inner() {
|
||||
disrupt
|
||||
}
|
||||
|
||||
function outer() {
|
||||
inner()
|
||||
} disruption {
|
||||
print("disruption caught in outer — from inner()")
|
||||
}
|
||||
|
||||
// Test 1: explicit disrupt with handler
|
||||
function test_explicit() {
|
||||
disrupt
|
||||
} disruption {
|
||||
print("test 1: explicit disrupt handled")
|
||||
}
|
||||
test_explicit()
|
||||
|
||||
// Test 2: type error disrupt (number + function)
|
||||
safe_add(1, print)
|
||||
|
||||
// Test 3: unwinding — inner disrupts, outer catches
|
||||
outer()
|
||||
|
||||
// Test 4: disrupt from inside disruption clause
|
||||
function test_nested() {
|
||||
disrupt
|
||||
} disruption {
|
||||
print("test 4: first disruption")
|
||||
}
|
||||
test_nested()
|
||||
|
||||
print("done")
|
||||
3382
vm_suite.ce
Normal file
3382
vm_suite.ce
Normal file
File diff suppressed because it is too large
Load Diff
@@ -252,29 +252,21 @@ loop: for (var m = 0; m < 3; m++) {
|
||||
}
|
||||
}
|
||||
|
||||
// --- try/catch/finally ---
|
||||
var tc
|
||||
try {
|
||||
throw "error"
|
||||
} catch (e) {
|
||||
tc = e
|
||||
// --- disrupt and disruption ---
|
||||
function disrupt_test() {
|
||||
disrupt
|
||||
}
|
||||
|
||||
var tcf = 0
|
||||
try {
|
||||
throw "err"
|
||||
} catch (e) {
|
||||
tcf = 1
|
||||
} finally {
|
||||
tcf += 10
|
||||
function disruption_test() {
|
||||
var x = 1
|
||||
} disruption {
|
||||
var y = 2
|
||||
}
|
||||
|
||||
// --- try/finally (no catch) ---
|
||||
var tf = 0
|
||||
try {
|
||||
tf = 1
|
||||
} finally {
|
||||
tf += 1
|
||||
function disrupt_with_disruption() {
|
||||
disrupt
|
||||
} disruption {
|
||||
var handled = true
|
||||
}
|
||||
|
||||
// --- delete operator ---
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
try { 1 } catch(e) { 2 }
|
||||
@@ -1 +0,0 @@
|
||||
var x = 1; try { throw 0 } catch(e) { x = 2 } finally { x = x + 1 }; x
|
||||
@@ -1 +0,0 @@
|
||||
var x = 1; try { x = 2 } finally { x = 3 }; x
|
||||
@@ -1 +0,0 @@
|
||||
try { throw "err" } catch(e) { e }
|
||||
Reference in New Issue
Block a user