lang0/0-lang0py/lang0py.py
Johan B.W. de Vries 670532059a Feat: Adds mapclear, mapgetkey, mapsetkey
Feat: Error on undefined function

Fix: All ids are now prefix to prevent native var clashes.
This was already done on it2, but now also in it0 and it1.
Redid it2 to match.

Fix: generate-recipes would not complain on missing exp.

Fix: Adds documentation and test for not

Chore: __check helper function for ease of use in it0, it1.

Chore: some reordering to match more between iterations.
2025-05-04 18:06:04 +02:00

534 lines
11 KiB
Python

import os
import sys
def emit(string):
sys.stdout.buffer.write(string.encode('latin_1'))
sys.stdout.flush()
def trace(header, value):
if os.environ.get('TRACE'):
sys.stderr.write(f'{header}={value!r}\n')
eof = chr(0xFF)
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('ERROR: ' + ' '.join(msg) + '\n')
sys.stderr.flush()
exit(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
idname = "id_" + id
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()
else:
emit('""')
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
__check(mapgetkey, ["FUNCREG", call, ""], ["Function", call, "does not exist"])
callid = registerid(call)
emit(' ' * indent)
emit(callid)
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)
emitln('):')
parseblock(1)
emit(eol)
def emitheader():
emitln("import os")
emitln("import sys")
emitln("")
emitln("def __eq(a: str, b: str) -> str:")
emitln(" return '1' if a == b else ''")
emitln("")
emitln("def __lt(a: str, b: str) -> str:")
emitln(" return '1' if a < b else ''")
emitln("")
emitln("def __add(a: str, b: str) -> str:")
emitln(" return a + b")
emitln("")
emitln("def __emit(string):")
emitln(" sys.stdout.buffer.write(string.encode('latin_1'))")
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(0xFF)")
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('ERROR: ' + ' '.join(msg) + '\\n')")
emitln(" sys.stderr.flush()")
emitln(" exit(1)")
emitln("")
emitln("# ### END OF RUNTIME ### #")
emitln("")
def emitfooter():
mainid = registerid("main")
emitln("if __name__ == '__main__':")
emit(" ")
emit(mainid)
emitln("()")
def main():
emitheader()
# Standard library constants
mapsetkey("REGISTERID", "eof", "__EOF")
mapsetkey("REGISTERID", "eol", "__EOL")
mapsetkey("REGISTERID", "quote", "__QUOTE")
# Standard library functions
mapsetkey("FUNCREG", "add", "1")
mapsetkey("REGISTERID", "add", "__add")
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("REGISTERID", "mapclear", "__mapclear")
mapsetkey("FUNCREG", "mapgetkey", "1")
mapsetkey("REGISTERID", "mapgetkey", "__mapgetkey")
mapsetkey("FUNCREG", "mapsetkey", "1")
mapsetkey("REGISTERID", "mapsetkey", "__mapsetkey")
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()