Commit 7c3181ab authored by Ruben Daniels's avatar Ruben Daniels
Browse files

First attempt at something of an interpreter

parent 5e550973
Showing with 193 additions and 6 deletions
+193 -6
main.swift 0 → 100644
//
// main.swift
// Kaleidoscope
//
// Created by Matthew Cheok on 15/11/15.
// Copyright © 2015 Matthew Cheok. All rights reserved.
//
import Foundation
let source = multiline(
"def foo(x, y)",
" x + y * 2 + (4 + 5) / 3",
"",
"foo(3, 4)"
)
let lexer = Lexer(input: source)
let tokens = lexer.tokenize()
print(tokens)
let parser = Parser(tokens: tokens)
do {
print(try parser.parse())
}
catch {
print(error)
}
\ No newline at end of file
......@@ -11,6 +11,7 @@
86CDB71E246D100D007EAE67 /* Parser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 86CDB71A246D100D007EAE67 /* Parser.swift */; };
86CDB71F246D100D007EAE67 /* Lexer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 86CDB71B246D100D007EAE67 /* Lexer.swift */; };
86CDB720246D100D007EAE67 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 86CDB71C246D100D007EAE67 /* main.swift */; };
86CDB722246D1402007EAE67 /* Compiler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 86CDB721246D1402007EAE67 /* Compiler.swift */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
......@@ -31,6 +32,7 @@
86CDB71A246D100D007EAE67 /* Parser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Parser.swift; sourceTree = "<group>"; };
86CDB71B246D100D007EAE67 /* Lexer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Lexer.swift; sourceTree = "<group>"; };
86CDB71C246D100D007EAE67 /* main.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = "<group>"; };
86CDB721246D1402007EAE67 /* Compiler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Compiler.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
......@@ -75,6 +77,7 @@
86CDB71C246D100D007EAE67 /* main.swift */,
86CDB719246D100D007EAE67 /* Nodes.swift */,
86CDB71A246D100D007EAE67 /* Parser.swift */,
86CDB721246D1402007EAE67 /* Compiler.swift */,
);
path = "expression-parser";
sourceTree = "<group>";
......@@ -140,6 +143,7 @@
files = (
86CDB71E246D100D007EAE67 /* Parser.swift in Sources */,
86CDB71F246D100D007EAE67 /* Lexer.swift in Sources */,
86CDB722246D1402007EAE67 /* Compiler.swift in Sources */,
86CDB720246D100D007EAE67 /* main.swift in Sources */,
86CDB71D246D100D007EAE67 /* Nodes.swift in Sources */,
);
......
//
// Compiler.swift
// memri-parser
//
// Created by Ruben Daniels on 5/14/20.
// Copyright © 2020 Memri. All rights reserved.
//
import Foundation
class Interpreter {
let ast: ExprNode
let lookup: (LookupNode) -> Any
let execFunc: (LookupNode, [Any?]) -> Any
var stack: [Any] = []
enum Signals {
case execElse
}
init(_ ast: ExprNode, _ lookup: @escaping (LookupNode) -> Any,
_ execFunc: @escaping (LookupNode, [Any?]) -> Any) {
self.ast = ast
self.lookup = lookup
self.execFunc = execFunc
}
func push(_ x:Any) { print("POP: \(x) \(stack.count)"); stack.append(x) }
func pop() -> Any? { stack.popLast() }
func execute() -> Any? {
execSingle(ast)
return pop()
}
func evaluateBoolean(_ x:Any?) -> Bool {
if let x = x as? Bool { return x }
else if let x = x as? Int { return x != 0 }
else if let x = x as? Double { return x != 0 }
else if let x = x as? String { return x != "" }
else if x == nil { return false }
else { return true }
}
func evaluateNumber(_ x:Any?) -> Double {
if let x = x as? Bool { return x ? 1 : 0 }
else if let x = x as? Int { return Double(x) }
else if let x = x as? Double { return x }
else if let x = x as? String { return (x as NSString).doubleValue }
else if x == nil { return .nan }
else { return .nan }
}
func compare(_ a:Any?, _ b:Any?) -> Bool {
if let a = a as? Bool { return a == evaluateBoolean(b) }
else if let a = a as? Int { return Double(a) == evaluateNumber(b) }
else if let a = a as? Double { return a == evaluateNumber(b) }
else if let a = a as? String { return a == "\(b ?? "")" }
else if a == nil { return b == nil }
else { return false }
}
func execSingle(_ expr:ExprNode) {
if let expr = expr as? BinaryOpNode {
execSingle(expr.lhs)
let result = pop()
switch expr.op {
case .ConditionStart:
if evaluateBoolean(result) {
execSingle(expr.rhs)
}
else {
push(Signals.execElse)
}
case .ConditionElse:
if result as? Signals == Signals.execElse {
execSingle(expr.rhs)
}
else {
// Done
}
case .ConditionEquals:
execSingle(expr.rhs)
let otherResult = pop()
push(compare(result, otherResult))
case .ConditionAND:
let boolLHS = evaluateBoolean(result)
if boolLHS { push(true) }
else {
execSingle(expr.rhs)
let otherResult = pop()
push(boolLHS && evaluateBoolean(otherResult))
}
case .ConditionOR:
let boolLHS = evaluateBoolean(result)
if boolLHS { push(true) }
else {
execSingle(expr.rhs)
let otherResult = pop()
push(boolLHS || evaluateBoolean(otherResult))
}
case .Division:
execSingle(expr.rhs)
let otherResult = pop()
push(evaluateNumber(result) / evaluateNumber(otherResult))
case .Minus:
execSingle(expr.rhs)
let otherResult = pop()
push(evaluateNumber(result) - evaluateNumber(otherResult))
case .Multiplication:
execSingle(expr.rhs)
let otherResult = pop()
push(evaluateNumber(result) * evaluateNumber(otherResult))
case .Plus:
execSingle(expr.rhs)
let otherResult = pop()
push(evaluateNumber(result) + evaluateNumber(otherResult))
}
}
else if let expr = expr as? NegationNode {
execSingle(expr.exp)
let result = pop()
push(!evaluateBoolean(result))
}
else if let expr = expr as? NumberNode { push(expr.value) }
else if let expr = expr as? StringNode { push(expr.value) }
else if let expr = expr as? BoolNode { push(expr.value) }
else if let expr = expr as? NumberExpressionNode {
execSingle(expr.exp)
let result = pop()
push(evaluateNumber(result))
}
// else if let expr = expr as? VariableNode { push(expr.value) }
else if let expr = expr as? LookupNode {
push(lookup(expr))
}
else if let expr = expr as? CallNode {
let args:[Any?] = expr.arguments.map { execSingle($0); return pop() }
push(execFunc(expr.lookup, args))
}
}
}
......@@ -10,7 +10,7 @@ public enum Token {
case Operator(Operator, Int)
case Bool(Bool, Int)
case Identifier(String, Int)
case Number(Float, Int)
case Number(Double, Int)
case Negation(Int)
case Comma(Int)
case ParensOpen(Int)
......@@ -86,7 +86,7 @@ public class Lexer {
func addToken(_ token:Token? = nil){
if isMode == .number {
tokens.append(.Number((keyword.joined() as NSString).floatValue, i))
tokens.append(.Number((keyword.joined() as NSString).doubleValue, i))
keyword = []
isMode = .idle
}
......
//
// Nodes.swift
// Kaleidoscope
//
// Created by Matthew Cheok on 15/11/15.
// Based on work by Matthew Cheok on 15/11/15.
// Copyright © 2015 Matthew Cheok. All rights reserved.
// Copyright © 2020 memri. All rights reserved.
//
import Foundation
......@@ -12,7 +12,7 @@ public protocol ExprNode: CustomStringConvertible {
}
public struct NumberNode: ExprNode {
public let value: Float
public let value: Double
public var description: String {
return "NumberNode(\(value))"
}
......
......@@ -234,6 +234,7 @@ class Parser {
var arguments = [ExprNode]()
if case Token.ParensClose = peekCurrentToken() {
// Do nothing
}
else {
while true {
......
......@@ -6,16 +6,27 @@
import Foundation
//"""
//!(test + -5.63537) or 4/3 ? variable.func() : me.address[primary = true].country ? ((4+5 * 10) + test[10]) : 'asdads\\'asdad' + '')
//"""
var lexer = Lexer(input: """
!(test + -5.63537) or 4/3 ? variable.func() : me.address[primary = true].country ? ((4+5 * 10) + test[10]) : 'asdads\\'asdad' + '')
""")
let dt = Date()
let tokens = lexer.tokenize()
print(tokens)
//print(tokens)
print (Date().timeIntervalSince(dt))
let parser = Parser(tokens)
let tree = try parser.parse()
//let analyzer = Analyzer(tree)
//try analyzer.analyze()
let interpreter = Interpreter(tree,
{ lookup in return 10 },
{ lookup, args in return 20 })
let result = interpreter.execute()
print(result ?? "ERROR")
print (Date().timeIntervalSince(dt))
print(tree)
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment