This file is messy and confusing, and makes sense to nobody but me - so don't worry about understanding it too much - better docs will come later.

===

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 - they're just an index representing a symbol, like "standard::clock"

	meta opcodes - EOF, PASS, ERROR,

	a "value" can be of any valid datatype, and may point to various parts of memory to define it's value

	Symbols will be awkward... I suspect the symbol table might need to be rebuilt on startup, as the order of the modules will not necessarily be the same each time

	The various instances of S could be the same array in memory, simply marked as "unused"? You could stick C on there as a value before "pushing" for a new routine

Things to consider later:
	type cast?
	rest parameter?
	index access and assign?

===

//variable instructions
READ
	read one value from C onto S
LOAD
	read one value from .data onto S
DECLARE
	read two words from C, create a new entry in E with the key E[SYMBOL(word1)], the type defined by word2, the value 'null'
DEFINE
	read one word from C, saves the pre-existing key E[SYMBOL(word)] to the value S(0), popping S(0)
ACCESS
	read one word from C, finds the pre-existing value of E[SYMBOL(word)], leaves the value on S

//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 comparison
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
NEGATE
	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 (relative to the current position)
JUMP_IF_FALSE
	read one value from C, pops S(0), and move the program counter to that location (relative to the current position) 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, push S, move the stack pointer to the specified routine, push a new E based on the contents of 'A'
FN_RETURN
	*read a list of return values specified in C into 'R', pop S, restore (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

//various action instructions
ASSERT
	if S(-1) is falsy, print S(0) and exit
PRINT
	pop S(0), and print the output
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_CALL
	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[SYMBOL(word)] into 'A'

	read word:
		determine where the routine is (is it new or is it a value?) and hold it for a moment
		push E and C into a frame marker on S
		jump C to the routine

	read word:
		read the following N parameter names, storing each member of 'A' as their value in E[SYMBOL(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[SYMBOL(word)] into 'R'

	pop E and S
	extract and restore E and C from the frame marker on S

	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[SYMBOL(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

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

===

