Fix: Python identifiers would mess up

Feat: Adds mapclear, mapgetkey, mapsetkey

Fix: Documents some more missing stdlib
This commit is contained in:
Johan B.W. de Vries 2025-02-02 15:43:37 +01:00
parent 1b8333fcfa
commit 7220bb82d2
5 changed files with 201 additions and 38 deletions

View File

@ -1,6 +1,9 @@
import os
import sys
def eq(a, b):
return a == b
def emit(string):
sys.stdout.write(string)
sys.stdout.flush()
@ -39,6 +42,46 @@ def skip():
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)
@ -81,12 +124,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 +155,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 +170,12 @@ def parsestatset(indent):
def parsestatcalc(indent):
skipchar(' ')
var = lexident()
varname = lexident()
varid = registerid(varname)
skipchar(' ')
emit(' ' * indent)
emit(var)
emit(varid)
emit(' = ')
parseexprcall()
@ -172,11 +220,13 @@ def parsestatreturn(indent):
def parsestatcheck(indent):
skipchar(' ')
emit(' ' * indent)
emit('assert ')
emit('__check(')
func_name = lexident()
emit(func_name)
emit('(')
funcname = lexident()
funcid = registerid(funcname)
trace("funcid", funcid)
emit(funcid)
emit(', [')
notfirst = False
while True:
@ -197,7 +247,7 @@ def parsestatcheck(indent):
skipchar(':')
emit('), (')
emit('], [')
notfirst = False
while True:
@ -214,19 +264,20 @@ def parsestatcheck(indent):
if eol == peek():
break
notfirst = True
emitln(')')
emitln('])')
def parsestattrace(indent):
skipchar(' ')
emit(' ' * indent)
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):
@ -269,8 +320,10 @@ def parsestat(indent):
parsestattrace(indent)
return
funcid = registerid(call)
emit(' ' * indent)
emit(call)
emit(funcid)
emit('(')
first = True
@ -307,19 +360,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():
@ -334,7 +390,8 @@ def parsefunc():
skipchar(':')
skipchar(eol)
emitln('):')
emit('): # ')
emitln(funcname)
parseblock(1)
@ -344,16 +401,16 @@ def emitheader():
emitln("import os")
emitln("import sys")
emitln("")
emitln("def eq(a, b):")
emitln("def __eq(a, b):")
emitln(" return a == b")
emitln("")
emitln("def lt(a, b):")
emitln("def __lt(a, b):")
emitln(" return a[0] < b[0]")
emitln("")
emitln("def addstringchar(a, b):")
emitln("def __addstringchar(a, b):")
emitln(" return a + b[0]")
emitln("")
emitln("def emit(string):")
emitln("def __emit(string):")
emitln(" sys.stdout.write(string)")
emitln(" sys.stdout.flush()")
emitln("")
@ -361,9 +418,9 @@ def emitheader():
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("__EOF = chr(0)")
emitln("__EOL = chr(10)")
emitln("__QUOTE = chr(34)")
emitln("")
emitln("STDINCOLNO = 0")
emitln("STDINLINENO = 1")
@ -373,39 +430,98 @@ def emitheader():
emitln(" char = sys.stdin.read(1)")
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(' '.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", "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

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

@ -121,6 +121,20 @@ Calls the given function with the given arguments. If the function's return valu
Writes the name and value of the variable passed to stderr if the TRACE environment variable is set.
### Standard library constants
#### eof
End of file character, zero byte.
#### eol
End of line character.
#### quote
Double quote character.
### Standard library functions
#### addstringchar a b
@ -143,6 +157,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.

View File

@ -9,7 +9,7 @@ CYTHON=cython3
CC=gcc
# builtincheckfalse is separate since it's supposed to crash. It's a test for the test 'framework', if you will.
TESTLIST=$(shell ls *.lang0 | grep -v 'builtincheckfalse' | sed 's/.lang0//')
TESTLIST=$(shell ls *.lang0 | grep -v -e 'builtincheckfalse' -e 'funcnotexists' | sed 's/.lang0//')
all: check
@ -18,17 +18,20 @@ check: all-it0.results all-it1.results all-it2.results
all-it0.results: $(addprefix build/,$(addsuffix .it0, $(TESTLIST))) build/builtincheckfalse.it0
-rm -f $@
(cat funcnotexists.lang0 | ../0-lang0py/lang0py.exe 2>&1 1>/dev/null || true) | grep 'Function forgottodeclare does not exist'
cat test-input.txt | build/builtincheckfalse.it0 2> /dev/null 2>&1 | grep 'Success'
$(foreach test,$(TESTLIST), echo -n "$(test) " >> $@; cat test-input.txt | ./build/$(test).it0 >> $@ ; echo "" >> $@ ;)
all-it1.results: $(addprefix build/,$(addsuffix .it1, $(TESTLIST))) build/builtincheckfalse.it1
-rm -f $@
cat test-input.txt | build/builtincheckfalse.it1 2> /dev/null 2>&1 | grep 'Success'
(cat funcnotexists.lang0 | ../1-lang0py/lang0py.exe 2>&1 1>/dev/null || true) | grep 'Function forgottodeclare does not exist'
$(foreach test,$(TESTLIST), echo -n "$(test) " >> $@; cat test-input.txt | ./build/$(test).it1 >> $@ ; echo "" >> $@ ;)
all-it2.results: $(addprefix build/,$(addsuffix .it2, $(TESTLIST))) build/builtincheckfalse.it2
-rm -f $@
cat test-input.txt | build/builtincheckfalse.it2 2> /dev/null 2>&1 | grep 'Success'
(cat funcnotexists.lang0 | ../2-lang0py/lang0c.exe 2>&1 1>/dev/null || true) | grep 'Function forgottodeclare does not exist'
$(foreach test,$(TESTLIST), echo -n "$(test) " >> $@; cat test-input.txt | ./build/$(test).it2 >> $@ ; echo "" >> $@ ;)
clean:

View File

@ -0,0 +1,4 @@
main:
declare foo
calc foo forgottodeclare "1" "2"
/