Skip to content

Instantly share code, notes, and snippets.

@naithar
Last active February 23, 2017 11:31
Show Gist options
  • Save naithar/b8b3e332b42b63135f8699fd5fd254da to your computer and use it in GitHub Desktop.
Save naithar/b8b3e332b42b63135f8699fd5fd254da to your computer and use it in GitHub Desktop.
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