From aa221bc7cfd738a631228216d14d995b19fd716b Mon Sep 17 00:00:00 2001 From: Igor Kulman Date: Wed, 20 Dec 2017 12:27:28 +0100 Subject: [PATCH 1/6] =?UTF-8?q?=E2=9C=A8=20adding=20the=20random=20functio?= =?UTF-8?q?n?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Interpreter/Interpreter+Standard.swift | 11 +++++++++++ .../Semantic analyzer/SymbolTable.swift | 1 + 2 files changed, 12 insertions(+) diff --git a/PascalInterpreter/PascalInterpreter/Interpreter/Interpreter+Standard.swift b/PascalInterpreter/PascalInterpreter/Interpreter/Interpreter+Standard.swift index b57cd43..5abf812 100644 --- a/PascalInterpreter/PascalInterpreter/Interpreter/Interpreter+Standard.swift +++ b/PascalInterpreter/PascalInterpreter/Interpreter/Interpreter+Standard.swift @@ -23,11 +23,22 @@ extension Interpreter { case "READLN": read(params: params, frame: frame) return .none + case "RANDOM": + return random(params: params) default: fatalError("Implement built in procedure \(procedure)") } } + private func random(params: [AST]) -> Value { + guard params.count == 1, let first = params.first, case let .number(.integer(l)) = eval(node: first) else { + fatalError("Random called with invalid parameters") + } + + let value = arc4random_uniform(UInt32(l)) + return .number(.integer(Int(value))) + } + private func write(params: [AST], newLine: Bool) { var s = "" for param in params { diff --git a/PascalInterpreter/PascalInterpreter/Semantic analyzer/SymbolTable.swift b/PascalInterpreter/PascalInterpreter/Semantic analyzer/SymbolTable.swift index 40e7ecf..66eb470 100644 --- a/PascalInterpreter/PascalInterpreter/Semantic analyzer/SymbolTable.swift +++ b/PascalInterpreter/PascalInterpreter/Semantic analyzer/SymbolTable.swift @@ -36,6 +36,7 @@ public class ScopedSymbolTable { symbols["WRITE"] = BuiltInProcedureSymbol(name: "WRITE", parameters: [], hasVariableParameters: true) symbols["READ"] = BuiltInProcedureSymbol(name: "READ", parameters: [], hasVariableParameters: true) symbols["READLN"] = BuiltInProcedureSymbol(name: "READLN", parameters: [], hasVariableParameters: true) + symbols["RANDOM"] = BuiltInProcedureSymbol(name: "RANDOM", parameters: [BuiltInTypeSymbol.integer], hasVariableParameters: false) } func insert(_ symbol: Symbol) { From 449ae22d91d199ab786dfe81308e9376bd912b96 Mon Sep 17 00:00:00 2001 From: Igor Kulman Date: Wed, 20 Dec 2017 13:18:42 +0100 Subject: [PATCH 2/6] =?UTF-8?q?=E2=9C=A8=20basic=20support=20for=20repeat?= =?UTF-8?q?=20until?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Examples/game.pas | 25 +++++++++++++++++ .../Interpreter/Interpreter.swift | 14 ++++++++++ .../PascalInterpreter/Lexer/Lexer.swift | 4 ++- .../Lexer/Token+Extensions.swift | 8 ++++++ .../PascalInterpreter/Lexer/Token.swift | 2 ++ .../Parser/AST+Extensions.swift | 4 +++ .../PascalInterpreter/Parser/AST.swift | 10 +++++++ .../PascalInterpreter/Parser/Parser.swift | 28 +++++++++++++++++-- .../Semantic analyzer/Visitor.swift | 8 ++++++ 9 files changed, 100 insertions(+), 3 deletions(-) create mode 100644 Examples/game.pas diff --git a/Examples/game.pas b/Examples/game.pas new file mode 100644 index 0000000..8f29d88 --- /dev/null +++ b/Examples/game.pas @@ -0,0 +1,25 @@ +Program Game; +Var + target, guess: Integer; + +Begin + guess := 10; + target := random(guess); + + Write('Guess a number between 0 and 10:'); + Read(guess); + + repeat + if (guess > target) then + begin + Writeln('Too much, try again'); + Read(guess); + end + else + begin + Writeln('Too low, try again'); + Read(guess); + end + until (target = guess); + Writeln('You won!') +End. \ No newline at end of file diff --git a/PascalInterpreter/PascalInterpreter/Interpreter/Interpreter.swift b/PascalInterpreter/PascalInterpreter/Interpreter/Interpreter.swift index dae2b5c..05078fe 100644 --- a/PascalInterpreter/PascalInterpreter/Interpreter/Interpreter.swift +++ b/PascalInterpreter/PascalInterpreter/Interpreter/Interpreter.swift @@ -46,6 +46,8 @@ public class Interpreter { return eval(condition: condition) case let ifElse as IfElse: return eval(ifElse: ifElse) + case let repeatUntil as RepeatUntil: + return eval(repeatUntil: repeatUntil) default: return .none } @@ -165,6 +167,18 @@ public class Interpreter { } } + func eval(repeatUntil: RepeatUntil) -> Value { + eval(node: repeatUntil.statement) + var value = eval(condition: repeatUntil.condition) + + while case .boolean(false) = value { + eval(node: repeatUntil.statement) + value = eval(condition: repeatUntil.condition) + } + + return .none + } + private func callFunction(function: String, params: [AST], frame: Frame) -> Value { guard let symbol = frame.scope.lookup(function), let procedureSymbol = symbol as? ProcedureSymbol else { fatalError("Symbol(procedure) not found '\(function)'") diff --git a/PascalInterpreter/PascalInterpreter/Lexer/Lexer.swift b/PascalInterpreter/PascalInterpreter/Lexer/Lexer.swift index 049c7b9..8da8e65 100644 --- a/PascalInterpreter/PascalInterpreter/Lexer/Lexer.swift +++ b/PascalInterpreter/PascalInterpreter/Lexer/Lexer.swift @@ -37,7 +37,9 @@ public class Lexer { "IF": .if, "ELSE": .else, "THEN": .then, - "FUNCTION": .function + "FUNCTION": .function, + "REPEAT": .repeat, + "UNTIL": .until ] public init(_ text: String) { diff --git a/PascalInterpreter/PascalInterpreter/Lexer/Token+Extensions.swift b/PascalInterpreter/PascalInterpreter/Lexer/Token+Extensions.swift index 1d30ae6..d3223da 100644 --- a/PascalInterpreter/PascalInterpreter/Lexer/Token+Extensions.swift +++ b/PascalInterpreter/PascalInterpreter/Lexer/Token+Extensions.swift @@ -59,6 +59,10 @@ extension Token: Equatable { return true case (.apostrophe, .apostrophe): return true + case (.repeat, .repeat): + return true + case (.until, .until): + return true default: return false } @@ -191,6 +195,10 @@ extension Token: CustomStringConvertible { return "FUNCTION" case .apostrophe: return "APOSTROPHE" + case .repeat: + return "REPEAT" + case .until: + return "UNTIL" } } } diff --git a/PascalInterpreter/PascalInterpreter/Lexer/Token.swift b/PascalInterpreter/PascalInterpreter/Lexer/Token.swift index 60aa7c4..0bf2e29 100644 --- a/PascalInterpreter/PascalInterpreter/Lexer/Token.swift +++ b/PascalInterpreter/PascalInterpreter/Lexer/Token.swift @@ -60,4 +60,6 @@ public enum Token { case greaterThan case function case apostrophe + case `repeat` + case until } diff --git a/PascalInterpreter/PascalInterpreter/Parser/AST+Extensions.swift b/PascalInterpreter/PascalInterpreter/Parser/AST+Extensions.swift index 7e42527..a7a4e1c 100644 --- a/PascalInterpreter/PascalInterpreter/Parser/AST+Extensions.swift +++ b/PascalInterpreter/PascalInterpreter/Parser/AST+Extensions.swift @@ -116,6 +116,8 @@ extension AST { return string case let boolean as Bool: return boolean ? "TRUE" : "FALSE" + case is RepeatUntil: + return "REPEAT" default: fatalError("Missed AST case \(self)") } @@ -179,6 +181,8 @@ extension AST { return [] case is Bool: return [] + case let repeatUntil as RepeatUntil: + return [repeatUntil.condition, repeatUntil.statement] default: fatalError("Missed AST case \(self)") } diff --git a/PascalInterpreter/PascalInterpreter/Parser/AST.swift b/PascalInterpreter/PascalInterpreter/Parser/AST.swift index f2bb117..6cbffcd 100644 --- a/PascalInterpreter/PascalInterpreter/Parser/AST.swift +++ b/PascalInterpreter/PascalInterpreter/Parser/AST.swift @@ -197,3 +197,13 @@ class IfElse: AST { self.falseExpression = falseExpression } } + +class RepeatUntil: AST { + let statement: AST + let condition: Condition + + init(statement: AST, condition: Condition) { + self.statement = statement + self.condition = condition + } +} diff --git a/PascalInterpreter/PascalInterpreter/Parser/Parser.swift b/PascalInterpreter/PascalInterpreter/Parser/Parser.swift index e119549..d1725f8 100644 --- a/PascalInterpreter/PascalInterpreter/Parser/Parser.swift +++ b/PascalInterpreter/PascalInterpreter/Parser/Parser.swift @@ -276,6 +276,7 @@ public class Parser { | procedure_call | if_else_statement | assignment_statement + | repeat_until | empty */ private func statement() -> AST { @@ -284,6 +285,8 @@ public class Parser { return compoundStatement() case .if: return ifElseStatement() + case .repeat: + return repeatUntilLoop() case .id: if nextToken == .parenthesis(.left) { return functionCall() @@ -295,6 +298,27 @@ public class Parser { } } + /** + Rule: + + repeat_until : REPEAT statement UNTIL condition + */ + private func repeatUntilLoop() -> RepeatUntil { + eat(.repeat) + + var statements: [AST] = [] + + while currentToken != .until { + statements.append(statement()) + if currentToken == .semi { + eat(.semi) + } + } + eat(.until) + let condition = self.condition() + return RepeatUntil(statement: Compound(children: statements), condition: condition) + } + /** Rule: @@ -483,10 +507,10 @@ public class Parser { let result = expr() eat(.parenthesis(.right)) return result - case .constant(.string(let value)): + case let .constant(.string(value)): eat(.constant(.string(value))) return value - case .constant(.boolean(let value)): + case let .constant(.boolean(value)): eat(.constant(.boolean(value))) return value default: diff --git a/PascalInterpreter/PascalInterpreter/Semantic analyzer/Visitor.swift b/PascalInterpreter/PascalInterpreter/Semantic analyzer/Visitor.swift index d94fba9..fcdffa4 100644 --- a/PascalInterpreter/PascalInterpreter/Semantic analyzer/Visitor.swift +++ b/PascalInterpreter/PascalInterpreter/Semantic analyzer/Visitor.swift @@ -27,6 +27,7 @@ protocol Visitor: class { func visit(call: FunctionCall) func visit(condition: Condition) func visit(ifElse: IfElse) + func visit(repeatUntil: RepeatUntil) } extension Visitor { @@ -66,6 +67,8 @@ extension Visitor { visit(condition: condition) case let ifElse as IfElse: visit(ifElse: ifElse) + case let repeatUntil as RepeatUntil: + visit(repeatUntil: repeatUntil) default: fatalError("Unsupported node type \(node)") } @@ -160,4 +163,9 @@ extension Visitor { visit(node: falseExpression) } } + + func visit(repeatUntil: RepeatUntil) { + visit(node: repeatUntil.statement) + visit(node: repeatUntil.condition) + } } From 60b8674941a01ac469756779f64f9daf07365106 Mon Sep 17 00:00:00 2001 From: Igor Kulman Date: Wed, 20 Dec 2017 16:16:34 +0100 Subject: [PATCH 3/6] =?UTF-8?q?=F0=9F=90=9B=20making=20conditions=20wotk?= =?UTF-8?q?=20without=20parenthesis=20as=20it=20should=20be?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Examples/factorial.pas | 2 +- Examples/game.pas | 6 ++--- .../PascalInterpreter/Parser/Parser.swift | 9 +++++-- .../InterpreterTests.swift | 25 +++++++++++++++--- .../PascalInterpreterTests/ParserTests.swift | 26 ++++++++++++++++--- grammar.md | 8 +++++- 6 files changed, 63 insertions(+), 13 deletions(-) diff --git a/Examples/factorial.pas b/Examples/factorial.pas index 0bd67e9..c337dad 100644 --- a/Examples/factorial.pas +++ b/Examples/factorial.pas @@ -3,7 +3,7 @@ function Factorial(number: Integer): Integer; begin -if (number > 1) then +if number > 1 then Factorial := number * Factorial(number-1) else Factorial := 1 diff --git a/Examples/game.pas b/Examples/game.pas index 8f29d88..0a30efb 100644 --- a/Examples/game.pas +++ b/Examples/game.pas @@ -6,11 +6,11 @@ guess := 10; target := random(guess); - Write('Guess a number between 0 and 10:'); + Writeln('Guess a number between 0 and 10:'); Read(guess); repeat - if (guess > target) then + if guess > target then begin Writeln('Too much, try again'); Read(guess); @@ -20,6 +20,6 @@ Writeln('Too low, try again'); Read(guess); end - until (target = guess); + until target = guess; Writeln('You won!') End. \ No newline at end of file diff --git a/PascalInterpreter/PascalInterpreter/Parser/Parser.swift b/PascalInterpreter/PascalInterpreter/Parser/Parser.swift index d1725f8..d4dc676 100644 --- a/PascalInterpreter/PascalInterpreter/Parser/Parser.swift +++ b/PascalInterpreter/PascalInterpreter/Parser/Parser.swift @@ -370,9 +370,12 @@ public class Parser { /** Rule: condition: expr (= | < | >) expr + | LPAREN expr (= | < | >) expr RPAREN */ private func condition() -> Condition { - eat(.parenthesis(.left)) + if currentToken == .parenthesis(.left) { + eat(.parenthesis(.left)) + } let left = expr() var type: ConditionType = .equals switch currentToken { @@ -389,7 +392,9 @@ public class Parser { fatalError("Invalid condition type \(type)") } let right = expr() - eat(.parenthesis(.right)) + if currentToken == .parenthesis(.right) { + eat(.parenthesis(.right)) + } return Condition(type: type, leftSide: left, rightSide: right) } diff --git a/PascalInterpreter/PascalInterpreterTests/InterpreterTests.swift b/PascalInterpreter/PascalInterpreterTests/InterpreterTests.swift index 04aacc7..8fdccd0 100644 --- a/PascalInterpreter/PascalInterpreterTests/InterpreterTests.swift +++ b/PascalInterpreter/PascalInterpreterTests/InterpreterTests.swift @@ -144,7 +144,7 @@ class InterpreterTests: XCTestCase { function Factorial(number: Integer): Integer; begin - if (number > 1) then + if number > 1 then Factorial := number * Factorial(number-1) else Factorial := 1 @@ -172,7 +172,7 @@ class InterpreterTests: XCTestCase { function Factorial(number: Integer): Integer; begin - if (number > 1) then + if number > 1 then Factorial := number * Factorial(number-1) else Factorial := 1 @@ -201,7 +201,7 @@ class InterpreterTests: XCTestCase { function Factorial(number: Integer): Integer; begin - if (number > 1) then + if number > 1 then Factorial := number * Factorial(number-1) else Factorial := 1 @@ -221,4 +221,23 @@ class InterpreterTests: XCTestCase { XCTAssert(boolState == [:]) XCTAssert(stringState == [:]) } + + func testProgramWithRepeatUntil() { + let program = + """ + program Main; + var x: integer; + + begin + x:=0; + repeat + x:=x+1; + until x = 6; + writeln(x); + end. { Main } + """ + + let interpeter = Interpreter(program) + interpeter.interpret() + } } diff --git a/PascalInterpreter/PascalInterpreterTests/ParserTests.swift b/PascalInterpreter/PascalInterpreterTests/ParserTests.swift index 086679f..87a1a8b 100644 --- a/PascalInterpreter/PascalInterpreterTests/ParserTests.swift +++ b/PascalInterpreter/PascalInterpreterTests/ParserTests.swift @@ -276,7 +276,7 @@ class ParserTests: XCTestCase { begin { Main } y := 5; - if (y = 5) then + if y = 5 then x:=2 else x:= 3 @@ -299,7 +299,7 @@ class ParserTests: XCTestCase { begin { Main } y := 5; - if (y = 5) then + if y = 5 then x:=2 end. { Main } """ @@ -361,7 +361,7 @@ class ParserTests: XCTestCase { function Factorial(number: Integer): Integer; begin - if (number > 1) then + if number > 1 then Factorial := number * Factorial(number-1) else Factorial := 1 @@ -404,4 +404,24 @@ class ParserTests: XCTestCase { let node = Program(name: "Main", block: block) XCTAssertEqual(result, node) } + + func testProgramWithRepeatUntil() { + let program = + """ + program Main; + var x: integer; + + begin + x:=0; + repeat + x:=x+1; + until x = 6; + writeln(x); + end. { Main } + """ + + let parser = Parser(program) + let result = parser.parse() + } + } diff --git a/grammar.md b/grammar.md index e759667..888a010 100644 --- a/grammar.md +++ b/grammar.md @@ -23,9 +23,10 @@ statement_list : statement | statement SEMI statement_list statement : compound_statement - | procedure_call + | procedure_call | if_else_statement | assignment_statement + | repeat_until | empty function_call : id LPAREN (factor (factor COLON)* )* RPAREN (COLON type_spec){0,1} @@ -33,6 +34,11 @@ function_call : id LPAREN (factor (factor COLON)* )* RPAREN (COLON type_spec){0 if_else_statement : IF condition statement | IF condition THEN statement ELSE statement +condition : expr (= | < | >) expr + | LPAREN expr (= | < | >) expr RPAREN + +repeat_until : REPEAT statement UNTIL condition + assignment_statement : variable ASSIGN expr empty : From 41e36c06940ca9d8bc2eca0a4f18cf8b303fbbf7 Mon Sep 17 00:00:00 2001 From: Igor Kulman Date: Wed, 20 Dec 2017 16:59:46 +0100 Subject: [PATCH 4/6] =?UTF-8?q?=F0=9F=9A=A8=20tests=20for=20the=20new=20to?= =?UTF-8?q?kens?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../PascalInterpreterTests/LexerTests.swift | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/PascalInterpreter/PascalInterpreterTests/LexerTests.swift b/PascalInterpreter/PascalInterpreterTests/LexerTests.swift index 12eef50..c37f5de 100644 --- a/PascalInterpreter/PascalInterpreterTests/LexerTests.swift +++ b/PascalInterpreter/PascalInterpreterTests/LexerTests.swift @@ -434,4 +434,19 @@ class LexerTests: XCTestCase { XCTAssert(lexer.getNextToken() == .constant(.string("Test string"))) XCTAssert(lexer.getNextToken() == .eof) } + + func testLoopTokens() { + let lexer = Lexer("repeat x:= x+1 until x=5") + XCTAssert(lexer.getNextToken() == .repeat) + XCTAssert(lexer.getNextToken() == .id("x")) + XCTAssert(lexer.getNextToken() == .assign) + XCTAssert(lexer.getNextToken() == .id("x")) + XCTAssert(lexer.getNextToken() == .operation(.plus)) + XCTAssert(lexer.getNextToken() == .constant(.integer(1))) + XCTAssert(lexer.getNextToken() == .until) + XCTAssert(lexer.getNextToken() == .id("x")) + XCTAssert(lexer.getNextToken() == .equals) + XCTAssert(lexer.getNextToken() == .constant(.integer(5))) + XCTAssert(lexer.getNextToken() == .eof) + } } From 70b5a24461e2ea5d56bc0662576e0f764142b482 Mon Sep 17 00:00:00 2001 From: Igor Kulman Date: Wed, 20 Dec 2017 17:11:52 +0100 Subject: [PATCH 5/6] =?UTF-8?q?=F0=9F=8C=90=20README=20updated?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 4ccb7d7..fe715a1 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,8 @@ Simple Swift interpreter for the Pascal language inspired by the [Let’s Build ![Playground](Images/cli.gif) +There are a few sample Pascal programs in the [Examples directory](Examples), like a simple [number guessing game](Examples/game.pas) and a [factorial computation](Examples/factorial.pas). + ## Scructure ### Lexer @@ -51,7 +53,7 @@ var result: integer; function Factorial(number: Integer): Integer; begin -if (number > 1) then +if number > 1 then Factorial := number * Factorial(number-1) else Factorial := 1 From 00bfac2781b2f34725ac8c7f2287a5424fe829ec Mon Sep 17 00:00:00 2001 From: Igor Kulman Date: Wed, 20 Dec 2017 18:23:35 +0100 Subject: [PATCH 6/6] =?UTF-8?q?=F0=9F=9A=A8=20complete=20tests=20for=20rep?= =?UTF-8?q?eat-until?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PascalInterpreter/PascalInterpreter/Parser/Parser.swift | 4 ++-- .../PascalInterpreterTests/InterpreterTests.swift | 9 +++++++-- .../PascalInterpreterTests/ParserTests.swift | 5 +++++ .../PascalInterpreterTests/XCTestCase+FatalError.swift | 3 +++ 4 files changed, 17 insertions(+), 4 deletions(-) diff --git a/PascalInterpreter/PascalInterpreter/Parser/Parser.swift b/PascalInterpreter/PascalInterpreter/Parser/Parser.swift index d4dc676..2b381bb 100644 --- a/PascalInterpreter/PascalInterpreter/Parser/Parser.swift +++ b/PascalInterpreter/PascalInterpreter/Parser/Parser.swift @@ -300,7 +300,7 @@ public class Parser { /** Rule: - + repeat_until : REPEAT statement UNTIL condition */ private func repeatUntilLoop() -> RepeatUntil { @@ -316,7 +316,7 @@ public class Parser { } eat(.until) let condition = self.condition() - return RepeatUntil(statement: Compound(children: statements), condition: condition) + return RepeatUntil(statement: statements.count == 1 ? statements[0] : Compound(children: statements), condition: condition) } /** diff --git a/PascalInterpreter/PascalInterpreterTests/InterpreterTests.swift b/PascalInterpreter/PascalInterpreterTests/InterpreterTests.swift index 8fdccd0..6f42572 100644 --- a/PascalInterpreter/PascalInterpreterTests/InterpreterTests.swift +++ b/PascalInterpreter/PascalInterpreterTests/InterpreterTests.swift @@ -195,7 +195,7 @@ class InterpreterTests: XCTestCase { func testProgramWithRecursiveFunctionsAndParameterTheSameName() { let program = - """ + """ program Main; var number, result: integer; @@ -224,7 +224,7 @@ class InterpreterTests: XCTestCase { func testProgramWithRepeatUntil() { let program = - """ + """ program Main; var x: integer; @@ -239,5 +239,10 @@ 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 == [:]) } } diff --git a/PascalInterpreter/PascalInterpreterTests/ParserTests.swift b/PascalInterpreter/PascalInterpreterTests/ParserTests.swift index 87a1a8b..93762a2 100644 --- a/PascalInterpreter/PascalInterpreterTests/ParserTests.swift +++ b/PascalInterpreter/PascalInterpreterTests/ParserTests.swift @@ -422,6 +422,11 @@ class ParserTests: XCTestCase { let parser = Parser(program) let result = parser.parse() + let xDec = VariableDeclaration(variable: Variable(name: "x"), type: VariableType(type: .integer)) + let compound = Compound(children: [Assignment(left: Variable(name: "x"), right: Number.integer(0)), RepeatUntil(statement: Assignment(left: Variable(name: "x"), right: BinaryOperation(left: Variable(name: "x"), operation: BinaryOperationType.plus, right: Number.integer(1))), condition: Condition(type: .equals, leftSide: Variable(name: "x"), rightSide: Number.integer(6))), FunctionCall(name: "writeln", actualParameters: [Variable(name: "x")]), NoOp()]) + let block = Block(declarations: [xDec], compound: compound) + let node = Program(name: "Main", block: block) + XCTAssertEqual(result, node) } } diff --git a/PascalInterpreter/PascalInterpreterTests/XCTestCase+FatalError.swift b/PascalInterpreter/PascalInterpreterTests/XCTestCase+FatalError.swift index 06ef4a7..71144a4 100644 --- a/PascalInterpreter/PascalInterpreterTests/XCTestCase+FatalError.swift +++ b/PascalInterpreter/PascalInterpreterTests/XCTestCase+FatalError.swift @@ -134,6 +134,9 @@ func XCTAssertEqual(_ left: AST, _ right: AST) { XCTAssert(left == right) case let (left as Bool, right as Bool): XCTAssert(left == right) + case let (left as RepeatUntil, right as RepeatUntil): + XCTAssertEqual(left.condition, right.condition) + XCTAssertEqual(left.statement, right.statement) default: XCTFail("\(left) and \(right) are not equal") }