forked from HeroTransitions/Hero
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Parser.swift
executable file
·167 lines (135 loc) · 3.62 KB
/
Parser.swift
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
159
160
161
162
163
164
165
166
167
//
// Parser.swift
// Kaleidoscope
//
// Created by Matthew Cheok on 15/11/15.
// Copyright © 2015 Matthew Cheok. All rights reserved.
//
import Foundation
public enum ParseError: Error {
case unexpectToken
case undefinedOperator(String)
case expectCharacter(Character)
case expectExpression
case expectArgumentList
case expectFunctionName
}
public class Parser {
let tokens: [Token]
var index = 0
public init(tokens: [Token]) {
self.tokens = tokens
}
func peekCurrentToken() -> Token {
if index >= tokens.count {
return .other("", 0..<0)
}
return tokens[index]
}
@discardableResult func popCurrentToken() -> Token {
defer { index += 1 }
return tokens[index]
}
func parseNumber() throws -> ExprNode {
guard case let .number(value, _) = popCurrentToken() else {
throw ParseError.unexpectToken
}
return NumberNode(value: value)
}
func parseExpression() throws -> ExprNode {
let node = try parsePrimary()
return try parseBinaryOp(node: node)
}
func parseParens() throws -> ExprNode {
guard case .parensOpen = popCurrentToken() else {
throw ParseError.expectCharacter("(")
}
let exp = try parseExpression()
guard case .parensClose = popCurrentToken() else {
throw ParseError.expectCharacter(")")
}
return exp
}
func parseIdentifier() throws -> ExprNode {
guard case let .identifier(name, _) = popCurrentToken() else {
throw ParseError.unexpectToken
}
guard case .parensOpen = peekCurrentToken() else {
return VariableNode(name: name)
}
popCurrentToken()
var arguments = [ExprNode]()
if case .parensClose = peekCurrentToken() {
} else {
while true {
let argument = try parseExpression()
arguments.append(argument)
if case .parensClose = peekCurrentToken() {
break
}
guard case .comma = popCurrentToken() else {
throw ParseError.expectArgumentList
}
}
}
popCurrentToken()
return CallNode(name: name, arguments: arguments)
}
func parsePrimary() throws -> ExprNode {
switch peekCurrentToken() {
case .identifier:
return try parseIdentifier()
case .number:
return try parseNumber()
case .parensOpen:
return try parseParens()
default:
throw ParseError.expectExpression
}
}
let operatorPrecedence: [String: Int] = [
"+": 20,
"-": 20,
"*": 40,
"/": 40
]
func getCurrentTokenPrecedence() throws -> Int {
guard index < tokens.count else {
return -1
}
guard case let .other(op, _) = peekCurrentToken() else {
return -1
}
guard let precedence = operatorPrecedence[op] else {
throw ParseError.undefinedOperator(op)
}
return precedence
}
func parseBinaryOp(node: ExprNode, exprPrecedence: Int = 0) throws -> ExprNode {
var lhs = node
while true {
let tokenPrecedence = try getCurrentTokenPrecedence()
if tokenPrecedence < exprPrecedence {
return lhs
}
guard case let .other(op, _) = popCurrentToken() else {
throw ParseError.unexpectToken
}
var rhs = try parsePrimary()
let nextPrecedence = try getCurrentTokenPrecedence()
if tokenPrecedence < nextPrecedence {
rhs = try parseBinaryOp(node: rhs, exprPrecedence: tokenPrecedence+1)
}
lhs = BinaryOpNode(name: op, lhs: lhs, rhs: rhs)
}
}
public func parse() throws -> [ExprNode] {
index = 0
var nodes = [ExprNode]()
while index < tokens.count {
let expr = try parsePrimary()
nodes.append(expr)
}
return nodes
}
}