/
jcli_parser.py
106 lines (91 loc) · 2.67 KB
/
jcli_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
import collections
import jcli_tokenizer
from jcli_datatypes import linked_list, sym, quote
syntax = collections.namedtuple(
'syntax', ['value', 'line_no', 'char_no'])
def parse(string):
return parse_tokens(jcli_tokenizer.tokenize(string))
def parse_tokens(tokens):
match = match_exprs(tokens)
if match is not None:
return match[0]
def match_exprs(tokens):
index = 0
out = []
while index < len(tokens):
match = match_expr(tokens[index:])
if match is None:
raise ParserError(tokens[index])
out.append(match[0])
index += match[1]
return out, index
def match_expr(tokens):
out = (match_compound(tokens) or
match_quote(tokens) or
match_literal(tokens))
return out
def match_compound(tokens):
if len(tokens) <= 1:
return None
if tokens[0].name != 'LPAREN':
return None
output = []
index = 1
while index < len(tokens) - 1:
match = match_expr(tokens[index:])
if match is None:
break
output.append(match[0])
index += match[1]
if tokens[index].name != 'RPAREN':
return None
output = linked_list(output)
return syntax(output,
tokens[0].line_no,
tokens[0].char_no), index + 1
def match_quote(tokens):
if len(tokens) < 2:
return None
if tokens[0].name != 'QUOTE':
return None
match = match_expr(tokens[1:])
if match is not None:
t = tokens[0]
quote = syntax(sym('quote'), t.line_no, t.char_no)
expr = linked_list([quote, match[0]])
return syntax(expr, t.line_no, t.char_no), match[1] + 1
def match_literal(tokens):
if len(tokens) == 0:
return None
token = tokens[0]
name = token.name
string = token.string
value = None
if name == 'INTEGER':
value = int(string)
if name == 'FLOAT':
value = float(string)
if name == 'BOOL':
value = bool(string)
if name == 'STRING':
value = str(string)
if name == 'ID':
value = sym(string)
if value is not None:
return syntax(value, token.line_no, token.char_no), 1
class ParserError(Exception):
def __init__(self, token):
self.token = token
def __str__(self):
t = self.token
return ("Syntax error at line %s, char %s: %s"%(
t.line_no, t.char_no, t.string))
def syntax_to_list(ast):
v = ast.value
if isinstance(v, linked_list):
return linked_list(list(map(syntax_to_list, v)))
return v
if __name__ == '__main__':
import jcli_tokenizer
tokens = jcli_tokenizer.tokenize("'1")
print(parse_tokens(tokens))