lang0/0-lang0py/lang0py.py
Johan B.W. de Vries 2eaa763a2c Remove skipchar, add stdinlineno / stdincolno
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.
2025-02-09 15:38:02 +01:00

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()