-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathparser.nim
142 lines (129 loc) · 4.16 KB
/
parser.nim
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
import sequtils, strutils
import strformat, re
import node
type
TokenTreeKind = enum
Branch
Leaf
TokenTree = object
symbol: string
case kind*: TokenTreeKind
of Branch: children: seq[TokenTree]
else: discard
func `$`*(tree: TokenTree): string =
if tree.kind == Leaf:
tree.symbol
else:
let children = tree.children.map(`$`).join(" ")
fmt"({tree.symbol} {children})"
func halfBlacketAt(expression: string): int =
var leftBracketCount = 1
for readAt, character in expression.pairs:
if character == '(':
leftBracketCount.inc
elif character == ')' and (leftBracketCount > 0):
leftBracketCount.dec
if leftBracketCount == 0: return readAt + 1
else: continue
func readWord(expression: string): string =
for character in expression:
if character in ['(', ')', ' ']:
break
else:
result &= character
func tokenize*(expression: string): TokenTree =
if expression.len > 2 and expression[0..2] == "'()":
return TokenTree(kind: Branch, symbol: ".", children: @[])
if expression.len > 2 and expression[0..1] == "'(":
result = TokenTree(kind: Branch)
result.symbol = "."
let misic = tokenize(expression[1..^1])
result.children = concat(@[TokenTree(kind: Leaf, symbol: misic.symbol)], misic.children)
return
if expression[0] != '(':
return TokenTree(kind: Leaf, symbol: readWord(expression))
if expression[^1] != ')':
raise newException(CatchableError, "Cannot find left bracket )")
var skipTo: int
let peeled = expression[1..^2]
result = TokenTree(kind: Branch)
for readAt, character in peeled.pairs:
if readAt < skipTo:
continue
elif character == ' ' or character == ')':
continue
elif character == '(':
skipTo = halfBlacketAt(peeled[(readAt + 1)..^1]) + readAt
result.children.add(tokenize(peeled[readAt..skipTo]))
elif character == '\'' and peeled[readAt + 1] == '(':
skipTo = halfBlacketAt(peeled[(readAt + 2)..^1]) + readAt + 1
let misic = tokenize(peeled[(readAt + 1)..skipTo])
if misic.symbol == "":
result.children.add(TokenTree(kind: Branch, symbol: ".", children: @[]))
else:
result.children.add(TokenTree(
kind: Branch,
symbol: ".",
children: concat(@[TokenTree(kind: Leaf, symbol: misic.symbol)], misic.children)
))
elif readAt == 0:
let word = readWord(peeled[readAt..^1])
skipTo = word.len
result.symbol = word
else:
let word = readWord(peeled[readAt..^1])
skipTo = word.len + readAt
result.children.add(
TokenTree(
kind: Leaf,
symbol: word
)
)
func parse*(tree: TokenTree): LispNode =
if tree.kind == Leaf:
if tree.symbol.match(re"^-??\d+$"):
LispNode(
kind: NumberLiteral,
topValue: tree.symbol.parseInt,
bottomValue: 1
)
elif tree.symbol.match(re"^-??\d+\.\d+$"):
LispNode(kind: FloatLiteral, floatValue: tree.symbol.parseFloat)
elif tree.symbol in ["true", "false"]:
LispNode(kind: BooleanLiteral, booleanValue: tree.symbol.parseBool)
elif tree.symbol.match(re"^'\w+$"):
LispNode(kind: SymbolLiteral, symbolValue: tree.symbol[1..^1])
else:
LispNode(kind: IdentifierLiteral, identifierValue: tree.symbol)
elif tree.symbol == "." and tree.children.len == 0:
LispNode(kind: NilLiteral)
elif tree.symbol == "." and tree.children.len == 1:
LispNode(
kind: PairLiteral,
car: tree.children[0].parse,
cdr: LispNode(kind: NilLiteral)
)
elif tree.symbol == ".":
LispNode(
kind: PairLiteral,
car: tree.children[0].parse,
cdr: TokenTree(
kind: Branch,
symbol: ".",
children: tree.children[1..^1]
).parse
)
elif tree.symbol == "":
LispNode(
kind: Expression,
operationLiteral: tree.children[0].parse,
arguments: tree.children[1..^1].map(parse)
)
else:
LispNode(
kind: Expression,
operationLiteral: LispNode(kind: IdentifierLiteral, identifierValue: tree.symbol),
arguments: tree.children.map(parse)
)
proc parse*(expression: string): LispNode =
expression.tokenize.parse