-
Notifications
You must be signed in to change notification settings - Fork 0
/
SyntacticAnalysis.py
158 lines (136 loc) · 4.31 KB
/
SyntacticAnalysis.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
# Copyright Joan Montas
# All rights reserved.
# License under GNU General Public License v3.0
from ply import yacc
from LexericalAnalysis import *
from Differentiator import *
class SyntacticAnalyzer:
# <expression> := group
# | <unary>
# | <binary>
# | <terminal>
# <group> := LPAREN <expression> RPAREN
#
# <unary> := MINUS <expression>
# <binary> := <expression> PLUS <expression>
# | <expression> MINUS <expression>
# | <expression> TIMES <expression>
# | <expression> DIVIDE <expression>
# | <expression> POWER <expression>
#
# <terminal> := <variable>
# | <number>
# | <function>
# | <constant>
#
# <variable> := [a-zA-Z]+
# <number> := d+
# <constant> := sin | cos | tan | ...
# <function> := <constant> group
precedence = (
("left", "PLUS", "MINUS"),
("left", "TIMES", "DIVIDE"),
("right", "POWER"),
("right", "UMINUS"),
("left", "LPAREN", "RPAREN"),
)
def __init__(self, lexer):
self.lex = lexer
self.tokens = lexer.tokens
self.parser = yacc.yacc(module=self)
self.errors = []
def p_expression(self, p):
"""expression : group
| unary
| binary
| terminal
"""
p[0] = p[1]
def p_terminal(self, p):
"""terminal : variable
| number
| constant
| function"""
p[0] = p[1]
def p_variable(self, p):
"""variable : VARIABLE"""
p[0] = variableAST(p[1])
def p_number(self, p):
"""number : NUMBER"""
p[0] = numberAST(int(p[1]))
def p_constant(self, p):
"""constant : CONSTANT"""
# NOTE() Add more constant as needed
if p[1] == "e":
p[0] = eulerAST()
else:
self.er = f"ERROR: Syntax error at token {p}, unkown contant of type {p[2]}"
yacc.errok
def p_binary(self, p):
"""binary : expression PLUS expression
| expression MINUS expression
| expression TIMES expression
| expression DIVIDE expression
| expression POWER expression"""
# NOTE() Add more binary operation as needed
if p[2] == "+":
p[0] = addAST(p[1], p[3])
elif p[2] == "-":
p[0] = subAST(p[1], p[3])
elif p[2] == "*":
p[0] = multAST(p[1], p[3])
elif p[2] == "/":
p[0] = divAST(p[1], p[3])
elif p[2] == "^":
p[0] = powAST(p[1], p[3])
else:
self.er = f"ERROR: Syntax error at token {p}, unkown binary of type {p[2]}"
yacc.errok
def p_unary(self, p):
"""unary : MINUS expression %prec UMINUS"""
# NOTE() Add more unary operation as needed
p[0] = negativeAST(p[2])
def p_group(self, p):
"""group : LPAREN expression RPAREN"""
p[0] = p[2]
def p_function(self, p):
"""function : FUNCTION group"""
# NOTE() Add more function as needed
if p[1] == "sin":
p[0] = sinAST(p[2])
elif p[1] == "cos":
p[0] = cosAST(p[2])
elif p[1] == "tan":
p[0] = tanAST(p[2])
elif p[1] == "arcsine":
p[0] = arcsineAST(p[2])
elif p[1] == "arccosine":
p[0] = arccosineAST(p[2])
elif p[1] == "arctan":
p[0] = arctanAST(p[2])
elif p[1] == "sec":
p[0] = secAST(p[2])
elif p[1] == "csc":
p[0] = cscAST(p[2])
elif p[1] == "cot":
p[0] = cotAST(p[2])
elif p[1] == "csc2":
p[0] = csc2AST(p[2])
elif p[1] == "sec2":
p[0] = sec2AST(p[2])
else:
self.er = (
f"ERROR: Syntax error at token {p}, unkown function of type {p[2]}"
)
yacc.errok
def p_error(self, p):
# TODO(Joan) Handle error - Joan
print("Syntax error at token", p)
self.er = f"Syntax error at token {p}. Unable to match to any rule"
yacc.errok
if __name__ == "__main__":
l = LexicalAnalyzer()
s = SyntacticAnalyzer(l)
equation = "-4 * (-10)"
ast = s.parser.parse(equation, lexer=l.lexer)
print(ast)