Compare commits
1 Commits
68e3c12d80
...
7220bb82d2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7220bb82d2 |
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
37
README.md
37
README.md
@ -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.
|
||||
|
||||
@ -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:
|
||||
|
||||
4
tests/funcnotexists.lang0
Normal file
4
tests/funcnotexists.lang0
Normal file
@ -0,0 +1,4 @@
|
||||
main:
|
||||
declare foo
|
||||
calc foo forgottodeclare "1" "2"
|
||||
/
|
||||
Loading…
x
Reference in New Issue
Block a user