Skip to content

Commit b282e73

Browse files
authored
Merge pull request #11 from igorkulman/rework
Calling procedures with parameters
2 parents e8afb22 + 5b63c9b commit b282e73

File tree

23 files changed

+1221
-512
lines changed

23 files changed

+1221
-512
lines changed

Images/lexer.png

89.3 KB
Loading

Images/parser.png

147 KB
Loading
File renamed without changes.

PascalInterpreter/PascalInterpreter.xcodeproj/project.pbxproj

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,10 @@
2828
F300A0EF1FDDB77000E5894D /* XCTestCase+FatalError.swift in Sources */ = {isa = PBXBuildFile; fileRef = F300A0EA1FDDB77000E5894D /* XCTestCase+FatalError.swift */; };
2929
F300A0F01FDDB77000E5894D /* LexerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F300A0EB1FDDB77000E5894D /* LexerTests.swift */; };
3030
F300A0F11FDDB77000E5894D /* InterpreterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F300A0EC1FDDB77000E5894D /* InterpreterTests.swift */; };
31-
F300A0F21FDDB77000E5894D /* SemanticAnalyzer.swift in Sources */ = {isa = PBXBuildFile; fileRef = F300A0ED1FDDB77000E5894D /* SemanticAnalyzer.swift */; };
31+
F300A0F21FDDB77000E5894D /* SemanticAnalyzerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F300A0ED1FDDB77000E5894D /* SemanticAnalyzerTests.swift */; };
32+
F34109A81FE2683900271A57 /* Visitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = F34109A71FE2683900271A57 /* Visitor.swift */; };
33+
F3EF28D01FE12EBF001F36AB /* Frame.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3EF28CF1FE12EBF001F36AB /* Frame.swift */; };
34+
F3EF28D21FE12F4D001F36AB /* Stack.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3EF28D11FE12F4D001F36AB /* Stack.swift */; };
3235
/* End PBXBuildFile section */
3336

3437
/* Begin PBXContainerItemProxy section */
@@ -66,7 +69,10 @@
6669
F300A0EA1FDDB77000E5894D /* XCTestCase+FatalError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "XCTestCase+FatalError.swift"; sourceTree = "<group>"; };
6770
F300A0EB1FDDB77000E5894D /* LexerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LexerTests.swift; sourceTree = "<group>"; };
6871
F300A0EC1FDDB77000E5894D /* InterpreterTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InterpreterTests.swift; sourceTree = "<group>"; };
69-
F300A0ED1FDDB77000E5894D /* SemanticAnalyzer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SemanticAnalyzer.swift; sourceTree = "<group>"; };
72+
F300A0ED1FDDB77000E5894D /* SemanticAnalyzerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SemanticAnalyzerTests.swift; sourceTree = "<group>"; };
73+
F34109A71FE2683900271A57 /* Visitor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Visitor.swift; sourceTree = "<group>"; };
74+
F3EF28CF1FE12EBF001F36AB /* Frame.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Frame.swift; sourceTree = "<group>"; };
75+
F3EF28D11FE12F4D001F36AB /* Stack.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Stack.swift; sourceTree = "<group>"; };
7076
/* End PBXFileReference section */
7177

7278
/* Begin PBXFrameworksBuildPhase section */
@@ -124,7 +130,7 @@
124130
F300A0EC1FDDB77000E5894D /* InterpreterTests.swift */,
125131
F300A0EB1FDDB77000E5894D /* LexerTests.swift */,
126132
F300A0E91FDDB77000E5894D /* ParserTests.swift */,
127-
F300A0ED1FDDB77000E5894D /* SemanticAnalyzer.swift */,
133+
F300A0ED1FDDB77000E5894D /* SemanticAnalyzerTests.swift */,
128134
F300A0F31FDDB77300E5894D /* Supporting files */,
129135
);
130136
path = PascalInterpreterTests;
@@ -134,8 +140,10 @@
134140
isa = PBXGroup;
135141
children = (
136142
F300A0C51FDDB6BE00E5894D /* Arithmetics.swift */,
143+
F3EF28CF1FE12EBF001F36AB /* Frame.swift */,
137144
F300A0C61FDDB6BE00E5894D /* Interpreter.swift */,
138145
F300A0C41FDDB6BD00E5894D /* Result.swift */,
146+
F3EF28D11FE12F4D001F36AB /* Stack.swift */,
139147
);
140148
path = Interpreter;
141149
sourceTree = "<group>";
@@ -178,6 +186,7 @@
178186
F300A0E21FDDB74700E5894D /* Symbol.swift */,
179187
F300A0E11FDDB74700E5894D /* Symbol+Extensions.swift */,
180188
F300A0E31FDDB74700E5894D /* SymbolTable.swift */,
189+
F34109A71FE2683900271A57 /* Visitor.swift */,
181190
);
182191
path = "Semantic analyzer";
183192
sourceTree = "<group>";
@@ -337,9 +346,12 @@
337346
buildActionMask = 2147483647;
338347
files = (
339348
F300A0C91FDDB6BE00E5894D /* Interpreter.swift in Sources */,
349+
F3EF28D01FE12EBF001F36AB /* Frame.swift in Sources */,
340350
F300A0C71FDDB6BE00E5894D /* Result.swift in Sources */,
351+
F3EF28D21FE12F4D001F36AB /* Stack.swift in Sources */,
341352
F300A0E71FDDB74800E5894D /* SymbolTable.swift in Sources */,
342353
F300A0D81FDDB6F400E5894D /* AST+Extensions.swift in Sources */,
354+
F34109A81FE2683900271A57 /* Visitor.swift in Sources */,
343355
F300A0D11FDDB6E400E5894D /* Lexer.swift in Sources */,
344356
F300A0D91FDDB6F400E5894D /* AST.swift in Sources */,
345357
F300A0E61FDDB74800E5894D /* Symbol.swift in Sources */,
@@ -361,7 +373,7 @@
361373
F300A0F11FDDB77000E5894D /* InterpreterTests.swift in Sources */,
362374
F300A0EF1FDDB77000E5894D /* XCTestCase+FatalError.swift in Sources */,
363375
F300A0F01FDDB77000E5894D /* LexerTests.swift in Sources */,
364-
F300A0F21FDDB77000E5894D /* SemanticAnalyzer.swift in Sources */,
376+
F300A0F21FDDB77000E5894D /* SemanticAnalyzerTests.swift in Sources */,
365377
F300A0EE1FDDB77000E5894D /* ParserTests.swift in Sources */,
366378
);
367379
runOnlyForDeploymentPostprocessing = 0;
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
//
2+
// Frame.swift
3+
// PascalInterpreter
4+
//
5+
// Created by Igor Kulman on 13/12/2017.
6+
// Copyright © 2017 Igor Kulman. All rights reserved.
7+
//
8+
9+
import Foundation
10+
11+
class Frame {
12+
var integerMemory: [String: Int] = [:]
13+
var realMemory: [String: Double] = [:]
14+
let scope: ScopedSymbolTable
15+
let previousFrame: Frame?
16+
17+
init(scope: ScopedSymbolTable, previousFrame: Frame?) {
18+
self.scope = scope
19+
self.previousFrame = previousFrame
20+
}
21+
22+
func set(variable: String, value: Number) {
23+
// variable define in current scole (procedure declataion, etc)
24+
if let symbol = scope.lookup(variable, currentScopeOnly: true),
25+
let variableSymbol = symbol as? VariableSymbol,
26+
let type = variableSymbol.type as? BuiltInTypeSymbol {
27+
28+
switch type {
29+
case .integer:
30+
switch value {
31+
case .integer(let value):
32+
integerMemory[variable] = value
33+
case .real:
34+
fatalError("Cannot assign Real value to Int variable \(variable)")
35+
}
36+
case .real:
37+
switch value {
38+
case .integer(let value):
39+
realMemory[variable] = Double(value)
40+
case .real(let value):
41+
realMemory[variable] = value
42+
}
43+
}
44+
return
45+
}
46+
47+
// previous scope, eg global
48+
previousFrame!.set(variable: variable, value: value)
49+
}
50+
51+
func get(variable: String) -> Number {
52+
// variable define in current scole (procedure declataion, etc)
53+
if let symbol = scope.lookup(variable, currentScopeOnly: true),
54+
let variableSymbol = symbol as? VariableSymbol,
55+
let type = variableSymbol.type as? BuiltInTypeSymbol {
56+
57+
switch type {
58+
case .integer:
59+
return .integer(integerMemory[variable]!)
60+
case .real:
61+
return .real(realMemory[variable]!)
62+
}
63+
}
64+
65+
// previous scope, eg global
66+
return previousFrame!.get(variable: variable)
67+
}
68+
}

PascalInterpreter/PascalInterpreter/Interpreter/Interpreter.swift

Lines changed: 121 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -9,127 +9,150 @@
99
import Foundation
1010

1111
public class Interpreter {
12-
private var integerMemory: [String: Int] = [:]
13-
private var realMemory: [String: Double] = [:]
12+
private var callStack = Stack<Frame>()
1413
private let tree: AST
14+
private let scopes: [String: ScopedSymbolTable]
1515

1616
public init(_ text: String) {
1717
let parser = Parser(text)
1818
tree = parser.parse()
1919
let semanticAnalyzer = SemanticAnalyzer()
20-
semanticAnalyzer.analyze(node: tree)
20+
scopes = semanticAnalyzer.analyze(node: tree)
2121
}
2222

23-
@discardableResult private func eval(_ node: AST) -> Number? {
23+
@discardableResult private func eval(node: AST) -> Number? {
2424
switch node {
25-
case let .number(value):
26-
return value
27-
case let .unaryOperation(operation: operation, child: child):
28-
guard let result = eval(child) else {
29-
fatalError("Cannot use unary \(operation) on non number")
30-
}
31-
switch operation {
32-
case .plus:
33-
return +result
34-
case .minus:
35-
return -result
36-
}
37-
case let .binaryOperation(left: left, operation: operation, right: right):
38-
guard let leftResult = eval(left), let rightResult = eval(right) else {
39-
fatalError("Cannot use binary \(operation) on non numbers")
40-
}
41-
switch operation {
42-
case .plus:
43-
return leftResult + rightResult
44-
case .minus:
45-
return leftResult - rightResult
46-
case .mult:
47-
return leftResult * rightResult
48-
case .integerDiv:
49-
return leftResult rightResult
50-
case .floatDiv:
51-
return leftResult / rightResult
52-
}
53-
case let .compound(children):
54-
for chid in children {
55-
eval(chid)
56-
}
25+
case let number as Number:
26+
return eval(number: number)
27+
case let unaryOperation as UnaryOperation:
28+
return eval(unaryOperation: unaryOperation)
29+
case let binaryOperation as BinaryOperation:
30+
return eval(binaryOperation: binaryOperation)
31+
case let compound as Compound:
32+
return eval(compound: compound)
33+
case let assignment as Assignment:
34+
return eval(assignment: assignment)
35+
case let variable as Variable:
36+
return eval(variable: variable)
37+
case let block as Block:
38+
return eval(block: block)
39+
case let program as Program:
40+
return eval(program: program)
41+
case let call as ProcedureCall:
42+
return eval(call: call)
43+
default:
5744
return nil
58-
case let .assignment(left, right):
59-
guard case let .variable(name) = left else {
60-
fatalError("Assignment left side is not a variable, check Parser implementation")
61-
}
45+
}
46+
}
6247

63-
if integerMemory.keys.contains(name) {
64-
switch eval(right)! {
65-
case let .integer(value):
66-
integerMemory[name] = value
67-
return nil
68-
case .real:
69-
fatalError("Cannot assign Real value to Int variable \(name)")
70-
}
71-
}
48+
func eval(number: Number) -> Number? {
49+
return number
50+
}
7251

73-
if realMemory.keys.contains(name) {
74-
switch eval(right)! {
75-
case let .integer(value):
76-
realMemory[name] = Double(value)
77-
return nil
78-
case let .real(value):
79-
realMemory[name] = value
80-
return nil
81-
}
82-
}
52+
func eval(unaryOperation: UnaryOperation) -> Number? {
53+
guard let result = eval(node: unaryOperation.operand) else {
54+
fatalError("Cannot use unary \(unaryOperation.operation) on non number")
55+
}
8356

84-
fatalError("Variable \(name) not found, check the SemanticAnalyzer implementation")
85-
case let .variable(name):
86-
if let value = integerMemory[name] {
87-
return .integer(value)
88-
}
89-
if let value = realMemory[name] {
90-
return .real(value)
91-
}
92-
fatalError("Variable \(name) not found, check the SemanticAnalyzer implementation")
93-
case .noOp:
94-
return nil
95-
case let .block(declarations, compound):
96-
for declaration in declarations {
97-
eval(declaration)
98-
}
99-
return eval(compound)
100-
case let .variableDeclaration(name: name, type: type):
101-
guard case let .type(type) = type, case let .variable(name: name) = name else {
102-
fatalError("Invalid variable declaration, check Parser implementation")
103-
}
104-
switch type {
105-
case .integer:
106-
integerMemory[name] = 0
107-
case .real:
108-
realMemory[name] = 0
57+
switch unaryOperation.operation {
58+
case .plus:
59+
return +result
60+
case .minus:
61+
return -result
62+
}
63+
}
64+
func eval(binaryOperation: BinaryOperation) -> Number? {
65+
guard let leftResult = eval(node: binaryOperation.left), let rightResult = eval(node: binaryOperation.right) else {
66+
fatalError("Cannot use binary \(binaryOperation.operation) on non numbers")
67+
}
68+
69+
switch binaryOperation.operation {
70+
case .plus:
71+
return leftResult + rightResult
72+
case .minus:
73+
return leftResult - rightResult
74+
case .mult:
75+
return leftResult * rightResult
76+
case .integerDiv:
77+
return leftResult rightResult
78+
case .floatDiv:
79+
return leftResult / rightResult
80+
}
81+
}
82+
83+
func eval(compound: Compound) -> Number? {
84+
for child in compound.children {
85+
eval(node: child)
86+
}
87+
return nil
88+
}
89+
90+
func eval(assignment: Assignment) -> Number? {
91+
guard let currentFrame = callStack.peek() else {
92+
fatalError("No call stack frame")
93+
}
94+
95+
currentFrame.set(variable: assignment.left.name, value: eval(node: assignment.right)!)
96+
return nil
97+
}
98+
99+
func eval(variable: Variable) -> Number? {
100+
guard let currentFrame = callStack.peek() else {
101+
fatalError("No call stack frame")
102+
}
103+
104+
return currentFrame.get(variable: variable.name)
105+
}
106+
107+
func eval(block: Block) -> Number? {
108+
for declaration in block.declarations {
109+
eval(node: declaration)
110+
}
111+
112+
return eval(node: block.compound)
113+
}
114+
115+
func eval(program: Program) -> Number? {
116+
let frame = Frame(scope: scopes["global"]!, previousFrame: nil)
117+
callStack.push(frame)
118+
return eval(node: program.block)
119+
}
120+
121+
func eval(call: ProcedureCall) -> Number? {
122+
let current = callStack.peek()!
123+
let frame = Frame(scope: scopes[call.name]!, previousFrame: current)
124+
callStack.push(frame)
125+
callProcedure(procedure: call.name, params: call.actualParameters, frame: frame)
126+
callStack.pop()
127+
return nil
128+
}
129+
130+
private func callProcedure(procedure: String, params: [Number], frame: Frame) {
131+
guard let symbol = frame.scope.lookup(procedure), let procedureSymbol = symbol as? ProcedureSymbol else {
132+
fatalError("Symbol(procedure) not found '\(procedure)'")
133+
}
134+
135+
if procedureSymbol.params.count > 0 {
136+
137+
for i in 0 ... procedureSymbol.params.count - 1 {
138+
frame.set(variable: procedureSymbol.params[i].name, value: params[i])
109139
}
110-
return nil
111-
case .type:
112-
return nil
113-
case let .program(_, block):
114-
return eval(block)
115-
case .procedure:
116-
return nil
117-
case .param:
118-
return nil
119140
}
141+
142+
eval(node: procedureSymbol.body.block)
120143
}
121144

122145
public func interpret() {
123-
eval(tree)
146+
eval(node: tree)
124147
}
125148

126149
func getState() -> ([String: Int], [String: Double]) {
127-
return (integerMemory, realMemory)
150+
return (callStack.peek()!.integerMemory, callStack.peek()!.realMemory)
128151
}
129152

130153
public func printState() {
131-
print("Final interpreter memory state:")
132-
print("Int: \(integerMemory)")
133-
print("Real: \(realMemory)")
154+
print("Final interpreter memory state (\(callStack.peek()!.scope.name)):")
155+
print("Int: \(callStack.peek()!.integerMemory)")
156+
print("Real: \(callStack.peek()!.realMemory)")
134157
}
135158
}

0 commit comments

Comments
 (0)