Created
January 11, 2021 19:22
-
-
Save jmdejong/48f36b909100435b8a23cbd7e2101edd to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| { | |
| # macros that translate directly to builtin commands (but know the amount of arguments) | |
| !putchar(c){c .putchar} | |
| !putint(i){i .putint} | |
| !add(a b){a b .add} | |
| !mult(a b){a b .mult} | |
| !mod(a b){a b .div} | |
| !mod(a b){a b .mod} | |
| !exit(code){code .exit} | |
| !jump(to){to .jump} | |
| !jumpifz(condition to){condition to .jumpifz} | |
| !setstack(to){to .setstack} | |
| !not(a){a .not} | |
| # macros that almost translate directly to builtins | |
| !minus(val){val .neg} | |
| !isnegative(a){a .negative} | |
| !deref (ptr) {ptr .movefrom} | |
| !setref(ptr value){ | |
| ptr | |
| value | |
| .swap | |
| .moveto | |
| } | |
| # other macros | |
| !printint(val){ | |
| val | |
| putchar(40) | |
| .putint | |
| putchar(41) | |
| } | |
| !sub(a b){ | |
| add(a minus(b)) | |
| } | |
| !lt (left right){ | |
| isnegative(sub(left right)) | |
| } | |
| !neq(a b){ | |
| sub(a b) | |
| } | |
| !eq (a b){ | |
| not(sub(a b)) | |
| } | |
| !dowhile (condition body)(:whilestart) { | |
| :whilestart | |
| body | |
| jumpifz(not(condition) whilestart) | |
| } | |
| !if(condition body)(:ifend) { | |
| jumpifz(condition ifend) | |
| body | |
| :ifend | |
| } | |
| !and(a b) (:end){ | |
| a | |
| jumpifz(.dup end) | |
| .drop | |
| b | |
| :end | |
| } | |
| !ifelse(condition ifbody elsebody)(:elsepart :ifend) { | |
| jumpifz(condition elsepart) | |
| ifbody | |
| jump(ifend) | |
| :elsepart | |
| elsebody | |
| :ifend | |
| } | |
| !while (condition body) { | |
| if(condition { | |
| dowhile (condition body) | |
| }) | |
| } | |
| !varptr(id) { | |
| add(deref(@funcmem) id) | |
| } | |
| !setvar(id value){ | |
| setref(varptr(id) value) | |
| } | |
| !getvar(id){ | |
| deref(varptr(id)) | |
| } | |
| !addto (pos val){ | |
| setref(pos add(deref(pos) val)) | |
| } | |
| !call(fun)(:returnpos){ | |
| returnpos | |
| jump(fun) | |
| :returnpos | |
| } | |
| !returnm(nreturns){ | |
| # store old stack and old mem location location in temporary globals | |
| setref(@memtmp getvar(-1)) | |
| setref(@calltmp getvar(-2)) | |
| nreturns | |
| @returntmp | |
| .moveto | |
| # move return values to top of regular stack | |
| # source location start | |
| .getstack | |
| minus(deref(@returntmp)) | |
| .add | |
| # amount of return values | |
| deref(@returntmp) | |
| .swap | |
| # destination location start | |
| sub(deref(@funcmem) add(getvar(0) 2)) | |
| # remember destination location for later (because getvar(0) might get overwritten) | |
| .dup | |
| @funcmem | |
| .moveto | |
| # actually move the return values | |
| .memmove | |
| # move stack to end of return values | |
| setstack(add(deref(@funcmem) deref(@returntmp))) | |
| # recall old stack location | |
| setref(@funcmem deref(@memtmp)) | |
| # return to previous position | |
| jump(deref(@calltmp)) | |
| } | |
| !return(val){ | |
| val | |
| returnm(1) | |
| } | |
| !function(label nargs nvars body){ | |
| label | |
| # remember old stack location | |
| deref(@funcmem) | |
| # remember current stack location | |
| .getstack | |
| @funcmem | |
| .moveto | |
| # remember argument number | |
| nargs | |
| # reserve space for local variables | |
| .getstack | |
| nvars | |
| .add | |
| .setstack | |
| # execute function body | |
| body | |
| .getstack | |
| minus(add(add(deref(@funcmem) nvars) 1)) | |
| .add | |
| returnm({ }) | |
| } | |
| !print(string)(:stringstart :stringend){ | |
| stringstart | |
| while( neq(.dup stringend) { | |
| putchar(deref(.dup)) | |
| add({ } 1) | |
| }) | |
| jump(stringend) | |
| :stringstart | |
| string | |
| :stringend | |
| .drop | |
| } | |
| !println(string){ | |
| print(string) | |
| putchar(10) | |
| } | |
| # actual code part | |
| # some initializer metadata | |
| [ | |
| 0 | |
| @codestart # initial location of the code pointer | |
| @codeend # initial location of the stack pointer | |
| ] | |
| # place where the code starts executing | |
| :codestart | |
| .noop | |
| # print all printable ascii characters | |
| 32 # if there is just a number then that number is pushed to the stack | |
| while (lt(.dup 127) { # dup copies the top value of the stack | |
| # curly braces form a code block | |
| # this allows multiple expressions to be passed as a single macro argument | |
| putchar(.dup) | |
| add({ } 1) # { } is an empty code block (without return value). It can be used in place of a macro argument to take the top value from the stack instead | |
| }) | |
| .drop # drop the top value from the stack | |
| # call the printnl function | |
| call(@printnl) | |
| # call the factorial function and print the result as integer | |
| # This function pops one value from the stack | |
| 6 | |
| putint(call(@factorial)) | |
| call(@printnl) | |
| # print some text | |
| println("hello world") | |
| # call the fizzbuzz function, which also takes one value from the stack | |
| #40 | |
| #call(@fizzbuzz) | |
| # end with exit code 0 | |
| exit(0) | |
| # function to print a newline character | |
| function( | |
| :printnl # function name (label) | |
| 0 # number of arguments | |
| 0 # number of local variables | |
| putchar(10) # function body | |
| ) | |
| function ( # define a new function | |
| :factorial # name (label) of the function | |
| 1 # number of arguments | |
| 1 # number of local variables | |
| { # function body | |
| # setvar and getvar set/return local variables | |
| # local variables are represented by positive ids starting at 1 | |
| # arguments are represented by negative ids up to -3 | |
| # the ids -2, -1 and 0 are reserved | |
| setvar(1 1) | |
| while({ getvar(-3)} { | |
| setvar(1 { | |
| mult(getvar(1) getvar(-3)) | |
| }) | |
| setvar(-3 add(getvar(-3) -1)) | |
| }) | |
| getvar(1) # if there's no explicit return then whatever is still on the stack at the end of the function is returned | |
| # this could be multiple values | |
| } | |
| ) | |
| function (:fizzbuzz 1 1 { | |
| setvar(1 1) | |
| while (lt(getvar(1) getvar(-3)) { | |
| ifelse({ | |
| and(not(mod(getvar(1) 3)) not(mod(getvar(1) 5))) | |
| } { | |
| println("FizzBuzz") | |
| } { | |
| ifelse (not(mod(getvar(1) 3)) { | |
| println("Fizz") | |
| } { | |
| ifelse (not(mod(getvar(1) 5)) { | |
| println("Buzz") | |
| } { | |
| putint(getvar(1)) | |
| putchar(10) | |
| }) | |
| }) | |
| }) | |
| setvar(1 add(getvar(1) 1)) | |
| }) | |
| }) | |
| # some extra space of memory | |
| # this is used for global variables | |
| [0] | |
| :funcmem | |
| [0] | |
| :calltmp | |
| [0] | |
| :memtmp | |
| [0] | |
| :returntmp | |
| [0] | |
| # the stack begins here | |
| :codeend | |
| [0] | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment