User:Iconmaster/iscom.lua

From Esolang
Jump to navigation Jump to search

This is an implementation of ISCOM in Lua.

-------------------------------------
--
-- ISCOM interpreter
-- Made by Joshua Robbins, 3/23/11
-- Interprets ISCOM src.
--
-------------------------------------

--Variable setup

bit = require("bit")
socket = require("socket")
mem = {}
ip = 1
numclass = "[%[%]0-9a-zA-Z%#%@%$%_]+"
opclass  = "[%+%-%*%/%%%&%|%!%<%>%=%~%^%{%}]"
code = {}
const = {}
if arg then
	while #arg ~= 0 do
		c = table.remove(arg,1)
		if c == "-f" then
			src = io.open(table.remove(arg,1),"r"):read("*a")
		elseif c == "-d" then
			DEBUG = true
		else
			src = c
		end
	end
end
if not src then src = io.read("*l") end


function getmem(addr)
	local nests,num,i
	nests = #(addr:match("^%[*"))
	num = addr:match("[0-9a-zA-Z#@$_]+")
	if const[num] then num = const[num] end
	if not num then return 0 end
	num = num:gsub("_","-")
	if num == "#" then num = ip end
	if num == "@" then num = fin() end
	if num == "$" then num = port end
	num = tonumber(num)
	if nests == 0 then return num end
	for i=1,nests do
		num = mem[num] or 0
	end
	return num
end
function setmem(addr,val)
	local nests,num,i
	nests = #(addr:match("^%[*"))
	num = addr:match("[0-9#@$_A-Za-z]+")
	if const[num] then num = const[num] end
	num = num:gsub("_","-")
	if num == "#" then ip = val - 1 return end
	if num == "@" then fout(val) return end
	if num == "$" then port=val fin=ins[port] fout=outs[port] return end
	num = tonumber(num)
	if nests == 0 then mem[num]=val end
	for i=1,nests do
		num = mem[num] or 0
	end
	mem[num]=val
end

-- the operator table.

ops = {}
ops["+"] = function(n1,n2) return n1 + n2 end
ops["-"] = function(n1,n2) return n1 - n2 end
ops["*"] = function(n1,n2) return n1 * n2 end
ops["/"] = function(n1,n2) return math.floor(n1 / n2) end
ops["%"] = function(n1,n2) return n1 % n2 end
ops["&"] = function(n1,n2) return bit.band(n1,n2) end
ops["|"] = function(n1,n2) return bit.bor(n1,n2) end
ops["!"] = function(n1) if n1~=0 then return 0 else return 1 end end
ops[":"] = function(n1) return bit.bnot(n1) end
ops["^"] = function(n1,n2) return bit.bxor(n1,n2) end
ops["="] = function(n1,n2) if n1 == n2 then return 1 else return 0 end end
ops["~"] = function(n1,n2) if n1 ~= n2 then return 1 else return 0 end end
ops["<"] = function(n1,n2) if n1 < n2 then return 1 else return 0 end end
ops[">"] = function(n1,n2) if n1 > n2 then return 1 else return 0 end end
ops["{"] = function(n1,n2) return bit.rol(n1,n2) end
ops["}"] = function(n1,n2) return bit.ror(n1,n2) end

--The Default Port Table.

ins = {}
outs = {}
port = 0
cfn = ""
socport = 0
socstate = 0
socaddr = ""
sendbuf = ""
randmax = 100
lbls = {}

ins[0] = function()
	local ret
	if bfr then
		ret = bfr:sub(1,1)
		bfr = bfr:sub(2)
		if bfr == "" then bfr = nil end
		return ret:byte()
	else
		bfr = io.read("*l") .. "\n"
		ret = bfr:sub(1,1)
		bfr = bfr:sub(2)
		if bfr == "" then bfr = nil end
		return ret:byte()
	end
end
outs[0]= function(n) io.write(string.char(n)) end
ins[1] = function()  return io.read("*n") end
outs[1]= function(n) io.write(tostring(n)) end
ins[2] = function()  return #cfn end
outs[2]= function(n) if cfn == 10 then cfn = "" else cfn = cfn .. string.char(n) end cfile = io.open(cfn,"r+") end
ins[3] = function()  if cfn then return cfile:seek() else return 0 end end
outs[3]= function(n) if cfn then cfile:seek("set",n) end end
ins[4] = function()  if not cfile then cfile = io.open(cfn,"w+") end c = cfile:read(1) if c then return c:byte() else return -1 end end
outs[4]= function(n) if not cfile then cfile = io.open(cfn,"w+") end cfile:write(string.char(n)) end
ins[5] = function()  if not cfile then cfile = io.open(cfn,"w+") end c = cfile:read("*n") return c or -1 end
outs[5]= function(n) if not cfile then cfile = io.open(cfn,"w+") end cfile:write(tostring(n)) end
ins[6] = function()  return socstate end
outs[6]= function(n)
	socstate = n
	if socstate == 0 and soc then
		soc:close()
		soc = nil
		if cli then cli:close() cli = nil end
	elseif socstate == 1 then
		soc = socket.connect(socaddr,socport)
	elseif socstate == 2 then
		soc = socket.bind(socaddr,socport)
		cli = soc:accept()
	end
end
ins[7] = function()  return #socaddr end
outs[7]= function(n) if n == 10 then socaddr = "" else socaddr = socaddr .. string.char(n) end end
ins[8] = function()  return socport end
outs[8]= function(n) socport = n end
ins[9] = function()
	if socstate == 1 then --client
		if not soc then return -1 end
		c = soc:receive(1)
		if c then return c:byte() else return -1 end
	elseif socstate == 2 then --server
		if not cli then return -1 end
		c = cli:receive(1)
		if c then return c:byte() else return -1 end
	end
end
outs[9]= function(n)
	if socstate == 1 then --client
		if not soc then return end
		if n == -1 then soc:send(sendbuf) sendbuf = "" return end
		sendbuf = sendbuf .. string.char(n)
	elseif socstate == 2 then --server
		if not cli then return end
		if n == -1 then cli:send(sendbuf) sendbuf = "" return end
		sendbuf = sendbuf .. string.char(n)
	end
end
ins[10] = function()  return socket.gettime() end
outs[10]= function(n) socket.sleep(n) end
ins[11] = function()  return math.random(randmax) end
outs[11]= function(n) randmax = n end

fin = ins[0]
fout=outs[0]
--parse src
function parse(src)
	local code,ocode = {},{}
	local ii,src2,i,v
	src = src:gsub("\"([^\"]*)\"",function(t)
		local c,s
		s = ""
		for c in t:gmatch(".") do
			s = s .. " @='" .. c .. " "
		end
		return s
	end,nil)
	src = src:gsub("%b()","")
	src = src:gsub("\'(.)",function(m) return tostring(m:byte()) end)
	ii = 0
	src2 = src
	for i in src2:gmatch("%S+") do
		if i:match("^;;") then
			if i:match("=") then
				src = src:gsub(i,"")
				a,b = i:match(";;([^=]*)=(.*)")
				const[a] = b
			else
				src = src:gsub(i,"")
				const[i:sub(3)] = tostring(ii + 1)
				lbls[ii] = i
			end
		else
			ii = ii + 1
		end
	end
	ii = nil
	src2 = nil
	ocode = {}
	for i in src:gmatch("%S+") do
		table.insert(code,i)
		table.insert(ocode,i)
	end
	for i,v in ipairs(code) do
		if v:sub(1,1)=="?" then
			code[i] = {"?",v:match("("..numclass..")("..opclass..")("..numclass..")")}
		else
			cellto = v:match("^"..numclass)
			if cellto then
				v = v:sub(#cellto+2)
				op = v:match("^"..opclass)
				if op then
					code[i] = {"+",cellto,{op,v:match(numclass)}}
					v = v:sub(2)
					for op,num in v:gmatch("("..opclass..")("..numclass..")") do
						table.insert(code[i],{op,num})
					end
				else
					code[i] = {"=",cellto,v:match(numclass)}
				end
			end
		end
	end
	return code,ocode
end

code,ocode = parse(src)

function step()
	if ip>#code then return false end
	cmd = code[ip]
	if cmd[1]=="+" then
		num = getmem("["..cmd[2])
		for i,v in ipairs(cmd) do
			if type(v)=="table" then
				num = ops[v[1]](num,getmem(v[2]))
			end
		end
		setmem(cmd[2],num)
	elseif cmd[1]=="=" then
		setmem(cmd[2],getmem(cmd[3]))
	elseif cmd[1]=="?" then
		if ops[cmd[3]](getmem(cmd[2]),getmem(cmd[4]))==0 then ip = ip + 1 end
	end
	ip=ip+1
	return true
end

if DEBUG then
	tracking = {}
	function track()
		io.write("#: " .. ip .. " $: " .. port .. " [#]: " .. (ocode[ip] or "nil") .. " " .. (lbls[ip-1] or "") .. " ")
		for i,v in pairs(tracking) do
			io.write(i .. ": " .. getmem(v) .. " ")
		end
		io.write("\n")
	end

	while true do
		io.write(">>")
		dc = io.read("*l")
		dcmd = {}
		for s in dc:gmatch("%S+") do
			table.insert(dcmd,s)
		end
		if #dcmd == 0 then
			if not step() then
				print("program done.")
			else
				if verbose then track() end
			end
		elseif ops[dcmd[1]] then
			print(ops[dcmd[1]](getmem(dcmd[2]),getmem(dcmd[3])))
		elseif getmem(dcmd[1]) and dcmd[2] and getmem(dcmd[2]) then
			for i=getmem(dcmd[1]),getmem(dcmd[2]) do
				io.write((mem[i] or 0) .. " ")
			end
			io.write("\n")
		elseif getmem(dcmd[1]) then
			print(mem[getmem(dcmd[1])] or 0)
		elseif dcmd[1] == "r" then
			track()
		elseif dcmd[1] == "j" then
			ip = getmem(dcmd[2])
		elseif dcmd[1] == "p" then
			port = getmem(dcmd[2])
			fin = ins[port]
			fout = outs[port]
		elseif dcmd[1] == "s" then
			setmem(dcmd[2],getmem(dcmd[3]))
		elseif dcmd[1] == "l" then
			if dcmd[2] and dcmd[3] then
				for i=getmem(dcmd[2]),getmem(dcmd[3]) do
					print(i .. ": " .. ocode[i] .. " " .. (lbls[i-1] or ""))
				end
			elseif dcmd[2] then
					print(getmem(dcmd[2]) .. ": " .. ocode[getmem(dcmd[2])] .. " " .. (lbls[getmem(dcmd[2])-1] or ""))
			else
				for i=1,#code do
					print(i .. ": " .. ocode[i] .. " " .. (lbls[i-1] or ""))
				end
			end
		elseif dcmd[1] == "c" then
			if dcmd[2] and dcmd[3] then
				const[dcmd[2]] = dcmd[3]
			elseif dcmd[2] then
				print(const[dcmd[2]])
			else
				for i,v in pairs(const) do
					print(i.."="..v)
				end
			end
		elseif dcmd[1] == "g" then
			if dcmd[2] == nil then dcmd[2] = #code else dcmd[2] = getmem(dcmd[2]) end
			while ip <= dcmd[2] do
				if not step() then
					print("program done.")
					break
				else
					if verbose then track() end
				end
			end
			print("Finished executing segment.")
		elseif dcmd[1] == "w" then
			ncode,nocode = parse(table.concat(dcmd," ",2))
			t = {}
			for i,v in ipairs(code) do
				table.insert(t,v)
			end
			for i,v in ipairs(ncode) do
				table.insert(t,v)
			end
			code = t
			t = {}
			for i,v in ipairs(ocode) do
				table.insert(t,v)
			end
			for i,v in ipairs(nocode) do
				table.insert(t,v)
			end
			ocode = t
		elseif dcmd[1] == "i" then
			dcmd[2] = getmem(dcmd[2])
			ncode,nocode = parse(table.concat(dcmd," ",3))
			t = {}
			for i,v in ipairs(code) do
				table.insert(t,v)
			end
			for i,v in ipairs(ncode) do
				table.insert(t,dcmd[2],v)
			end
			code = t
			t = {}
			for i,v in ipairs(ocode) do
				table.insert(t,v)
			end
			for i,v in ipairs(nocode) do
				table.insert(t,dcmd[2],v)
			end
			ocode = t
		elseif dcmd[1] == "d" then
			for i=1,getmem(dcmd[3]) do
				table.remove(code,getmem(dcmd[2]))
				table.remove(ocode,getmem(dcmd[2]))
			end
		elseif dcmd[1] == "q" then
			os.exit()
		elseif dcmd[1] == "b" then
			mem = {}
			ip = 1
			port = 0
			fin = ins[0]
			fout=outs[0]
			cfn = ""
			socport = 0
			socstate = 0
			socaddr = ""
			sendbuf = ""
			randmax = 100
		elseif dcmd[1] == "v" then
			verbose = not verbose
		elseif dcmd[1] == "t" then
			if dcmd[2] and dcmd[3] then
				tracking[dcmd[2]] = dcmd[3]
			elseif dcmd[2] then
				tracking[dcmd[2]] = nil
			else
				tracking = {}
			end
		end
	end
else
	while step() do
	end
end