537 lines
11 KiB
Python
537 lines
11 KiB
Python
import os
|
|
import sys
|
|
|
|
def eq(a, b):
|
|
return a == b
|
|
|
|
def emit(string):
|
|
sys.stdout.write(string)
|
|
sys.stdout.flush()
|
|
|
|
def trace(header, value):
|
|
if os.environ.get('TRACE'):
|
|
sys.stderr.write(f'{header}={value!r}\n')
|
|
|
|
eof = chr(0)
|
|
eol = chr(10)
|
|
quote = chr(34)
|
|
PEEK = None
|
|
LINE = 1
|
|
|
|
def peek():
|
|
global PEEK
|
|
|
|
if PEEK is None:
|
|
char = sys.stdin.read(1)
|
|
trace('char', char)
|
|
if not char:
|
|
PEEK = eof
|
|
else:
|
|
PEEK = char
|
|
|
|
return PEEK
|
|
|
|
peek()
|
|
|
|
def skip():
|
|
global LINE
|
|
global PEEK
|
|
|
|
if eol == PEEK:
|
|
LINE += 1
|
|
|
|
PEEK = None
|
|
|
|
def __check(func, args: list[str], msg: list[str]) -> None:
|
|
result = func(*args)
|
|
if result:
|
|
return
|
|
|
|
sys.stderr.write(' '.join(msg) + '\n')
|
|
sys.stderr.flush()
|
|
exit(1)
|
|
|
|
def intinc(a: str) -> str:
|
|
if not a:
|
|
return "1"
|
|
|
|
return str(int(a) + 1)
|
|
|
|
MAPSTORE = {}
|
|
|
|
def mapclear(mapname: str) -> str:
|
|
MAPSTORE.pop(mapname)
|
|
return ""
|
|
|
|
def mapgetkey(mapname: str, key: str, default: str) -> str:
|
|
return MAPSTORE.get(mapname, {}).get(key, default)
|
|
|
|
def mapsetkey(mapname: str, key: str, value: str) -> str:
|
|
MAPSTORE.setdefault(mapname, {})
|
|
MAPSTORE[mapname][key] = value
|
|
return ""
|
|
|
|
def registerid(id: str) -> str:
|
|
idname = mapgetkey("REGISTERID", id, "")
|
|
if idname:
|
|
return idname
|
|
newidx = mapgetkey("REGISTERID", "__len__", "0")
|
|
idname = "identifier" + newidx
|
|
mapsetkey("REGISTERID", id, idname)
|
|
newlen = intinc(newidx)
|
|
mapsetkey("REGISTERID", "__len__", newlen)
|
|
return idname
|
|
|
|
def emitln(data):
|
|
emit(data)
|
|
emit(eol)
|
|
|
|
def lexident():
|
|
word = ''
|
|
while True:
|
|
char = peek()
|
|
|
|
if char < 'a' or char > 'z':
|
|
break
|
|
|
|
word += char
|
|
skip()
|
|
|
|
return word
|
|
|
|
def skipchar(char):
|
|
global LINE
|
|
assert char == peek(), (LINE, char, peek())
|
|
skip()
|
|
|
|
def parseconststring():
|
|
skipchar(quote)
|
|
emit(quote)
|
|
|
|
escaped = False
|
|
while True:
|
|
char = peek()
|
|
if char == eof:
|
|
break
|
|
if char == quote:
|
|
break
|
|
|
|
emit(char)
|
|
|
|
skip()
|
|
|
|
skipchar(quote)
|
|
emit(quote)
|
|
|
|
def parseexprvarref():
|
|
varname = lexident()
|
|
varid = registerid(varname)
|
|
emit(varid)
|
|
|
|
def parseexprcall():
|
|
funcname = lexident()
|
|
__check(mapgetkey, ["FUNCREG", funcname, ""], ["Function", funcname, "does not exist"])
|
|
funcid = registerid(funcname)
|
|
emit(funcid)
|
|
emit('(')
|
|
|
|
first = True
|
|
while ' ' == peek():
|
|
skip()
|
|
if not first:
|
|
emit(', ')
|
|
if quote == peek():
|
|
parseconststring()
|
|
else:
|
|
parseexprvarref()
|
|
first = False
|
|
|
|
emit(')')
|
|
|
|
def parsestatdeclare(indent):
|
|
skipchar(' ')
|
|
var = lexident()
|
|
skipchar(eol)
|
|
|
|
def parsestatset(indent):
|
|
skipchar(' ')
|
|
varname = lexident()
|
|
varid = registerid(varname)
|
|
skipchar(' ')
|
|
|
|
emit(' ' * indent)
|
|
emit(varid)
|
|
emit(' = ')
|
|
|
|
parseconststring()
|
|
|
|
emit(eol)
|
|
skipchar(eol)
|
|
|
|
def parsestatcalc(indent):
|
|
skipchar(' ')
|
|
varname = lexident()
|
|
varid = registerid(varname)
|
|
skipchar(' ')
|
|
|
|
emit(' ' * indent)
|
|
emit(varid)
|
|
emit(' = ')
|
|
|
|
parseexprcall()
|
|
|
|
emit(eol)
|
|
skipchar(eol)
|
|
|
|
def parsestatif(indent):
|
|
skipchar(' ')
|
|
emit(' ' * indent)
|
|
emit('if ')
|
|
parseexprvarref()
|
|
emitln(':')
|
|
skipchar(eol)
|
|
|
|
parseblock(indent + 1)
|
|
|
|
def parsestatforever(indent):
|
|
emit(' ' * indent)
|
|
emitln('while True:')
|
|
skipchar(eol)
|
|
|
|
parseblock(indent + 1)
|
|
|
|
def parsestatbreak(indent):
|
|
emit(' ' * indent)
|
|
emitln('break')
|
|
skipchar(eol)
|
|
|
|
def parsestatreturn(indent):
|
|
emit(' ' * indent)
|
|
emit('return ')
|
|
if ' ' == peek():
|
|
skip()
|
|
if quote == peek():
|
|
parseconststring()
|
|
else:
|
|
parseexprvarref()
|
|
emit(eol)
|
|
skipchar(eol)
|
|
|
|
def parsestatcheck(indent):
|
|
skipchar(' ')
|
|
emit(' ' * indent)
|
|
emit('__check(')
|
|
|
|
funcname = lexident()
|
|
funcid = registerid(funcname)
|
|
trace("funcid", funcid)
|
|
emit(funcid)
|
|
emit(', [')
|
|
notfirst = False
|
|
|
|
while True:
|
|
skipchar(' ')
|
|
|
|
if ':' == peek():
|
|
break
|
|
|
|
if notfirst:
|
|
emit(', ')
|
|
|
|
if quote == peek():
|
|
parseconststring()
|
|
else:
|
|
parseexprvarref()
|
|
|
|
notfirst = True
|
|
|
|
skipchar(':')
|
|
|
|
emit('], [')
|
|
notfirst = False
|
|
|
|
while True:
|
|
skipchar(' ')
|
|
|
|
if notfirst:
|
|
emit(', ')
|
|
|
|
if quote == peek():
|
|
parseconststring()
|
|
else:
|
|
parseexprvarref()
|
|
|
|
if eol == peek():
|
|
break
|
|
notfirst = True
|
|
emitln('])')
|
|
|
|
def parsestattrace(indent):
|
|
skipchar(' ')
|
|
emit(' ' * indent)
|
|
emit('trace("')
|
|
|
|
varname = lexident()
|
|
varid = registerid(varname)
|
|
skipchar(eol)
|
|
|
|
emit(varname)
|
|
emit('", ')
|
|
emit(varid)
|
|
emitln(')')
|
|
|
|
def parsestat(indent):
|
|
call = lexident()
|
|
trace('call', call)
|
|
|
|
if call == "declare":
|
|
parsestatdeclare(indent)
|
|
return
|
|
|
|
if call == "set":
|
|
parsestatset(indent)
|
|
return
|
|
|
|
if call == "calc":
|
|
parsestatcalc(indent)
|
|
return
|
|
|
|
if call == "if":
|
|
parsestatif(indent)
|
|
return
|
|
|
|
if call == "forever":
|
|
parsestatforever(indent)
|
|
return
|
|
|
|
if call == "break":
|
|
parsestatbreak(indent)
|
|
return
|
|
|
|
if call == "return":
|
|
parsestatreturn(indent)
|
|
return
|
|
|
|
if call == "check":
|
|
parsestatcheck(indent)
|
|
return
|
|
|
|
if call == "trace":
|
|
parsestattrace(indent)
|
|
return
|
|
|
|
funcid = registerid(call)
|
|
|
|
emit(' ' * indent)
|
|
emit(funcid)
|
|
emit('(')
|
|
|
|
first = True
|
|
while ' ' == peek():
|
|
skip()
|
|
if not first:
|
|
emit(", ")
|
|
if '"' == peek():
|
|
parseconststring()
|
|
else:
|
|
parseexprvarref()
|
|
first = False
|
|
|
|
skipchar(eol)
|
|
|
|
emitln(')')
|
|
|
|
def parseblock(indent):
|
|
while True:
|
|
while eol == peek():
|
|
skip()
|
|
|
|
for _ in range(indent - 1):
|
|
skipchar('\t')
|
|
|
|
if '/' == peek():
|
|
skip()
|
|
skipchar(eol)
|
|
break
|
|
|
|
skipchar('\t')
|
|
|
|
parsestat(indent)
|
|
|
|
def parsefunc():
|
|
funcname = lexident()
|
|
funcid = registerid(funcname)
|
|
mapsetkey("FUNCREG", funcname, "1")
|
|
|
|
trace('funcname', funcname)
|
|
emit('def ')
|
|
emit(funcid)
|
|
emit('(')
|
|
|
|
first = True
|
|
while ' ' == peek():
|
|
skip()
|
|
varname = lexident()
|
|
varid = registerid(varname)
|
|
if not first:
|
|
emit(", ")
|
|
emit(varid)
|
|
first = False
|
|
|
|
if '/' == peek():
|
|
# Ahead declaration
|
|
skipchar('/')
|
|
skipchar(eol)
|
|
emitln("):")
|
|
emitln(" pass # ahead declaration")
|
|
emit(eol)
|
|
return
|
|
|
|
skipchar(':')
|
|
skipchar(eol)
|
|
|
|
emit('): # ')
|
|
emitln(funcname)
|
|
|
|
parseblock(1)
|
|
|
|
emit(eol)
|
|
|
|
def emitheader():
|
|
emitln("import os")
|
|
emitln("import sys")
|
|
emitln("")
|
|
emitln("def __eq(a, b):")
|
|
emitln(" return a == b")
|
|
emitln("")
|
|
emitln("def __lt(a, b):")
|
|
emitln(" return a[0] < b[0]")
|
|
emitln("")
|
|
emitln("def __addstringchar(a, b):")
|
|
emitln(" return a + b[0]")
|
|
emitln("")
|
|
emitln("def __emit(string):")
|
|
emitln(" sys.stdout.write(string)")
|
|
emitln(" sys.stdout.flush()")
|
|
emitln("")
|
|
emitln("def trace(header, value):")
|
|
emitln(" if os.environ.get('TRACE'):")
|
|
emitln(" sys.stderr.write(f'{header}={value!r}\\n')")
|
|
emitln("")
|
|
emitln("__EOF = chr(0)")
|
|
emitln("__EOL = chr(10)")
|
|
emitln("__QUOTE = chr(34)")
|
|
emitln("")
|
|
emitln("STDINCOLNO = 0")
|
|
emitln("STDINLINENO = 1")
|
|
emitln("STDINPEEK = None")
|
|
emitln("")
|
|
emitln("def _readchar():")
|
|
emitln(" char = sys.stdin.read(1)")
|
|
emitln(" trace('char', char)")
|
|
emitln(" if not char:")
|
|
emitln(" return __EOF")
|
|
emitln(" return char")
|
|
emitln("")
|
|
emitln("def __peek():")
|
|
emitln(" return STDINPEEK")
|
|
emitln("")
|
|
emitln("def __skip():")
|
|
emitln(" global STDINCOLNO")
|
|
emitln(" global STDINLINENO")
|
|
emitln(" global STDINPEEK")
|
|
emitln(" if __EOL == STDINPEEK:")
|
|
emitln(" STDINLINENO += 1")
|
|
emitln(" STDINCOLNO = 0")
|
|
emitln(" STDINCOLNO += 1")
|
|
emitln(" STDINPEEK = _readchar()")
|
|
emitln("")
|
|
emitln("def __stdinlineno():")
|
|
emitln(" global STDINLINENO")
|
|
emitln(" return str(STDINLINENO)")
|
|
emitln("")
|
|
emitln("def __stdincolno():")
|
|
emitln(" global STDINCOLNO")
|
|
emitln(" return str(STDINCOLNO)")
|
|
emitln("")
|
|
emitln("__skip()")
|
|
emitln("")
|
|
emitln("MAPSTORE = {}")
|
|
emitln("")
|
|
emitln("def mapclear(mapname: str) -> str:")
|
|
emitln(" MAPSTORE.pop(mapname)")
|
|
emitln(" return ''")
|
|
emitln("")
|
|
emitln("def mapgetkey(mapname: str, key: str, default: str) -> str:")
|
|
emitln(" return MAPSTORE.get(mapname, {}).get(key, default)")
|
|
emitln("")
|
|
emitln("def mapsetkey(mapname: str, key: str, value: str) -> str:")
|
|
emitln(" MAPSTORE.setdefault(mapname, {})")
|
|
emitln(" MAPSTORE[mapname][key] = value")
|
|
emitln(" return ''")
|
|
emitln("")
|
|
emitln("def __not(a: str) -> str:")
|
|
emitln(" if a:")
|
|
emitln(" return ''")
|
|
emitln(" return '1'")
|
|
emitln("")
|
|
emitln("def __check(func, args: list[str], msg: list[str]) -> None:")
|
|
emitln(" result = func(*args)")
|
|
emitln(" if result:")
|
|
emitln(" return")
|
|
emitln("")
|
|
emitln(" sys.stderr.write(' '.join(msg) + '\\n')")
|
|
emitln(" sys.stderr.flush()")
|
|
emitln(" exit(1)")
|
|
|
|
def emitfooter():
|
|
mainname = registerid("main")
|
|
emitln("if __name__ == '__main__':")
|
|
emit(" ")
|
|
emit(mainname)
|
|
emitln("()")
|
|
|
|
def main():
|
|
emitheader()
|
|
|
|
# Standard library constants
|
|
mapsetkey("REGISTERID", "eof", "__EOF")
|
|
mapsetkey("REGISTERID", "eol", "__EOL")
|
|
mapsetkey("REGISTERID", "quote", "__QUOTE")
|
|
|
|
# Standard library functions
|
|
mapsetkey("FUNCREG", "addstringchar", "1")
|
|
mapsetkey("REGISTERID", "addstringchar", "__addstringchar")
|
|
mapsetkey("FUNCREG", "emit", "1")
|
|
mapsetkey("REGISTERID", "emit", "__emit")
|
|
mapsetkey("FUNCREG", "eq", "1")
|
|
mapsetkey("REGISTERID", "eq", "__eq")
|
|
mapsetkey("FUNCREG", "lt", "1")
|
|
mapsetkey("REGISTERID", "lt", "__lt")
|
|
mapsetkey("FUNCREG", "not", "1")
|
|
mapsetkey("REGISTERID", "not", "__not")
|
|
mapsetkey("FUNCREG", "mapclear", "1")
|
|
mapsetkey("FUNCREG", "mapgetkey", "1")
|
|
mapsetkey("FUNCREG", "mapsetkey", "1")
|
|
mapsetkey("FUNCREG", "peek", "1")
|
|
mapsetkey("REGISTERID", "peek", "__peek")
|
|
mapsetkey("FUNCREG", "skip", "1")
|
|
mapsetkey("REGISTERID", "skip", "__skip")
|
|
mapsetkey("FUNCREG", "stdincolno", "1")
|
|
mapsetkey("REGISTERID", "stdincolno", "__stdincolno")
|
|
mapsetkey("FUNCREG", "stdinlineno", "1")
|
|
mapsetkey("REGISTERID", "stdinlineno", "__stdinlineno")
|
|
|
|
while True:
|
|
if eof == peek():
|
|
break
|
|
|
|
while eol == peek():
|
|
skip()
|
|
|
|
parsefunc()
|
|
emitfooter()
|
|
|
|
if __name__ == '__main__':
|
|
main()
|