Feat: Error on undefined function Fix: All ids are now prefix to prevent native var clashes. Chore: __check helper function for ease of use
599 lines
10 KiB
Plaintext
599 lines
10 KiB
Plaintext
registerid id:
|
|
declare idname
|
|
declare newidx
|
|
|
|
calc idname mapgetkey "REGISTERID" id ""
|
|
if idname
|
|
return idname
|
|
/
|
|
|
|
calc idname add "id_" id
|
|
return idname
|
|
/
|
|
|
|
emitln data:
|
|
emit data
|
|
emit eol
|
|
/
|
|
|
|
lexident:
|
|
declare word
|
|
set word ""
|
|
forever
|
|
calc char peek
|
|
calc isbeforea lt char "a"
|
|
if isbeforea
|
|
break
|
|
/
|
|
calc isafterz lt "z" char
|
|
if isafterz
|
|
break
|
|
/
|
|
calc word add word char
|
|
skip
|
|
/
|
|
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
|
|
forever
|
|
calc char peek
|
|
calc iseof eq char eof
|
|
if iseof
|
|
break
|
|
/
|
|
calc isquote eq char quote
|
|
if isquote
|
|
break
|
|
/
|
|
emit char
|
|
skip
|
|
/
|
|
skipchar quote
|
|
emit quote
|
|
/
|
|
|
|
parseexprvarref:
|
|
calc varname lexident
|
|
calc varid registerid varname
|
|
emit varid
|
|
/
|
|
|
|
parseexprcall:
|
|
calc funcname lexident
|
|
calc funcid registerid funcname
|
|
check mapgetkey "FUNCREG" funcname "" : "Function" funcname "does not exist"
|
|
emit funcid
|
|
emit "("
|
|
set first "1"
|
|
forever
|
|
calc char peek
|
|
calc isspace eq char " "
|
|
calc isnotspace not isspace
|
|
if isnotspace
|
|
break
|
|
/
|
|
skip
|
|
calc isfirst eq first "1"
|
|
calc isnotfirst not isfirst
|
|
if isnotfirst
|
|
emit ", "
|
|
/
|
|
calc char peek
|
|
calc isquote eq char quote
|
|
calc isnotquote not isquote
|
|
if isquote
|
|
parseconststring
|
|
/
|
|
if isnotquote
|
|
parseexprvarref
|
|
/
|
|
set first "0"
|
|
/
|
|
emit ")"
|
|
/
|
|
|
|
parsestatdeclare indent:
|
|
skipchar " "
|
|
calc varname lexident
|
|
calc varid registerid varname
|
|
skipchar eol
|
|
/
|
|
|
|
parsestatset indent:
|
|
skipchar " "
|
|
calc varname lexident
|
|
calc varid registerid varname
|
|
skipchar " "
|
|
emit indent
|
|
emit varid
|
|
emit " = "
|
|
parseconststring
|
|
emit eol
|
|
skipchar eol
|
|
/
|
|
|
|
parsestatcalc indent:
|
|
skipchar " "
|
|
calc varname lexident
|
|
calc varid registerid varname
|
|
skipchar " "
|
|
emit indent
|
|
emit varid
|
|
emit " = "
|
|
parseexprcall
|
|
emit eol
|
|
skipchar eol
|
|
/
|
|
|
|
parseblock indent/
|
|
|
|
parsestatif indent:
|
|
skipchar " "
|
|
emit indent
|
|
emit "if "
|
|
parseexprvarref
|
|
emitln ":"
|
|
skipchar eol
|
|
calc indent increaseindent indent
|
|
parseblock indent
|
|
/
|
|
|
|
parsestatforever indent:
|
|
emit indent
|
|
emitln "while True:"
|
|
skipchar eol
|
|
calc indent increaseindent indent
|
|
parseblock indent
|
|
/
|
|
|
|
parsestatbreak indent:
|
|
emit indent
|
|
emitln "break"
|
|
skipchar eol
|
|
/
|
|
|
|
parsestatreturn indent:
|
|
emit indent
|
|
emit "return "
|
|
calc char peek
|
|
calc isspace eq char " "
|
|
calc isnotspace not isspace
|
|
if isspace
|
|
skip
|
|
calc char peek
|
|
calc isquote eq char quote
|
|
calc isnotquote not isquote
|
|
if isquote
|
|
parseconststring
|
|
/
|
|
if isnotquote
|
|
parseexprvarref
|
|
/
|
|
/
|
|
if isnotspace
|
|
emit quote
|
|
emit quote
|
|
/
|
|
emit eol
|
|
skipchar eol
|
|
/
|
|
|
|
parsestatcheck indent:
|
|
declare char
|
|
declare funcname
|
|
declare iscolon
|
|
declare isnotquote
|
|
declare isquote
|
|
declare notfirst
|
|
|
|
skipchar " "
|
|
emit indent
|
|
emit "__check("
|
|
|
|
calc funcname lexident
|
|
calc funcid registerid funcname
|
|
emit funcid
|
|
emit ", ["
|
|
|
|
set notfirst ""
|
|
forever
|
|
skipchar " "
|
|
calc char peek
|
|
calc iscolon eq char ":"
|
|
if iscolon
|
|
break
|
|
/
|
|
|
|
if notfirst
|
|
emit ", "
|
|
/
|
|
|
|
calc char peek
|
|
calc isquote eq char quote
|
|
calc isnotquote not isquote
|
|
if isquote
|
|
parseconststring
|
|
/
|
|
if isnotquote
|
|
parseexprvarref
|
|
/
|
|
|
|
set notfirst "1"
|
|
/
|
|
|
|
skipchar ":"
|
|
|
|
emit "], ["
|
|
|
|
set notfirst ""
|
|
|
|
forever
|
|
skipchar " "
|
|
|
|
if notfirst
|
|
emit ", "
|
|
/
|
|
|
|
calc char peek
|
|
calc isquote eq char quote
|
|
calc isnotquote not isquote
|
|
if isquote
|
|
parseconststring
|
|
/
|
|
if isnotquote
|
|
parseexprvarref
|
|
/
|
|
|
|
calc char peek
|
|
calc iseol eq char eol
|
|
if iseol
|
|
break
|
|
/
|
|
|
|
set notfirst "1"
|
|
/
|
|
|
|
emitln "])"
|
|
/
|
|
|
|
parsestattrace indent:
|
|
emit indent
|
|
emit "__trace("
|
|
emit quote
|
|
skipchar " "
|
|
calc varname lexident
|
|
calc varid registerid varname
|
|
emit varname
|
|
emit quote
|
|
emit ", "
|
|
emit varid
|
|
emitln ")"
|
|
skipchar eol
|
|
/
|
|
|
|
parsestat indent:
|
|
calc call lexident
|
|
trace call
|
|
calc isset eq call "declare"
|
|
if isset
|
|
parsestatdeclare indent
|
|
return
|
|
/
|
|
calc isset eq call "set"
|
|
if isset
|
|
parsestatset indent
|
|
return
|
|
/
|
|
calc iscalc eq call "calc"
|
|
if iscalc
|
|
parsestatcalc indent
|
|
return
|
|
/
|
|
calc isif eq call "if"
|
|
if isif
|
|
parsestatif indent
|
|
return
|
|
/
|
|
calc isforever eq call "forever"
|
|
if isforever
|
|
parsestatforever indent
|
|
return
|
|
/
|
|
calc isbreak eq call "break"
|
|
if isbreak
|
|
parsestatbreak indent
|
|
return
|
|
/
|
|
calc isreturn eq call "return"
|
|
if isreturn
|
|
parsestatreturn indent
|
|
return
|
|
/
|
|
calc istrace eq call "trace"
|
|
if istrace
|
|
parsestattrace indent
|
|
return
|
|
/
|
|
calc ischeck eq call "check"
|
|
if ischeck
|
|
parsestatcheck indent
|
|
return
|
|
/
|
|
calc callid registerid call
|
|
emit indent
|
|
emit callid
|
|
emit "("
|
|
set first "1"
|
|
forever
|
|
calc char peek
|
|
calc isspace eq char " "
|
|
calc isnotspace not isspace
|
|
if isnotspace
|
|
break
|
|
/
|
|
skip
|
|
calc isfirst eq first "1"
|
|
calc isnotfirst not isfirst
|
|
if isnotfirst
|
|
emit ", "
|
|
/
|
|
calc char peek
|
|
calc isquote eq char quote
|
|
calc isnotquote not isquote
|
|
if isquote
|
|
parseconststring
|
|
/
|
|
if isnotquote
|
|
parseexprvarref
|
|
/
|
|
set first "0"
|
|
/
|
|
skipchar eol
|
|
emitln ")"
|
|
/
|
|
|
|
parseblock indent:
|
|
forever
|
|
forever
|
|
calc char peek
|
|
calc iseol eq char eol
|
|
calc isnoteol not iseol
|
|
if isnoteol
|
|
break
|
|
/
|
|
skip
|
|
/
|
|
set copy " "
|
|
forever
|
|
calc isdone eq copy indent
|
|
if isdone
|
|
break
|
|
/
|
|
skipchar "\t"
|
|
calc copy increaseindent copy
|
|
/
|
|
calc char peek
|
|
calc iseoblock eq char "/"
|
|
if iseoblock
|
|
skip
|
|
skipchar eol
|
|
break
|
|
/
|
|
skipchar "\t"
|
|
parsestat indent
|
|
/
|
|
/
|
|
|
|
parsefunc:
|
|
calc funcname lexident
|
|
calc funcid registerid funcname
|
|
trace funcname
|
|
mapsetkey "FUNCREG" funcname "1"
|
|
emit "def "
|
|
emit funcid
|
|
emit "("
|
|
set isnotfirst ""
|
|
forever
|
|
calc char peek
|
|
calc isspace eq char " "
|
|
calc isnotspace not isspace
|
|
if isnotspace
|
|
break
|
|
/
|
|
skip
|
|
calc varname lexident
|
|
calc varid registerid varname
|
|
if isnotfirst
|
|
emit ", "
|
|
/
|
|
emit varid
|
|
set isnotfirst "1"
|
|
/
|
|
calc char peek
|
|
calc iseoblock eq char "/"
|
|
if iseoblock
|
|
skipchar "/"
|
|
skipchar eol
|
|
emitln "):"
|
|
emitln " pass # ahead declaration"
|
|
emit eol
|
|
return
|
|
/
|
|
skipchar ":"
|
|
skipchar eol
|
|
emitln "):"
|
|
parseblock " "
|
|
emit eol
|
|
/
|
|
|
|
emitheader:
|
|
emitln "import os"
|
|
emitln "import sys"
|
|
emitln ""
|
|
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 " return '1' if a < b else ''"
|
|
emitln ""
|
|
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 " sys.stdout.buffer.write(string.encode('latin_1'))"
|
|
emitln " sys.stdout.flush()"
|
|
emitln ""
|
|
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 ""
|
|
emitln "STDINCOLNO = 0"
|
|
emitln "STDINLINENO = 1"
|
|
emitln "STDINPEEK = None"
|
|
emitln ""
|
|
emitln "def _readchar():"
|
|
emitln " char = sys.stdin.read(1)"
|
|
emitln " __trace('char', char)"
|
|
emitln " if not char:"
|
|
emitln " return __EOF"
|
|
emitln " return char"
|
|
emitln ""
|
|
emitln "def __peek():"
|
|
emitln " return STDINPEEK"
|
|
emitln ""
|
|
emitln "def __skip():"
|
|
emitln " global STDINCOLNO"
|
|
emitln " global STDINLINENO"
|
|
emitln " global STDINPEEK"
|
|
emitln " if __EOL == STDINPEEK:"
|
|
emitln " STDINLINENO += 1"
|
|
emitln " STDINCOLNO = 0"
|
|
emitln " STDINCOLNO += 1"
|
|
emitln " STDINPEEK = _readchar()"
|
|
emitln ""
|
|
emitln "def __stdinlineno():"
|
|
emitln " global STDINLINENO"
|
|
emitln " return str(STDINLINENO)"
|
|
emitln ""
|
|
emitln "def __stdincolno():"
|
|
emitln " global STDINCOLNO"
|
|
emitln " return str(STDINCOLNO)"
|
|
emitln ""
|
|
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__':"
|
|
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
|
|
if iseof
|
|
break
|
|
/
|
|
forever
|
|
calc char peek
|
|
calc iseol eq char eol
|
|
calc isnoteol not iseol
|
|
if isnoteol
|
|
break
|
|
/
|
|
skip
|
|
/
|
|
parsefunc
|
|
/
|
|
emitfooter
|
|
/
|