Skip to content

Commit ef9c303

Browse files
committed
🚧 basic recursion
1 parent f5c71d2 commit ef9c303

File tree

17 files changed

+343
-42
lines changed

17 files changed

+343
-42
lines changed

PascalInterpreter/PascalInterpreter.xcodeproj/project.pbxproj

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
/* Begin PBXBuildFile section */
1010
F300A0A51FDDB63F00E5894D /* PascalInterpreter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F300A09B1FDDB63F00E5894D /* PascalInterpreter.framework */; };
1111
F300A0AC1FDDB63F00E5894D /* PascalInterpreter.h in Headers */ = {isa = PBXBuildFile; fileRef = F300A09E1FDDB63F00E5894D /* PascalInterpreter.h */; settings = {ATTRIBUTES = (Public, ); }; };
12-
F300A0C71FDDB6BE00E5894D /* Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = F300A0C41FDDB6BD00E5894D /* Result.swift */; };
12+
F300A0C71FDDB6BE00E5894D /* Value.swift in Sources */ = {isa = PBXBuildFile; fileRef = F300A0C41FDDB6BD00E5894D /* Value.swift */; };
1313
F300A0C81FDDB6BE00E5894D /* Arithmetics.swift in Sources */ = {isa = PBXBuildFile; fileRef = F300A0C51FDDB6BE00E5894D /* Arithmetics.swift */; };
1414
F300A0C91FDDB6BE00E5894D /* Interpreter.swift in Sources */ = {isa = PBXBuildFile; fileRef = F300A0C61FDDB6BE00E5894D /* Interpreter.swift */; };
1515
F300A0D11FDDB6E400E5894D /* Lexer.swift in Sources */ = {isa = PBXBuildFile; fileRef = F300A0CE1FDDB6E300E5894D /* Lexer.swift */; };
@@ -50,7 +50,7 @@
5050
F300A09F1FDDB63F00E5894D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
5151
F300A0A41FDDB63F00E5894D /* PascalInterpreterTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PascalInterpreterTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
5252
F300A0AB1FDDB63F00E5894D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
53-
F300A0C41FDDB6BD00E5894D /* Result.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Result.swift; sourceTree = "<group>"; };
53+
F300A0C41FDDB6BD00E5894D /* Value.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Value.swift; sourceTree = "<group>"; };
5454
F300A0C51FDDB6BE00E5894D /* Arithmetics.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Arithmetics.swift; sourceTree = "<group>"; };
5555
F300A0C61FDDB6BE00E5894D /* Interpreter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Interpreter.swift; sourceTree = "<group>"; };
5656
F300A0CE1FDDB6E300E5894D /* Lexer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Lexer.swift; sourceTree = "<group>"; };
@@ -142,7 +142,7 @@
142142
F300A0C51FDDB6BE00E5894D /* Arithmetics.swift */,
143143
F3EF28CF1FE12EBF001F36AB /* Frame.swift */,
144144
F300A0C61FDDB6BE00E5894D /* Interpreter.swift */,
145-
F300A0C41FDDB6BD00E5894D /* Result.swift */,
145+
F300A0C41FDDB6BD00E5894D /* Value.swift */,
146146
F3EF28D11FE12F4D001F36AB /* Stack.swift */,
147147
);
148148
path = Interpreter;
@@ -347,7 +347,7 @@
347347
files = (
348348
F300A0C91FDDB6BE00E5894D /* Interpreter.swift in Sources */,
349349
F3EF28D01FE12EBF001F36AB /* Frame.swift in Sources */,
350-
F300A0C71FDDB6BE00E5894D /* Result.swift in Sources */,
350+
F300A0C71FDDB6BE00E5894D /* Value.swift in Sources */,
351351
F3EF28D21FE12F4D001F36AB /* Stack.swift in Sources */,
352352
F300A0E71FDDB74800E5894D /* SymbolTable.swift in Sources */,
353353
F300A0D81FDDB6F400E5894D /* AST+Extensions.swift in Sources */,

PascalInterpreter/PascalInterpreter/FatalError.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@ import Foundation
1111
/**
1212
Taken from https://medium.com/@marcosantadev/how-to-test-fatalerror-in-swift-e1be9ff11a29
1313
*/
14-
func fatalError(_ message: @autoclosure () -> String = "", file: StaticString = #file, line: UInt = #line) -> Never {
14+
/*func fatalError(_ message: @autoclosure () -> String = "", file: StaticString = #file, line: UInt = #line) -> Never {
1515
FatalErrorUtil.fatalErrorClosure(message(), file, line)
16-
}
16+
}*/
1717

1818
struct FatalErrorUtil {
1919

PascalInterpreter/PascalInterpreter/Interpreter/Interpreter.swift

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ public class Interpreter {
4040
return eval(program: program)
4141
case let call as ProcedureCall:
4242
return eval(call: call)
43+
case let condition as Condition:
44+
return eval(condition: condition)
45+
case let ifElse as IfElse:
46+
return eval(ifElse: ifElse)
4347
default:
4448
return nil
4549
}
@@ -56,28 +60,28 @@ public class Interpreter {
5660

5761
switch unaryOperation.operation {
5862
case .plus:
59-
return .number(+result)
63+
return .number(+result)
6064
case .minus:
61-
return .number(-result)
65+
return .number(-result)
6266
}
6367
}
6468
func eval(binaryOperation: BinaryOperation) -> Value? {
65-
guard let leftValue = eval(node: binaryOperation.left), case let .number(leftResult) = leftValue,
69+
guard let leftValue = eval(node: binaryOperation.left), case let .number(leftResult) = leftValue,
6670
let rightValue = eval(node: binaryOperation.right), case let .number(rightResult) = rightValue else {
6771
fatalError("Cannot use binary \(binaryOperation.operation) on non numbers")
6872
}
6973

7074
switch binaryOperation.operation {
7175
case .plus:
72-
return .number(leftResult + rightResult)
76+
return .number(leftResult + rightResult)
7377
case .minus:
74-
return .number(leftResult - rightResult)
78+
return .number(leftResult - rightResult)
7579
case .mult:
76-
return .number(leftResult * rightResult)
80+
return .number(leftResult * rightResult)
7781
case .integerDiv:
78-
return .number(leftResult rightResult)
82+
return .number(leftResult rightResult)
7983
case .floatDiv:
80-
return .number(leftResult / rightResult)
84+
return .number(leftResult / rightResult)
8185
}
8286
}
8387

@@ -101,7 +105,6 @@ public class Interpreter {
101105
guard let currentFrame = callStack.peek() else {
102106
fatalError("No call stack frame")
103107
}
104-
105108
return currentFrame.get(variable: variable.name)
106109
}
107110

@@ -121,13 +124,39 @@ public class Interpreter {
121124

122125
func eval(call: ProcedureCall) -> Value? {
123126
let current = callStack.peek()!
124-
let frame = Frame(scope: scopes[call.name]!, previousFrame: current)
127+
let newScope = current.scope.level == 1 ? scopes[call.name]! : ScopedSymbolTable(name: call.name, level: scopes[call.name]!.level+1, enclosingScope: scopes[call.name]!)
128+
let frame = Frame(scope: newScope, previousFrame: current)
125129
callStack.push(frame)
126130
callProcedure(procedure: call.name, params: call.actualParameters, frame: frame)
127131
callStack.pop()
128132
return nil
129133
}
130134

135+
func eval(condition: Condition) -> Value {
136+
let left = eval(node: condition.leftSide)
137+
let right = eval(node: condition.rightSide)
138+
139+
guard let leftResult = left, let rightResult = right else {
140+
fatalError("Invalid condition \(left) = \(right)")
141+
}
142+
143+
return .boolean(leftResult == rightResult)
144+
}
145+
146+
func eval(ifElse: IfElse) -> Value? {
147+
guard case let .boolean(value) = eval(condition: ifElse.condition) else {
148+
fatalError("Condition not boolean")
149+
}
150+
151+
if value {
152+
return eval(node: ifElse.trueExpression)
153+
} else if let falseExpression = ifElse.falseExpression {
154+
return eval(node: falseExpression)
155+
} else {
156+
return nil
157+
}
158+
}
159+
131160
private func callProcedure(procedure: String, params: [AST], frame: Frame) {
132161
guard let symbol = frame.scope.lookup(procedure), let procedureSymbol = symbol as? ProcedureSymbol else {
133162
fatalError("Symbol(procedure) not found '\(procedure)'")

PascalInterpreter/PascalInterpreter/Interpreter/Result.swift

Lines changed: 0 additions & 14 deletions
This file was deleted.
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
//
2+
// Result.swift
3+
// SwiftPascalInterpreter
4+
//
5+
// Created by Igor Kulman on 10/12/2017.
6+
// Copyright © 2017 Igor Kulman. All rights reserved.
7+
//
8+
9+
import Foundation
10+
11+
enum Value {
12+
case number(Number)
13+
case boolean(Bool)
14+
}
15+
16+
extension Value: Equatable {
17+
static func ==(lhs: Value, rhs: Value) -> Bool {
18+
switch (lhs, rhs) {
19+
case let (.number(left), .number(right)):
20+
return left == right
21+
case let (.boolean(left), .boolean(right)):
22+
return left == right
23+
default:
24+
return false
25+
}
26+
}
27+
28+
}

PascalInterpreter/PascalInterpreter/Lexer/Lexer.swift

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,10 @@ public class Lexer {
3232
"END": .end,
3333
"PROCEDURE": .procedure,
3434
"TRUE": .constant(.boolean(true)),
35-
"FALSE": .constant(.boolean(false))
35+
"FALSE": .constant(.boolean(false)),
36+
"IF": .`if`,
37+
"ELSE": .`else`,
38+
"THEN": .then
3639
]
3740

3841
public init(_ text: String) {
@@ -223,6 +226,11 @@ public class Lexer {
223226
return .parenthesis(.right)
224227
}
225228

229+
if currentCharacter == "=" {
230+
advance()
231+
return .equals
232+
}
233+
226234
fatalError("Unrecognized character \(currentCharacter) at position \(currentPosition)")
227235
}
228236

PascalInterpreter/PascalInterpreter/Lexer/Token+Extensions.swift

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,14 @@ extension Token: Equatable {
4343
return left == right
4444
case (.procedure, .procedure):
4545
return true
46+
case (.if, .if):
47+
return true
48+
case (.else, .else):
49+
return true
50+
case (.equals, .equals):
51+
return true
52+
case (.then, .then):
53+
return true
4654
default:
4755
return false
4856
}
@@ -56,6 +64,8 @@ extension Constant: Equatable {
5664
return left == right
5765
case let (.real(left), .real(right)):
5866
return left == right
67+
case let (.boolean(left), .boolean(right)):
68+
return left == right
5969
default:
6070
return false
6171
}
@@ -151,6 +161,14 @@ extension Token: CustomStringConvertible {
151161
return constant.description
152162
case .procedure:
153163
return "PROCEDURE"
164+
case .`if`:
165+
return "IF"
166+
case .`else`:
167+
return "ELSE"
168+
case .then:
169+
return "THEN"
170+
case .equals:
171+
return "EQUALS"
154172
}
155173
}
156174
}

PascalInterpreter/PascalInterpreter/Lexer/Token.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,4 +50,8 @@ public enum Token {
5050
case type(Type)
5151
case constant(Constant)
5252
case procedure
53+
case `if`
54+
case `else`
55+
case then
56+
case equals
5357
}

PascalInterpreter/PascalInterpreter/Parser/AST+Extensions.swift

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,21 @@ extension Number: CustomStringConvertible {
3030
}
3131
}
3232

33+
extension Number: Equatable {
34+
public static func == (lhs: Number, rhs: Number) -> Bool {
35+
switch (lhs, rhs) {
36+
case let (.integer(left), .integer(right)):
37+
return left == right
38+
case let (.real(left), .real(right)):
39+
return left == right
40+
case let (.real(left), .integer(right)):
41+
return left == Double(right)
42+
case let (.integer(left), .real(right)):
43+
return Double(left) == right
44+
}
45+
}
46+
}
47+
3348
extension BinaryOperationType: CustomStringConvertible {
3449
public var description: String {
3550
switch self {
@@ -78,6 +93,10 @@ extension AST {
7893
return "param(\(param.name))"
7994
case let call as ProcedureCall:
8095
return "\(call.name)()"
96+
case is IfElse:
97+
return "IF"
98+
case is Condition:
99+
return "="
81100
default:
82101
fatalError("Missed AST case \(self)")
83102
}
@@ -123,6 +142,13 @@ extension AST {
123142
return [param.type]
124143
case let call as ProcedureCall:
125144
return call.actualParameters
145+
case let ifelse as IfElse:
146+
if let falseExpression = ifelse.falseExpression {
147+
return [ifelse.condition, ifelse.trueExpression, falseExpression]
148+
}
149+
return [ifelse.condition, ifelse.trueExpression]
150+
case let condition as Condition:
151+
return [condition.leftSide, condition.rightSide]
126152
default:
127153
fatalError("Missed AST case \(self)")
128154
}

PascalInterpreter/PascalInterpreter/Parser/AST.swift

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,3 +152,25 @@ class ProcedureCall: AST {
152152
self.actualParameters = actualParameters
153153
}
154154
}
155+
156+
class Condition: AST {
157+
let leftSide: AST
158+
let rightSide: AST
159+
160+
init(leftSide: AST, rightSide: AST) {
161+
self.leftSide = leftSide
162+
self.rightSide = rightSide
163+
}
164+
}
165+
166+
class IfElse: AST {
167+
let condition: Condition
168+
let trueExpression: AST
169+
let falseExpression: AST?
170+
171+
init(condition: Condition, trueExpression: AST, falseExpression: AST?) {
172+
self.condition = condition
173+
self.trueExpression = trueExpression
174+
self.falseExpression = falseExpression
175+
}
176+
}

0 commit comments

Comments
 (0)