Twocode/Doublecode

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

 * 1) Doublecode (twocode + onecode) interpreter

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 }
 * 1) Utility functions

dcode = {}
 * 1) Main namespace

dcode.debug = false
 * 1) debug level, false=no debug, 1=onecode debug, 2=twocode debug


 * 1) 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 ""		return l[i+n]	}	def: next {		if: !defined(l,i) return ""		return l[i++]	}	def: accept(symbol) {		local n = next		if: n != symbol raise("Syntax error: Unexpexted " + n + ", expecting " + symbol)	}	# stmt := {";" }	def: stmt {		local a = expr		while: seek == ";" {			accept(";")			a = expr		}		return a	}	# logic := {("|"|"&"|"^") }	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 := {("="|"<"|">") }	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" {accept(">")if: a>add a=1 else: a=0} else: return a		} }	# add := {("+"|"-") } 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 := {("*"|"/") } 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 := "*"  ["~" ] | ["~" ] |  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 := "(" ")" | "*"  | "!" | "(" [ {"," }] ")" | 	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 := | | "'" "'"	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 }


 * 1) print::dcode.onecode("s~0;*s~\"h\\ne\\n\\l\\n\\n\";puts(s);*(s+6)", {vars={},memory=[],a=0,x=0,y=0})


 * 1) Twocode commands

dcode.commands = {}


 * 1) 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 } }


 * 1) 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 = "" }


 * 1) 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

dcode.program = dcode.program->split("\n") foreach(dcode.program->keys)::def(key) dcode.program[key] = dcode.program[key]->split("")
 * 1) Program array

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 }


 * 1) Runtime ~ twocode interpreter

runtime = {} runtime.direction = :EAST runtime.output = "" runtime.program = dcode.program
 * 1) Runtime instance

runtime.vars = {} runtime.memory = []
 * 1) storage

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 }

print::runtime.output
 * 1) Flush output