Skip to content

Commit 36ed2b8

Browse files
committed
Add support for enhanced for loops
1 parent ff2d490 commit 36ed2b8

File tree

7 files changed

+80
-2
lines changed

7 files changed

+80
-2
lines changed

src/types/ast/astExtractor/statement-extractor.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,12 @@ import {
3030
ExpressionStatementCtx,
3131
LocalVariableTypeCtx,
3232
VariableDeclaratorListCtx,
33-
VariableDeclaratorCtx
33+
VariableDeclaratorCtx,
34+
EnhancedForStatementCtx
3435
} from 'java-parser'
3536
import {
3637
BasicForStatement,
38+
EnhancedForStatement,
3739
ExpressionStatement,
3840
IfStatement,
3941
MethodInvocation,
@@ -46,6 +48,7 @@ import { Location } from '../types'
4648
import { ExpressionExtractor } from './expression-extractor'
4749
import { BlockStatementExtractor } from './block-statement-extractor'
4850
import { TypeExtractor } from './type-extractor'
51+
import { getLocation } from './utils'
4952

5053
export class StatementExtractor extends BaseJavaCstVisitorWithDefaults {
5154
constructor() {
@@ -395,4 +398,18 @@ export class StatementExtractor extends BaseJavaCstVisitorWithDefaults {
395398
})
396399
return declarations
397400
}
401+
402+
enhancedForStatement(ctx: EnhancedForStatementCtx): EnhancedForStatement {
403+
const blockStatementExtractor = new BlockStatementExtractor()
404+
const expressionExtractor = new ExpressionExtractor()
405+
const statementExtractor = new StatementExtractor()
406+
return {
407+
kind: 'EnhancedForStatement',
408+
localVariableType: blockStatementExtractor.visit(ctx.localVariableType),
409+
variableDeclaratorId: blockStatementExtractor.visit(ctx.variableDeclaratorId),
410+
expression: expressionExtractor.extract(ctx.expression[0]),
411+
statement: statementExtractor.extract(ctx.statement[0]),
412+
location: getLocation(ctx.For[0])
413+
}
414+
}
398415
}

src/types/ast/types/blocks-and-statements.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,11 @@ export interface EmptyStatement extends BaseNode {
6565

6666
export interface EnhancedForStatement extends BaseNode {
6767
kind: 'EnhancedForStatement'
68+
localVariableType: LocalVariableType
69+
variableDeclaratorId: VariableDeclaratorId
70+
expression: Expression
71+
statement: Statement
72+
location?: Location
6873
}
6974

7075
export interface ExplicitConstructorInvocation extends BaseNode {

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

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,35 @@ const testcases: {
7171
for (int i = 0; i < arr.length; i++) {}
7272
`,
7373
result: { type: null, errors: [] }
74+
},
75+
{
76+
input: `
77+
int[] arr = {1, 2, 3, 4, 5};
78+
for (int num : arr) {}
79+
`,
80+
result: { type: null, errors: [] }
81+
},
82+
{
83+
input: `
84+
int[] arr = {1, 2, 3, 4, 5};
85+
for (double num : arr) {}
86+
`,
87+
result: { type: null, errors: [] }
88+
},
89+
{
90+
input: `
91+
int[] arr = {1, 2, 3, 4, 5};
92+
for (String num : arr) {}
93+
`,
94+
result: { type: null, errors: [new IncompatibleTypesError()] }
95+
},
96+
{
97+
input: `
98+
int[] arr = {1, 2, 3, 4, 5};
99+
for (int num : arr) {}
100+
String test = num;
101+
`,
102+
result: { type: null, errors: [new CannotFindSymbolError()] }
74103
}
75104
]
76105

src/types/checker/index.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
ArrayRequiredError,
88
BadOperandTypesError,
99
IncompatibleTypesError,
10+
NotApplicableToExpressionTypeError,
1011
VariableAlreadyDefinedError
1112
} from '../errors'
1213
import {
@@ -213,6 +214,24 @@ export const check = (node: Node, frame: Frame = Frame.globalFrame()): Result =>
213214
case 'EmptyStatement': {
214215
return OK_RESULT
215216
}
217+
case 'EnhancedForStatement': {
218+
const variableType = frame.getType(node.localVariableType)
219+
if (variableType instanceof Error) return newResult(null, [variableType])
220+
const expressionCheck = check(node.expression, frame)
221+
if (expressionCheck.errors.length > 0) return newResult(null, expressionCheck.errors)
222+
const expressionType = expressionCheck.currentType
223+
if (!(expressionType instanceof ArrayType))
224+
return newResult(null, [new NotApplicableToExpressionTypeError()])
225+
const arrayContentType = expressionType.getContentType()
226+
if (!variableType.canBeAssigned(arrayContentType))
227+
return newResult(null, [new IncompatibleTypesError()])
228+
const forExpressionFrame = frame.newChildFrame()
229+
const error = forExpressionFrame.setVariable(node.variableDeclaratorId, variableType)
230+
if (error) return newResult(null, [error])
231+
const statementCheck = check(node.statement, forExpressionFrame)
232+
if (statementCheck.errors.length > 0) return newResult(null, statementCheck.errors)
233+
return OK_RESULT
234+
}
216235
case 'ExpressionName': {
217236
const type = frame.getVariable(node.name)
218237
if (type instanceof Error) return newResult(null, [type])

src/types/errors.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,12 @@ export class FloatTooSmallError extends Error {
3434
}
3535
}
3636

37+
export class NotApplicableToExpressionTypeError extends Error {
38+
constructor() {
39+
super('not applicable to expression type')
40+
}
41+
}
42+
3743
export class IllegalUnderscoreError extends Error {
3844
constructor() {
3945
super('illegal underscore')

src/types/inputs/Main.class

120 Bytes
Binary file not shown.

src/types/inputs/Main.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
public class Main {
22
public static void main(String[] args) {
3-
int[] numbers = new int[-1];
3+
int[] arr = {1, 2, 3};
4+
for (int num : arr) {}
5+
System.out.println(num);
46
}
57
}

0 commit comments

Comments
 (0)