363 lines
6.4 KiB
Python
363 lines
6.4 KiB
Python
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[0] < b[0]\n")
|
|
emit("\n")
|
|
emit("def addstringchar(a, b):\n")
|
|
emit(" return a + b[0]\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()
|