more/better examples

This commit is contained in:
Richard Thier 2025-01-27 04:03:44 +01:00
parent 9ec2dffd1b
commit fb236bd835
6 changed files with 163 additions and 35 deletions

18
examples/builtins.txt Normal file
View File

@ -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!

View File

@ -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{}
}
}
]
]
;

27
examples/dowhile.slc Normal file
View File

@ -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{}
;

View File

@ -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
;

11
examples/if_compiler.slc Normal file
View File

@ -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$:)
;

20
examples/loop.slc Normal file
View File

@ -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!