With the new check function, this helper method doesn't make much sense to put in the standard library. To replicate the same result, we do need to expose the current line number; adding column number is a nice bonus. Also; made it a bit clearer when a check failes in it2. Also; the builtincheckfalse was not working. Also; separated out the test input file to have more data.
421 lines
7.4 KiB
Python
421 lines
7.4 KiB
Python
import os
|
|
import sys
|
|
|
|
def emit(string):
|
|
sys.stdout.write(string)
|
|
sys.stdout.flush()
|
|
|
|
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
|
|
|
|
peek()
|
|
|
|
def skip():
|
|
global LINE
|
|
global PEEK
|
|
|
|
if eol == PEEK:
|
|
LINE += 1
|
|
|
|
PEEK = None
|
|
|
|
def emitln(data):
|
|
emit(data)
|
|
emit(eol)
|
|
|
|
def lexident():
|
|
word = ''
|
|
while True:
|
|
char = peek()
|
|
|
|
if char < 'a' or char > 'z':
|
|
break
|
|
|
|
word += char
|
|
skip()
|
|
|
|
return word
|
|
|
|
def skipchar(char):
|
|
global LINE
|
|
assert char == peek(), (LINE, char, peek())
|
|
skip()
|
|
|
|
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 parsestatdeclare(indent):
|
|
skipchar(' ')
|
|
var = lexident()
|
|
skipchar(eol)
|
|
|
|
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()
|
|
emitln(':')
|
|
skipchar(eol)
|
|
|
|
parseblock(indent + 1)
|
|
|
|
def parsestatforever(indent):
|
|
emit(' ' * indent)
|
|
emitln('while True:')
|
|
skipchar(eol)
|
|
|
|
parseblock(indent + 1)
|
|
|
|
def parsestatbreak(indent):
|
|
emit(' ' * indent)
|
|
emitln('break')
|
|
skipchar(eol)
|
|
|
|
def parsestatreturn(indent):
|
|
emit(' ' * indent)
|
|
emit('return ')
|
|
if ' ' == peek():
|
|
skip()
|
|
if quote == peek():
|
|
parseconststring()
|
|
else:
|
|
parseexprvarref()
|
|
emit(eol)
|
|
skipchar(eol)
|
|
|
|
def parsestatcheck(indent):
|
|
skipchar(' ')
|
|
emit(' ' * indent)
|
|
emit('assert ')
|
|
|
|
func_name = lexident()
|
|
emit(func_name)
|
|
emit('(')
|
|
notfirst = False
|
|
|
|
while True:
|
|
skipchar(' ')
|
|
|
|
if ':' == peek():
|
|
break
|
|
|
|
if notfirst:
|
|
emit(', ')
|
|
|
|
if quote == peek():
|
|
parseconststring()
|
|
else:
|
|
parseexprvarref()
|
|
|
|
notfirst = True
|
|
|
|
skipchar(':')
|
|
|
|
emit('), (')
|
|
notfirst = False
|
|
|
|
while True:
|
|
skipchar(' ')
|
|
|
|
if notfirst:
|
|
emit(', ')
|
|
|
|
if quote == peek():
|
|
parseconststring()
|
|
else:
|
|
parseexprvarref()
|
|
|
|
if eol == peek():
|
|
break
|
|
notfirst = True
|
|
emitln(')')
|
|
|
|
def parsestattrace(indent):
|
|
skipchar(' ')
|
|
emit(' ' * indent)
|
|
emit('trace("')
|
|
|
|
var_name = lexident()
|
|
skipchar(eol)
|
|
|
|
emit(var_name)
|
|
emit('", ')
|
|
emit(var_name)
|
|
emitln(')')
|
|
|
|
def parsestat(indent):
|
|
call = lexident()
|
|
trace('call', call)
|
|
|
|
if call == "declare":
|
|
parsestatdeclare(indent)
|
|
return
|
|
|
|
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 == "check":
|
|
parsestatcheck(indent)
|
|
return
|
|
|
|
if call == "trace":
|
|
parsestattrace(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)
|
|
|
|
emitln(')')
|
|
|
|
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
|
|
|
|
if '/' == peek():
|
|
# Ahead declaration
|
|
skipchar('/')
|
|
skipchar(eol)
|
|
emitln("):")
|
|
emitln(" pass # ahead declaration")
|
|
emit(eol)
|
|
return
|
|
|
|
skipchar(':')
|
|
skipchar(eol)
|
|
|
|
emitln('):')
|
|
|
|
parseblock(1)
|
|
|
|
emit(eol)
|
|
|
|
def emitheader():
|
|
emitln("import os")
|
|
emitln("import sys")
|
|
emitln("")
|
|
emitln("def eq(a, b):")
|
|
emitln(" return a == b")
|
|
emitln("")
|
|
emitln("def lt(a, b):")
|
|
emitln(" return a[0] < b[0]")
|
|
emitln("")
|
|
emitln("def addstringchar(a, b):")
|
|
emitln(" return a + b[0]")
|
|
emitln("")
|
|
emitln("def emit(string):")
|
|
emitln(" sys.stdout.write(string)")
|
|
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(0)")
|
|
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("")
|
|
|
|
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()
|