Skip to content

Commit d9a7b48

Browse files
authored
Merge pull request #18 from igorkulman/repeat-until
Repeat until
2 parents c76bc9d + 00bfac2 commit d9a7b48

File tree

18 files changed

+204
-15
lines changed

18 files changed

+204
-15
lines changed

Examples/factorial.pas

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
function Factorial(number: Integer): Integer;
55
begin
6-
if (number > 1) then
6+
if number > 1 then
77
Factorial := number * Factorial(number-1)
88
else
99
Factorial := 1

Examples/game.pas

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
Program Game;
2+
Var
3+
target, guess: Integer;
4+
5+
Begin
6+
guess := 10;
7+
target := random(guess);
8+
9+
Writeln('Guess a number between 0 and 10:');
10+
Read(guess);
11+
12+
repeat
13+
if guess > target then
14+
begin
15+
Writeln('Too much, try again');
16+
Read(guess);
17+
end
18+
else
19+
begin
20+
Writeln('Too low, try again');
21+
Read(guess);
22+
end
23+
until target = guess;
24+
Writeln('You won!')
25+
End.

PascalInterpreter/PascalInterpreter/Interpreter/Interpreter+Standard.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,22 @@ extension Interpreter {
2323
case "READLN":
2424
read(params: params, frame: frame)
2525
return .none
26+
case "RANDOM":
27+
return random(params: params)
2628
default:
2729
fatalError("Implement built in procedure \(procedure)")
2830
}
2931
}
3032

33+
private func random(params: [AST]) -> Value {
34+
guard params.count == 1, let first = params.first, case let .number(.integer(l)) = eval(node: first) else {
35+
fatalError("Random called with invalid parameters")
36+
}
37+
38+
let value = arc4random_uniform(UInt32(l))
39+
return .number(.integer(Int(value)))
40+
}
41+
3142
private func write(params: [AST], newLine: Bool) {
3243
var s = ""
3344
for param in params {

PascalInterpreter/PascalInterpreter/Interpreter/Interpreter.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ public class Interpreter {
4646
return eval(condition: condition)
4747
case let ifElse as IfElse:
4848
return eval(ifElse: ifElse)
49+
case let repeatUntil as RepeatUntil:
50+
return eval(repeatUntil: repeatUntil)
4951
default:
5052
return .none
5153
}
@@ -165,6 +167,18 @@ public class Interpreter {
165167
}
166168
}
167169

170+
func eval(repeatUntil: RepeatUntil) -> Value {
171+
eval(node: repeatUntil.statement)
172+
var value = eval(condition: repeatUntil.condition)
173+
174+
while case .boolean(false) = value {
175+
eval(node: repeatUntil.statement)
176+
value = eval(condition: repeatUntil.condition)
177+
}
178+
179+
return .none
180+
}
181+
168182
private func callFunction(function: String, params: [AST], frame: Frame) -> Value {
169183
guard let symbol = frame.scope.lookup(function), let procedureSymbol = symbol as? ProcedureSymbol else {
170184
fatalError("Symbol(procedure) not found '\(function)'")

PascalInterpreter/PascalInterpreter/Lexer/Lexer.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,9 @@ public class Lexer {
3737
"IF": .if,
3838
"ELSE": .else,
3939
"THEN": .then,
40-
"FUNCTION": .function
40+
"FUNCTION": .function,
41+
"REPEAT": .repeat,
42+
"UNTIL": .until
4143
]
4244

4345
public init(_ text: String) {

PascalInterpreter/PascalInterpreter/Lexer/Token+Extensions.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,10 @@ extension Token: Equatable {
5959
return true
6060
case (.apostrophe, .apostrophe):
6161
return true
62+
case (.repeat, .repeat):
63+
return true
64+
case (.until, .until):
65+
return true
6266
default:
6367
return false
6468
}
@@ -191,6 +195,10 @@ extension Token: CustomStringConvertible {
191195
return "FUNCTION"
192196
case .apostrophe:
193197
return "APOSTROPHE"
198+
case .repeat:
199+
return "REPEAT"
200+
case .until:
201+
return "UNTIL"
194202
}
195203
}
196204
}

PascalInterpreter/PascalInterpreter/Lexer/Token.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,4 +60,6 @@ public enum Token {
6060
case greaterThan
6161
case function
6262
case apostrophe
63+
case `repeat`
64+
case until
6365
}

PascalInterpreter/PascalInterpreter/Parser/AST+Extensions.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,8 @@ extension AST {
116116
return string
117117
case let boolean as Bool:
118118
return boolean ? "TRUE" : "FALSE"
119+
case is RepeatUntil:
120+
return "REPEAT"
119121
default:
120122
fatalError("Missed AST case \(self)")
121123
}
@@ -179,6 +181,8 @@ extension AST {
179181
return []
180182
case is Bool:
181183
return []
184+
case let repeatUntil as RepeatUntil:
185+
return [repeatUntil.condition, repeatUntil.statement]
182186
default:
183187
fatalError("Missed AST case \(self)")
184188
}

PascalInterpreter/PascalInterpreter/Parser/AST.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,3 +197,13 @@ class IfElse: AST {
197197
self.falseExpression = falseExpression
198198
}
199199
}
200+
201+
class RepeatUntil: AST {
202+
let statement: AST
203+
let condition: Condition
204+
205+
init(statement: AST, condition: Condition) {
206+
self.statement = statement
207+
self.condition = condition
208+
}
209+
}

PascalInterpreter/PascalInterpreter/Parser/Parser.swift

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,7 @@ public class Parser {
276276
| procedure_call
277277
| if_else_statement
278278
| assignment_statement
279+
| repeat_until
279280
| empty
280281
*/
281282
private func statement() -> AST {
@@ -284,6 +285,8 @@ public class Parser {
284285
return compoundStatement()
285286
case .if:
286287
return ifElseStatement()
288+
case .repeat:
289+
return repeatUntilLoop()
287290
case .id:
288291
if nextToken == .parenthesis(.left) {
289292
return functionCall()
@@ -295,6 +298,27 @@ public class Parser {
295298
}
296299
}
297300

301+
/**
302+
Rule:
303+
304+
repeat_until : REPEAT statement UNTIL condition
305+
*/
306+
private func repeatUntilLoop() -> RepeatUntil {
307+
eat(.repeat)
308+
309+
var statements: [AST] = []
310+
311+
while currentToken != .until {
312+
statements.append(statement())
313+
if currentToken == .semi {
314+
eat(.semi)
315+
}
316+
}
317+
eat(.until)
318+
let condition = self.condition()
319+
return RepeatUntil(statement: statements.count == 1 ? statements[0] : Compound(children: statements), condition: condition)
320+
}
321+
298322
/**
299323
Rule:
300324

@@ -346,9 +370,12 @@ public class Parser {
346370
/**
347371
Rule:
348372
condition: expr (= | < | >) expr
373+
| LPAREN expr (= | < | >) expr RPAREN
349374
*/
350375
private func condition() -> Condition {
351-
eat(.parenthesis(.left))
376+
if currentToken == .parenthesis(.left) {
377+
eat(.parenthesis(.left))
378+
}
352379
let left = expr()
353380
var type: ConditionType = .equals
354381
switch currentToken {
@@ -365,7 +392,9 @@ public class Parser {
365392
fatalError("Invalid condition type \(type)")
366393
}
367394
let right = expr()
368-
eat(.parenthesis(.right))
395+
if currentToken == .parenthesis(.right) {
396+
eat(.parenthesis(.right))
397+
}
369398
return Condition(type: type, leftSide: left, rightSide: right)
370399
}
371400

@@ -483,10 +512,10 @@ public class Parser {
483512
let result = expr()
484513
eat(.parenthesis(.right))
485514
return result
486-
case .constant(.string(let value)):
515+
case let .constant(.string(value)):
487516
eat(.constant(.string(value)))
488517
return value
489-
case .constant(.boolean(let value)):
518+
case let .constant(.boolean(value)):
490519
eat(.constant(.boolean(value)))
491520
return value
492521
default:

0 commit comments

Comments
 (0)