/
parser.py
112 lines (97 loc) · 3.05 KB
/
parser.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
# Logo (cut-down) Parser
# Leeds Hack 2012
# Monkey Tennis
import ply.yacc as yacc
# Get the token map from the lexer. This is required.
from lexer import tokens
def p_commands(p):
'''commands : command
| commands command
'''
if len(p) > 2:
p[0] = p[1]
if isinstance(p[2], list):
p[0].extend(p[2]) #e.g. repeat is already a list
else:
p[0].append(p[2])
else:
if isinstance(p[1], list):
p[0] = p[1] ##e.g. repeat is already a list
else:
p[0] = [p[1]]
#def p_commands_repeat(p):
#'''commands : repeat
#'''
#r, c = p[1][:-1], p[1][-1]
#p[0] = [r] + c + [('endrepeat')] #flatten so in-place
def p_command(p):
'''command : movement
| repeat
| to
| ID
'''
if isinstance(p[1], basestring): #ID
p[0] = ('call', p[1].lower()) #standalone, name is a call #todo we could add ns/lookup info here
#todo removed: we may not know now in case of recursion (or call after def in same string)
# but we will at runtime
#if hasattr(p.lexer, 'context'):
#ns = p.lexer.context.namespace_lookup(p[1].lower())
#if ns is not None:
#p[0] = ('call', p[1].lower()) #standalone, known-name is a call #todo we could add ns/lookup info here
#else:
##todo if we're inside a TO definition with this name then allow this recursive call too
##- it will then be resolved ok at runtime
#print "I don't know how to %s" % p[1]
#raise SyntaxError("I don't know how to %s" % p[1])
else:
p[0] = p[1]
def p_repeat(p):
'''repeat : REPEAT NUMBER LBRACKET commands RBRACKET
| REPEAT REPCOUNT LBRACKET commands RBRACKET
'''
#todo combine NUMBER/REPCOUNT!
#todo handle missing NUMBER syntax error
p[0] = [(p[1], p[2])]
p[0].extend(p[4])
p[0].append(('endrepeat', None))
def p_to(p):
'to : TO ID commands END'
p[0] = [(p[1], p[2])]
p[0].extend(p[3])
p[0].append(('endto', None))
def p_movement(p):
'''movement : movement_type NUMBER
| movement_type REPCOUNT
| HOME
| PENUP
| PENDOWN
| SETPENCOLOR ID
'''
#todo combine NUMBER/REPCOUNT!
if len(p) > 2:
p[0] = (p[1], p[2])
else:
p[0] = (p[1], None)
def p_movement_type(p):
'''movement_type : FORWARD
| BACKWARD
| RIGHT
| LEFT
'''
p[0] = p[1]
def p_error(p):
print "Syntax error in input! %s" % (p)
yacc.restart()
parser = yacc.yacc()
if __name__ == "__main__":
import ply.lex as lex
# Build the parser
parser = yacc.yacc()
while True:
try:
s = raw_input('test > ')
except EOFError:
break
if not s: continue
result = parser.parse(s, lexer=lex.lex())
print result