WIP
This commit is contained in:
parent
51b01d8335
commit
d0e34417bc
45
README.txt
45
README.txt
@ -85,20 +85,27 @@ expressions and store variables:
|
|||||||
from sly import Lexer, Parser
|
from sly import Lexer, Parser
|
||||||
|
|
||||||
class CalcLexer(Lexer):
|
class CalcLexer(Lexer):
|
||||||
tokens = { NAME, NUMBER }
|
tokens = { NAME, NUMBER, PLUS, TIMES, MINUS, DIVIDE, ASSIGN, LPAREN, RPAREN }
|
||||||
ignore = ' \t'
|
ignore = ' \t'
|
||||||
literals = { '=', '+', '-', '*', '/', '(', ')' }
|
|
||||||
|
|
||||||
# Tokens
|
# Tokens
|
||||||
NAME = r'[a-zA-Z_][a-zA-Z0-9_]*'
|
NAME = r'[a-zA-Z_][a-zA-Z0-9_]*'
|
||||||
|
NUMBER = r'\d+'
|
||||||
|
|
||||||
@_(r'\d+')
|
# Special symbols
|
||||||
def NUMBER(self, t):
|
PLUS = r'\+'
|
||||||
t.value = int(t.value)
|
MINUS = r'-'
|
||||||
return t
|
TIMES = r'\*'
|
||||||
|
DIVIDE = r'/'
|
||||||
|
ASSIGN = r'='
|
||||||
|
LPAREN = r'\('
|
||||||
|
RPAREN = r'\)'
|
||||||
|
|
||||||
@_(r'\n+')
|
# Ignored pattern
|
||||||
def newline(self, t):
|
ignore_newline = r'\n+'
|
||||||
|
|
||||||
|
# Extra action for newlines
|
||||||
|
def ignore_newline(self, t):
|
||||||
self.lineno += t.value.count('\n')
|
self.lineno += t.value.count('\n')
|
||||||
|
|
||||||
def error(self, t):
|
def error(self, t):
|
||||||
@ -109,15 +116,15 @@ expressions and store variables:
|
|||||||
tokens = CalcLexer.tokens
|
tokens = CalcLexer.tokens
|
||||||
|
|
||||||
precedence = (
|
precedence = (
|
||||||
('left', '+', '-'),
|
('left', PLUS, MINUS),
|
||||||
('left', '*', '/'),
|
('left', TIMES, DIVIDE),
|
||||||
('right', 'UMINUS'),
|
('right', UMINUS),
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.names = { }
|
self.names = { }
|
||||||
|
|
||||||
@_('NAME "=" expr')
|
@_('NAME ASSIGN expr')
|
||||||
def statement(self, p):
|
def statement(self, p):
|
||||||
self.names[p.NAME] = p.expr
|
self.names[p.NAME] = p.expr
|
||||||
|
|
||||||
@ -125,27 +132,27 @@ expressions and store variables:
|
|||||||
def statement(self, p):
|
def statement(self, p):
|
||||||
print(p.expr)
|
print(p.expr)
|
||||||
|
|
||||||
@_('expr "+" expr')
|
@_('expr PLUS expr')
|
||||||
def expr(self, p):
|
def expr(self, p):
|
||||||
return p.expr0 + p.expr1
|
return p.expr0 + p.expr1
|
||||||
|
|
||||||
@_('expr "-" expr')
|
@_('expr MINUS expr')
|
||||||
def expr(self, p):
|
def expr(self, p):
|
||||||
return p.expr0 - p.expr1
|
return p.expr0 - p.expr1
|
||||||
|
|
||||||
@_('expr "*" expr')
|
@_('expr TIMES expr')
|
||||||
def expr(self, p):
|
def expr(self, p):
|
||||||
return p.expr0 * p.expr1
|
return p.expr0 * p.expr1
|
||||||
|
|
||||||
@_('expr "/" expr')
|
@_('expr DIVIDE expr')
|
||||||
def expr(self, p):
|
def expr(self, p):
|
||||||
return p.expr0 / p.expr1
|
return p.expr0 / p.expr1
|
||||||
|
|
||||||
@_('"-" expr %prec UMINUS')
|
@_('MINUS expr %prec UMINUS')
|
||||||
def expr(self, p):
|
def expr(self, p):
|
||||||
return -p.expr
|
return -p.expr
|
||||||
|
|
||||||
@_('"(" expr ")"')
|
@_('LPAREN expr RPAREN')
|
||||||
def expr(self, p):
|
def expr(self, p):
|
||||||
return p.expr
|
return p.expr
|
||||||
|
|
||||||
@ -158,7 +165,7 @@ expressions and store variables:
|
|||||||
try:
|
try:
|
||||||
return self.names[p.NAME]
|
return self.names[p.NAME]
|
||||||
except LookupError:
|
except LookupError:
|
||||||
print("Undefined name '%s'" % p.NAME)
|
print(f'Undefined name {p.NAME!r}')
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
@ -3,32 +3,32 @@
|
|||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
sys.path.insert(0, "../..")
|
sys.path.append('../..')
|
||||||
|
|
||||||
from sly import Lexer, Parser
|
from sly import Lexer, Parser
|
||||||
|
|
||||||
class CalcLexer(Lexer):
|
class CalcLexer(Lexer):
|
||||||
# Set of token names. This is always required
|
tokens = { NAME, NUMBER, PLUS, TIMES, MINUS, DIVIDE, ASSIGN, LPAREN, RPAREN }
|
||||||
tokens = { NUMBER, PLUS, MINUS, TIMES, DIVIDE, LPAREN, RPAREN }
|
|
||||||
|
|
||||||
# String containing ignored characters between tokens
|
|
||||||
ignore = ' \t'
|
ignore = ' \t'
|
||||||
|
|
||||||
# Regular expression rules for tokens
|
# Tokens
|
||||||
|
NAME = r'[a-zA-Z_][a-zA-Z0-9_]*'
|
||||||
|
NUMBER = r'\d+'
|
||||||
|
|
||||||
|
# Special symbols
|
||||||
PLUS = r'\+'
|
PLUS = r'\+'
|
||||||
MINUS = r'-'
|
MINUS = r'-'
|
||||||
TIMES = r'\*'
|
TIMES = r'\*'
|
||||||
DIVIDE = r'/'
|
DIVIDE = r'/'
|
||||||
|
ASSIGN = r'='
|
||||||
LPAREN = r'\('
|
LPAREN = r'\('
|
||||||
RPAREN = r'\)'
|
RPAREN = r'\)'
|
||||||
|
|
||||||
@_(r'\d+')
|
# Ignored pattern
|
||||||
def NUMBER(self, t):
|
ignore_newline = r'\n+'
|
||||||
t.value = int(t.value)
|
|
||||||
return t
|
|
||||||
|
|
||||||
@_(r'\n+')
|
# Extra action for newlines
|
||||||
def newline(self, t):
|
def ignore_newline(self, t):
|
||||||
self.lineno += t.value.count('\n')
|
self.lineno += t.value.count('\n')
|
||||||
|
|
||||||
def error(self, t):
|
def error(self, t):
|
||||||
@ -36,50 +36,68 @@ class CalcLexer(Lexer):
|
|||||||
self.index += 1
|
self.index += 1
|
||||||
|
|
||||||
class CalcParser(Parser):
|
class CalcParser(Parser):
|
||||||
# Get the token list from the lexer (required)
|
|
||||||
tokens = CalcLexer.tokens
|
tokens = CalcLexer.tokens
|
||||||
|
|
||||||
# Grammar rules and actions
|
precedence = (
|
||||||
@_('expr PLUS term')
|
('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):
|
def expr(self, p):
|
||||||
return p.expr + p.term
|
return p.expr0 + p.expr1
|
||||||
|
|
||||||
@_('expr MINUS term')
|
@_('expr MINUS expr')
|
||||||
def expr(self, p):
|
def expr(self, p):
|
||||||
return p.expr - p.term
|
return p.expr0 - p.expr1
|
||||||
|
|
||||||
@_('term')
|
@_('expr TIMES expr')
|
||||||
def expr(self, p):
|
def expr(self, p):
|
||||||
return p.term
|
return p.expr0 * p.expr1
|
||||||
|
|
||||||
@_('term TIMES factor')
|
@_('expr DIVIDE expr')
|
||||||
def term(self, p):
|
def expr(self, p):
|
||||||
return p.term * p.factor
|
return p.expr0 / p.expr1
|
||||||
|
|
||||||
@_('term DIVIDE factor')
|
@_('MINUS expr %prec UMINUS')
|
||||||
def term(self, p):
|
def expr(self, p):
|
||||||
return p.term / p.factor
|
return -p.expr
|
||||||
|
|
||||||
@_('factor')
|
|
||||||
def term(self, p):
|
|
||||||
return p.factor
|
|
||||||
|
|
||||||
@_('NUMBER')
|
|
||||||
def factor(self, p):
|
|
||||||
return p.NUMBER
|
|
||||||
|
|
||||||
@_('LPAREN expr RPAREN')
|
@_('LPAREN expr RPAREN')
|
||||||
def factor(self, p):
|
def expr(self, p):
|
||||||
return p.expr
|
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__':
|
if __name__ == '__main__':
|
||||||
lexer = CalcLexer()
|
lexer = CalcLexer()
|
||||||
parser = CalcParser()
|
parser = CalcParser()
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
text = input('calc > ')
|
text = input('calc > ')
|
||||||
result = parser.parse(lexer.tokenize(text))
|
|
||||||
print(result)
|
|
||||||
except EOFError:
|
except EOFError:
|
||||||
break
|
break
|
||||||
|
if text:
|
||||||
|
parser.parse(lexer.tokenize(text))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user