mirror of
https://github.com/krgamestudios/Toy.git
synced 2026-04-15 14:54:07 +10:00
Wrote SECD-concept.txt, probably overdid it
This commit is contained in:
173
.notes/SECD-concept.txt
Normal file
173
.notes/SECD-concept.txt
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
SECD = State, Environment, Control, Dump
|
||||||
|
|
||||||
|
The idea of "Landin's SECD Machine" is to store the working memory in S, the variable-value bindings in E, the code/instructions in C, and the program stack in D.
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
DEFINE = DECLARE + SET
|
||||||
|
|
||||||
|
The environment, denoted with an E, is created on routine start, and destroyed on routine end - however, it uses the parent routine's environment as the starting point for it's creation, so closures work as expected
|
||||||
|
|
||||||
|
unlike version 1, identifiers are not a valid datatype.
|
||||||
|
|
||||||
|
placeholder opcodes - EOF, PASS, ERROR,
|
||||||
|
|
||||||
|
Things to consider later:
|
||||||
|
type cast?
|
||||||
|
rest parameter?
|
||||||
|
index access and assign?
|
||||||
|
|
||||||
|
===
|
||||||
|
|
||||||
|
//general instructions
|
||||||
|
READ
|
||||||
|
read one value from C onto S
|
||||||
|
LOAD
|
||||||
|
read one value from .data onto S
|
||||||
|
ASSERT
|
||||||
|
if S(-1) is falsy, print S(0) and exit
|
||||||
|
PRINT
|
||||||
|
pop S(0), and print the output
|
||||||
|
SET
|
||||||
|
read one word from C, saves the key E[word] to the value S(0), popping S(0)
|
||||||
|
GET
|
||||||
|
read one word from C, finds the value of E[word], leaves the value on S
|
||||||
|
DECLARE
|
||||||
|
read two words from C, create a new entry in E with the key E[word1], the type defined by word2, the value 'null'
|
||||||
|
DEFINE
|
||||||
|
read two words from C, create a new entry in E with the key E[word1], the type defined by word2, the value popped from S(0)
|
||||||
|
|
||||||
|
|
||||||
|
//arithmetic instructions
|
||||||
|
ADD
|
||||||
|
performs the specified operation on S(-1) and S(0), popping both, leaving the result on S
|
||||||
|
SUBTRACT
|
||||||
|
performs the specified operation on S(-1) and S(0), popping both, leaving the result on S
|
||||||
|
MULTIPLY
|
||||||
|
performs the specified operation on S(-1) and S(0), popping both, leaving the result on S
|
||||||
|
DIVIDE
|
||||||
|
performs the specified operation on S(-1) and S(0), popping both, leaving the result on S
|
||||||
|
MODULO
|
||||||
|
performs the specified operation on S(-1) and S(0), popping both, leaving the result on S
|
||||||
|
|
||||||
|
|
||||||
|
//comparison instructions
|
||||||
|
COMPARE_EQUAL
|
||||||
|
pops S(-1) and S(0), replacing it with TRUE or FALSE, depending on equality
|
||||||
|
COMPARE_LESS
|
||||||
|
pops S(-1) and S(0), replacing it with TRUE or FALSE, depending on comparisoncomparison
|
||||||
|
COMPARE_LESS_EQUAL
|
||||||
|
pops S(-1) and S(0), replacing it with TRUE or FALSE, depending on comparison
|
||||||
|
COMPARE_GREATER
|
||||||
|
pops S(-1) and S(0), replacing it with TRUE or FALSE, depending on comparison
|
||||||
|
COMPARE_GREATER_EQUAL
|
||||||
|
pops S(-1) and S(0), replacing it with TRUE or FALSE, depending on comparison
|
||||||
|
|
||||||
|
|
||||||
|
//logical instructions
|
||||||
|
AND
|
||||||
|
pops S(-1) and S(0), replacing it with TRUE or FALSE, depending on truthiness
|
||||||
|
OR
|
||||||
|
pops S(-1) and S(0), replacing it with TRUE or FALSE, depending on truthiness
|
||||||
|
TRUTHY
|
||||||
|
pops S(0), replacing it with TRUE or FALSE, depending on truthiness
|
||||||
|
INVERT
|
||||||
|
pops S(0), replacing it with TRUE or FALSE, depending on truthiness
|
||||||
|
|
||||||
|
|
||||||
|
//control instructions
|
||||||
|
JUMP
|
||||||
|
read one value from C, and move the program counter to that location
|
||||||
|
JUMP_IF_FALSE
|
||||||
|
read one value from C, pops S(0), and move the program counter to that location if the popped value is falsy
|
||||||
|
FN_CALL
|
||||||
|
*read a list of arguments specified in C into 'A', store (S, E, C, D) as D, wipe S and E, move the stack pointer to the specified routine, set E based on the contents of 'A'
|
||||||
|
FN_RETURN
|
||||||
|
*read a list of return values specified in C into 'R', wipe S and E, restoroutine re (S, E, C, D) from D(0) popping it, store the contents of 'R' in E or S based on the next few parts of C
|
||||||
|
|
||||||
|
//bespoke utility instructions
|
||||||
|
IMPORT
|
||||||
|
//invoke an external library into the current scope
|
||||||
|
CONCAT
|
||||||
|
//combine two strings
|
||||||
|
SCOPE_BEGIN
|
||||||
|
//push an inner environment to E, which should be automatically popped at the routine's end
|
||||||
|
SCOPE_END
|
||||||
|
//pop an inner environment from E, only if it was created with SCOPE_BEGIN
|
||||||
|
|
||||||
|
===
|
||||||
|
|
||||||
|
FN_CALLonly
|
||||||
|
read word: read the following N arguments
|
||||||
|
|
||||||
|
for 0 to N do:
|
||||||
|
read word as match: # this allows literals and identifiers as arguments
|
||||||
|
stack: then pop S(0) into 'A'
|
||||||
|
**env: then read word, load E[word]*** into 'A'
|
||||||
|
|
||||||
|
read word:
|
||||||
|
store (S,E,C,D) as D
|
||||||
|
wipe S and E
|
||||||
|
jump C to routines[word]
|
||||||
|
|
||||||
|
read word:
|
||||||
|
read the following N parameter names, storing each member of 'A' as their value in E[name]***
|
||||||
|
|
||||||
|
continue
|
||||||
|
|
||||||
|
FN_RETURN
|
||||||
|
read word: read the following N return values
|
||||||
|
|
||||||
|
for 0 to N do:
|
||||||
|
read word as match: # this allows literals and identifiers as arguments
|
||||||
|
stack: then pop S(0) into 'R'
|
||||||
|
**env: then read word, load E[word]*** into 'R'
|
||||||
|
|
||||||
|
restore (S,E,C,D) from D(0), popping it # this wipes S and C from the routine, and returns C to the pre-call position
|
||||||
|
|
||||||
|
read word: read the following N storage locations for the values within `R`
|
||||||
|
|
||||||
|
for 0 to N do:
|
||||||
|
read word as match: # you're effectively reversing the prior reads
|
||||||
|
stack: then push from 'R' onto S
|
||||||
|
**env: then read word, save 'R' into E[word]***
|
||||||
|
|
||||||
|
**This could work by listing the sources as e.g. "SSSExS" - three stacks and one environment variable loaded onto the stack, then one more stack for a total of four values
|
||||||
|
|
||||||
|
***E[word] would more accurately be E[.data[word]], where '.data' is for the currently loaded routine
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
the bytecode of a funtion call would look like:
|
||||||
|
|
||||||
|
FN_CALL N [stack|env word]... N [stack|env word]...
|
||||||
|
|
||||||
|
the value of C stored in D points to the second N, while it waits to pick up where it left off
|
||||||
|
|
||||||
|
===
|
||||||
|
|
||||||
|
.header:
|
||||||
|
N total length
|
||||||
|
N .args count
|
||||||
|
N .data count
|
||||||
|
N .routine count
|
||||||
|
.args start
|
||||||
|
.code start
|
||||||
|
.datatable start
|
||||||
|
.data start
|
||||||
|
.routine start
|
||||||
|
//any additional metadata can go here
|
||||||
|
|
||||||
|
.args: # these keys stored in E before execution begins
|
||||||
|
|
||||||
|
.code:
|
||||||
|
READ 0
|
||||||
|
LOAD 0
|
||||||
|
ASSERT
|
||||||
|
|
||||||
|
.datatable: # could list the starts as a jump table, since members of data and routines have unknown sizes
|
||||||
|
0 -> 0x00
|
||||||
|
|
||||||
|
.data:
|
||||||
|
"Hello world"
|
||||||
|
|
||||||
|
.routines: # this stores inner routines, in sequence
|
||||||
|
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
The following opcodes would be embedded into the final bytecode
|
|
||||||
|
|
||||||
===
|
|
||||||
|
|
||||||
//idk
|
|
||||||
EOF, PASS, ERROR,
|
|
||||||
|
|
||||||
//general commands
|
|
||||||
READ_INTO
|
|
||||||
[register] //read from the bytecode into the stack
|
|
||||||
LOAD_INTO
|
|
||||||
[register, data] //load from the data section into the stack
|
|
||||||
ASSERT
|
|
||||||
[register, "message"] //message could be embedded, possibly in a data section
|
|
||||||
PRINT
|
|
||||||
[register]
|
|
||||||
|
|
||||||
//can double as "assign"
|
|
||||||
ADD
|
|
||||||
[lhs, rhs, dest]
|
|
||||||
SUBTRACT
|
|
||||||
[lhs, rhs, dest]
|
|
||||||
MULTIPLY
|
|
||||||
[lhs, rhs, dest]
|
|
||||||
DIVIDE
|
|
||||||
[lhs, rhs, dest]
|
|
||||||
MODULO
|
|
||||||
[lhs, rhs, dest]
|
|
||||||
|
|
||||||
//GT can be replaced, probably
|
|
||||||
COMPARE_EQUAL
|
|
||||||
[lhs, rhs, dest]
|
|
||||||
COMPARE_NOT
|
|
||||||
[lhs, rhs, dest]
|
|
||||||
COMPARE_LESS
|
|
||||||
[lhs, rhs, dest]
|
|
||||||
COMPARE_LESS_EQUAL
|
|
||||||
[lhs, rhs, dest]
|
|
||||||
COMPARE_GREATER
|
|
||||||
[lhs, rhs, dest]
|
|
||||||
COMPARE_GREATER_EQUAL
|
|
||||||
[lhs, rhs, dest]
|
|
||||||
|
|
||||||
COMPARE_TRUTHY
|
|
||||||
[lhs, dest]
|
|
||||||
AND
|
|
||||||
[lhs, rhs, dest]
|
|
||||||
OR
|
|
||||||
[lhs, rhs, dest]
|
|
||||||
INVERT
|
|
||||||
[lhs, dest]
|
|
||||||
|
|
||||||
//jump around the bytecode
|
|
||||||
JUMP
|
|
||||||
[pos]
|
|
||||||
JUMP_IF_FALSE
|
|
||||||
[pos, lhs]
|
|
||||||
FN_CALL
|
|
||||||
//push args and move
|
|
||||||
FN_RETURN
|
|
||||||
//push results and move - closures are required
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
CONCAT?
|
|
||||||
[lhs, rhs, dest]
|
|
||||||
|
|
||||||
|
|
||||||
IMPORT? (as?)
|
|
||||||
|
|
||||||
RETURN? //some kind of jump with a return value
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
SCOPE_BEGIN?
|
|
||||||
SCOPE_END?
|
|
||||||
|
|
||||||
cast?
|
|
||||||
rest?
|
|
||||||
index access and assign?
|
|
||||||
Reference in New Issue
Block a user