Feat: Adds mapclear, mapgetkey, mapsetkey

Feat: Error on undefined function

Fix: All ids are now prefix to prevent native var clashes.

Chore: __check helper function for ease of use
This commit is contained in:
Johan B.W. de Vries 2025-02-02 15:43:37 +01:00
parent 6055cddab2
commit 68e3c12d80
4 changed files with 309 additions and 101 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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.