Skip to content

Commit 8717640

Browse files
committed
Complete fixing bugs for method invocation
1 parent 8a4d420 commit 8717640

File tree

9 files changed

+440
-125
lines changed

9 files changed

+440
-125
lines changed

src/types/checker/__tests__/methodInvocation.test.ts

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
1+
import { CannotFindSymbolError } from "../../errors";
12
import { check } from "..";
23
import { parse } from "../../../ast/parser";
34
import { Type } from "../../types/type";
5+
import {
6+
IncompatibleTypesError,
7+
MethodCannotBeAppliedError,
8+
} from "../../errors";
49

510
const createProgram = (methods: string) => `
611
public class Main {
@@ -20,6 +25,60 @@ const testcases: {
2025
`,
2126
result: { type: null, errors: [] },
2227
},
28+
{
29+
input: `
30+
public static void main(String[] args) { printMessage(100); }
31+
public static void printMessage(String message) {}
32+
`,
33+
result: { type: null, errors: [new IncompatibleTypesError()] },
34+
},
35+
{
36+
input: `
37+
public static void main(String[] args) { printMessage("Hello", "World"); }
38+
public static void printMessage(String message) {}
39+
`,
40+
result: { type: null, errors: [new MethodCannotBeAppliedError()] },
41+
},
42+
{
43+
input: `
44+
public static void main(String[] args) {
45+
printMessage("Hello, World!");
46+
printMessage("This is number ", 5);
47+
}
48+
public static void printMessage(String message) {}
49+
public static void printMessage(String message, int number) {}
50+
`,
51+
result: { type: null, errors: [] },
52+
},
53+
{
54+
input: `
55+
public static void main(String[] args) {
56+
nonExistentMethod(); // This method does not exist
57+
}
58+
`,
59+
result: { type: null, errors: [new CannotFindSymbolError()] },
60+
},
61+
{
62+
input: `
63+
public static void main(String[] args) { getStringLength("Hello World!"); }
64+
public static String getStringLength(String input) { return input; }
65+
`,
66+
result: { type: null, errors: [] },
67+
},
68+
{
69+
input: `
70+
public static void main(String[] args) { int test = getStringLength("Hello World!"); }
71+
public static String getStringLength(String input) { return input; }
72+
`,
73+
result: { type: null, errors: [new IncompatibleTypesError()] },
74+
},
75+
{
76+
input: `
77+
public static void main(String[] args) { int test = getStringLength("Hello World!"); }
78+
public static int getStringLength(String input) { return input; }
79+
`,
80+
result: { type: null, errors: [new IncompatibleTypesError()] },
81+
},
2382
];
2483

2584
describe("Type Checker", () => {

src/types/checker/environment.ts

Lines changed: 36 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,8 @@
11
import * as NonPrimitives from "../types/nonPrimitives";
22
import * as Primitives from "../types/primitives";
3-
import { Method, Type } from "../types/type";
4-
5-
export class FrameError extends Error {}
6-
7-
export class CannotFindSymbolError extends FrameError {
8-
constructor() {
9-
super("cannot find symbol");
10-
}
11-
}
12-
13-
export class VariableAlreadyDefinedError extends FrameError {
14-
constructor() {
15-
super("variable is already defined");
16-
}
17-
}
3+
import { Method } from "../types/methods";
4+
import { Type } from "../types/type";
5+
import { CannotFindSymbolError, VariableAlreadyDefinedError } from "../errors";
186

197
const GLOBAL_TYPE_ENVIRONMENT: { [key: string]: Type } = {
208
boolean: new Primitives.Boolean(),
@@ -35,19 +23,23 @@ const GLOBAL_TYPE_ENVIRONMENT: { [key: string]: Type } = {
3523
Long: new NonPrimitives.Long(),
3624
Short: new NonPrimitives.Short(),
3725
String: new NonPrimitives.String(),
26+
// TODO: Fix to array type
27+
"String[]": new NonPrimitives.String(),
3828
};
3929

4030
export class Frame {
4131
private _methods = new Map<string, Method>();
4232
private _types = new Map<string, Type>();
4333
private _variables = new Map<string, Type>();
4434

35+
private _returnType: Type | null = null;
36+
4537
private _parentFrame: Frame | null = null;
4638
private _childrenFrames: Frame[] = [];
4739

4840
private constructor() {}
4941

50-
public getMethod(name: string): Method | FrameError {
42+
public getMethod(name: string): Method | Error {
5143
let frame: Frame | null = this;
5244
while (frame) {
5345
const method = frame._methods.get(name);
@@ -57,52 +49,70 @@ export class Frame {
5749
return new CannotFindSymbolError();
5850
}
5951

60-
public getVariable(name: string): Type | FrameError {
52+
public getReturn(): Type | Error {
6153
let frame: Frame | null = this;
6254
while (frame) {
63-
const type = frame._variables.get(name);
55+
const type = frame._returnType;
6456
if (type) return type;
6557
frame = frame._parentFrame;
6658
}
67-
return new CannotFindSymbolError();
59+
return new Error("Cannot find return type.");
6860
}
6961

70-
public isVariableInFrame(name: string): boolean {
71-
return !!this._variables.get(name);
62+
public getType(name: string): Type | Error {
63+
let frame: Frame | null = this;
64+
while (frame) {
65+
const type = frame._types.get(name);
66+
if (type) return type;
67+
frame = frame._parentFrame;
68+
}
69+
return new CannotFindSymbolError();
7270
}
7371

74-
public getType(name: string): Type | FrameError {
72+
public getVariable(name: string): Type | Error {
7573
let frame: Frame | null = this;
7674
while (frame) {
77-
const type = frame._types.get(name);
75+
const type = frame._variables.get(name);
7876
if (type) return type;
7977
frame = frame._parentFrame;
8078
}
8179
return new CannotFindSymbolError();
8280
}
8381

82+
public isMethodInFrame(name: string): boolean {
83+
return !!this._methods.get(name);
84+
}
85+
86+
public isVariableInFrame(name: string): boolean {
87+
return !!this._variables.get(name);
88+
}
89+
8490
public newChildFrame(): Frame {
8591
const childFrame = new Frame();
8692
this._childrenFrames.push(childFrame);
8793
childFrame._parentFrame = this;
8894
return childFrame;
8995
}
9096

91-
public setMethod(name: string, method: Method): null | FrameError {
97+
public setMethod(name: string, method: Method): null | Error {
9298
const existingMethod = this._methods.get(name);
9399
if (existingMethod) return new VariableAlreadyDefinedError();
94100
this._methods.set(name, method);
95101
return null;
96102
}
97103

98-
public setType(name: string, type: Type): null | FrameError {
104+
public setReturnType(type: Type): void {
105+
this._returnType = type;
106+
}
107+
108+
public setType(name: string, type: Type): null | Error {
99109
const existingType = this._types.get(name);
100110
if (existingType) return new VariableAlreadyDefinedError();
101111
this._types.set(name, type);
102112
return null;
103113
}
104114

105-
public setVariable(name: string, type: Type): null | FrameError {
115+
public setVariable(name: string, type: Type): null | Error {
106116
const existingVariable = this._types.get(name);
107117
if (existingVariable) return new VariableAlreadyDefinedError();
108118
this._variables.set(name, type);
@@ -125,9 +135,3 @@ export class Frame {
125135
return globalFrame;
126136
}
127137
}
128-
129-
export type FrameOld = {
130-
types: Record<string, Type>;
131-
variables: Record<string, Type>;
132-
previousFrame: FrameOld | null;
133-
};

src/types/checker/index.ts

Lines changed: 88 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
1+
import { Frame } from "./environment";
2+
import { Method } from "../types/methods";
3+
import { Node } from "../../ast/types/ast";
4+
import { String } from "../types/nonPrimitives";
5+
import { Type } from "../types/type";
16
import {
27
BadOperandTypesError,
38
IncompatibleTypesError,
4-
MethodCannotBeAppliedError,
9+
VariableAlreadyDefinedError,
510
} from "../errors";
6-
import { Frame, VariableAlreadyDefinedError } from "./environment";
7-
import { Node } from "../../ast/types/ast";
8-
import { String } from "../types/nonPrimitives";
9-
import { ClassMethod, Parameter, Type } from "../types/type";
11+
import {
12+
createArgumentList,
13+
createMethod,
14+
createMethodSignature,
15+
} from "../typeFactories/methodFactory";
1016
import {
1117
Expression,
1218
ExpressionName,
@@ -51,6 +57,7 @@ export const check = (
5157
throw new Error(
5258
"Right side of assignment statment should return a type."
5359
);
60+
console.log(node.left, leftType, node.right, currentType);
5461
if (!leftType.canBeAssigned(currentType))
5562
return newResult(null, [new IncompatibleTypesError()]);
5663
return OK_RESULT;
@@ -284,61 +291,85 @@ export const check = (
284291
}, OK_RESULT);
285292
}
286293
case "MethodInvocation": {
294+
const errors: Error[] = [];
287295
const method = frame.getMethod(node.identifier);
288296
if (method instanceof Error) return newResult(null, [method]);
289-
if (method.parameters.length !== node.argumentList.length)
290-
return newResult(null, [new MethodCannotBeAppliedError()]);
291-
const errors: Error[] = [];
292-
for (let i = 0; i < method.parameters.length; i++) {
293-
const parameter = method.parameters[i];
294-
const argument = node.argumentList[i];
295-
const argumentCheck = check(argument, frame);
296-
if (argumentCheck.errors.length > 0) {
297-
errors.push(...argumentCheck.errors);
297+
const argumentTypes: Type[] = [];
298+
for (const argument of node.argumentList) {
299+
const argumentResult = check(argument, frame);
300+
if (argumentResult.errors.length > 0) {
301+
errors.push(...argumentResult.errors);
298302
continue;
299303
}
300-
if (!argumentCheck.currentType)
301-
throw new Error("Argument check should result in a type.");
302-
if (!parameter.canBeAssigned(argumentCheck.currentType))
303-
errors.push(new IncompatibleTypesError());
304+
if (!argumentResult.currentType)
305+
throw new Error("Arguments should have a type.");
306+
argumentTypes.push(argumentResult.currentType);
304307
}
305-
return newResult(method.returnType, errors);
308+
const argumentList = createArgumentList(...argumentTypes);
309+
if (argumentList instanceof Error)
310+
return newResult(null, [...errors, argumentList]);
311+
const returnType = method.invoke(argumentList);
312+
if (returnType instanceof Error)
313+
return newResult(null, [...errors, returnType]);
314+
return newResult(returnType, errors);
306315
}
307316
case "NormalClassDeclaration": {
308317
const errors: Error[] = [];
309318
const classFrame = frame.newChildFrame();
319+
const furtherChecks: (() => void)[] = [];
310320
node.classBody.forEach((bodyDeclaration) => {
311321
if (bodyDeclaration.kind === "MethodDeclaration") {
312-
const method = new ClassMethod();
313-
method.modifiers = bodyDeclaration.methodModifier;
314-
const returnType = frame.getType(bodyDeclaration.methodHeader.result);
315-
if (returnType instanceof Error) {
316-
errors.push(returnType);
317-
return;
318-
}
319-
method.returnType = returnType;
320-
const parameters: Parameter[] = [];
321-
bodyDeclaration.methodHeader.formalParameterList.forEach(
322-
(parameter) => {
323-
const type = classFrame.getType(parameter.unannType);
324-
if (type instanceof Type) {
325-
const param = new Parameter();
326-
param.name = parameter.identifier;
327-
param.type = type;
328-
parameters.push(param);
329-
}
322+
const methodName = bodyDeclaration.methodHeader.identifier;
323+
if (classFrame.isMethodInFrame(methodName)) {
324+
const method = classFrame.getMethod(methodName) as Method;
325+
const overloadSignature = createMethodSignature(
326+
classFrame,
327+
bodyDeclaration
328+
);
329+
if (overloadSignature instanceof Error) {
330+
errors.push(overloadSignature);
331+
return;
330332
}
331-
);
332-
method.parameters = parameters;
333-
classFrame.setMethod(bodyDeclaration.methodHeader.identifier, method);
334-
}
335-
});
336-
node.classBody.forEach((bodyDeclaration) => {
337-
if (bodyDeclaration.kind === "MethodDeclaration") {
338-
const checkResult = check(bodyDeclaration.methodBody, classFrame);
339-
errors.push(...checkResult.errors);
333+
const error = method.addOverload(overloadSignature);
334+
if (error) errors.push(error);
335+
furtherChecks.push(() => {
336+
const methodFrame = classFrame.newChildFrame();
337+
overloadSignature.mapParameters((name, type) => {
338+
const error = methodFrame.setVariable(name, type);
339+
if (error) errors.push(error);
340+
});
341+
methodFrame.setReturnType(overloadSignature.getReturnType());
342+
const checkResult = check(
343+
bodyDeclaration.methodBody,
344+
methodFrame
345+
);
346+
errors.push(...checkResult.errors);
347+
});
348+
} else {
349+
const method = createMethod(frame, bodyDeclaration);
350+
if (method instanceof Error) {
351+
errors.push(method);
352+
return;
353+
}
354+
classFrame.setMethod(methodName, method);
355+
furtherChecks.push(() => {
356+
const methodFrame = classFrame.newChildFrame();
357+
const methodSignature = method.getOverload(0);
358+
methodSignature.mapParameters((name, type) => {
359+
const error = methodFrame.setVariable(name, type);
360+
if (error) errors.push(error);
361+
});
362+
methodFrame.setReturnType(methodSignature.getReturnType());
363+
const checkResult = check(
364+
bodyDeclaration.methodBody,
365+
methodFrame
366+
);
367+
errors.push(...checkResult.errors);
368+
});
369+
}
340370
}
341371
});
372+
furtherChecks.forEach((furtherCheck) => furtherCheck());
342373
return newResult(null, errors);
343374
}
344375
case "PostfixExpression": {
@@ -392,6 +423,17 @@ export const check = (
392423
);
393424
}
394425
}
426+
case "ReturnStatement": {
427+
const expressionCheck = check(node.exp, frame);
428+
if (expressionCheck.errors.length > 0) return expressionCheck;
429+
if (!expressionCheck.currentType)
430+
throw new Error("Expression check should return a type.");
431+
const returnType = frame.getReturn();
432+
if (returnType instanceof Error) return newResult(null, [returnType]);
433+
if (!returnType.canBeAssigned(expressionCheck.currentType))
434+
return newResult(null, [new IncompatibleTypesError()]);
435+
return OK_RESULT;
436+
}
395437
case "TernaryExpression": {
396438
const conditionCheck = check(node.condition, frame);
397439
if (conditionCheck.errors.length > 0) return conditionCheck;
@@ -432,7 +474,6 @@ export const check = (
432474
return OK_RESULT;
433475
}
434476
default:
435-
console.log(node);
436477
throw new Error(
437478
`Check is not implemented for this type of node ${node.kind}.`
438479
);

0 commit comments

Comments
 (0)