diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6f81061 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/it0-out0.diff +/it1-out* diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..2907ad4 --- /dev/null +++ b/Makefile @@ -0,0 +1,17 @@ +TEE := + +run: + cat it1-in.lang0 | python3 it0-out.py $(if $(TEE),| tee,> ) it1-out0.py + + cat it1-in.lang0 | python3 it1-out0.py $(if $(TEE),| tee,> ) it1-out1.py + diff it1-out0.py it1-out1.py > it1-out1.diff + cat it1-in.lang0 | python3 it1-out1.py $(if $(TEE),| tee,> ) it1-out2.py + diff it1-out1.py it1-out2.py > it1-out2.diff + cat it1-in.lang0 | python3 it1-out2.py $(if $(TEE),| tee,> ) it1-out3.py + diff it1-out2.py it1-out3.py > it1-out3.diff + + # See how much our hand written code differs from resulting code + -diff it0-out.py it1-out0.py > it0-out0.diff + +clean: + -rm it1-out* diff --git a/it0-out.py b/it0-out.py new file mode 100644 index 0000000..6593f7e --- /dev/null +++ b/it0-out.py @@ -0,0 +1,362 @@ +import os +import sys + +def emit(string): + sys.stdout.write(string) + +def trace(header, value): + if os.environ.get('TRACE'): + sys.stderr.write(f'{header}={value!r}\n') + +eof = chr(0) +eol = chr(10) +quote = chr(34) +PEEK = None +LINE = 1 + +def peek(): + global PEEK + + if PEEK is None: + char = sys.stdin.read(1) + trace('char', char) + if not char: + PEEK = eof + else: + PEEK = char + + return PEEK + +def skip(): + global LINE + global PEEK + + if eol == PEEK: + LINE += 1 + + PEEK = None + +def skipchar(char): + global LINE + assert char == peek(), (LINE, char, peek()) + skip() + +def lexident(): + word = '' + while True: + char = peek() + + if char < 'a' or char > 'z': + break + + word += char + skip() + + return word + +def parseconststring(): + skipchar(quote) + emit(quote) + + escaped = False + while True: + char = peek() + if char == eof: + break + if char == quote: + break + + emit(char) + + skip() + + skipchar(quote) + emit(quote) + +def parseexprvarref(): + var = lexident() + emit(var) + +def parseexprcall(): + funcname = lexident() + emit(funcname) + emit('(') + + first = True + while ' ' == peek(): + skip() + if not first: + emit(', ') + if quote == peek(): + parseconststring() + else: + parseexprvarref() + first = False + + emit(')') + +def parsestatset(indent): + skipchar(' ') + var = lexident() + skipchar(' ') + + emit(' ' * indent) + emit(var) + emit(' = ') + + parseconststring() + + emit(eol) + skipchar(eol) + +def parsestatcalc(indent): + skipchar(' ') + var = lexident() + skipchar(' ') + + emit(' ' * indent) + emit(var) + emit(' = ') + + parseexprcall() + + emit(eol) + skipchar(eol) + +def parsestatif(indent): + skipchar(' ') + emit(' ' * indent) + emit('if ') + parseexprvarref() + emit(':\n') + skipchar(eol) + + parseblock(indent + 1) + +def parsestatforever(indent): + emit(' ' * indent) + emit('while True:\n') + skipchar(eol) + + parseblock(indent + 1) + +def parsestatbreak(indent): + emit(' ' * indent) + emit('break\n') + skipchar(eol) + +def parsestatreturn(indent): + emit(' ' * indent) + emit('return ') + if ' ' == peek(): + skip() + parseexprvarref() + emit(eol) + skipchar(eol) + +def parsestatemit(indent): + skipchar(' ') + emit(' ' * indent) + emit('emit(') + + if quote == peek(): + parseconststring() + else: + parseexprvarref() + skipchar(eol) + + emit(')\n') + +def parsestattrace(indent): + skipchar(' ') + emit(' ' * indent) + emit('trace("') + + var_name = lexident() + skipchar(eol) + + emit(var_name) + emit('", ') + emit(var_name) + emit(')\n') + +def parsestatskipchar(indent): + skipchar(' ') + emit(' ' * indent) + emit('skipchar(') + if quote == peek(): + parseconststring() + else: + parseexprvarref() + emit(')\n') + skipchar(eol) + +def parsestat(indent): + call = lexident() + trace('call', call) + + if call == "set": + parsestatset(indent) + return + + if call == "calc": + parsestatcalc(indent) + return + + if call == "if": + parsestatif(indent) + return + + if call == "forever": + parsestatforever(indent) + return + + if call == "break": + parsestatbreak(indent) + return + + if call == "return": + parsestatreturn(indent) + return + + if call == "emit": + parsestatemit(indent) + return + + if call == "trace": + parsestattrace(indent) + return + + if call == "skipchar": + parsestatskipchar(indent) + return + + emit(' ' * indent) + emit(call) + emit('(') + + first = True + while ' ' == peek(): + skip() + if not first: + emit(", ") + if '"' == peek(): + parseconststring() + else: + parseexprvarref() + first = False + + skipchar(eol) + + emit(')\n') + +def parseblock(indent): + while True: + while eol == peek(): + skip() + + for _ in range(indent - 1): + skipchar('\t') + + if '/' == peek(): + skip() + skipchar(eol) + break + + skipchar('\t') + + parsestat(indent) + +def parsefunc(): + funcname = lexident() + trace('funcname', funcname) + emit('def ') + emit(funcname) + emit('(') + + first = True + while ' ' == peek(): + skip() + var = lexident() + if not first: + emit(", ") + emit(var) + first = False + + skipchar(':') + skipchar(eol) + + emit('):\n') + + parseblock(1) + + emit("\n") + +def emitheader(): + emit("import os\n") + emit("import sys\n") + emit("\n") + emit("def eq(a, b):\n") + emit(" return a == b\n") + emit("\n") + emit("def lt(a, b):\n") + emit(" return a < b\n") + emit("\n") + emit("def addstringchar(a, b):\n") + emit(" return a + b\n") + emit("\n") + emit("def emit(string):\n") + emit(" sys.stdout.write(string)\n") + emit("\n") + emit("def trace(header, value):\n") + emit(" if os.environ.get('TRACE'):\n") + emit(" sys.stderr.write(f'{header}={value!r}\\n')\n") + emit("\n") + emit("eof = chr(0)\n") + emit("eol = chr(10)\n") + emit("quote = chr(34)\n") + emit("PEEK = None\n") + emit("LINE = 1\n") + emit("\n") + emit("def peek():\n") + emit(" global PEEK\n") + emit(" if PEEK is None:\n") + emit(" char = sys.stdin.read(1)\n") + emit(" trace('char', char)\n") + emit(" if not char:\n") + emit(" PEEK = eof\n") + emit(" else:\n") + emit(" PEEK = char\n") + emit(" return PEEK\n") + emit("\n") + emit("def skip():\n") + emit(" global LINE\n") + emit(" global PEEK\n") + emit(" if eol == PEEK:\n") + emit(" LINE += 1\n") + emit(" PEEK = None\n") + emit("\n") + emit("def skipchar(char):\n") + emit(" global LINE\n") + emit(" assert char == peek(), (LINE, char, peek())\n") + emit(" skip()\n") + emit("\n") + +def emitfooter(): + emit("if __name__ == '__main__':\n") + emit(" main()\n") + +def main(): + emitheader() + while True: + if eof == peek(): + break + + while eol == peek(): + skip() + + parsefunc() + emitfooter() + +if __name__ == '__main__': + main() diff --git a/it1-in.lang0 b/it1-in.lang0 new file mode 100644 index 0000000..0c10e96 --- /dev/null +++ b/it1-in.lang0 @@ -0,0 +1,413 @@ +increaseindent indent: + calc indent addstringchar indent " " + calc indent addstringchar indent " " + calc indent addstringchar indent " " + calc indent addstringchar indent " " + return indent +/ + +lexident: + set word "" + forever + calc char peek + calc isbeforea lt char "a" + if isbeforea + break + / + calc isafterz lt "z" char + if isafterz + break + / + calc word addstringchar word char + skip + / + return word +/ + +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 + emit varname +/ + +parseexprcall: + calc funcname lexident + emit funcname + 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 ")" +/ + +parsestatset indent: + skipchar " " + calc var lexident + skipchar " " + emit indent + emit var + emit " = " + parseconststring + emit eol + skipchar eol +/ + +parsestatcalc indent: + skipchar " " + calc var lexident + skipchar " " + emit indent + emit var + emit " = " + parseexprcall + emit eol + skipchar eol +/ + +parsestatif indent: + skipchar " " + emit indent + emit "if " + parseexprvarref + emit ":\n" + skipchar eol + calc indent increaseindent indent + parseblock indent +/ + +parsestatforever indent: + emit indent + emit "while True:\n" + skipchar eol + calc indent increaseindent indent + parseblock indent +/ + +parsestatbreak indent: + emit indent + emit "break\n" + skipchar eol +/ + +parsestatreturn indent: + emit indent + emit "return " + calc char peek + calc isspace eq char " " + if isspace + skip + parseexprvarref + / + emit eol + skipchar eol +/ + +parsestatemit indent: + emit indent + emit "emit(" + skipchar " " + calc char peek + calc isquote eq char quote + calc isnotquote not isquote + if isquote + parseconststring + / + if isnotquote + parseexprvarref + / + emit ")\n" + skipchar eol +/ + +parsestattrace indent: + emit indent + emit "trace(" + emit quote + skipchar " " + calc varname lexident + emit varname + emit quote + emit ", " + emit varname + emit ")\n" + skipchar eol +/ + +parsestatskipchar indent: + skipchar " " + emit indent + emit "skipchar(" + calc char peek + calc isquote eq char quote + calc isnotquote not isquote + if isquote + parseconststring + / + if isnotquote + parseexprvarref + / + emit ")\n" + skipchar eol +/ + +parsestat indent: + calc call lexident + trace call + 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 isemit eq call "emit" + if isemit + parsestatemit indent + return + / + calc istrace eq call "trace" + if istrace + parsestattrace indent + return + / + calc isskipchar eq call "skipchar" + if isskipchar + parsestatskipchar indent + return + / + emit indent + emit call + 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 + if isquote + parseconststring + / + if isnotquote + parseexprvarref + / + calc isfirst eq first "1" + calc isnotfirst not isfirst + if isnotfirst + emit ", " + / + set first "0" + / + skipchar eol + emit ")\n" +/ + +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 + trace funcname + emit "def " + emit funcname + emit "(" + set first "1" + forever + calc char peek + calc isspace eq char " " + calc isnotspace not isspace + if isnotspace + break + / + skip + calc var lexident + calc isfirst eq first "1" + calc isnotfirst not isfirst + if isnotfirst + emit ", " + / + emit var + set first "0" + / + skipchar ":" + skipchar eol + emit "):\n" + parseblock " " + emit "\n" +/ + +emitheader: + emit "import os\n" + emit "import sys\n" + emit "\n" + emit "def eq(a, b):\n" + emit " return a == b\n" + emit "\n" + emit "def lt(a, b):\n" + emit " return a < b\n" + emit "\n" + emit "def addstringchar(a, b):\n" + emit " return a + b\n" + emit "\n" + emit "def emit(string):\n" + emit " sys.stdout.write(string)\n" + emit "\n" + emit "def trace(header, value):\n" + emit " if os.environ.get('TRACE'):\n" + emit " sys.stderr.write(f'{header}={value!r}\\n')\n" + emit "\n" + emit "eof = chr(0)\n" + emit "eol = chr(10)\n" + emit "quote = chr(34)\n" + emit "PEEK = None\n" + emit "LINE = 1\n" + emit "\n" + emit "def peek():\n" + emit " global PEEK\n" + emit " if PEEK is None:\n" + emit " char = sys.stdin.read(1)\n" + emit " trace('char', char)\n" + emit " if not char:\n" + emit " PEEK = eof\n" + emit " else:\n" + emit " PEEK = char\n" + emit " return PEEK\n" + emit "\n" + emit "def skip():\n" + emit " global LINE\n" + emit " global PEEK\n" + emit " if eol == PEEK:\n" + emit " LINE += 1\n" + emit " PEEK = None\n" + emit "\n" + emit "def skipchar(char):\n" + emit " global LINE\n" + emit " assert char == peek(), (LINE, char, peek())\n" + emit " skip()\n" + emit "\n" +/ + +emitfooter: + emit "if __name__ == '__main__':\n" + emit " main()\n" +/ + +main: + emitheader + 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 +/