one-file-projects/gerlang.py
2013-09-26 21:59:25 +02:00

197 lines
4.4 KiB
Python

class Analyzer:
def __init__(self, itr):
self.itr = itr
def next(self,amount=1):
if len(self.itr) == 0: return None
if amount == 1: return self.itr[0]
return self.itr[:amount]
def take(self,amount=1):
m, self.itr = self.next(amount), self.itr[amount:]
return m
def __len__(self):
return len(self.itr)
class StringAnalyzer(Analyzer):
def nextOrd(self):
m = self.next()
if m is None: return None
return ord(m)
def between(self,cmin,cmax):
c = self.nextOrd()
if c is None: return False
return c >= ord(cmin) and c <= ord(cmax)
def is_a(self,c):
return self.next() == c
class TokenListAnalyzer(Analyzer):
def takeUntilType(self,end):
t = []
while self.next() is not None and self.next()[0] != end:
t.append( self.take() )
return t
class Lexer:
keywords = ["setze","auf","durch","schreibe"]
operators = ["plus","minus","mal","geteilt"]
IDENT = 0
KEYWORD = 1
INT = 2
FLOAT = 3
OP = 4
BRACE_OPEN = 5
BRACE_CLOSE = 6
NEWLINE = 7
def lex(self, source):
tokens = []
sa = StringAnalyzer(source)
braces = 0
while len(sa) != 0:
if sa.between('a', 'z') or sa.between('A', 'Z'): #identifier or keyword
ident = ""
while sa.between('a', 'z') or sa.between('A', 'Z') or sa.between('0', '9') or sa.is_a('_'):
ident += sa.take()
if ident.lower() in self.keywords:
tokens.append( (self.KEYWORD,ident.lower()) )
elif ident.lower() in self.operators:
tokens.append( (self.OP,ident.lower()) )
else:
tokens.append( (self.IDENT,ident) )
elif sa.between('0', '9'): #number
num = ""
t = (self.INT, int)
while sa.between('0', '9'):
num += sa.take()
if sa.is_a(','):
t = (self.FLOAT, float)
sa.take()
num += "."
while sa.between('0', '9'):
num += sa.take()
tokens.append( (t[0],t[1](num)) )
elif sa.is_a('('):
tokens.append( (self.BRACE_OPEN,braces) )
braces+=1
elif sa.is_a(')'):
braces-=1
tokens.append( (self.BRACE_CLOSE,braces) )
elif sa.is_a('\n'):
tokens.append( (self.NEWLINE,) )
sa.take()
elif sa.is_a(' ') or sa.is_a('\t') or sa.is_a('\r'):
sa.take()
else:
raise ParserException("WTF is %s" % sa.take() )
return tokens
class ParserException(Exception):
pass
class Parser:
def parse(self,tokens):
block = BlockTerm()
ta = TokenListAnalyzer(tokens)
while len(ta) > 0:
if ta.next()[0] == Lexer.KEYWORD:
if ta.next()[1] == "setze":
ta.take()
if ta.next()[0] != Lexer.IDENT:
raise ParseException("missing identifier after setze")
ident = ta.take()[1]
if ta.next()[0] != Lexer.KEYWORD or ta.next()[1] != "auf":
raise ParserException("missing auf after identifier")
ta.take()
term = self.__parseTerm(ta.takeUntilType(Lexer.NEWLINE))
ta.take()
block.append(AssignmentTerm(ident,term))
elif ta.next()[1] == "schreibe":
ta.take()
term = self.__parseTerm(ta.takeUntilType(Lexer.NEWLINE))
block.append(PrintTerm(term))
elif ta.next()[0] == Lexer.NEWLINE:
ta.take()
return block
def __parseTerm(self,tokens):
t = tokens[0]
if t[0] == Lexer.IDENT:
return IdentifierTerm(t[1])
elif t[0] == Lexer.INT or t[0] == Lexer.FLOAT:
return ValueTerm(t[1])
else:
raise ParseException("Unexpected token %s" % t)
class Context():
pass
class SubContext(Context):
pass
class Term:
def run(self,context):
raise Exception("get_value must be overwritten")
class IdentifierTerm:
def __init__(self, ident):
self.ident = ident
def run(self,context):
return context[self.ident]
class ValueTerm:
def __init__(self, value):
self.value = value
def run(self,context):
return self.value
class AssignmentTerm(Term):
def __init__(self,ident,term):
self.ident = ident
self.term = term
def run(self,context):
context[self.ident] = self.term.run(context)
return None
class PrintTerm(Term):
def __init__(self,term):
self.term = term
def run(self,context):
print self.term.run(context)
return None
class BlockTerm(Term):
def __init__(self):
self.terms = []
def append(self,item):
self.terms.append(item)
def run(self,context):
result = None
for term in self.terms:
result = term.run(context)
return result
def main():
context = {}
while True:
code = raw_input(">>> ")
try:
tokens = Lexer().lex(code)
term = Parser().parse(tokens)
term.run(context)
except Exception, e:
print e
if __name__ == '__main__':
main()