Last active
February 23, 2017 11:31
-
-
Save naithar/b8b3e332b42b63135f8699fd5fd254da to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
extension UnicodeScalar { | |
var isSpace: Bool { | |
return isspace(Int32(self.value)) != 0 | |
} | |
var isAlphanumeric: Bool { | |
return isalnum(Int32(self.value)) != 0 | |
} | |
} | |
class Lexer { | |
enum Token { | |
enum Bracket { | |
enum `Type` { | |
case open | |
case closed | |
} | |
case parentheses(Type) // () | |
case braces(Type) // {} | |
case brackets(Type) // [] | |
init?(from value: UnicodeScalar) { | |
switch value { | |
case "(": | |
self = .parentheses(.open) | |
case ")": | |
self = .parentheses(.closed) | |
case "{": | |
self = .braces(.open) | |
case "}": | |
self = .braces(.closed) | |
case "[": | |
self = .brackets(.open) | |
case "]": | |
self = .brackets(.closed) | |
default: | |
return nil | |
} | |
} | |
} | |
enum Operator: UnicodeScalar { | |
case plus = "+" | |
case minus = "-" | |
case power = "*" | |
case divide = "/" | |
case modulo = "%" | |
case equal = "=" | |
} | |
enum Keyword: String { | |
case `func` = "func" | |
case `if` = "if" | |
case `else` = "else" | |
} | |
case number(Double) | |
case `operator`(Operator) | |
case bracket(Bracket) | |
case keyword(Keyword) | |
case semicolon // ; | |
case apostrophe // ' | |
case identifier(String) | |
init?(character value: UnicodeScalar) { | |
if let bracket = Bracket(from: value) { | |
self = .bracket(bracket) | |
} else if let `operator` = Operator(rawValue: value) { | |
self = .operator(`operator`) | |
} else if value == ";" { | |
self = .semicolon | |
} else if value == "'" { | |
self = .apostrophe | |
} else { | |
return nil | |
} | |
} | |
init?(value: String) { | |
guard value.unicodeScalars.count > 0 else { return nil } | |
if let character = Token.init(character: value.unicodeScalars.first!) { | |
self = character | |
} else if let keyword = Keyword(rawValue: value) { | |
self = .keyword(keyword) | |
} else if let number = Double(value) { | |
self = .number(number) | |
} else { | |
self = .identifier(value) | |
} | |
} | |
} | |
var input: [UnicodeScalar] | |
private var index = 0 | |
init(input: String) { | |
self.input = Array(input.unicodeScalars) | |
} | |
private var currentCharacter: UnicodeScalar? { | |
return self.index < self.input.count ? self.input[self.index] : nil | |
} | |
private func read() -> String { | |
var result = "" | |
while let character = self.currentCharacter, character.isAlphanumeric { | |
result.unicodeScalars.append(character) | |
self.advance() | |
} | |
return result | |
} | |
private func advance(by count: Int = 1) { | |
self.index += count | |
} | |
private func next() -> Token? { | |
while let character = self.currentCharacter, character.isSpace { | |
self.advance() | |
} | |
guard let character = self.currentCharacter else { | |
return nil | |
} | |
if let token = Token(character: character) { | |
self.advance() | |
return token | |
} | |
if character.isAlphanumeric { | |
var identifier = self.read() | |
return Token(value: identifier) | |
} | |
return nil | |
} | |
func tokenize() -> [Token] { | |
var result = [Token]() | |
while let token = self.next() { | |
result.append(token) | |
} | |
return result | |
} | |
} | |
class Parser { | |
indirect enum Expression { | |
case number(Double) | |
case binary(Expression, Lexer.Token.Operator, Expression) | |
case variable(String) | |
case condition(Expression, Expression, Expression) // if (eX1) { ex2 } ex3 (else lala) | |
} | |
var tokens: [Lexer.Token] | |
private var index = 0 | |
init(input: [Lexer.Token]) { | |
self.tokens = input | |
} | |
private func advance(by count: Int = 1) { | |
self.index += count | |
} | |
private func advance(to token: Lexer.Token) throws { | |
} | |
private func advance(with token: Lexer.Token) throws { | |
} | |
private var currentToken: Lexer.Token? { | |
return self.index < self.tokens.count ? self.tokens[self.index] : nil | |
} | |
func parse() -> [Expression] { | |
var result = [Expression]() | |
while self.currentToken != nil { | |
guard let expression = self.expression() else { | |
continue //TODO: throw error | |
} | |
if case .operator(let `operator`)? = self.currentToken { | |
self.advance() | |
guard let right = self.expression() else { | |
return [] // TODO: throw error | |
} | |
result.append(.binary(expression, `operator`, right)) | |
} else { | |
result.append(expression) | |
} | |
} | |
return result | |
} | |
private func expression() -> Expression? { | |
guard let token = self.currentToken else { | |
return nil | |
} | |
switch token { | |
case .number(let value): | |
self.advance() | |
return .number(value) | |
case .semicolon: | |
self.advance() | |
default: | |
break | |
} | |
return nil | |
} | |
} | |
let tokens = Lexer(input: "5 * 1;").tokenize() | |
print(tokens) | |
let expressions = Parser(input: tokens).parse() | |
print(expressions) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment