Skip to content

Commit 11be1c6

Browse files
authored
Merge pull request #14 from igorkulman/if
If-Else statements and Boolean type
2 parents b282e73 + fc5ad3b commit 11be1c6

28 files changed

+522
-112
lines changed

Images/lexer.png

-32.2 KB
Loading

Images/parser.png

-47.4 KB
Loading

Images/playground.png

-161 KB
Loading

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/Arithmetics.swift

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,4 +136,50 @@ extension Number {
136136
fatalError("Integer division DIV can only be applied to two integers")
137137
}
138138
}
139+
140+
static func < (left: Number, right: Number) -> Bool {
141+
switch (left, right) {
142+
case let (.integer(left), .integer(right)):
143+
return Double(left) < Double(right)
144+
case let (.real(left), .real(right)):
145+
return left < right
146+
case let (.integer(left), .real(right)):
147+
return Double(left) < right
148+
case let (.real(left), .integer(right)):
149+
return left < Double(right)
150+
}
151+
}
152+
153+
static func > (left: Number, right: Number) -> Bool {
154+
switch (left, right) {
155+
case let (.integer(left), .integer(right)):
156+
return Double(left) > Double(right)
157+
case let (.real(left), .real(right)):
158+
return left > right
159+
case let (.integer(left), .real(right)):
160+
return Double(left) > right
161+
case let (.real(left), .integer(right)):
162+
return left > Double(right)
163+
}
164+
}
165+
}
166+
167+
extension Value {
168+
static func < (lhs: Value, rhs: Value) -> Bool {
169+
switch (lhs, rhs) {
170+
case let (.number(left), .number(right)):
171+
return left < right
172+
default:
173+
fatalError("Cannot compare \(lhs) and \(rhs)")
174+
}
175+
}
176+
177+
static func > (lhs: Value, rhs: Value) -> Bool {
178+
switch (lhs, rhs) {
179+
case let (.number(left), .number(right)):
180+
return left > right
181+
default:
182+
fatalError("Cannot compare \(lhs) and \(rhs)")
183+
}
184+
}
139185
}

PascalInterpreter/PascalInterpreter/Interpreter/Frame.swift

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import Foundation
1111
class Frame {
1212
var integerMemory: [String: Int] = [:]
1313
var realMemory: [String: Double] = [:]
14+
var booleanMemory: [String: Bool] = [:]
1415
let scope: ScopedSymbolTable
1516
let previousFrame: Frame?
1617

@@ -19,7 +20,7 @@ class Frame {
1920
self.previousFrame = previousFrame
2021
}
2122

22-
func set(variable: String, value: Number) {
23+
func set(variable: String, value: Value) {
2324
// variable define in current scole (procedure declataion, etc)
2425
if let symbol = scope.lookup(variable, currentScopeOnly: true),
2526
let variableSymbol = symbol as? VariableSymbol,
@@ -28,17 +29,34 @@ class Frame {
2829
switch type {
2930
case .integer:
3031
switch value {
31-
case .integer(let value):
32-
integerMemory[variable] = value
33-
case .real:
34-
fatalError("Cannot assign Real value to Int variable \(variable)")
32+
case let .number(number):
33+
switch number {
34+
case let .integer(value):
35+
integerMemory[variable] = value
36+
case .real:
37+
fatalError("Cannot assign Real value to Int variable \(variable)")
38+
}
39+
case .boolean:
40+
fatalError("Cannot assign Boolean value to Int variable \(variable)")
3541
}
3642
case .real:
3743
switch value {
38-
case .integer(let value):
39-
realMemory[variable] = Double(value)
40-
case .real(let value):
41-
realMemory[variable] = value
44+
case let .number(number):
45+
switch number {
46+
case let .integer(value):
47+
realMemory[variable] = Double(value)
48+
case let .real(value):
49+
realMemory[variable] = value
50+
}
51+
case .boolean:
52+
fatalError("Cannot assign Boolean value to Real variable \(variable)")
53+
}
54+
case .boolean:
55+
switch value {
56+
case let .boolean(boolean):
57+
booleanMemory[variable] = boolean
58+
default:
59+
fatalError("Cannot assign \(value) value to Boolean variable \(variable)")
4260
}
4361
}
4462
return
@@ -48,17 +66,19 @@ class Frame {
4866
previousFrame!.set(variable: variable, value: value)
4967
}
5068

51-
func get(variable: String) -> Number {
69+
func get(variable: String) -> Value {
5270
// variable define in current scole (procedure declataion, etc)
5371
if let symbol = scope.lookup(variable, currentScopeOnly: true),
5472
let variableSymbol = symbol as? VariableSymbol,
5573
let type = variableSymbol.type as? BuiltInTypeSymbol {
5674

5775
switch type {
5876
case .integer:
59-
return .integer(integerMemory[variable]!)
77+
return .number(.integer(integerMemory[variable]!))
6078
case .real:
61-
return .real(realMemory[variable]!)
79+
return .number(.real(realMemory[variable]!))
80+
case .boolean:
81+
return .boolean(booleanMemory[variable]!)
6282
}
6383
}
6484

PascalInterpreter/PascalInterpreter/Interpreter/Interpreter.swift

Lines changed: 64 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ public class Interpreter {
2020
scopes = semanticAnalyzer.analyze(node: tree)
2121
}
2222

23-
@discardableResult private func eval(node: AST) -> Number? {
23+
@discardableResult private func eval(node: AST) -> Value? {
2424
switch node {
2525
case let number as Number:
2626
return eval(number: number)
@@ -40,54 +40,59 @@ 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
}
4650
}
4751

48-
func eval(number: Number) -> Number? {
49-
return number
52+
func eval(number: Number) -> Value? {
53+
return .number(number)
5054
}
5155

52-
func eval(unaryOperation: UnaryOperation) -> Number? {
53-
guard let result = eval(node: unaryOperation.operand) else {
56+
func eval(unaryOperation: UnaryOperation) -> Value? {
57+
guard let value = eval(node: unaryOperation.operand), case let .number(result) = value else {
5458
fatalError("Cannot use unary \(unaryOperation.operation) on non number")
5559
}
5660

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

6974
switch binaryOperation.operation {
7075
case .plus:
71-
return leftResult + rightResult
76+
return .number(leftResult + rightResult)
7277
case .minus:
73-
return leftResult - rightResult
78+
return .number(leftResult - rightResult)
7479
case .mult:
75-
return leftResult * rightResult
80+
return .number(leftResult * rightResult)
7681
case .integerDiv:
77-
return leftResult rightResult
82+
return .number(leftResult rightResult)
7883
case .floatDiv:
79-
return leftResult / rightResult
84+
return .number(leftResult / rightResult)
8085
}
8186
}
8287

83-
func eval(compound: Compound) -> Number? {
88+
func eval(compound: Compound) -> Value? {
8489
for child in compound.children {
8590
eval(node: child)
8691
}
8792
return nil
8893
}
8994

90-
func eval(assignment: Assignment) -> Number? {
95+
func eval(assignment: Assignment) -> Value? {
9196
guard let currentFrame = callStack.peek() else {
9297
fatalError("No call stack frame")
9398
}
@@ -96,46 +101,81 @@ public class Interpreter {
96101
return nil
97102
}
98103

99-
func eval(variable: Variable) -> Number? {
104+
func eval(variable: Variable) -> Value? {
100105
guard let currentFrame = callStack.peek() else {
101106
fatalError("No call stack frame")
102107
}
103-
104108
return currentFrame.get(variable: variable.name)
105109
}
106110

107-
func eval(block: Block) -> Number? {
111+
func eval(block: Block) -> Value? {
108112
for declaration in block.declarations {
109113
eval(node: declaration)
110114
}
111115

112116
return eval(node: block.compound)
113117
}
114118

115-
func eval(program: Program) -> Number? {
119+
func eval(program: Program) -> Value? {
116120
let frame = Frame(scope: scopes["global"]!, previousFrame: nil)
117121
callStack.push(frame)
118122
return eval(node: program.block)
119123
}
120124

121-
func eval(call: ProcedureCall) -> Number? {
125+
func eval(call: ProcedureCall) -> Value? {
122126
let current = callStack.peek()!
123-
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)
124129
callStack.push(frame)
125130
callProcedure(procedure: call.name, params: call.actualParameters, frame: frame)
126131
callStack.pop()
127132
return nil
128133
}
129134

130-
private func callProcedure(procedure: String, params: [Number], frame: Frame) {
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 \(String(describing: left)) = \(String(describing: right))")
141+
}
142+
143+
switch condition.type {
144+
case .equals:
145+
return .boolean(leftResult == rightResult)
146+
case .greaterThan:
147+
return .boolean(leftResult > rightResult)
148+
case .lessThan:
149+
return .boolean(leftResult < rightResult)
150+
}
151+
}
152+
153+
func eval(ifElse: IfElse) -> Value? {
154+
guard case let .boolean(value) = eval(condition: ifElse.condition) else {
155+
fatalError("Condition not boolean")
156+
}
157+
158+
if value {
159+
return eval(node: ifElse.trueExpression)
160+
} else if let falseExpression = ifElse.falseExpression {
161+
return eval(node: falseExpression)
162+
} else {
163+
return nil
164+
}
165+
}
166+
167+
private func callProcedure(procedure: String, params: [AST], frame: Frame) {
131168
guard let symbol = frame.scope.lookup(procedure), let procedureSymbol = symbol as? ProcedureSymbol else {
132169
fatalError("Symbol(procedure) not found '\(procedure)'")
133170
}
134171

135172
if procedureSymbol.params.count > 0 {
136173

137174
for i in 0 ... procedureSymbol.params.count - 1 {
138-
frame.set(variable: procedureSymbol.params[i].name, value: params[i])
175+
guard let evaluated = eval(node: params[i]) else {
176+
fatalError("Cannot assing empty value")
177+
}
178+
frame.set(variable: procedureSymbol.params[i].name, value: evaluated)
139179
}
140180
}
141181

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+
}

0 commit comments

Comments
 (0)