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
	/
	check mapgetkey "FUNCREG" call "" : "Function" call "does not exist"
	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)"
	emitln ""
	emitln "# ### END OF RUNTIME ### #"
	emitln ""
/

emitfooter:
	declare mainid
	calc mainid registerid "main"

	emitln "if __name__ == '__main__':"
	emit "    "
	emit mainid
	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
/
