diff --git a/.gitignore b/.gitignore index 878aaba..bce464b 100644 --- a/.gitignore +++ b/.gitignore @@ -66,3 +66,5 @@ fastlane/Preview.html fastlane/screenshots fastlane/test_output /Examples/SPI +/PascalInterpreter/.DS_Store +.DS_Store diff --git a/PascalInterpreter/PascalInterpreter.xcodeproj/project.pbxproj b/PascalInterpreter/PascalInterpreter.xcodeproj/project.pbxproj index cccedee..a71b408 100644 --- a/PascalInterpreter/PascalInterpreter.xcodeproj/project.pbxproj +++ b/PascalInterpreter/PascalInterpreter.xcodeproj/project.pbxproj @@ -30,6 +30,7 @@ F300A0F11FDDB77000E5894D /* InterpreterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F300A0EC1FDDB77000E5894D /* InterpreterTests.swift */; }; F300A0F21FDDB77000E5894D /* SemanticAnalyzerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F300A0ED1FDDB77000E5894D /* SemanticAnalyzerTests.swift */; }; F34109A81FE2683900271A57 /* Visitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = F34109A71FE2683900271A57 /* Visitor.swift */; }; + F385A7DB1FF109E5004BAD6F /* Value+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F385A7DA1FF109E5004BAD6F /* Value+Extensions.swift */; }; F3D90E131FE6A0C600FA79B3 /* Interpreter+Standard.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3D90E121FE6A0C600FA79B3 /* Interpreter+Standard.swift */; }; F3EF28D01FE12EBF001F36AB /* Frame.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3EF28CF1FE12EBF001F36AB /* Frame.swift */; }; F3EF28D21FE12F4D001F36AB /* Stack.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3EF28D11FE12F4D001F36AB /* Stack.swift */; }; @@ -72,6 +73,7 @@ F300A0EC1FDDB77000E5894D /* InterpreterTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InterpreterTests.swift; sourceTree = ""; }; F300A0ED1FDDB77000E5894D /* SemanticAnalyzerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SemanticAnalyzerTests.swift; sourceTree = ""; }; F34109A71FE2683900271A57 /* Visitor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Visitor.swift; sourceTree = ""; }; + F385A7DA1FF109E5004BAD6F /* Value+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Value+Extensions.swift"; sourceTree = ""; }; F3D90E121FE6A0C600FA79B3 /* Interpreter+Standard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Interpreter+Standard.swift"; sourceTree = ""; }; F3EF28CF1FE12EBF001F36AB /* Frame.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Frame.swift; sourceTree = ""; }; F3EF28D11FE12F4D001F36AB /* Stack.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Stack.swift; sourceTree = ""; }; @@ -147,6 +149,7 @@ F3D90E121FE6A0C600FA79B3 /* Interpreter+Standard.swift */, F3EF28D11FE12F4D001F36AB /* Stack.swift */, F300A0C41FDDB6BD00E5894D /* Value.swift */, + F385A7DA1FF109E5004BAD6F /* Value+Extensions.swift */, ); path = Interpreter; sourceTree = ""; @@ -353,6 +356,7 @@ F300A0C71FDDB6BE00E5894D /* Value.swift in Sources */, F3D90E131FE6A0C600FA79B3 /* Interpreter+Standard.swift in Sources */, F3EF28D21FE12F4D001F36AB /* Stack.swift in Sources */, + F385A7DB1FF109E5004BAD6F /* Value+Extensions.swift in Sources */, F300A0E71FDDB74800E5894D /* SymbolTable.swift in Sources */, F300A0D81FDDB6F400E5894D /* AST+Extensions.swift in Sources */, F34109A81FE2683900271A57 /* Visitor.swift in Sources */, diff --git a/PascalInterpreter/PascalInterpreter/Interpreter/Frame.swift b/PascalInterpreter/PascalInterpreter/Interpreter/Frame.swift index 1b1837c..4093024 100644 --- a/PascalInterpreter/PascalInterpreter/Interpreter/Frame.swift +++ b/PascalInterpreter/PascalInterpreter/Interpreter/Frame.swift @@ -9,10 +9,7 @@ import Foundation class Frame { - var integerMemory: [String: Int] = [:] - var realMemory: [String: Double] = [:] - var booleanMemory: [String: Bool] = [:] - var stringMemory: [String: String] = [:] + var memory: [String: Value] = [:] let scope: ScopedSymbolTable let previousFrame: Frame? var returnValue: Value = .none @@ -23,21 +20,7 @@ class Frame { } func remove(variable: String) { - if let symbol = scope.lookup(variable, currentScopeOnly: true), - let variableSymbol = symbol as? VariableSymbol, - let type = variableSymbol.type as? BuiltInTypeSymbol { - - switch type { - case .integer: - integerMemory.removeValue(forKey: variable) - case .real: - realMemory.removeValue(forKey: variable) - case .boolean: - booleanMemory.removeValue(forKey: variable) - case .string: - stringMemory.removeValue(forKey: variable) - } - } + memory.removeValue(forKey: variable) } func set(variable: String, value: Value) { @@ -52,46 +35,21 @@ class Frame { let variableSymbol = symbol as? VariableSymbol, let type = variableSymbol.type as? BuiltInTypeSymbol { - switch type { - case .integer: - switch value { - case let .number(number): - switch number { - case let .integer(value): - integerMemory[variable] = value - case .real: - fatalError("Cannot assign Real value to Int variable \(variable)") - } - default: - fatalError("Cannot assign \(value) to Int variable \(variable)") - } - case .real: - switch value { - case let .number(number): - switch number { - case let .integer(value): - realMemory[variable] = Double(value) - case let .real(value): - realMemory[variable] = value - } - default: - fatalError("Cannot assign \(value) to Real variable \(variable)") - } - case .boolean: - switch value { - case let .boolean(boolean): - booleanMemory[variable] = boolean - default: - fatalError("Cannot assign \(value) value to Boolean variable \(variable)") - } - case .string: - switch value { - case let .string(string): - stringMemory[variable] = string - default: - fatalError("Cannot assign \(value) value to String variable \(variable)") - } + switch (value, type) { + case (.number(.integer), .integer ): + memory[variable] = value + case (.number(.integer(let value)), .real ): + memory[variable] = .number(.real(Double(value))) + case (.number(.real), .real ): + memory[variable] = value + case (.boolean, .boolean ): + memory[variable] = value + case (.string, .string ): + memory[variable] = value + default: + fatalError("Cannot assing \(value) to \(type)") } + return } @@ -101,20 +59,8 @@ class Frame { func get(variable: String) -> Value { // variable define in current scole (procedure declataion, etc) - if let symbol = scope.lookup(variable, currentScopeOnly: true), - let variableSymbol = symbol as? VariableSymbol, - let type = variableSymbol.type as? BuiltInTypeSymbol { - - switch type { - case .integer: - return .number(.integer(integerMemory[variable]!)) - case .real: - return .number(.real(realMemory[variable]!)) - case .boolean: - return .boolean(booleanMemory[variable]!) - case .string: - return .string(stringMemory[variable]!) - } + if scope.lookup(variable, currentScopeOnly: true) != nil { + return memory[variable]! } // previous scope, eg global diff --git a/PascalInterpreter/PascalInterpreter/Interpreter/Interpreter.swift b/PascalInterpreter/PascalInterpreter/Interpreter/Interpreter.swift index 2a27cd5..cc9c17f 100644 --- a/PascalInterpreter/PascalInterpreter/Interpreter/Interpreter.swift +++ b/PascalInterpreter/PascalInterpreter/Interpreter/Interpreter.swift @@ -236,15 +236,12 @@ public class Interpreter { eval(node: tree) } - func getState() -> ([String: Int], [String: Double], [String: Bool], [String: String]) { - return (callStack.peek()!.integerMemory, callStack.peek()!.realMemory, callStack.peek()!.booleanMemory, callStack.peek()!.stringMemory) + func getState() -> ([String: Value]) { + return (callStack.peek()!.memory) } public func printState() { print("Final interpreter memory state (\(callStack.peek()!.scope.name)):") - print("Int: \(callStack.peek()!.integerMemory)") - print("Real: \(callStack.peek()!.realMemory)") - print("Boolean: \(callStack.peek()!.booleanMemory)") - print("String: \(callStack.peek()!.stringMemory)") + print(callStack.peek()!.memory) } } diff --git a/PascalInterpreter/PascalInterpreter/Interpreter/Value+Extensions.swift b/PascalInterpreter/PascalInterpreter/Interpreter/Value+Extensions.swift new file mode 100644 index 0000000..b434e2c --- /dev/null +++ b/PascalInterpreter/PascalInterpreter/Interpreter/Value+Extensions.swift @@ -0,0 +1,44 @@ +// +// Value+Extensions.swift +// PascalInterpreter +// +// Created by Igor Kulman on 25/12/2017. +// Copyright © 2017 Igor Kulman. All rights reserved. +// + +import Foundation + +extension Value: Equatable { + static func == (lhs: Value, rhs: Value) -> Bool { + switch (lhs, rhs) { + case let (.number(left), .number(right)): + return left == right + case let (.boolean(left), .boolean(right)): + return left == right + case let (.string(left), .string(right)): + return left == right + default: + return false + } + } +} + +extension Value: CustomStringConvertible { + public var description: String { + switch self { + case .none: + return "NIL" + case .boolean(let value): + return "BOOLEAN(\(value))" + case .string(let value): + return "STRING(\(value))" + case .number(let number): + switch number { + case .integer(let value): + return "INTEGER(\(value)" + case .real(let value): + return "REAL(\(value)" + } + } + } +} diff --git a/PascalInterpreter/PascalInterpreter/Interpreter/Value.swift b/PascalInterpreter/PascalInterpreter/Interpreter/Value.swift index e062ec5..c4215fd 100644 --- a/PascalInterpreter/PascalInterpreter/Interpreter/Value.swift +++ b/PascalInterpreter/PascalInterpreter/Interpreter/Value.swift @@ -14,18 +14,3 @@ enum Value { case boolean(Bool) case string(String) } - -extension Value: Equatable { - static func == (lhs: Value, rhs: Value) -> Bool { - switch (lhs, rhs) { - case let (.number(left), .number(right)): - return left == right - case let (.boolean(left), .boolean(right)): - return left == right - case let (.string(left), .string(right)): - return left == right - default: - return false - } - } -} diff --git a/PascalInterpreter/PascalInterpreter/Lexer/Lexer.swift b/PascalInterpreter/PascalInterpreter/Lexer/Lexer.swift index 41b0fb5..8548b50 100644 --- a/PascalInterpreter/PascalInterpreter/Lexer/Lexer.swift +++ b/PascalInterpreter/PascalInterpreter/Lexer/Lexer.swift @@ -43,7 +43,7 @@ public class Lexer { "FOR": .for, "TO": .to, "DO": .do, - "WHILE": .while, + "WHILE": .while ] public init(_ text: String) { diff --git a/PascalInterpreter/PascalInterpreterTests/InterpreterTests.swift b/PascalInterpreter/PascalInterpreterTests/InterpreterTests.swift index 7e245ec..8f36646 100644 --- a/PascalInterpreter/PascalInterpreterTests/InterpreterTests.swift +++ b/PascalInterpreter/PascalInterpreterTests/InterpreterTests.swift @@ -25,11 +25,8 @@ class InterpreterTests: XCTestCase { let interpeter = Interpreter(program) interpeter.interpret() - let (integerState, realState, boolState, stringState) = interpeter.getState() - XCTAssert(integerState == ["a": 2]) - XCTAssert(realState == [:]) - XCTAssert(boolState == [:]) - XCTAssert(stringState == [:]) + let state = interpeter.getState() + XCTAssert(state == ["a": Value.number(.integer(2))]) } func testMoreComplexProgram() { @@ -51,11 +48,8 @@ class InterpreterTests: XCTestCase { let interpeter = Interpreter(program) interpeter.interpret() - let (integerState, realState, boolState, stringState) = interpeter.getState() - XCTAssert(integerState == ["b": 25, "number": 2, "a": 2, "x": 11, "c": 27]) - XCTAssert(realState == [:]) - XCTAssert(boolState == [:]) - XCTAssert(stringState == [:]) + let state = interpeter.getState() + XCTAssert(state == ["b": Value.number(.integer(25)), "number": Value.number(.integer(2)), "a": Value.number(.integer(2)), "x": Value.number(.integer(11)), "c": Value.number(.integer(27))]) } func testProgramWithDeclarations() { @@ -75,11 +69,8 @@ class InterpreterTests: XCTestCase { let interpeter = Interpreter(program) interpeter.interpret() - let (integerState, realState, boolState, stringState) = interpeter.getState() - XCTAssert(integerState == ["b": 25, "a": 2]) - XCTAssert(realState == ["y": 5.9971428571428573]) - XCTAssert(boolState == [:]) - XCTAssert(stringState == [:]) + let state = interpeter.getState() + XCTAssert(state == ["b": Value.number(.integer(25)), "a": Value.number(.integer(2)), "y": Value.number(.real( 5.9971428571428573))]) } func testProgramWithProcedureCallAndNoParameters() { @@ -103,11 +94,8 @@ class InterpreterTests: XCTestCase { let interpeter = Interpreter(program) interpeter.interpret() - let (integerState, realState, boolState, stringState) = interpeter.getState() - XCTAssert(integerState == [:]) - XCTAssert(realState == ["x": 7, "y": 5]) - XCTAssert(boolState == [:]) - XCTAssert(stringState == [:]) + let state = interpeter.getState() + XCTAssert(state == ["x": Value.number(.real(7)), "y": Value.number(.real( 5))]) } func testProgramWithProcedureCallAndParameters() { @@ -129,11 +117,8 @@ class InterpreterTests: XCTestCase { let interpeter = Interpreter(program) interpeter.interpret() - let (integerState, realState, boolState, stringState) = interpeter.getState() - XCTAssert(integerState == [:]) - XCTAssert(realState == ["x": 5, "y": 3]) - XCTAssert(boolState == [:]) - XCTAssert(stringState == [:]) + let state = interpeter.getState() + XCTAssert(state == ["x": Value.number(.real(5)), "y": Value.number(.real(3))]) } func testProgramWithRecursiveFunction() { @@ -157,11 +142,8 @@ class InterpreterTests: XCTestCase { let interpeter = Interpreter(program) interpeter.interpret() - let (integerState, realState, boolState, stringState) = interpeter.getState() - XCTAssert(realState == [:]) - XCTAssert(integerState == ["result": 720]) - XCTAssert(boolState == [:]) - XCTAssert(stringState == [:]) + let state = interpeter.getState() + XCTAssert(state == ["result": Value.number(.integer(720))]) } func testProgramWithRecursiveAndBuiltInFunctions() { @@ -186,11 +168,8 @@ class InterpreterTests: XCTestCase { let interpeter = Interpreter(program) interpeter.interpret() - let (integerState, realState, boolState, stringState) = interpeter.getState() - XCTAssert(realState == [:]) - XCTAssert(integerState == ["result": 720]) - XCTAssert(boolState == [:]) - XCTAssert(stringState == [:]) + let state = interpeter.getState() + XCTAssert(state == ["result": Value.number(.integer(720))]) } func testProgramWithRecursiveFunctionsAndParameterTheSameName() { @@ -215,11 +194,8 @@ class InterpreterTests: XCTestCase { let interpeter = Interpreter(program) interpeter.interpret() - let (integerState, realState, boolState, stringState) = interpeter.getState() - XCTAssert(realState == [:]) - XCTAssert(integerState == ["result": 720, "number": 6]) - XCTAssert(boolState == [:]) - XCTAssert(stringState == [:]) + let state = interpeter.getState() + XCTAssert(state == ["result": Value.number(.integer(720)), "number": Value.number(.integer(6))]) } func testProgramWithRepeatUntil() { @@ -239,11 +215,8 @@ class InterpreterTests: XCTestCase { let interpeter = Interpreter(program) interpeter.interpret() - let (integerState, realState, boolState, stringState) = interpeter.getState() - XCTAssert(realState == [:]) - XCTAssert(integerState == ["x": 6]) - XCTAssert(boolState == [:]) - XCTAssert(stringState == [:]) + let state = interpeter.getState() + XCTAssert(state == ["x": Value.number(.integer(6))]) } func testProgramWithForLoop() { @@ -262,10 +235,7 @@ class InterpreterTests: XCTestCase { let interpeter = Interpreter(program) interpeter.interpret() - let (integerState, realState, boolState, stringState) = interpeter.getState() - XCTAssert(realState == [:]) - XCTAssert(integerState == ["x": 6]) - XCTAssert(boolState == [:]) - XCTAssert(stringState == [:]) + let state = interpeter.getState() + XCTAssert(state == ["x": Value.number(.integer(6))]) } } diff --git a/SPI/SPI.xcodeproj/project.pbxproj b/SPI/SPI.xcodeproj/project.pbxproj index 9d626fe..f1925ef 100644 --- a/SPI/SPI.xcodeproj/project.pbxproj +++ b/SPI/SPI.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + F385A7DD1FF10A0E004BAD6F /* Value+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F385A7DC1FF10A0E004BAD6F /* Value+Extensions.swift */; }; F385B5C41FE6CBB9001B57F4 /* Interpreter+Standard.swift in Sources */ = {isa = PBXBuildFile; fileRef = F385B5C31FE6CBB9001B57F4 /* Interpreter+Standard.swift */; }; F3D90DD91FE68E4C00FA79B3 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3D90DD81FE68E4C00FA79B3 /* main.swift */; }; F3D90E001FE68EE300FA79B3 /* AST+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3D90DEA1FE68EE300FA79B3 /* AST+Extensions.swift */; }; @@ -42,6 +43,7 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + F385A7DC1FF10A0E004BAD6F /* Value+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Value+Extensions.swift"; sourceTree = ""; }; F385B5C31FE6CBB9001B57F4 /* Interpreter+Standard.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Interpreter+Standard.swift"; sourceTree = ""; }; F3D90DD51FE68E4C00FA79B3 /* SPI */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = SPI; sourceTree = BUILT_PRODUCTS_DIR; }; F3D90DD81FE68E4C00FA79B3 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; }; @@ -147,6 +149,7 @@ F385B5C31FE6CBB9001B57F4 /* Interpreter+Standard.swift */, F3D90DF81FE68EE300FA79B3 /* Stack.swift */, F3D90DF91FE68EE300FA79B3 /* Value.swift */, + F385A7DC1FF10A0E004BAD6F /* Value+Extensions.swift */, ); name = Interpreter; path = ../PascalInterpreter/PascalInterpreter/Interpreter; @@ -242,6 +245,7 @@ F3D90E091FE68EE300FA79B3 /* Arithmetics.swift in Sources */, F3D90E101FE68EE300FA79B3 /* Token.swift in Sources */, F3D90E0B1FE68EE300FA79B3 /* Interpreter.swift in Sources */, + F385A7DD1FF10A0E004BAD6F /* Value+Extensions.swift in Sources */, F3D90E111FE68EE300FA79B3 /* Naming+Extensions.swift in Sources */, F3D90E001FE68EE300FA79B3 /* AST+Extensions.swift in Sources */, F3D90E081FE68EE300FA79B3 /* Utils.swift in Sources */,