From d0e34417bc5973e708d6581f62999139b8912802 Mon Sep 17 00:00:00 2001 From: David Beazley Date: Tue, 30 Jan 2018 07:42:27 -0600 Subject: [PATCH] WIP --- README.txt | 45 ++++++++++-------- example/calc/calc.py | 106 +++++++++++++++++++++++++------------------ 2 files changed, 88 insertions(+), 63 deletions(-) diff --git a/README.txt b/README.txt index 7007383..2aabb78 100644 --- a/README.txt +++ b/README.txt @@ -85,20 +85,27 @@ expressions and store variables: from sly import Lexer, Parser class CalcLexer(Lexer): - tokens = { NAME, NUMBER } + tokens = { NAME, NUMBER, PLUS, TIMES, MINUS, DIVIDE, ASSIGN, LPAREN, RPAREN } ignore = ' \t' - literals = { '=', '+', '-', '*', '/', '(', ')' } # Tokens NAME = r'[a-zA-Z_][a-zA-Z0-9_]*' + NUMBER = r'\d+' - @_(r'\d+') - def NUMBER(self, t): - t.value = int(t.value) - return t + # Special symbols + PLUS = r'\+' + MINUS = r'-' + TIMES = r'\*' + DIVIDE = r'/' + ASSIGN = r'=' + LPAREN = r'\(' + RPAREN = r'\)' - @_(r'\n+') - def newline(self, t): + # Ignored pattern + ignore_newline = r'\n+' + + # Extra action for newlines + def ignore_newline(self, t): self.lineno += t.value.count('\n') def error(self, t): @@ -109,15 +116,15 @@ expressions and store variables: tokens = CalcLexer.tokens precedence = ( - ('left', '+', '-'), - ('left', '*', '/'), - ('right', 'UMINUS'), + ('left', PLUS, MINUS), + ('left', TIMES, DIVIDE), + ('right', UMINUS), ) def __init__(self): self.names = { } - @_('NAME "=" expr') + @_('NAME ASSIGN expr') def statement(self, p): self.names[p.NAME] = p.expr @@ -125,27 +132,27 @@ expressions and store variables: def statement(self, p): print(p.expr) - @_('expr "+" expr') + @_('expr PLUS expr') def expr(self, p): return p.expr0 + p.expr1 - @_('expr "-" expr') + @_('expr MINUS expr') def expr(self, p): return p.expr0 - p.expr1 - @_('expr "*" expr') + @_('expr TIMES expr') def expr(self, p): return p.expr0 * p.expr1 - @_('expr "/" expr') + @_('expr DIVIDE expr') def expr(self, p): return p.expr0 / p.expr1 - @_('"-" expr %prec UMINUS') + @_('MINUS expr %prec UMINUS') def expr(self, p): return -p.expr - @_('"(" expr ")"') + @_('LPAREN expr RPAREN') def expr(self, p): return p.expr @@ -158,7 +165,7 @@ expressions and store variables: try: return self.names[p.NAME] except LookupError: - print("Undefined name '%s'" % p.NAME) + print(f'Undefined name {p.NAME!r}') return 0 if __name__ == '__main__': diff --git a/example/calc/calc.py b/example/calc/calc.py index f7825ec..80c192b 100644 --- a/example/calc/calc.py +++ b/example/calc/calc.py @@ -3,32 +3,32 @@ # ----------------------------------------------------------------------------- import sys -sys.path.insert(0, "../..") +sys.path.append('../..') from sly import Lexer, Parser class CalcLexer(Lexer): - # Set of token names. This is always required - tokens = { NUMBER, PLUS, MINUS, TIMES, DIVIDE, LPAREN, RPAREN } - - # String containing ignored characters between tokens + tokens = { NAME, NUMBER, PLUS, TIMES, MINUS, DIVIDE, ASSIGN, LPAREN, RPAREN } ignore = ' \t' - # Regular expression rules for tokens - PLUS = r'\+' - MINUS = r'-' - TIMES = r'\*' - DIVIDE = r'/' - LPAREN = r'\(' - RPAREN = r'\)' + # Tokens + NAME = r'[a-zA-Z_][a-zA-Z0-9_]*' + NUMBER = r'\d+' - @_(r'\d+') - def NUMBER(self, t): - t.value = int(t.value) - return t + # Special symbols + PLUS = r'\+' + MINUS = r'-' + TIMES = r'\*' + DIVIDE = r'/' + ASSIGN = r'=' + LPAREN = r'\(' + RPAREN = r'\)' - @_(r'\n+') - def newline(self, t): + # Ignored pattern + ignore_newline = r'\n+' + + # Extra action for newlines + def ignore_newline(self, t): self.lineno += t.value.count('\n') def error(self, t): @@ -36,50 +36,68 @@ class CalcLexer(Lexer): self.index += 1 class CalcParser(Parser): - # Get the token list from the lexer (required) tokens = CalcLexer.tokens - # Grammar rules and actions - @_('expr PLUS term') + precedence = ( + ('left', PLUS, MINUS), + ('left', TIMES, DIVIDE), + ('right', UMINUS) + ) + + def __init__(self): + self.names = { } + + @_('NAME ASSIGN expr') + def statement(self, p): + self.names[p.NAME] = p.expr + + @_('expr') + def statement(self, p): + print(p.expr) + + @_('expr PLUS expr') def expr(self, p): - return p.expr + p.term + return p.expr0 + p.expr1 - @_('expr MINUS term') + @_('expr MINUS expr') def expr(self, p): - return p.expr - p.term + return p.expr0 - p.expr1 - @_('term') + @_('expr TIMES expr') def expr(self, p): - return p.term + return p.expr0 * p.expr1 - @_('term TIMES factor') - def term(self, p): - return p.term * p.factor + @_('expr DIVIDE expr') + def expr(self, p): + return p.expr0 / p.expr1 - @_('term DIVIDE factor') - def term(self, p): - return p.term / p.factor - - @_('factor') - def term(self, p): - return p.factor - - @_('NUMBER') - def factor(self, p): - return p.NUMBER + @_('MINUS expr %prec UMINUS') + def expr(self, p): + return -p.expr @_('LPAREN expr RPAREN') - def factor(self, p): + def expr(self, p): return p.expr + @_('NUMBER') + def expr(self, p): + return int(p.NUMBER) + + @_('NAME') + def expr(self, p): + try: + return self.names[p.NAME] + except LookupError: + print(f'Undefined name {p.NAME!r}') + return 0 + if __name__ == '__main__': lexer = CalcLexer() parser = CalcParser() - while True: try: text = input('calc > ') - result = parser.parse(lexer.tokenize(text)) - print(result) except EOFError: break + if text: + parser.parse(lexer.tokenize(text))