Skip to content

Commit 689b626

Browse files
authored
Merge pull request #16 from igorkulman/functions
Functions
2 parents a4529dd + b5cd92d commit 689b626

File tree

21 files changed

+263
-53
lines changed

21 files changed

+263
-53
lines changed

Images/playground.png

87.5 KB
Loading

PascalInterpreter/PascalInterpreter/Interpreter/Frame.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,20 @@ class Frame {
1414
var booleanMemory: [String: Bool] = [:]
1515
let scope: ScopedSymbolTable
1616
let previousFrame: Frame?
17+
var returnValue: Value?
1718

1819
init(scope: ScopedSymbolTable, previousFrame: Frame?) {
1920
self.scope = scope
2021
self.previousFrame = previousFrame
2122
}
2223

2324
func set(variable: String, value: Value) {
25+
// setting function return value
26+
if variable == scope.name && scope.level > 1 {
27+
returnValue = value
28+
return
29+
}
30+
2431
// variable define in current scole (procedure declataion, etc)
2532
if let symbol = scope.lookup(variable, currentScopeOnly: true),
2633
let variableSymbol = symbol as? VariableSymbol,

PascalInterpreter/PascalInterpreter/Interpreter/Interpreter.swift

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ public class Interpreter {
3838
return eval(block: block)
3939
case let program as Program:
4040
return eval(program: program)
41-
case let call as ProcedureCall:
41+
case let call as FunctionCall:
4242
return eval(call: call)
4343
case let condition as Condition:
4444
return eval(condition: condition)
@@ -122,14 +122,14 @@ public class Interpreter {
122122
return eval(node: program.block)
123123
}
124124

125-
func eval(call: ProcedureCall) -> Value? {
125+
func eval(call: FunctionCall) -> Value? {
126126
let current = callStack.peek()!
127127
let newScope = current.scope.level == 1 ? scopes[call.name]! : ScopedSymbolTable(name: call.name, level: scopes[call.name]!.level + 1, enclosingScope: scopes[call.name]!)
128128
let frame = Frame(scope: newScope, previousFrame: current)
129129
callStack.push(frame)
130-
callProcedure(procedure: call.name, params: call.actualParameters, frame: frame)
130+
let result = callFunction(function: call.name, params: call.actualParameters, frame: frame)
131131
callStack.pop()
132-
return nil
132+
return result
133133
}
134134

135135
func eval(condition: Condition) -> Value {
@@ -164,9 +164,9 @@ public class Interpreter {
164164
}
165165
}
166166

167-
private func callProcedure(procedure: String, params: [AST], frame: Frame) {
168-
guard let symbol = frame.scope.lookup(procedure), let procedureSymbol = symbol as? ProcedureSymbol else {
169-
fatalError("Symbol(procedure) not found '\(procedure)'")
167+
private func callFunction(function: String, params: [AST], frame: Frame) -> Value? {
168+
guard let symbol = frame.scope.lookup(function), let procedureSymbol = symbol as? ProcedureSymbol else {
169+
fatalError("Symbol(procedure) not found '\(function)'")
170170
}
171171

172172
if procedureSymbol.params.count > 0 {
@@ -180,6 +180,7 @@ public class Interpreter {
180180
}
181181

182182
eval(node: procedureSymbol.body.block)
183+
return frame.returnValue
183184
}
184185

185186
public func interpret() {

PascalInterpreter/PascalInterpreter/Lexer/Lexer.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ public class Lexer {
3535
"FALSE": .constant(.boolean(false)),
3636
"IF": .`if`,
3737
"ELSE": .`else`,
38-
"THEN": .then
38+
"THEN": .then,
39+
"FUNCTION": .function
3940
]
4041

4142
public init(_ text: String) {

PascalInterpreter/PascalInterpreter/Lexer/Token+Extensions.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ extension Token: Equatable {
5555
return true
5656
case (.greaterThan, .greaterThan):
5757
return true
58+
case (.function, .function):
59+
return true
5860
default:
5961
return false
6062
}
@@ -177,6 +179,8 @@ extension Token: CustomStringConvertible {
177179
return "LT"
178180
case .greaterThan:
179181
return "GT"
182+
case .function:
183+
return "FUNCTION"
180184
}
181185
}
182186
}

PascalInterpreter/PascalInterpreter/Lexer/Token.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,4 +56,5 @@ public enum Token {
5656
case equals
5757
case lessThan
5858
case greaterThan
59+
case function
5960
}

PascalInterpreter/PascalInterpreter/Parser/AST+Extensions.swift

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,11 +100,13 @@ extension AST {
100100
return "\(type.type)"
101101
case let program as Program:
102102
return program.name
103+
case let function as Function:
104+
return "\(function.name):\(function.returnType.type)"
103105
case let procedure as Procedure:
104106
return procedure.name
105107
case let param as Param:
106108
return "param(\(param.name))"
107-
case let call as ProcedureCall:
109+
case let call as FunctionCall:
108110
return "\(call.name)()"
109111
case is IfElse:
110112
return "IF"
@@ -144,6 +146,13 @@ extension AST {
144146
return []
145147
case let program as Program:
146148
return [program.block]
149+
case let function as Function:
150+
var nodes: [AST] = []
151+
for param in function.params {
152+
nodes.append(param)
153+
}
154+
nodes.append(function.block)
155+
return nodes
147156
case let procedure as Procedure:
148157
var nodes: [AST] = []
149158
for param in procedure.params {
@@ -153,7 +162,7 @@ extension AST {
153162
return nodes
154163
case let param as Param:
155164
return [param.type]
156-
case let call as ProcedureCall:
165+
case let call as FunctionCall:
157166
return call.actualParameters
158167
case let ifelse as IfElse:
159168
if let falseExpression = ifelse.falseExpression {

PascalInterpreter/PascalInterpreter/Parser/AST.swift

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,15 @@ class Procedure: Declaration {
139139
}
140140
}
141141

142+
class Function: Procedure {
143+
let returnType: VariableType
144+
145+
init(name: String, params: [Param], block: Block, returnType: VariableType) {
146+
self.returnType = returnType
147+
super.init(name: name, params: params, block: block)
148+
}
149+
}
150+
142151
class Param: AST {
143152
let name: String
144153
let type: VariableType
@@ -149,7 +158,7 @@ class Param: AST {
149158
}
150159
}
151160

152-
class ProcedureCall: AST {
161+
class FunctionCall: AST {
153162
let name: String
154163
let actualParameters: [AST]
155164

PascalInterpreter/PascalInterpreter/Parser/Parser.swift

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ public class Parser {
8181
/**
8282
declarations : VAR (variable_declaration SEMI)+
8383
| (PROCEDURE ID (LPAREN formal_parameter_list RPAREN)? SEMI block SEMI)*
84+
| (FUNCTION ID (LPAREN formal_parameter_list RPAREN)? COLON type_spec SEMI block SEMI)*
8485
| empty
8586
*/
8687
private func declarations() -> [Declaration] {
@@ -118,6 +119,30 @@ public class Parser {
118119
eat(.semi)
119120
}
120121

122+
while currentToken == .function {
123+
eat(.function)
124+
guard case let .id(name) = currentToken else {
125+
fatalError("Function name expected, got \(currentToken)")
126+
}
127+
eat(.id(name))
128+
129+
var params: [Param] = []
130+
if currentToken == .parenthesis(.left) {
131+
eat(.parenthesis(.left))
132+
params = formalParameterList()
133+
eat(.parenthesis(.right))
134+
}
135+
136+
eat(.colon)
137+
let type = typeSpec()
138+
139+
eat(.semi)
140+
let body = block()
141+
let function = Function(name: name, params: params, block: body, returnType: type)
142+
declarations.append(function)
143+
eat(.semi)
144+
}
145+
121146
return declarations
122147
}
123148

@@ -255,7 +280,7 @@ public class Parser {
255280
return ifElseStatement()
256281
case .id:
257282
if nextToken == .parenthesis(.left) {
258-
return procedureCall()
283+
return functionCall()
259284
} else {
260285
return assignmentStatement()
261286
}
@@ -267,9 +292,9 @@ public class Parser {
267292
/**
268293
Rule:
269294

270-
procedure_call : id( (factor (factor,)* )* )
295+
function_call : id LPAREN (factor (factor COLON)* )* RPAREN
271296
*/
272-
private func procedureCall() -> ProcedureCall {
297+
private func functionCall() -> FunctionCall {
273298
guard case let .id(name) = currentToken else {
274299
fatalError("Procedure name expected, got \(currentToken)")
275300
}
@@ -291,7 +316,7 @@ public class Parser {
291316
eat(.parenthesis(.right))
292317
}
293318

294-
return ProcedureCall(name: name, actualParameters: parameters)
319+
return FunctionCall(name: name, actualParameters: parameters)
295320
}
296321

297322
/**
@@ -431,6 +456,7 @@ public class Parser {
431456
| REAL_CONST
432457
| LPAREN expr RPAREN
433458
| variable
459+
| function_call
434460
*/
435461
private func factor() -> AST {
436462
let token = currentToken
@@ -453,7 +479,11 @@ public class Parser {
453479
eat(.parenthesis(.right))
454480
return result
455481
default:
456-
return variable()
482+
if nextToken == .parenthesis(.left) {
483+
return functionCall()
484+
} else {
485+
return variable()
486+
}
457487
}
458488
}
459489

PascalInterpreter/PascalInterpreter/Semantic analyzer/SemanticAnalyzer.swift

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@ public class SemanticAnalyzer: Visitor {
5454
scope.insert(VariableSymbol(name: variableDeclaration.variable.name, type: symbolType))
5555
}
5656

57+
func visit(function: Function) {
58+
visit(procedure: function)
59+
}
60+
5761
func visit(procedure: Procedure) {
5862
let scope = ScopedSymbolTable(name: procedure.name, level: (currentScope?.level ?? 0) + 1, enclosingScope: currentScope)
5963
scopes[scope.name] = scope
@@ -68,14 +72,24 @@ public class SemanticAnalyzer: Visitor {
6872
parameters.append(variable)
6973
scope.insert(variable)
7074
}
71-
let proc = ProcedureSymbol(name: procedure.name, parameters: parameters, body: procedure)
72-
scope.enclosingScope?.insert(proc)
75+
76+
switch procedure {
77+
case let function as Function:
78+
guard let symbol = scope.lookup(function.returnType.type.description) else {
79+
fatalError("Type not found '\(function.returnType.type.description)'")
80+
}
81+
let fn = FunctionSymbol(name: procedure.name, parameters: parameters, body: procedure, returnType: symbol)
82+
scope.enclosingScope?.insert(fn)
83+
default:
84+
let proc = ProcedureSymbol(name: procedure.name, parameters: parameters, body: procedure)
85+
scope.enclosingScope?.insert(proc)
86+
}
7387

7488
visit(node: procedure.block)
7589
currentScope = currentScope?.enclosingScope
7690
}
7791

78-
func visit(call: ProcedureCall) {
92+
func visit(call: FunctionCall) {
7993
guard let symbol = currentScope?.lookup(call.name), let procedure = symbol as? ProcedureSymbol else {
8094
fatalError("Symbol(procedure) not found '\(call.name)'")
8195
}

0 commit comments

Comments
 (0)