Skip to content

Commit 6d10e3f

Browse files
committed
✨ For loops
1 parent 71371df commit 6d10e3f

File tree

19 files changed

+211
-22
lines changed

19 files changed

+211
-22
lines changed

PascalInterpreter/PascalInterpreter/Interpreter/Frame.swift

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,24 @@ class Frame {
2222
self.previousFrame = previousFrame
2323
}
2424

25+
func remove(variable: String) {
26+
if let symbol = scope.lookup(variable, currentScopeOnly: true),
27+
let variableSymbol = symbol as? VariableSymbol,
28+
let type = variableSymbol.type as? BuiltInTypeSymbol {
29+
30+
switch type {
31+
case .integer:
32+
integerMemory.removeValue(forKey: variable)
33+
case .real:
34+
realMemory.removeValue(forKey: variable)
35+
case .boolean:
36+
booleanMemory.removeValue(forKey: variable)
37+
case .string:
38+
stringMemory.removeValue(forKey: variable)
39+
}
40+
}
41+
}
42+
2543
func set(variable: String, value: Value) {
2644
// setting function return value
2745
if variable == scope.name && scope.level > 1 {

PascalInterpreter/PascalInterpreter/Interpreter/Interpreter.swift

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ public class Interpreter {
4848
return eval(ifElse: ifElse)
4949
case let repeatUntil as RepeatUntil:
5050
return eval(repeatUntil: repeatUntil)
51+
case let forLoop as For:
52+
return eval(forLoop: forLoop)
5153
default:
5254
return .none
5355
}
@@ -73,6 +75,7 @@ public class Interpreter {
7375
return .number(-result)
7476
}
7577
}
78+
7679
func eval(binaryOperation: BinaryOperation) -> Value {
7780
guard case let .number(leftResult) = eval(node: binaryOperation.left), case let .number(rightResult) = eval(node: binaryOperation.right) else {
7881
fatalError("Cannot use binary \(binaryOperation.operation) on non numbers")
@@ -179,6 +182,24 @@ public class Interpreter {
179182
return .none
180183
}
181184

185+
func eval(forLoop: For) -> Value {
186+
guard let currentFrame = callStack.peek() else {
187+
fatalError("No call stack frame")
188+
}
189+
190+
guard case let .number(.integer(start)) = eval(node: forLoop.startValue), case let .number(.integer(end)) = eval(node: forLoop.endValue) else {
191+
fatalError("Cannot do a for loop on non integer values")
192+
}
193+
194+
for i in start ... end {
195+
currentFrame.set(variable: forLoop.variable.name, value: .number(.integer(i)))
196+
eval(node: forLoop.statement)
197+
}
198+
currentFrame.remove(variable: forLoop.variable.name)
199+
200+
return .none
201+
}
202+
182203
private func callFunction(function: String, params: [AST], frame: Frame) -> Value {
183204
guard let symbol = frame.scope.lookup(function), let procedureSymbol = symbol as? ProcedureSymbol else {
184205
fatalError("Symbol(procedure) not found '\(function)'")

PascalInterpreter/PascalInterpreter/Interpreter/Value.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,5 +28,4 @@ extension Value: Equatable {
2828
return false
2929
}
3030
}
31-
3231
}

PascalInterpreter/PascalInterpreter/Lexer/Lexer.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,10 @@ public class Lexer {
3939
"THEN": .then,
4040
"FUNCTION": .function,
4141
"REPEAT": .repeat,
42-
"UNTIL": .until
42+
"UNTIL": .until,
43+
"FOR": .for,
44+
"TO": .to,
45+
"DO": .do,
4346
]
4447

4548
public init(_ text: String) {

PascalInterpreter/PascalInterpreter/Lexer/Token+Extensions.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,12 @@ extension Token: Equatable {
6363
return true
6464
case (.until, .until):
6565
return true
66+
case (.for, .for):
67+
return true
68+
case (.to, .to):
69+
return true
70+
case (.do, .do):
71+
return true
6672
default:
6773
return false
6874
}
@@ -199,6 +205,12 @@ extension Token: CustomStringConvertible {
199205
return "REPEAT"
200206
case .until:
201207
return "UNTIL"
208+
case .for:
209+
return "FOR"
210+
case .to:
211+
return "TO"
212+
case .do:
213+
return "DO"
202214
}
203215
}
204216
}

PascalInterpreter/PascalInterpreter/Lexer/Token.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,4 +62,7 @@ public enum Token {
6262
case apostrophe
6363
case `repeat`
6464
case until
65+
case `for`
66+
case to
67+
case `do`
6568
}

PascalInterpreter/PascalInterpreter/Parser/AST+Extensions.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,8 @@ extension AST {
118118
return boolean ? "TRUE" : "FALSE"
119119
case is RepeatUntil:
120120
return "REPEAT"
121+
case is For:
122+
return "FOR"
121123
default:
122124
fatalError("Missed AST case \(self)")
123125
}
@@ -183,6 +185,8 @@ extension AST {
183185
return []
184186
case let repeatUntil as RepeatUntil:
185187
return [repeatUntil.condition, repeatUntil.statement]
188+
case let forLoop as For:
189+
return [forLoop.variable, forLoop.startValue, forLoop.endValue, forLoop.statement]
186190
default:
187191
fatalError("Missed AST case \(self)")
188192
}

PascalInterpreter/PascalInterpreter/Parser/AST.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,3 +207,17 @@ class RepeatUntil: AST {
207207
self.condition = condition
208208
}
209209
}
210+
211+
class For: AST {
212+
let statement: AST
213+
let variable: Variable
214+
let startValue: AST
215+
let endValue: AST
216+
217+
init(statement: AST, variable: Variable, startValue: AST, endValue: AST) {
218+
self.statement = statement
219+
self.variable = variable
220+
self.startValue = startValue
221+
self.endValue = endValue
222+
}
223+
}

PascalInterpreter/PascalInterpreter/Parser/Parser.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ public class Parser {
1818
private var currentToken: Token {
1919
return tokens[tokenIndex]
2020
}
21+
2122
private var nextToken: Token {
2223
return tokens[tokenIndex + 1]
2324
}
@@ -277,6 +278,7 @@ public class Parser {
277278
| if_else_statement
278279
| assignment_statement
279280
| repeat_until
281+
| for_loop
280282
| empty
281283
*/
282284
private func statement() -> AST {
@@ -287,6 +289,8 @@ public class Parser {
287289
return ifElseStatement()
288290
case .repeat:
289291
return repeatUntilLoop()
292+
case .for:
293+
return forLoop()
290294
case .id:
291295
if nextToken == .parenthesis(.left) {
292296
return functionCall()
@@ -319,6 +323,18 @@ public class Parser {
319323
return RepeatUntil(statement: statements.count == 1 ? statements[0] : Compound(children: statements), condition: condition)
320324
}
321325

326+
private func forLoop() -> For {
327+
eat(.for)
328+
let variable = self.variable()
329+
eat(.assign)
330+
let start = expr()
331+
eat(.to)
332+
let end = expr()
333+
eat(.do)
334+
let statement = self.statement()
335+
return For(statement: statement, variable: variable, startValue: start, endValue: end)
336+
}
337+
322338
/**
323339
Rule:
324340

PascalInterpreter/PascalInterpreter/Semantic analyzer/SemanticAnalyzer.swift

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ public class SemanticAnalyzer: Visitor {
1313
private var scopes: [String: ScopedSymbolTable] = [:]
1414

1515
public init() {
16-
1716
}
1817

1918
public func analyze(node: AST) -> [String: ScopedSymbolTable] {
@@ -104,8 +103,7 @@ public class SemanticAnalyzer: Visitor {
104103
}
105104
}
106105

107-
private func checkBuiltInProcedure(call: FunctionCall, procedure: BuiltInProcedureSymbol) {
108-
106+
private func checkBuiltInProcedure(call _: FunctionCall, procedure _: BuiltInProcedureSymbol) {
109107
}
110108

111109
private func checkFunction(call: FunctionCall, procedure: ProcedureSymbol) {
@@ -137,4 +135,12 @@ public class SemanticAnalyzer: Visitor {
137135
}
138136
}
139137
}
138+
139+
func visit(forLoop: For) {
140+
currentScope?.insert(VariableSymbol(name: forLoop.variable.name, type: currentScope!.lookup("INTEGER")!))
141+
142+
visit(node: forLoop.startValue)
143+
visit(node: forLoop.endValue)
144+
visit(node: forLoop.statement)
145+
}
140146
}

0 commit comments

Comments
 (0)