(dm me if you have a better name)
anyway, on to the things you may not know
because i made it to write parsers, one of the features i have planned is simple backtracking
like when you're (i was) writing a recursive descent parser with parser combinators, the concat combinator is hard to write in a non recursive way (you have to write your own stack)
so that's one feature i planned
then to have backtracking in a "sane" way, the values have to be immutable
at some point i thought "oh jq does basically the same thing right?" and copied the entire jv (jq value) implementation and called it pv
(and quickly found out how much the different jv kinds (types) reference internal details)
so i just copied (most of) the header files and some of the implementation for the more complex kinds
but now i'm mostly done with rewriting jv in a way that i like
so it has immutable values like jv and 9 kinds: null, bool, int, double, string, array, object, iterator, function (i split number into int and double, and added iterator and function)
it also has some functions with single or double dispatch - equal, hash, to_string
now on top of pv i made pl, which is a based stack vm (like lua)
it's not too unusual (i think) - there are a couple of opcode sections:
+
-
*
/
etc.i currently have functional iteration and function calls - both as normal function and as generator
there's also an assembler, which takes code like this:
def n1 num 4 def n2 num 34 def a arr n1 n2 defglobals add a defmain main func add // takes 2 arguments add ret endfunc func main pushglobal 0 pushint 15 pushint 52 call 2 ret endfunc
and a debug code executor (which can execute that assembled code):
main bytecode: PUSHGLOBAL 0 PUSHINT 15 PUSHINT 52 CALL 2 RET return value: .: 67 global 0: .: idk it has kind 8 (pl_func) global 1: .: [] ./0: 4.000000 ./1: 34.000000
and finally, i have a compiler (Try It Online!)
it's a simple tree walking / recursive descent compiler with no optimization
some optimizations that i could implement are:
this is a reference if you want to write stuff in parserlang
amount of whitespace (including newlines) is not significant, and there are no semicolons or other statement delimiters
there are only single line comments with #
string literals are double quoted
there are a few escape sequences: \\, \", \n, \x## (hex escape code)
all functions are like generators
there are two ways to call a function: direct (normal function call) and using the gcall() builtin
a direct call acts like a normal function call - it returns one value at a time - and it adds a backtracking marker so the rest of the code is run with each value yielded from the call
a gcall() call returns a generator / iterator instead
variables are declared and defined at the same time using the keyword def: def <variable> = <value>
variable assignment is (as usual) <variable> = <value>
return may have a value or not
it yields a value if it has one and then ends the execution of the function
yield always has a value
it yields the value and does not end the function
block statement is just a list of statements enclosed by {}
if statement is if <condition> then <body (statement or block)>
the condition does not need parentheses
there is no else or else if statement... :/
for statement is for <variable> in <expression (iterable)> do <body (statement or block)>
while statement is while <condition> do <body (statement or block)>
a function definition is fn <name>(<argument names>) <body (always a block)>
fun fact: i may have decided on no parentheses in if and while and defining functions with fn before i got into rust
all variables are confined to their scope, which starts at the definition and ends at the surrounding brackets (except in cases like if condition then def var = 3, where var's scope ends after the if statement (this is useless (why would you do this))
function definitions are also scoped like variables, but they are hoisted to the top of their scope like in javascript
there are a few builtin functions right now