Compare commits

...

1 Commits

Author SHA1 Message Date
Johan B.W. de Vries
7220bb82d2 Fix: Python identifiers would mess up
Feat: Adds mapclear, mapgetkey, mapsetkey

Fix: Documents some more missing stdlib
2025-05-03 18:40:11 +02:00
5 changed files with 201 additions and 38 deletions

View File

@ -1,6 +1,9 @@
import os import os
import sys import sys
def eq(a, b):
return a == b
def emit(string): def emit(string):
sys.stdout.write(string) sys.stdout.write(string)
sys.stdout.flush() sys.stdout.flush()
@ -39,6 +42,46 @@ def skip():
PEEK = None 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): def emitln(data):
emit(data) emit(data)
emit(eol) emit(eol)
@ -81,12 +124,15 @@ def parseconststring():
emit(quote) emit(quote)
def parseexprvarref(): def parseexprvarref():
var = lexident() varname = lexident()
emit(var) varid = registerid(varname)
emit(varid)
def parseexprcall(): def parseexprcall():
funcname = lexident() funcname = lexident()
emit(funcname) __check(mapgetkey, ["FUNCREG", funcname, ""], ["Function", funcname, "does not exist"])
funcid = registerid(funcname)
emit(funcid)
emit('(') emit('(')
first = True first = True
@ -109,11 +155,12 @@ def parsestatdeclare(indent):
def parsestatset(indent): def parsestatset(indent):
skipchar(' ') skipchar(' ')
var = lexident() varname = lexident()
varid = registerid(varname)
skipchar(' ') skipchar(' ')
emit(' ' * indent) emit(' ' * indent)
emit(var) emit(varid)
emit(' = ') emit(' = ')
parseconststring() parseconststring()
@ -123,11 +170,12 @@ def parsestatset(indent):
def parsestatcalc(indent): def parsestatcalc(indent):
skipchar(' ') skipchar(' ')
var = lexident() varname = lexident()
varid = registerid(varname)
skipchar(' ') skipchar(' ')
emit(' ' * indent) emit(' ' * indent)
emit(var) emit(varid)
emit(' = ') emit(' = ')
parseexprcall() parseexprcall()
@ -172,11 +220,13 @@ def parsestatreturn(indent):
def parsestatcheck(indent): def parsestatcheck(indent):
skipchar(' ') skipchar(' ')
emit(' ' * indent) emit(' ' * indent)
emit('assert ') emit('__check(')
func_name = lexident() funcname = lexident()
emit(func_name) funcid = registerid(funcname)
emit('(') trace("funcid", funcid)
emit(funcid)
emit(', [')
notfirst = False notfirst = False
while True: while True:
@ -197,7 +247,7 @@ def parsestatcheck(indent):
skipchar(':') skipchar(':')
emit('), (') emit('], [')
notfirst = False notfirst = False
while True: while True:
@ -214,19 +264,20 @@ def parsestatcheck(indent):
if eol == peek(): if eol == peek():
break break
notfirst = True notfirst = True
emitln(')') emitln('])')
def parsestattrace(indent): def parsestattrace(indent):
skipchar(' ') skipchar(' ')
emit(' ' * indent) emit(' ' * indent)
emit('trace("') emit('trace("')
var_name = lexident() varname = lexident()
varid = registerid(varname)
skipchar(eol) skipchar(eol)
emit(var_name) emit(varname)
emit('", ') emit('", ')
emit(var_name) emit(varid)
emitln(')') emitln(')')
def parsestat(indent): def parsestat(indent):
@ -269,8 +320,10 @@ def parsestat(indent):
parsestattrace(indent) parsestattrace(indent)
return return
funcid = registerid(call)
emit(' ' * indent) emit(' ' * indent)
emit(call) emit(funcid)
emit('(') emit('(')
first = True first = True
@ -307,19 +360,22 @@ def parseblock(indent):
def parsefunc(): def parsefunc():
funcname = lexident() funcname = lexident()
funcid = registerid(funcname)
mapsetkey("FUNCREG", funcname, "1")
trace('funcname', funcname) trace('funcname', funcname)
emit('def ') emit('def ')
emit(funcname) emit(funcid)
emit('(') emit('(')
first = True first = True
while ' ' == peek(): while ' ' == peek():
skip() skip()
var = lexident() varname = lexident()
varid = registerid(varname)
if not first: if not first:
emit(", ") emit(", ")
emit(var) emit(varid)
first = False first = False
if '/' == peek(): if '/' == peek():
@ -334,7 +390,8 @@ def parsefunc():
skipchar(':') skipchar(':')
skipchar(eol) skipchar(eol)
emitln('):') emit('): # ')
emitln(funcname)
parseblock(1) parseblock(1)
@ -344,16 +401,16 @@ def emitheader():
emitln("import os") emitln("import os")
emitln("import sys") emitln("import sys")
emitln("") emitln("")
emitln("def eq(a, b):") emitln("def __eq(a, b):")
emitln(" return a == b") emitln(" return a == b")
emitln("") emitln("")
emitln("def lt(a, b):") emitln("def __lt(a, b):")
emitln(" return a[0] < b[0]") emitln(" return a[0] < b[0]")
emitln("") emitln("")
emitln("def addstringchar(a, b):") emitln("def __addstringchar(a, b):")
emitln(" return a + b[0]") emitln(" return a + b[0]")
emitln("") emitln("")
emitln("def emit(string):") emitln("def __emit(string):")
emitln(" sys.stdout.write(string)") emitln(" sys.stdout.write(string)")
emitln(" sys.stdout.flush()") emitln(" sys.stdout.flush()")
emitln("") emitln("")
@ -361,9 +418,9 @@ def emitheader():
emitln(" if os.environ.get('TRACE'):") emitln(" if os.environ.get('TRACE'):")
emitln(" sys.stderr.write(f'{header}={value!r}\\n')") emitln(" sys.stderr.write(f'{header}={value!r}\\n')")
emitln("") emitln("")
emitln("eof = chr(0)") emitln("__EOF = chr(0)")
emitln("eol = chr(10)") emitln("__EOL = chr(10)")
emitln("quote = chr(34)") emitln("__QUOTE = chr(34)")
emitln("") emitln("")
emitln("STDINCOLNO = 0") emitln("STDINCOLNO = 0")
emitln("STDINLINENO = 1") emitln("STDINLINENO = 1")
@ -373,39 +430,98 @@ def emitheader():
emitln(" char = sys.stdin.read(1)") emitln(" char = sys.stdin.read(1)")
emitln(" trace('char', char)") emitln(" trace('char', char)")
emitln(" if not char:") emitln(" if not char:")
emitln(" return eof") emitln(" return __EOF")
emitln(" return char") emitln(" return char")
emitln("") emitln("")
emitln("def peek():") emitln("def __peek():")
emitln(" return STDINPEEK") emitln(" return STDINPEEK")
emitln("") emitln("")
emitln("def skip():") emitln("def __skip():")
emitln(" global STDINCOLNO") emitln(" global STDINCOLNO")
emitln(" global STDINLINENO") emitln(" global STDINLINENO")
emitln(" global STDINPEEK") emitln(" global STDINPEEK")
emitln(" if eol == STDINPEEK:") emitln(" if __EOL == STDINPEEK:")
emitln(" STDINLINENO += 1") emitln(" STDINLINENO += 1")
emitln(" STDINCOLNO = 0") emitln(" STDINCOLNO = 0")
emitln(" STDINCOLNO += 1") emitln(" STDINCOLNO += 1")
emitln(" STDINPEEK = _readchar()") emitln(" STDINPEEK = _readchar()")
emitln("") emitln("")
emitln("def stdinlineno():") emitln("def __stdinlineno():")
emitln(" global STDINLINENO") emitln(" global STDINLINENO")
emitln(" return str(STDINLINENO)") emitln(" return str(STDINLINENO)")
emitln("") emitln("")
emitln("def stdincolno():") emitln("def __stdincolno():")
emitln(" global STDINCOLNO") emitln(" global STDINCOLNO")
emitln(" return str(STDINCOLNO)") emitln(" return str(STDINCOLNO)")
emitln("") 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 __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(): def emitfooter():
emit("if __name__ == '__main__':\n") mainname = registerid("main")
emit(" main()\n") emitln("if __name__ == '__main__':")
emit(" ")
emit(mainname)
emitln("()")
def main(): def main():
emitheader() 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: while True:
if eof == peek(): if eof == peek():
break break

View File

@ -36,5 +36,8 @@ lang0py.py: lang0py.lang0 lang0py2.exe
mv $@.tmp $@ mv $@.tmp $@
-diff lang0py2.py lang0py.py -diff lang0py2.py lang0py.py
$(LANG0PY): $(CURDIR)/../0-lang0py/lang0py.py $(CURDIR)/../0-lang0py/lang0py.lang0
cd ../0-lang0py && make lang0py.exe
clean: clean:
rm -f lang0py*.py lang0py*.c lang0py*.o lang0py*.exe 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. 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 ### Standard library functions
#### addstringchar a b #### addstringchar a b
@ -143,6 +157,29 @@ Return true if the given strings are the same.
Return true if a would sort before b. 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 #### peek
Checks stdin for the next character and returns it. Checks stdin for the next character and returns it.

View File

@ -9,7 +9,7 @@ CYTHON=cython3
CC=gcc CC=gcc
# builtincheckfalse is separate since it's supposed to crash. It's a test for the test 'framework', if you will. # 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 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 all-it0.results: $(addprefix build/,$(addsuffix .it0, $(TESTLIST))) build/builtincheckfalse.it0
-rm -f $@ -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' 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 "" >> $@ ;) $(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 all-it1.results: $(addprefix build/,$(addsuffix .it1, $(TESTLIST))) build/builtincheckfalse.it1
-rm -f $@ -rm -f $@
cat test-input.txt | build/builtincheckfalse.it1 2> /dev/null 2>&1 | grep 'Success' 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 "" >> $@ ;) $(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 all-it2.results: $(addprefix build/,$(addsuffix .it2, $(TESTLIST))) build/builtincheckfalse.it2
-rm -f $@ -rm -f $@
cat test-input.txt | build/builtincheckfalse.it2 2> /dev/null 2>&1 | grep 'Success' 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 "" >> $@ ;) $(foreach test,$(TESTLIST), echo -n "$(test) " >> $@; cat test-input.txt | ./build/$(test).it2 >> $@ ; echo "" >> $@ ;)
clean: clean:

View File

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