more/better examples
This commit is contained in:
parent
9ec2dffd1b
commit
fb236bd835
18
examples/builtins.txt
Normal file
18
examples/builtins.txt
Normal 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!
|
@ -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
27
examples/dowhile.slc
Normal 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{}
|
||||
;
|
@ -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
11
examples/if_compiler.slc
Normal 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
20
examples/loop.slc
Normal 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!
|
Loading…
x
Reference in New Issue
Block a user