You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

167 lines
3.6 KiB

//
// 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
}
}