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 char
	declare isbeforea
	declare isafterz
	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:
	declare char
	declare iseof
	declare isquote
	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
	return
/

parseexprvarref:
	declare varid
	declare varname
	calc varname lexident
	calc varid registerid varname
	emit varid
	return
/

parseexprcall:
	declare char
	declare first
	declare funcid
	declare funcname
	declare isspace
	declare isnotspace
	declare isfirst
	declare isnotfirst
	declare isquote
	declare isnotquote
	calc funcname lexident
	check mapgetkey "FUNCREG" funcname "" : "Function" funcname "does not exist"
	calc funcid registerid funcname
	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 ")"
	return
/

parsestatdeclare indent:
	declare varid
	declare varname
	skipchar " "
	calc varname lexident
	calc varid registerid varname
	emit indent
	emit "char * "
	emit varid
	emit ";"
	emit eol
	skipchar eol
	return
/

parsestatset indent:
	declare varid
	declare varname
	skipchar " "
	calc varname lexident
	calc varid registerid varname
	skipchar " "
	emit indent
	emit varid
	emit " = "
	parseconststring
	emit ";"
	emit eol
	skipchar eol
	return
/

parsestatcalc indent:
	declare varid
	declare varname
	skipchar " "
	calc varname lexident
	calc varid registerid varname
	skipchar " "
	emit indent
	emit varid
	emit " = "
	parseexprcall
	emit ";"
	emit eol
	skipchar eol
	return
/

parseblock indent/

parsestatif indent:
	declare indentt
	skipchar " "
	emit indent
	emit "if ( 0 < strlen("
	parseexprvarref
	emitln ") )"
	emit indent
	emitln "{"
	skipchar eol
	calc indentt increaseindent indent
	parseblock indentt
	emit indent
	emitln "}"
	return
/

parsestatforever indent:
	declare indentt
	emit indent
	emitln "while (1)"
	emit indent
	emitln "{"
	skipchar eol
	calc indentt increaseindent indent
	parseblock indentt
	emit indent
	emitln "}"
	return
/

parsestatbreak indent:
	emit indent
	emitln "break;"
	skipchar eol
	return
/

parsestatreturn indent:
	declare char
	declare isspace
	declare isnotquote
	declare isquote
	declare isnotspace
	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 ";"
	emit eol
	skipchar eol
	return
/

parsestatcheck indent:
	declare char
	declare funcid
	declare funcname
	declare iscolon
	declare iseol
	declare isnotquote
	declare isquote
	declare notfirst

	skipchar " "
	emit indent
	emit "if( 0 == strlen("

	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 ":"

	emitln ")) )"
	emit indent
	emitln "{"

	emit indent
	emit "    fprintf(stderr, "
	emit quote
	emit "%s"
	emit quote
	emit ", "
	emit quote
	emit "ERROR:"
	emit quote
	emitln ");"

	forever
		skipchar " "

		emit indent
		emit "    fprintf(stderr, "
		emit quote
		emit " %s"
		emit quote
		emit ", "
		calc char peek
		calc isquote eq char quote
		calc isnotquote not isquote
		if isquote
			parseconststring
		/
		if isnotquote
			parseexprvarref
		/
		emitln ");"

		calc char peek
		calc iseol eq char eol
		if iseol
			break
		/
	/

	emit indent
	emit "    fprintf(stderr, "
	emit quote
	emit "%s"
	emit quote
	emit ", __EOL"
	emitln ");"

	emit indent
	emitln "    exit(1);"
	emit indent
	emitln "}"
/

parsestattrace indent:
	declare varid
	declare varname
	emit indent
	emit "__trace("
	emit quote
	skipchar " "
	calc varname lexident
	calc varid registerid varname
	emit varname
	emit quote
	emit ", "
	emit varid
	emitln ");"
	skipchar eol
	return
/

parsestat indent:
	declare call
	declare callid
	declare char
	declare first
	declare iscall
	declare isfirst
	declare isspace
	declare isquote
	declare isnotfirst
	declare isnotspace
	declare isnotquote
	calc call lexident
	trace call
	calc iscall eq call "declare"
	if iscall
		parsestatdeclare indent
		return
	/
	calc iscall eq call "set"
	if iscall
		parsestatset indent
		return
	/
	calc iscall eq call "calc"
	if iscall
		parsestatcalc indent
		return
	/
	calc iscall eq call "if"
	if iscall
		parsestatif indent
		return
	/
	calc iscall eq call "forever"
	if iscall
		parsestatforever indent
		return
	/
	calc iscall eq call "break"
	if iscall
		parsestatbreak indent
		return
	/
	calc iscall eq call "return"
	if iscall
		parsestatreturn indent
		return
	/
	calc iscall eq call "check"
	if iscall
		parsestatcheck indent
		return
	/
	calc iscall eq call "trace"
	if iscall
		parsestattrace 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 char peek
		calc isquote eq char quote
		calc isnotquote not isquote
		calc isfirst eq first "1"
		calc isnotfirst not isfirst
		if isnotfirst
			emit ", "
		/
		if isquote
			parseconststring
		/
		if isnotquote
			parseexprvarref
		/
		set first "0"
	/
	skipchar eol
	emitln ");"
	return
/

parseblock indent:
	declare char
	declare copy
	declare iseol
	declare isnoteol
	declare isdone
	declare iseoblock
	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
	/
	return
/

parsefunc:
	declare char
	declare first
	declare funcid
	declare funcname
	declare iseoblock
	declare isfirst
	declare ismain
	declare isnotmain
	declare isspace
	declare isnotfirst
	declare isnotspace
	declare varid
	declare varname
	calc funcname lexident
	mapsetkey "FUNCREG" funcname "1"
	calc funcid registerid funcname
	trace funcname
	emit "char * "
	emit funcid
	emit "("
	set first "1"
	forever
		calc char peek
		calc isspace eq char " "
		calc isnotspace not isspace
		if isnotspace
			break
		/
		skip
		calc varname lexident
		calc varid registerid varname
		calc isfirst eq first "1"
		calc isnotfirst not isfirst
		if isnotfirst
			emit ", "
		/
		emit "char * "
		emit varid
		set first "0"
	/
	calc char peek
	calc iseoblock eq char "/"
	if iseoblock
		skipchar "/"
		skipchar eol
		emitln ");"
		return
	/
	skipchar ":"
	skipchar eol
	emitln ")"
	emitln "{"
	parseblock "    "
	emitln "}"
	emit eol
	return
/

emitheader:
	emitln "#include <assert.h>"
	emitln "#include <stdio.h>"
	emitln "#include <stdlib.h>"
	emitln "#include <string.h>"
	emitln ""
	emitln ""
	emitln "char __EOF[2] = {0xFF, 0};"
	emitln "char __EOL[2] = {10, 0};"
	emitln "char __QUOTE[2] = {34, 0};"
	emitln "char __FALSE[1] = {0};"
	emitln "char __TRUE[2] = {'1', 0};"
	emitln ""
	emitln ""
	emitln "char * __eq(char * a, char * b)"
	emitln "{"
	emitln "    return (strcmp(a, b) == 0) ? __TRUE : __FALSE;"
	emitln "}"
	emitln ""
	emitln "char * __lt(char * a, char * b)"
	emitln "{"
	emitln "    return (strcmp(a, b) < 0) ? __TRUE : __FALSE;"
	emitln "}"
	emitln "char * __not(char * a)"
	emitln "{"
	emitln "    return (0 == strlen(a)) ? __TRUE : __FALSE;"
	emitln "}"
	emitln ""
	emitln "char * __add(char * lft, char * rgt)"
	emitln "{"
	emitln "    int lft_len = strlen(lft);"
	emitln "    int rgt_len = strlen(rgt);"
	emitln "    char * res = malloc((lft_len + rgt_len + 1) * sizeof(char));"
	emitln "    strcpy(res, lft);"
	emitln "    strcpy(res + lft_len, rgt);"
	emitln "    res[lft_len + rgt_len] = 0;"
	emitln "    return res;"
	emitln "}"
	emitln ""
	emitln "char * __emit(char * str)"
	emitln "{"
	emitln "    fputs(str, stdout);"
	emitln "    return 0;"
	emitln "}"
	emitln ""
	emitln "char * __trace(char * var_name, char * var_value)"
	emitln "{"
	emit "    const char * env_trace = getenv("
	emit quote
	emit "TRACE"
	emit quote
	emitln ");"
	emitln "    if( !env_trace ) return 0;"
	emitln "    fputs(var_name, stderr);"
	emit "    fputs("
	emit quote
	emit "="
	emit quote
	emitln ", stderr);"
	emitln "    fputs(var_value, stderr);"
	emitln "    fputs(__EOL, stderr);"
	emitln "    return 0;"
	emitln "}"
	emitln ""
	emitln "int STDINCOLNO = 1;"
	emitln "int STDINLINENO = 1;"
	emitln "char * STDINPEEK = 0;"
	emitln ""
	emitln "char * _readchar()"
	emitln "{"
	emitln "    char * res = malloc(2*sizeof(char));"
	emitln "    res[0] = getc(stdin);"
	emitln "    res[1] = 0;"
	emitln "    return res;"
	emitln "}"
	emitln ""
	emitln "char * __peek()"
	emitln "{"
	emitln "    if( !STDINPEEK ) STDINPEEK = _readchar(); // First byte read"
	emitln "    return STDINPEEK;"
	emitln "}"
	emitln ""
	emitln "void __skip()"
	emitln "{"
	emitln "    if( !STDINPEEK ) STDINPEEK = _readchar(); // First byte read, not even peek()'d"
	emitln "    if( STDINPEEK[0] == 10 ) {"
	emitln "        STDINLINENO += 1;"
	emitln "        STDINCOLNO = 0;"
	emitln "    }"
	emitln "    STDINCOLNO += 1;"
	emitln "    STDINPEEK = _readchar();"
	emitln "}"
	emitln ""
	emitln "char * __stdinlineno()"
	emitln "{"
	emitln "    char * res = malloc(8*sizeof(char));"
	emit "    sprintf(res, "
	emit quote
	emit "%d"
	emit quote
	emitln ", STDINLINENO);"
	emitln "    return res;"
	emitln "}"
	emitln ""
	emitln "char * __stdincolno()"
	emitln "{"
	emitln "    char * res = malloc(8*sizeof(char));"
	emit "    sprintf(res, "
	emit quote
	emit "%d"
	emit quote
	emitln ", STDINCOLNO);"
	emitln "    return res;"
	emitln "}"
	emitln ""
	emitln "struct KEYVALUE"
	emitln "{"
	emitln "    char * key;"
	emitln "    char * value;"
	emitln "    struct KEYVALUE * nextkeyvalue;"
	emitln "};"
	emitln ""
	emitln "struct MAP"
	emitln "{"
	emitln "    char * mapname;"
	emitln "    struct KEYVALUE * firstkeyvalue;"
	emitln "    struct MAP * nextmap;"
	emitln "};"
	emitln ""
	emitln "struct MAP * MAPSTORE = 0;"
	emitln ""
	emitln "struct MAP * __map_get(char * mapname, int create)"
	emitln "{"
	emitln "    struct MAP *prevmap, *map;"
	emitln ""
	emitln "    prevmap = 0;"
	emitln "    map = MAPSTORE;"
	emitln "    while( map ) {"
	emitln "        if( strcmp(mapname, map->mapname) == 0) break;"
	emitln "        prevmap = map;"
	emitln "        map = map->nextmap;"
	emitln "    }"
	emitln ""
	emitln "    if( map ) {"
	emitln "        return map;"
	emitln "    }"
	emitln ""
	emitln "    if( !create ) {"
	emitln "        return 0;"
	emitln "    }"
	emitln ""
	emitln "    map = malloc(sizeof(struct MAP));"
	emitln "    map->mapname = mapname;"
	emitln "    map->firstkeyvalue = 0;"
	emitln "    map->nextmap = 0;"
	emitln "    if( prevmap == 0) {"
	emitln "        MAPSTORE = map;"
	emitln "    } else {"
	emitln "        prevmap->nextmap = map;"
	emitln "    }"
	emitln ""
	emitln "    return map;"
	emitln "}"
	emitln ""
	emitln "struct KEYVALUE * __map_keyvalue_get(struct MAP * map, char * key, int create)"
	emitln "{"
	emitln "    struct KEYVALUE *prevkeyvalue, *keyvalue;"
	emitln ""
	emitln "    prevkeyvalue = 0;"
	emitln "    keyvalue = map->firstkeyvalue;"
	emitln "    while( keyvalue ) {"
	emitln "        if( strcmp(key, keyvalue->key) == 0) break;"
	emitln "        prevkeyvalue = keyvalue;"
	emitln "        keyvalue = keyvalue->nextkeyvalue;"
	emitln "    }"
	emitln ""
	emitln "    if( keyvalue ) {"
	emitln "        return keyvalue;"
	emitln "    }"
	emitln ""
	emitln "    if( !create ) {"
	emitln "        return 0;"
	emitln "    }"
	emitln ""
	emitln "    keyvalue = malloc(sizeof(struct KEYVALUE));"
	emitln "    keyvalue->key = key;"
	emitln "    keyvalue->nextkeyvalue = 0;"
	emitln "    if( prevkeyvalue == 0) {"
	emitln "        map->firstkeyvalue = keyvalue;"
	emitln "    } else {"
	emitln "        prevkeyvalue->nextkeyvalue = keyvalue;"
	emitln "    }"
	emitln ""
	emitln "    return keyvalue;"
	emitln "}"
	emitln ""
	emitln "char * __mapclear(char * mapname)"
	emitln "{"
	emitln "    struct MAP * map = __map_get(mapname, 0);"
	emitln "    if( map ) {"
	emitln "        map->firstkeyvalue = 0;"
	emitln "    }"
	emitln ""
	emit "    return "
	emit quote
	emit quote
	emitln ";"
	emitln "}"
	emitln ""
	emitln "char * __mapgetkey(char * mapname, char * key, char * def)"
	emitln "{"
	emitln "    struct MAP * map = __map_get(mapname, 0);"
	emitln "    if( !map ) return def;"
	emitln ""
	emitln "    struct KEYVALUE *keyvalue = __map_keyvalue_get(map, key, 0);"
	emitln "    if( !keyvalue ) return def;"
	emitln ""
	emitln "    return keyvalue->value;"
	emitln "}"
	emitln ""
	emitln "char * __mapsetkey(char * mapname, char * key, char * value)"
	emitln "{"
	emitln "    struct MAP *map = __map_get(mapname, 1);"
	emitln "    struct KEYVALUE *keyvalue = __map_keyvalue_get(map, key, 1);"
	emitln "    keyvalue->value = value;"
	emit "    return "
	emit quote
	emit quote
	emitln ";"
	emitln "}"
	emitln ""
	emitln "char * __strlen(char * inp)"
	emitln "{"
	emitln "    char * buffer = malloc(20);"
	emitln "    size_t len = strlen(inp);"
	emit "    snprintf(buffer, 20, "
	emit quote
	emit "%zu"
	emit quote
	emitln ", len);"
	emitln "    return buffer;"
	emitln "}"
	emitln ""
	emitln "char * __intinc(char * inp)"
	emitln "{"
	emitln "    char * buffer = malloc(20);"
	emitln "    char * endptr;"
	emitln "    unsigned long long number = strtoull(inp, &endptr, 10);"
	emit "    snprintf(buffer, 20, "
	emit quote
	emit "%llu"
	emit quote
	emitln ", number + 1);"
	emitln "    return buffer;"
	emitln "}"
	emitln ""
	emitln "// ### END OF RUNTIME ### //"
	emitln ""
	return
/

emitfooter:
	declare mainid
	calc mainid registerid "main"

	emitln "int main() {"
	emit "    "
	emit mainid
	emitln "();"
	emitln "    return 0;"
	emitln "}"
	return
/

main:
	declare char
	declare iseof
	declare iseol
	declare isnoteol

	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" "intinc" "1"
	mapsetkey "REGISTERID" "intinc" "__intinc"
	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"
	mapsetkey "FUNCREG" "strlen" "1"
	mapsetkey "REGISTERID" "strlen" "__strlen"

	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
	return
/
