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