Twocode/Doublecode
Jump to navigation
Jump to search
Doublecode is a twocode interpreter by User:Fergusq in Sve.
Usage
Interpreter reads lines from the standard input until a line with text "EOF" is encountered.
Source code
# Doublecode (twocode + onecode) interpreter ## Utility functions def: foreach (t, u) if: !u return def (u) foreach(t, u) else: { local l = len::t for: local i=0, i < l, i++ u(t[i]) } def: range(x,y) { local t = table() for: local i = 0, x+i <= y, i++ t[i] = (x + i) return t } ## Main namespace dcode = {} # debug level, false=no debug, 1=onecode debug, 2=twocode debug dcode.debug = false ## Onecode def: dcode.onecode(code, runtime) { # Lexer local r = code->split("(?<![a-zA-Z0-9])|(?![a-zA-Z0-9])") local l = [] local i = 0 local s = nil foreach(r)::def (t) { if: t==""; else: if: t=="\"" && !s { l[i++] = "\"" s="" } # ollaanko merkkijonon sisällä else: if: !s { if: t==""||t==" "||t=="\n"||t=="\r"||t=="\t"; else: l[i++] = t } else: { if: s->strlen > 0 && s->charat(s->strlen-1)->chr == "\\" { local c = t->charat(0)->chr if: c == "n" c="\n" if: c == "r" c="\r" if: c == "t" c="\t" s = s->substr(0,s->strlen-1) + c if: t->strlen > 1 s = s + t->substr(1) } else: if: t == "\"" { l[i++] = s; s = nil } else: s=s+t } } #local ts=""foreach(r)::def(c) ts=ts+","+c print::ts # Recursive descent parser i = 0 def: seek(n) { if: !n n = 0 if: !defined(l,i+n) return "<EOF>" return l[i+n] } def: next { if: !defined(l,i) return "<EOF>" return l[i++] } def: accept(symbol) { local n = next() if: n != symbol raise("Syntax error: Unexpexted " + n + ", expecting " + symbol) } # stmt := <expr> {";" <expr>} def: stmt { local a = expr() while: seek() == ";" { accept(";") a = expr() } return a } # logic := <cond> {("|"|"&"|"^") <cond>} def: expr { local a = cond() while: true { local n = seek() if: n == "|" {accept("|") if: a!=0||cond()!=0 a=1 else: a=0} else: if: n == "&" {accept("&") if: a!=0&&cond()!=0 a=1 else: a=0} else: if: n == "^" {accept("^") if: a>cond() a=1 else: a=0} else: return a } } # cond := <add> {("="|"<"|">") <add>} def: cond { local a = add() while: true { local n = seek() if: n == "=" {accept("=")if: a==add() a=1 else: a=0} else: if: n == "<" {accept("<")if: a<add() a=1 else: a=0} else: if: n == ">" {accept(">")if: a>add() a=1 else: a=0} else: return a } } # add := <mul> {("+"|"-") <mul>} def: add { local a = mul() while: true { local n = seek() if: n == "+" {accept("+")a=a+mul()} else: if: n == "-" {accept("-")a=a-mul()} else: return a } } # mul := <prim> {("*"|"/") <prim>} def: mul { local a = prim() while: true { local n = seek() #print::next if: n == "*" {accept("*")a=a*prim()} else: if: n == "/" {acccept("/")a=a/prim()} else: return a } } # prim := "*" <prim_t> ["~" <expr>] | <var> ["~" <expr>] | <prim_t> def: prim { if: seek() == "*" { accept("*") local addr = prim_t() if: seek() == "~" { accept("~") if: seek() == "[" { # list local p = addr accept("[") do: { runtime.memory[p++] = expr() if: seek()=="]" break else: accept(",") } while: true accept("]") return runtime.memory[addr] } if: seek() == "\"" { # string local p = addr accept("\"") local s = next() local l = s->strlen while: p-addr != l { runtime.memory[p] = s->charat(p-addr) p++ } runtime.memory[p] = 0 # null terminator return runtime.memory[addr] } runtime.memory[addr] = expr() return runtime.memory[addr] } if: defined(runtime.memory, addr) return runtime.memory[addr] return 0 } if: seek(1) == "~" { local var = next() accept("~") runtime.vars[var] = expr() return runtime.vars[var] } return prim_t() } # prim_t := "(" <expr> ")" | "*" <prim_t> | "!" <prim> | <function> "(" [<expr> {"," <expr>}] ")" | <value> def: prim_t { local n = seek() if: n == "(" { accept("(") local tmp = expr() accept(")") return tmp } if: n == "*" { accept("*") local addr = prim_t() if: defined(runtime.memory, addr) return runtime.memory[addr] return 0 } if: n == "!" { accept("!") local num = prim() if: num==0 return 1 else: return 0 } # functions if: defined(functions, n) { accept(n) return functions[n](runtime) } return value() } # value := <number> | <var> | "'" <chr> "'" def: value { local n = seek() if: n == "'" { accept("'") local ch = next() accept("'") return ch->charat(0) } if: !defined(runtime.vars, n) { local num = nil pcall(def num = number(next())) if: num return num else: raise("Unknown variable " + n) } return runtime.vars[next()] } local functions = { getc = def { accept("(") accept(")") # impossible to implement in sve :( return 0 }, putc = def { accept("(") local ch = expr() accept(")") runtime.output = runtime.output + chr(ch) return 0 }, getn = def { accept("(") accept(")") # impossible to implement in sve :( return 0 }, putn = def { accept("(") local ch = expr() accept(")") runtime.output = runtime.output + ch return 0 } } runtime.a = stmt() if: dcode.debug print::runtime.x+","+runtime.y+" "+code+" --> "+runtime.a return runtime.a } #print::dcode.onecode("s~0;*s~\"h\\ne\\n\\l\\n\\n\";puts(s);*(s+6)", {vars={},memory=[],a=0,x=0,y=0}) ## Twocode commands dcode.commands = {} # Direction changing commands dcode.commands./ = def(runtime) { if: runtime.direction == :SOUTH runtime.direction = :WEST else: if: runtime.direction == :WEST runtime.direction = :SOUTH else: if: runtime.direction == :NORTH runtime.direction = :EAST else: if: runtime.direction == :EAST runtime.direction = :NORTH } dcode.commands.\ = def(runtime) { if: runtime.direction == :SOUTH runtime.direction = :EAST else: if: runtime.direction == :EAST runtime.direction = :SOUTH else: if: runtime.direction == :NORTH runtime.direction = :WEST else: if: runtime.direction == :WEST runtime.direction = :NORTH } dcode.commands.L = def(runtime) { # turn left if: runtime.direction == :NORTH runtime.direction = :EAST else: if: runtime.direction == :EAST runtime.direction = :SOUTH else: if: runtime.direction == :SOUTH runtime.direction = :WEST else: if: runtime.direction == :WEST runtime.direction = :NORTH } dcode.commands.R = def(runtime) { # turn right if: runtime.direction == :SOUTH runtime.direction = :WEST else: if: runtime.direction == :WEST runtime.direction = :NORTH else: if: runtime.direction == :NORTH runtime.direction = :EAST else: if: runtime.direction == :EAST runtime.direction = :SOUTH } dcode.commands.v = def(runtime) { runtime.direction = :SOUTH } dcode.commands.^ = def(runtime) { runtime.direction = :NORTH } dcode.commands.< = def(runtime) { runtime.direction = :WEST } dcode.commands.> = def(runtime) { runtime.direction = :EAST } dcode.commands.Z = def(runtime) { if: runtime.a != 0 { if: runtime.direction == :SOUTH runtime.direction = :WEST else: if: runtime.direction == :WEST runtime.direction = :SOUTH else: if: runtime.direction == :NORTH runtime.direction = :EAST else: if: runtime.direction == :EAST runtime.direction = :NORTH } } dcode.commands.S = def(runtime) { if: runtime.a != 0 { if: runtime.direction == :SOUTH runtime.direction = :EAST else: if: runtime.direction == :EAST runtime.direction = :SOUTH else: if: runtime.direction == :NORTH runtime.direction = :WEST else: if: runtime.direction == :WEST runtime.direction = :NORTH } } # Onecode containers dcode.commands.{ = def(runtime) { runtime.direction = :EAST runtime.x = runtime.x + 1 local startx = runtime.x local code = "" local s = false while: runtime.program[runtime.y][runtime.x] != :} { if: runtime.program[runtime.y][runtime.x] == "\"" { if: s { if: runtime.program[runtime.y][runtime.x-1] != "\\" s = false } else: s = true } code = code + runtime.program[runtime.y][runtime.x] if: !runtime.program[runtime.y]->defined(runtime.x+1) || (!s&&runtime.program[runtime.y][runtime.x] == :;) || (!s&&runtime.program[runtime.y][runtime.x+1] == :\) { runtime.y = runtime.y+1 runtime.x = startx } else: runtime.x = runtime.x + 1 } dcode.onecode(code, runtime) } dcode.commands.} = def(runtime) { runtime.direction = :WEST local startx = runtime.x runtime.x = runtime.x - 1 local code = "" local s = false while: runtime.program[runtime.y][runtime.x] != :{ { if: runtime.program[runtime.y][runtime.x] == "\"" { if: s { if: runtime.program[runtime.y][runtime.x-1] != "\\" s = false } else: s = true } code = runtime.program[runtime.y][runtime.x] + code if: !runtime.program[runtime.y]->defined(runtime.x-1) || (!s&&runtime.program[runtime.y][runtime.x-1] == ":") { runtime.y = runtime.y-1 runtime.x = startx } else: runtime.x = runtime.x - 1 } dcode.onecode(code, runtime) } # IO dcode.commands.c = def(runtime) { runtime.output = runtime.output + chr(runtime.a) } dcode.commands.n = def(runtime) { runtime.output = runtime.output + runtime.a } dcode.commands.f = def(runtime) { print::runtime.output runtime.output = "" } ## Reading program if: !defined(dcode,:program) { dcode.program = "" local line = "" do: { line = readln() if: line == :EOF break else: dcode.program = dcode.program + line + "\n" } while: true } if: dcode.debug print::dcode.program # Program array dcode.program = dcode.program->split("\n") foreach(dcode.program->keys)::def(key) dcode.program[key] = dcode.program[key]->split("") def: dbgout(program, runtime) { foreach(range(0,program->len-1))::def (line) { local out = "" foreach(range(0,program[line]->len-1))::def(x) { if: x==runtime.x&&line==runtime.y out=out+"X" else: out=out+program[line][x] } print::out } print::runtime.vars+""+runtime.a } ## Runtime ~ twocode interpreter # Runtime instance runtime = {} runtime.direction = :EAST runtime.output = "" runtime.program = dcode.program # storage runtime.vars = {} runtime.memory = [] runtime.a = 0 # accumulator runtime.x = 0 runtime.y = 0 local i = 0 while: true { local cmd = dcode.program[runtime.y][runtime.x] if: dcode.debug==2 dcode.program->dbgout(runtime) if: defined(dcode.commands,cmd) dcode.commands[cmd](runtime) else: if: cmd == "#" break if: runtime.direction == :NORTH runtime.y = runtime.y - 1 if: runtime.direction == :SOUTH runtime.y = runtime.y + 1 if: runtime.direction == :EAST runtime.x = runtime.x + 1 if: runtime.direction == :WEST runtime.x = runtime.x - 1 if: i++ > 100000 break } # Flush output print::runtime.output