Skip to content

Instantly share code, notes, and snippets.

@jmdejong
Created January 11, 2021 19:22
Show Gist options
  • Select an option

  • Save jmdejong/48f36b909100435b8a23cbd7e2101edd to your computer and use it in GitHub Desktop.

Select an option

Save jmdejong/48f36b909100435b8a23cbd7e2101edd to your computer and use it in GitHub Desktop.
{
# 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