lang0/0-lang0py/lang0py.py
Johan B.W. de Vries a184d4c5e3 You can now return a constant string
Rather than having to store it as a variable
2025-02-09 15:03:11 +01:00

414 lines
7.2 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 skipchar(char):
global LINE
assert char == peek(), (LINE, char, peek())
skip()
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 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("PEEK = None")
emitln("LINE = 1")
emitln("")
emitln("def peek():")
emitln(" global PEEK")
emitln(" if PEEK is None:")
emitln(" char = sys.stdin.read(1)")
emitln(" trace('char', char)")
emitln(" if not char:")
emitln(" PEEK = eof")
emitln(" else:")
emitln(" PEEK = char")
emitln(" return PEEK")
emitln("")
emitln("peek()")
emitln("")
emitln("def skip():")
emitln(" global LINE")
emitln(" global PEEK")
emitln(" if eol == PEEK:")
emitln(" LINE += 1")
emitln(" PEEK = None")
emitln("")
emitln("def skipchar(char):")
emitln(" global LINE")
emitln(" assert char == peek(), (LINE, char, peek())")
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()