From fb236bd835613130a4d833ab3f9726217474cf51 Mon Sep 17 00:00:00 2001 From: Richard Thier Date: Mon, 27 Jan 2025 04:03:44 +0100 Subject: [PATCH] more/better examples --- examples/builtins.txt | 18 +++++++++++ examples/do.slc | 52 +++++++++++++++-------------- examples/dowhile.slc | 27 ++++++++++++++++ examples/if.slc | 70 ++++++++++++++++++++++++++++++++++------ examples/if_compiler.slc | 11 +++++++ examples/loop.slc | 20 ++++++++++++ 6 files changed, 163 insertions(+), 35 deletions(-) create mode 100644 examples/builtins.txt create mode 100644 examples/dowhile.slc create mode 100644 examples/if_compiler.slc create mode 100644 examples/loop.slc diff --git a/examples/builtins.txt b/examples/builtins.txt new file mode 100644 index 0000000..3e42ade --- /dev/null +++ b/examples/builtins.txt @@ -0,0 +1,18 @@ +We can just put all "builtins" into a file, then ship together with SLC and let it use them. + +I think this is cleaner than adding syntax that makes the parser and interpreter just unnecessarily more complex... + +So there does not need to be the following 4 variations: + +: regularword +:: compileword +:builtin regularword +::builtin compileword + +^^ if I somehoe let the interpreter know what the builtins are and define them all together in a differen parser state... + +So the parser would in that mode prefix everything with the prefix - and in the symbol table of course enter the real C builtins +with the prefix too. Also in the calls / code of these, we would prefix everything to look up well in the symbol table so the code +for builtins can be totally unprefixed and all. + +This really helps to simplify the parser-statemachine in my opinion - also the interpreter state machine should be unaffected! diff --git a/examples/do.slc b/examples/do.slc index 224ece5..0a14171 100644 --- a/examples/do.slc +++ b/examples/do.slc @@ -1,26 +1,28 @@ -/* -Parses this: +// A parseblock_outer should change return address, +// so parsing continues at a different location! +// This is needed because we have processed the +// stuff AFTER the word "do"... - do { - ...code... - } (...cond...) - -Into this: - - ...code... - while(...cond...) { - ...code... - } -*/ - -// -> [bytecodes] -::builtin do @body - "" parseblock {} // "" means no entry into the symbol table (unnamed function) - equals to 0 - do@body(.) // (.) does not removes - only (_) no need for gencode because - gen(while) // address top bit 1 when builtin from C - genopen() // equal to gencode(1) - "" parseblock () - genclose() - genopen{} - do@body - genclose{} +::builtin do + parseword + if("(" streq) { + drop parseword + if(")" streq) { + "" parseblock_outer() + } + } [ + if("[" streq) { + drop parseword + if("]" streq) { + "" parseblock_outer[] + } + } [ + if("{" streq) { + drop parseword + if("}" streq) { + "" parseblock_outer{} + } + } + ] + ] +; diff --git a/examples/dowhile.slc b/examples/dowhile.slc new file mode 100644 index 0000000..ab89f3e --- /dev/null +++ b/examples/dowhile.slc @@ -0,0 +1,27 @@ +/* +Parses this: + + dowhile { + ...code... + } (...cond...) + +Into this: + + ...code... + while(...cond...) { + ...code... + } +*/ + +// -> [bytecodes] +::builtin dowhile @body + "" parseblock {} // "" means no entry into the symbol table (unnamed function) - equals to 0 + do@body(.) // (.) does not removes - only (_) no need for gencode because + gen(while) // address top bit 1 when builtin from C + genopen() // equal to 1 gencode(_) + "" parseblock () + genclose() + genopen{} + do@body + genclose{} +; diff --git a/examples/if.slc b/examples/if.slc index 0556c6b..35ab44f 100644 --- a/examples/if.slc +++ b/examples/if.slc @@ -1,12 +1,62 @@ -// prefixed builtins with '#' -// when writing a compiler - like a c-like language +// Implementing if statement if its not a built-in in SLC! +// Not compiler writing in SLC language, but SLCs builtin itself... -:if - () - #asm(jz else$) - {} - #asm(jmp ifend$) - #asm(else$:) - [] - #asm(ifend$:) +::builtin if @cmp @then @else + "" parseblock() // (*) + if@cmp(.) + "" parseblock{} + if@then(_) + // returns 0 if there was no [] block! + // Calling 0 means just calling the + // intstruction pointer ("avoiding call") + "" parseblock[] + if@else(_) + + // (*) cmp result at top of stack + gen(!) gen(skipif) if@then + gen(skipif) if@else +; + +// Alternatively - by adding "branchcall" to the builtins +::builtin if + "" parseblock() + genpush(_) + "" parseblock{} + genpush(_) + "" parseblock[] // returns 0 if there was no block (its SKIP to 'call' 0) + genpush(_) + gen(branchcall) +; + +// Rem.: "branchcall" can be used in "real" code to split which word to call... + +: smaller + "smaller" +; + +: bigger + "bigger-or-eq" +; + +// Gets a b +// returns "smaller" or "bigger-or-eq" +: smaller_or_bigger + < + gen(smaller) + gen(bigger) + branchcall +; + +// Better example + +: one 1 ; +: zero 0 ; + +// Gets a b +// returns 1 if a < b, 0 otherwise +: smaller_or_bigger + < + gen(one) + gen(zero) + branchcall ; diff --git a/examples/if_compiler.slc b/examples/if_compiler.slc new file mode 100644 index 0000000..fe11bfc --- /dev/null +++ b/examples/if_compiler.slc @@ -0,0 +1,11 @@ +// when writing a compiler - like a c-like language + +:if + #do() + #asm(jz else$) + #do{} + #asm(jmp ifend$) + #asm(else$:) + #do[] + #asm(ifend$:) +; diff --git a/examples/loop.slc b/examples/loop.slc new file mode 100644 index 0000000..819b5d0 --- /dev/null +++ b/examples/loop.slc @@ -0,0 +1,20 @@ +// Easiest way to create loops are "misusing" the call stack! +// That is instead of creating "real" loops in SLC or labels and GOTOs, +// we can have a word (loop) that basically calls to its own code location. + +// A do-while loop is then as follows: + +loop +.... +if(feltétel) { ret } [ endloop ] + + +// A while-loop is then as follows: + +loop if(feltétel) { +... +ret +} endloop + +// Rem.: "ret" is literally the written-out word for returning; endloop is "removing" (and throwing away) call stack top. +// Rem.: This does not make "loops into recursion" because the stack does not grow while loop is going on!