Skip to content

Commit e8afb22

Browse files
authored
Merge pull request #10 from igorkulman/nested-scope
Nested scope
2 parents abe104e + 1e5f6da commit e8afb22

File tree

14 files changed

+290
-101
lines changed

14 files changed

+290
-101
lines changed

PascalInterpreter/.swiftlint.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,4 @@ disabled_rules:
66
- line_length
77
- function_body_length
88
- type_body_length
9-
9+
- file_length

PascalInterpreter/PascalInterpreter.xcodeproj/project.pbxproj

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -174,10 +174,10 @@
174174
F300A0E01FDDB74700E5894D /* Semantic analyzer */ = {
175175
isa = PBXGroup;
176176
children = (
177-
F300A0E11FDDB74700E5894D /* Symbol+Extensions.swift */,
177+
F300A0E41FDDB74700E5894D /* SemanticAnalyzer.swift */,
178178
F300A0E21FDDB74700E5894D /* Symbol.swift */,
179+
F300A0E11FDDB74700E5894D /* Symbol+Extensions.swift */,
179180
F300A0E31FDDB74700E5894D /* SymbolTable.swift */,
180-
F300A0E41FDDB74700E5894D /* SemanticAnalyzer.swift */,
181181
);
182182
path = "Semantic analyzer";
183183
sourceTree = "<group>";
@@ -209,12 +209,12 @@
209209
isa = PBXNativeTarget;
210210
buildConfigurationList = F300A0AF1FDDB63F00E5894D /* Build configuration list for PBXNativeTarget "PascalInterpreter" */;
211211
buildPhases = (
212+
F3B1C9101FDECB660047C9FA /* Swiftlint */,
213+
F3B1C9111FDECC4A0047C9FA /* Swiftformat */,
212214
F300A0961FDDB63F00E5894D /* Sources */,
213215
F300A0971FDDB63F00E5894D /* Frameworks */,
214216
F300A0981FDDB63F00E5894D /* Headers */,
215217
F300A0991FDDB63F00E5894D /* Resources */,
216-
F3B1C9101FDECB660047C9FA /* Swiftlint */,
217-
F3B1C9111FDECC4A0047C9FA /* Swiftformat */,
218218
);
219219
buildRules = (
220220
);
@@ -313,7 +313,7 @@
313313
);
314314
runOnlyForDeploymentPostprocessing = 0;
315315
shellPath = /bin/sh;
316-
shellScript = "if which swiftlint >/dev/null; then\nswiftlint aurocorrect\nswiftlint\nelse\necho \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi";
316+
shellScript = "if which swiftlint >/dev/null; then\nswiftlint autocorrect\nswiftlint\nelse\necho \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi";
317317
};
318318
F3B1C9111FDECC4A0047C9FA /* Swiftformat */ = {
319319
isa = PBXShellScriptBuildPhase;

PascalInterpreter/PascalInterpreter/Interpreter/Interpreter.swift

Lines changed: 29 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,13 @@ import Foundation
1111
public class Interpreter {
1212
private var integerMemory: [String: Int] = [:]
1313
private var realMemory: [String: Double] = [:]
14-
private let symbolTable: SymbolTable
1514
private let tree: AST
1615

1716
public init(_ text: String) {
1817
let parser = Parser(text)
1918
tree = parser.parse()
2019
let semanticAnalyzer = SemanticAnalyzer()
21-
symbolTable = semanticAnalyzer.build(node: tree)
20+
semanticAnalyzer.analyze(node: tree)
2221
}
2322

2423
@discardableResult private func eval(_ node: AST) -> Number? {
@@ -58,58 +57,65 @@ public class Interpreter {
5857
return nil
5958
case let .assignment(left, right):
6059
guard case let .variable(name) = left else {
61-
fatalError("Assignment left side is not a variable")
60+
fatalError("Assignment left side is not a variable, check Parser implementation")
6261
}
6362

64-
guard let symbol = symbolTable.lookup(name), case let .variable(name: _, type: type) = symbol else {
65-
fatalError("Variable \(name) not in the symbol table")
66-
}
67-
68-
switch type {
69-
case .integer:
63+
if integerMemory.keys.contains(name) {
7064
switch eval(right)! {
7165
case let .integer(value):
7266
integerMemory[name] = value
67+
return nil
7368
case .real:
74-
fatalError("Cannot assign real value to Int variable \(name)")
69+
fatalError("Cannot assign Real value to Int variable \(name)")
7570
}
76-
return nil
77-
case .real:
71+
}
72+
73+
if realMemory.keys.contains(name) {
7874
switch eval(right)! {
7975
case let .integer(value):
8076
realMemory[name] = Double(value)
77+
return nil
8178
case let .real(value):
8279
realMemory[name] = value
80+
return nil
8381
}
84-
return nil
8582
}
83+
84+
fatalError("Variable \(name) not found, check the SemanticAnalyzer implementation")
8685
case let .variable(name):
87-
guard let symbol = symbolTable.lookup(name), case let .variable(name: _, type: type) = symbol else {
88-
fatalError("Variable \(name) not in the symbol table")
86+
if let value = integerMemory[name] {
87+
return .integer(value)
8988
}
90-
91-
switch type {
92-
case .integer:
93-
return .integer(integerMemory[name]!)
94-
case .real:
95-
return .real(realMemory[name]!)
89+
if let value = realMemory[name] {
90+
return .real(value)
9691
}
92+
fatalError("Variable \(name) not found, check the SemanticAnalyzer implementation")
9793
case .noOp:
9894
return nil
9995
case let .block(declarations, compound):
10096
for declaration in declarations {
10197
eval(declaration)
10298
}
10399
return eval(compound)
104-
case .variableDeclaration:
105-
// process by the symbol table
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
109+
}
106110
return nil
107111
case .type:
108112
return nil
109113
case let .program(_, block):
110114
return eval(block)
111115
case .procedure:
112116
return nil
117+
case .param:
118+
return nil
113119
}
114120
}
115121

PascalInterpreter/PascalInterpreter/Parser/AST+Extensions.swift

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@ extension AST: Equatable {
4343
return leftName == rightName && leftType == rightType
4444
case let (.program(name: leftName, block: leftBlock), .program(name: rightName, block: rightBlock)):
4545
return leftName == rightName && leftBlock == rightBlock
46-
case let (.procedure(name: leftName, block: leftBlock), .procedure(name: rightName, block: rightBlock)):
47-
return leftName == rightName && leftBlock == rightBlock
46+
case let (.procedure(name: leftName, params: leftParams, block: leftBlock), .procedure(name: rightName, params: rightParams, block: rightBlock)):
47+
return leftName == rightName && leftBlock == rightBlock && leftParams == rightParams
4848
default:
4949
return false
5050
}
@@ -123,8 +123,12 @@ extension AST {
123123
return []
124124
case let .program(_, block):
125125
return [block]
126-
case let .procedure(name: _, block: block):
127-
return [block]
126+
case let .procedure(name: _, params: params, block: block):
127+
var children = params
128+
children.append(block)
129+
return children
130+
case let .param(name: name, type: type):
131+
return [.variable(name), type]
128132
}
129133
}
130134

@@ -152,8 +156,10 @@ extension AST {
152156
return type.description
153157
case let .program(name, _):
154158
return name
155-
case let .procedure(name, _):
159+
case let .procedure(name, _, _):
156160
return name
161+
case .param:
162+
return "param"
157163
}
158164
}
159165

PascalInterpreter/PascalInterpreter/Parser/AST.swift

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,17 +26,18 @@ public enum Number {
2626
case real(Double)
2727
}
2828

29-
public enum AST {
29+
public indirect enum AST {
3030
case number(Number)
31-
indirect case unaryOperation(operation: UnaryOperation, child: AST)
32-
indirect case binaryOperation(left: AST, operation: BinaryOperation, right: AST)
33-
indirect case compound(children: [AST])
34-
indirect case assignment(left: AST, right: AST)
31+
case unaryOperation(operation: UnaryOperation, child: AST)
32+
case binaryOperation(left: AST, operation: BinaryOperation, right: AST)
33+
case compound(children: [AST])
34+
case assignment(left: AST, right: AST)
3535
case variable(String)
3636
case noOp
37-
indirect case block(declarations: [AST], compound: AST)
38-
indirect case variableDeclaration(name: AST, type: AST)
37+
case block(declarations: [AST], compound: AST)
38+
case variableDeclaration(name: AST, type: AST)
3939
case type(Type)
40-
indirect case program(name: String, block: AST)
41-
indirect case procedure(name: String, block: AST)
40+
case program(name: String, block: AST)
41+
case procedure(name: String, params: [AST], block: AST)
42+
case param(name: String, type: AST)
4243
}

PascalInterpreter/PascalInterpreter/Parser/Parser.swift

Lines changed: 60 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ public class Parser {
6767

6868
/**
6969
declarations : VAR (variable_declaration SEMI)+
70-
| (PROCEDURE ID SEMI block SEMI)*
70+
| (PROCEDURE ID (LPAREN formal_parameter_list RPAREN)? SEMI block SEMI)*
7171
| empty
7272
*/
7373
private func declarations() -> [AST] {
@@ -85,19 +85,69 @@ public class Parser {
8585
while currentToken == .procedure {
8686
eat(.procedure)
8787
guard case let .id(name) = currentToken else {
88-
fatalError("Procedure name expected")
88+
fatalError("Procedure name expected, got \(currentToken)")
8989
}
9090
eat(.id(name))
91+
92+
var params: [AST] = []
93+
if currentToken == .parenthesis(.left) {
94+
eat(.parenthesis(.left))
95+
params = formalParameterList()
96+
eat(.parenthesis(.right))
97+
}
98+
9199
eat(.semi)
92100
let body = block()
93-
let procedure = AST.procedure(name: name, block: body)
101+
let procedure = AST.procedure(name: name, params: params, block: body)
94102
declarations.append(procedure)
95103
eat(.semi)
96104
}
97105

98106
return declarations
99107
}
100108

109+
/**
110+
formal_parameter_list : formal_parameters
111+
| formal_parameters SEMI formal_parameter_list
112+
*/
113+
private func formalParameterList() -> [AST] {
114+
guard case .id = currentToken else {
115+
return [] //procedure without parameters
116+
}
117+
118+
var parameters = formalParameters()
119+
while currentToken == .semi {
120+
eat(.semi)
121+
parameters.append(contentsOf: formalParameterList())
122+
}
123+
return parameters
124+
}
125+
126+
/**
127+
formal_parameters : ID (COMMA ID)* COLON type_spec
128+
*/
129+
private func formalParameters() -> [AST] {
130+
guard case let .id(name) = currentToken else {
131+
fatalError("Parameter name expected, got \(currentToken)")
132+
}
133+
134+
var parameters = [name]
135+
136+
eat(.id(name))
137+
while currentToken == .coma {
138+
eat(.coma)
139+
guard case let .id(name) = currentToken else {
140+
fatalError("Parameter name expected, got \(currentToken)")
141+
}
142+
eat(.id(name))
143+
parameters.append(name)
144+
}
145+
146+
eat(.colon)
147+
let type = typeSpec()
148+
return parameters.map({ .param(name: $0, type: type) })
149+
}
150+
101151
/**
102152
variable_declaration : ID (COMMA ID)* COLON type_spec
103153
*/
@@ -317,9 +367,15 @@ public class Parser {
317367

318368
block : declarations compound_statement
319369

320-
declarations : VAR (variable_declaration SEMI)+
370+
declarations : (VAR (variable_declaration SEMI)+)*
371+
| (PROCEDURE ID (LPAREN formal_parameter_list RPAREN)? SEMI block SEMI)*
321372
| empty
322373

374+
formal_parameter_list : formal_parameters
375+
| formal_parameters SEMI formal_parameter_list
376+
377+
formal_parameters : ID (COMMA ID)* COLON type_spec
378+
323379
variable_declaration : ID (COMMA ID)* COLON type_spec
324380

325381
type_spec : INTEGER

0 commit comments

Comments
 (0)