diff --git a/0-lang0py/lang0py.py b/0-lang0py/lang0py.py index 295898b..6e121fe 100644 --- a/0-lang0py/lang0py.py +++ b/0-lang0py/lang0py.py @@ -39,6 +39,36 @@ def skip(): 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) @@ -81,12 +111,15 @@ def parseconststring(): emit(quote) def parseexprvarref(): - var = lexident() - emit(var) + varname = lexident() + varid = registerid(varname) + emit(varid) def parseexprcall(): funcname = lexident() - emit(funcname) + __check(mapgetkey, ["FUNCREG", funcname, ""], ["Function", funcname, "does not exist"]) + funcid = registerid(funcname) + emit(funcid) emit('(') first = True @@ -109,11 +142,12 @@ def parsestatdeclare(indent): def parsestatset(indent): skipchar(' ') - var = lexident() + varname = lexident() + varid = registerid(varname) skipchar(' ') emit(' ' * indent) - emit(var) + emit(varid) emit(' = ') parseconststring() @@ -123,11 +157,12 @@ def parsestatset(indent): def parsestatcalc(indent): skipchar(' ') - var = lexident() + varname = lexident() + varid = registerid(varname) skipchar(' ') emit(' ' * indent) - emit(var) + emit(varid) emit(' = ') parseexprcall() @@ -174,11 +209,13 @@ def parsestatreturn(indent): def parsestatcheck(indent): skipchar(' ') emit(' ' * indent) - emit('if not ') + emit('__check(') - func_name = lexident() - emit(func_name) - emit('(') + funcname = lexident() + funcid = registerid(funcname) + trace("funcid", funcid) + emit(funcid) + emit(', [') notfirst = False while True: @@ -199,9 +236,7 @@ def parsestatcheck(indent): skipchar(':') - emitln('):') - emit(' ' * (indent + 1)) - emit('sys.stderr.write(\' \'.join(["ERROR:", ') + emit('], [') notfirst = False while True: @@ -218,23 +253,21 @@ def parsestatcheck(indent): if eol == peek(): break notfirst = True - emitln(']))') - emit(' ' * (indent + 1)) - emitln('sys.stderr.write(eol)') - emit(' ' * (indent + 1)) - emitln('sys.exit(1)') + + emitln('])') def parsestattrace(indent): skipchar(' ') emit(' ' * indent) - emit('trace("') + emit('__trace("') - var_name = lexident() + varname = lexident() + varid = registerid(varname) skipchar(eol) - emit(var_name) + emit(varname) emit('", ') - emit(var_name) + emit(varid) emitln(')') def parsestat(indent): @@ -277,8 +310,10 @@ def parsestat(indent): parsestattrace(indent) return + funcid = registerid(call) + emit(' ' * indent) - emit(call) + emit(funcid) emit('(') first = True @@ -315,19 +350,22 @@ def parseblock(indent): def parsefunc(): funcname = lexident() + funcid = registerid(funcname) + mapsetkey("FUNCREG", funcname, "1") trace('funcname', funcname) emit('def ') - emit(funcname) + emit(funcid) emit('(') first = True while ' ' == peek(): skip() - var = lexident() + varname = lexident() + varid = registerid(varname) if not first: emit(", ") - emit(var) + emit(varid) first = False if '/' == peek(): @@ -352,26 +390,26 @@ def emitheader(): emitln("import os") emitln("import sys") emitln("") - emitln("def eq(a: str, b: str) -> str:") + 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("def __lt(a: str, b: str) -> str:") emitln(" return '1' if a < b else ''") emitln("") - emitln("def add(a: str, b: str) -> str:") + emitln("def __add(a: str, b: str) -> str:") emitln(" return a + b") emitln("") - emitln("def emit(string):") + emitln("def __emit(string):") emitln(" sys.stdout.buffer.write(string.encode('latin_1'))") emitln(" sys.stdout.flush()") emitln("") - emitln("def trace(header, value):") + 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("__EOF = chr(0xFF)") + emitln("__EOL = chr(10)") + emitln("__QUOTE = chr(34)") emitln("") emitln("STDINCOLNO = 0") emitln("STDINLINENO = 1") @@ -379,41 +417,103 @@ def emitheader(): emitln("") emitln("def _readchar():") emitln(" char = sys.stdin.read(1)") - emitln(" trace('char', char)") + emitln(" __trace('char', char)") emitln(" if not char:") - emitln(" return eof") + emitln(" return __EOF") emitln(" return char") emitln("") - emitln("def peek():") + emitln("def __peek():") emitln(" return STDINPEEK") emitln("") - emitln("def skip():") + emitln("def __skip():") emitln(" global STDINCOLNO") emitln(" global STDINLINENO") emitln(" global STDINPEEK") - emitln(" if eol == STDINPEEK:") + emitln(" if __EOL == STDINPEEK:") emitln(" STDINLINENO += 1") emitln(" STDINCOLNO = 0") emitln(" STDINCOLNO += 1") emitln(" STDINPEEK = _readchar()") emitln("") - emitln("def stdinlineno():") + emitln("def __stdinlineno():") emitln(" global STDINLINENO") emitln(" return str(STDINLINENO)") emitln("") - emitln("def stdincolno():") + emitln("def __stdincolno():") emitln(" global STDINCOLNO") emitln(" return str(STDINCOLNO)") emitln("") - emitln("skip()") + 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)") def emitfooter(): - emit("if __name__ == '__main__':\n") - emit(" main()\n") + 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", "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 diff --git a/1-lang0py/Makefile b/1-lang0py/Makefile index ad189bd..e96fdf5 100644 --- a/1-lang0py/Makefile +++ b/1-lang0py/Makefile @@ -36,5 +36,8 @@ lang0py.py: lang0py.lang0 lang0py2.exe mv $@.tmp $@ -diff lang0py2.py lang0py.py +$(LANG0PY): $(CURDIR)/../0-lang0py/lang0py.py $(CURDIR)/../0-lang0py/lang0py.lang0 + cd ../0-lang0py && make lang0py.exe + clean: rm -f lang0py*.py lang0py*.c lang0py*.o lang0py*.exe diff --git a/1-lang0py/lang0py.lang0 b/1-lang0py/lang0py.lang0 index abfa2f9..bebf711 100644 --- a/1-lang0py/lang0py.lang0 +++ b/1-lang0py/lang0py.lang0 @@ -1,12 +1,14 @@ -skipchar exp: - declare act - declare lineno - declare colno - calc act peek - calc lineno stdinlineno - calc colno stdincolno - check eq exp act : "Unexpected character" act "expected" exp "at" lineno colno - skip +registerid id: + declare idname + declare newidx + + calc idname mapgetkey "REGISTERID" id "" + if idname + return idname + / + + calc idname add "id_" id + return idname / emitln data: @@ -14,11 +16,6 @@ emitln data: emit eol / -increaseindent indent: - calc indent add indent " " - return indent -/ - lexident: declare word set word "" @@ -38,6 +35,22 @@ lexident: return word / +skipchar exp: + declare act + declare lineno + declare colno + calc act peek + calc lineno stdinlineno + calc colno stdincolno + check eq exp act : "Unexpected character" act "expected" exp "at" lineno colno + skip +/ + +increaseindent indent: + calc indent add indent " " + return indent +/ + parseconststring: skipchar quote emit quote @@ -60,12 +73,15 @@ parseconststring: parseexprvarref: calc varname lexident - emit varname + calc varid registerid varname + emit varid / parseexprcall: calc funcname lexident - emit funcname + calc funcid registerid funcname + check mapgetkey "FUNCREG" funcname "" : "Function" funcname "does not exist" + emit funcid emit "(" set first "1" forever @@ -97,16 +113,18 @@ parseexprcall: parsestatdeclare indent: skipchar " " - calc var lexident + calc varname lexident + calc varid registerid varname skipchar eol / parsestatset indent: skipchar " " - calc var lexident + calc varname lexident + calc varid registerid varname skipchar " " emit indent - emit var + emit varid emit " = " parseconststring emit eol @@ -115,10 +133,11 @@ parsestatset indent: parsestatcalc indent: skipchar " " - calc var lexident + calc varname lexident + calc varid registerid varname skipchar " " emit indent - emit var + emit varid emit " = " parseexprcall emit eol @@ -188,11 +207,12 @@ parsestatcheck indent: skipchar " " emit indent - emit "if not " + emit "__check(" calc funcname lexident - emit funcname - emit "(" + calc funcid registerid funcname + emit funcid + emit ", [" set notfirst "" forever @@ -222,10 +242,7 @@ parsestatcheck indent: skipchar ":" - emitln "):" - calc indent increaseindent indent - emit indent - emit "sys.stderr.write(' '.join(['ERROR:', " + emit "], [" set notfirst "" @@ -255,23 +272,20 @@ parsestatcheck indent: set notfirst "1" / - emitln "]))" - emit indent - emitln "sys.stderr.write(eol)" - emit indent - emitln "sys.exit(1)" + emitln "])" / parsestattrace indent: emit indent - emit "trace(" + emit "__trace(" emit quote skipchar " " calc varname lexident + calc varid registerid varname emit varname emit quote emit ", " - emit varname + emit varid emitln ")" skipchar eol / @@ -324,8 +338,9 @@ parsestat indent: parsestatcheck indent return / + calc callid registerid call emit indent - emit call + emit callid emit "(" set first "1" forever @@ -390,9 +405,11 @@ parseblock indent: parsefunc: calc funcname lexident + calc funcid registerid funcname trace funcname + mapsetkey "FUNCREG" funcname "1" emit "def " - emit funcname + emit funcid emit "(" set isnotfirst "" forever @@ -403,11 +420,12 @@ parsefunc: break / skip - calc var lexident + calc varname lexident + calc varid registerid varname if isnotfirst emit ", " / - emit var + emit varid set isnotfirst "1" / calc char peek @@ -431,26 +449,31 @@ emitheader: emitln "import os" emitln "import sys" emitln "" - emitln "def eq(a: str, b: str) -> str:" + 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 "def __lt(a: str, b: str) -> str:" emitln " return '1' if a < b else ''" emitln "" - emitln "def add(a: str, b :str) -> str:" + emitln "def __not(a: str) -> str:" + emitln " if a:" + emitln " return ''" + emitln " return '1'" + emitln "" + emitln "def __add(a: str, b :str) -> str:" emitln " return a + b" emitln "" - emitln "def emit(string):" + emitln "def __emit(string):" emitln " sys.stdout.buffer.write(string.encode('latin_1'))" emitln " sys.stdout.flush()" emitln "" - emitln "def trace(header, value):" + 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 "__EOF = chr(0xFF)" + emitln "__EOL = chr(10)" + emitln "__QUOTE = chr(34)" emitln "" emitln "STDINCOLNO = 0" emitln "STDINLINENO = 1" @@ -458,43 +481,102 @@ emitheader: emitln "" emitln "def _readchar():" emitln " char = sys.stdin.read(1)" - emitln " trace('char', char)" + emitln " __trace('char', char)" emitln " if not char:" - emitln " return eof" + emitln " return __EOF" emitln " return char" emitln "" - emitln "def peek():" + emitln "def __peek():" emitln " return STDINPEEK" emitln "" - emitln "def skip():" + emitln "def __skip():" emitln " global STDINCOLNO" emitln " global STDINLINENO" emitln " global STDINPEEK" - emitln " if eol == STDINPEEK:" + emitln " if __EOL == STDINPEEK:" emitln " STDINLINENO += 1" emitln " STDINCOLNO = 0" emitln " STDINCOLNO += 1" emitln " STDINPEEK = _readchar()" emitln "" - emitln "def stdinlineno():" + emitln "def __stdinlineno():" emitln " global STDINLINENO" emitln " return str(STDINLINENO)" emitln "" - emitln "def stdincolno():" + emitln "def __stdincolno():" emitln " global STDINCOLNO" emitln " return str(STDINCOLNO)" emitln "" - emitln "skip()" + emitln "__skip()" emitln "" + 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 __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)" / emitfooter: + declare mainname + calc mainname registerid "main" + emitln "if __name__ == '__main__':" - emitln " main()" + emit " " + emit mainname + emitln "()" / main: emitheader + + mapsetkey "REGISTERID" "eof" "__EOF" + mapsetkey "REGISTERID" "eol" "__EOL" + mapsetkey "REGISTERID" "quote" "__QUOTE" + + 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" + + forever calc char peek calc iseof eq char eof diff --git a/README.md b/README.md index 1ddb987..24a3656 100644 --- a/README.md +++ b/README.md @@ -155,6 +155,29 @@ Return true if the given strings are the same. Return true if a would sort before b. +#### mapclear mapname + +Maps are global and can be used from any function. + +Clears all values set in the map named `mapname`. + +#### mapgetkey mapname key + +Maps are global and can be used from any function. + +Looks up `key` in the map named `mapname` and returns it. +If not found, returns an empty string. + +#### mapsetkey mapname key value + +Maps are global and can be used from any function. + +Adds a mapping from `key` to `value` to the map named `mapname`. + +#### not a + +Returns "1" if a is empty; otherwise, returns "". + #### peek Checks stdin for the next character and returns it.