Pebble Lang/VM Documentation

Pebble is an ultralight virtual machine and programming language
with the goal of being small, lightweight, and easy to embed into 
a larger project where larger scripting languages, such as Lua, are
too large, but where safety is still needed.

Pebble is designed to be simple at the VM / interpreter layer. Due
to this, it has no types or compiler, is written entirely in Zig and its own language, 
and compiles down to one single binary that includes embedded documentation, a 
test suite, virtual machine, interpreter, version information, and standard
library in less than 100kb compiled. It's only dependencies are a Zig compiler
and Zig standard library at compile-time and a POSIX libc and dynamic loader
at runtime (tested on OpenBSD 7.9 AMD64).


The VM embeds a few programs into itself, such as the main runner,
the documentation reader, the version tool, and the test suite. These can be
accessed by

> zig build

This should output a single binary at the root of the source dir named vm. 

./vm docs - reads the documentation 
./vm version - reads version info
./vm (filename) - runs a script written in Pebble
./vm test - runs the test suite, which is also written in Pebble.


The VM is keyword-based, meaning there are a few small keywords used to
perform functions. These functions are conspired to be the bare minimum 
needed to have a working language. The rest of functions are provided by a
Zig standard library.

VM keywords include

New - create / modify a variable
Func - start recording a function
End - stop recording a function
If - execute a function if the condition is true
Return - return from a function early
Call - call a recorded function
Escape - call a library function

Syntax for these keywords includes

	New (varname) (payload) - Assign a variable of the name varname with the
		data payload of payload. This keyword allows for the full suite of expressions.

	Func (funcname) - Begins recording instructions into the code tables until the
		End keyword is called to allow the function to be executed using the Call
		keyword at any point after it is defined.

	End - Ends recording of a function, allowing it to be called by the Call keyword.

	If (funcname) (condition) - Executes function funcname only if the result of
		condition is 0. Since this language is type-less, 0 represents true and 1 
		represents false. This keyword supports the full suite of expressions.

	Call (funcname) - Begin executing a stored function under the name funcname
		immediately unconditionally.

	Escape (sequence) - Run a library function compiled into the VM binary.


Some keywords allow the payload to be an expression. Expressions are resolved
before the operation and the data is simply substituted into the instruction after
it is resolved.

Each keyword has a value assigned at compile-time via src/limits.zig which defines how many
times that instruction is allowed to be executed. If that limit is exceeded, then the VM
simply kills itself.

Standard expression for this syntax includes
	Quoting rules
		" " = Literal - Perform an expression and return the result. Substitute 
				keywords that are not proper expression syntax with a 
				variable. Look up said variable and replace it with the
				data in said variable, then perform the operation.

		' ' = True Literal - Do not attempt to evaluate the data in any way.

		{ } = Forced Literal - Treat the quoted data as an indirect. Assume that
				is the name of a variable holding an expression and evaluate
				said expression as a Literal.

		No marking = Indirect - Replace the section with the data in said variable.

	Mathematical operations
		+ = Add the left and right
		- = Subtract right from the left
		/ = Divide the left from the right
		* = Multiply the left by the right

	Comparison operations
		> - Return 0 if the left is greater than the right, else return 1
		< - Return 0 if the right is greater than the left, else return 1
		== - Return 0 if the right is equal to the left, else return 1
		!= - Return 0 if the right is not equal to the left, else return 1

	String operations
		S++ - Merge the two strings into one, left string first
		?= - Return 0 if the two strings are equal, else return 1
		-?= - Return 0 if the substring on the left is found in the right string, else return 1
		s?= - Return 0 if the string on the right starts with the substring on the left, else return 1
		e?= - Return 0 if the string on the right ends with the substring on the left, else return 1

Arguments
	The VM provides variables VMARGC as the number of arguments provided and VMARGn as the data given as said
	argument. VMARG1 will be the name of the VM binary and should always be present, meaning VMARGC should always
	be at least 1.

The Standard Library
	For simplicity, the standard library (everything referenced by zig/libs/libs.zig) is simply compiled straight
	into the main VM binary. This allows zero runtime dependencies for the VM after compile-time. To add a library,
	create a .zig file in libs/zig/ performing the operation, add that file to src/libs/libs.zig and then add it
	to src/escapes.zig. To remove one, simply comment out the line adding it in src/escapes.zig.



	I/O
		Print - Takes one variable, ARG1, as an Indirect. Prints the data in said variable referenced by the
			indirect.
		PrintLn - Same as Print, but there will not be a trailing newline character.

		Input (POSIX) - Gets input from stdin and returns it in the variable input.

	Fs
		Fwrite (POSIX) - Takes two variables, ARG1 and ARG2 as indirects. Writes the data in the variable 
			referenced by the indirect in ARG1 to the name of the file name in the variable mentioned by ARG2.

		Fbwrite (POSIX) - Takes two variables, ARG1 and ARG2 as indirects. Writes the data in the variable 
			referenced by the indirect in ARG1 to the name of the file name in the variable mentioned 
			by ARG2. Does not format the data in any way.

		Fread (POSIX) - Takes one variable, ARG1, as an indirect. Reads the data in the variable referenced by the
			indirect as a file name. Reads the data in the file into the variable fileData.

		ListDir (POSIX) - Takes one variable, ARG1, as an indirect. Reads the contents of the directory name held
			in the variable referenced by the indirect and saves it to the variable dirContents, formatted
			as a string full of dir entries separated by newlines with one newline at the end.

		Gcwd (POSIX) - Gets the current working directory and saves it to variable cwd.

		Chcwd (POSIX) - Takes one variable, ARG1, as an indirect. Reads the data in the variable referenced by the
			indirect and changes the working directory to that new directory.

	Misc
		Random - Generates a random number and stores it into the variable random. NOT CRYPTOGRAPHIC
		Time (POSIX) - Grabs the current time and formats it all fancy.

	Str
		StrLen - Takes one variable, ARG1, as an indirect. Gets the length of the string in the variable
			referenced by said indirect and places it into the variable len.

		StrSplitNumL - Takes two variables, ARG1 and ARG2, as indirects. Returns the contents of the
			substring in the variable referenced by ARG1 before entry N in the variable referenced by ARG2.
			The substring is placed into the variable strLeft.

		StrSplitNumR - Takes two variables, ARG1 and ARG2, as indirects. Returns the contents of the
			substring in the variable referenced by ARG1 after entry N in the variable referenced by ARG2.
			The substring is placed into the variable strRight.

	Proc (POSIX)
		SpawnProc (POSIX) - Takes on arg, ARG1, as an indirect to the command and arguments of the process to run. 
			Spawns a POSIX process using the data in said variable referenced by said indirect and returns
			the PID of the new process in the variable pid. This treats the indirect address as an array-like
			structure - one string separated by spaces, the first being the command and the ones after it being
			the arguments of the command. Items in said array-like structure will be treated as indirects unless
			they do not match a variable name.

		WaitPid (POSIX) - Takes one arg, ARG1, as an indirect to the variable holding the POSIX PID of the
			process to wait for. Waits for said process to complete execution and will return variables
			killedBySig with the signal type it was killed by and otherTermination as true if it was 
			killed, or exitCode as the exit code of the process and otherTermination as false if it was
			not killed.

		Exit (POSIX) - Kills the VM process.

Examples

	New msg 'Hello, World!' # define a var named msg holding true literal Hello, World!
	New ARG1 'msg' # define a var named ARG1 holding true literal msg
	Escape Print # call the escape sequence Print 

	# since these are literals, they are evaluated then stored
	New a "ab -?= cd" # stores 0 if true
        New b "ab e?= cd" # stores 0 if true
        New c "ab s?= cd" # stores 0 if true
        New d "ab ?= cd" # stores 0 if true
        New e "ab s++ cd" # stores "abcd"
        New f "e s++ cd" # stores "abcdcd"

	# loop made from primitives
        Func count # start recording to func count
        New ARG1 'counter' # set ARG1 to true literal counter
        Escape Print # print
        New counter "counter - 1" # set counter to literal counter - 1
        If count "counter > 0" # run function count if expression counter > 0 = 0 (true)
        End # stop recording function
        New counter '10' # set counter to true literal 10
        Call count # begin executing function count


