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