Skip to content

Commit 6aedf00

Browse files
authored
Merge pull request #396 from lunasaw/20250807_support_function_post_definition
feat(function): post function reference
2 parents d94adc3 + d2af3f6 commit 6aedf00

File tree

9 files changed

+423
-5
lines changed

9 files changed

+423
-5
lines changed

src/main/java/com/alibaba/qlexpress4/aparser/QvmInstructionVisitor.java

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -184,12 +184,29 @@ public Void visitBlockStatements(BlockStatementsContext blockStatementsContext)
184184
.stream()
185185
.filter(bs -> !(bs instanceof EmptyStatementContext))
186186
.collect(Collectors.toList());
187+
188+
// First pass: process macro definitions to ensure they are available for functions
187189
for (BlockStatementContext child : nonEmptyChildren) {
188-
if (isPreExpress) {
189-
// pop if expression without acceptor
190-
addInstruction(new PopInstruction(PureErrReporter.INSTANCE));
190+
if (child instanceof MacroStatementContext) {
191+
child.accept(this);
192+
}
193+
}
194+
195+
// Second pass: process all function definitions to support forward references
196+
for (BlockStatementContext child : nonEmptyChildren) {
197+
if (child instanceof FunctionStatementContext) {
198+
child.accept(this);
199+
}
200+
}
201+
202+
// Third pass: process all other statements
203+
for (BlockStatementContext child : nonEmptyChildren) {
204+
if (!(child instanceof FunctionStatementContext) && !(child instanceof MacroStatementContext)) {
205+
if (isPreExpress) {
206+
addInstruction(new PopInstruction(PureErrReporter.INSTANCE));
207+
}
208+
isPreExpress = handleStmt(child);
191209
}
192-
isPreExpress = handleStmt(child);
193210
}
194211

195212
if (context == Context.BLOCK) {

src/test/java/com/alibaba/qlexpress4/TestSuiteRunner.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ public void suiteTest()
6161
@Test
6262
public void featureDebug()
6363
throws URISyntaxException, IOException {
64-
Path filePath = getTestSuiteRoot().resolve("java/property/null_set_invoke.ql");
64+
Path filePath = getTestSuiteRoot().resolve("independent/function/nested_function_calls.ql");
6565
handleFile(filePath, filePath.toString(), true);
6666
}
6767

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// Test functions with various parameter types and complexity
2+
assert(processData(5, true) == 15)
3+
assert(calculateComplex(1.5, 2.5, 10) == 160.0)
4+
5+
// Test function calls with complex expressions as parameters
6+
assert(mathOperations(add(2, 3), multiply(2, 2)) == 625) // 5^4 = 625
7+
8+
function processData(int count, boolean flag) {
9+
int result = count * 2;
10+
if (flag) {
11+
result = result + 5;
12+
}
13+
return result;
14+
}
15+
16+
function calculateComplex(double x, double y, int multiplier) {
17+
double base = x + y;
18+
return base * base * multiplier;
19+
}
20+
21+
function mathOperations(int a, int b) {
22+
return power(a, b);
23+
}
24+
25+
function add(int x, int y) {
26+
return x + y;
27+
}
28+
29+
function multiply(int x, int y) {
30+
return x * y;
31+
}
32+
33+
function power(int base, int exp) {
34+
if (exp == 0) {
35+
return 1;
36+
}
37+
int result = 1;
38+
for (int i = 0; i < exp; i++) {
39+
result *= base;
40+
}
41+
return result;
42+
}
43+
44+
// Test functions with no parameters
45+
assert(getConstant() == 42)
46+
assert(generateRandom() > 0)
47+
48+
function getConstant() {
49+
return 42;
50+
}
51+
52+
function generateRandom() {
53+
// Simple pseudo-random using current execution context
54+
return 123; // For deterministic testing
55+
}
56+
57+
// Test function overloading-like behavior with different parameter counts
58+
assert(calculate(5) == 25)
59+
assert(calculate2(5, 3) == 39) // 5*5 + 3*3 + 5 = 25 + 9 + 5 = 39
60+
61+
function calculate(int x) {
62+
return x * x;
63+
}
64+
65+
function calculate2(int x, int y) {
66+
return x * x + y * y + 5;
67+
}
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
// Test edge cases for function hoisting
2+
// Test function called immediately at global level before definition
3+
int immediateResult = callImmediately();
4+
assert(immediateResult == 42)
5+
6+
// Test function in conditional blocks called before definition
7+
if (true) {
8+
assert(conditionalFunction(5) == 10)
9+
}
10+
11+
// Test function in loop called before definition
12+
for (int i = 0; i < 2; i++) {
13+
assert(loopFunction(i) == i * 3)
14+
}
15+
16+
// Test function with same name as built-in (should work)
17+
assert(toString(123) == "custom_123")
18+
19+
// Test function that returns function call result
20+
assert(chainReturn() == 999)
21+
22+
// Function definitions (after all calls)
23+
function callImmediately() {
24+
return 42;
25+
}
26+
27+
function conditionalFunction(int x) {
28+
return x * 2;
29+
}
30+
31+
function loopFunction(int x) {
32+
return x * 3;
33+
}
34+
35+
function toString(int value) {
36+
return "custom_" + value;
37+
}
38+
39+
function chainReturn() {
40+
return getSpecialValue();
41+
}
42+
43+
function getSpecialValue() {
44+
return 999;
45+
}
46+
47+
// Test empty function
48+
assert(emptyFunction() == null)
49+
50+
function emptyFunction() {
51+
// Empty body
52+
}
53+
54+
// Test function that just returns constant
55+
assert(constantFunction() == "CONSTANT")
56+
57+
function constantFunction() {
58+
return "CONSTANT";
59+
}
60+
61+
// Test functions with early returns
62+
assert(earlyReturnFunction(true) == 1)
63+
assert(earlyReturnFunction(false) == 2)
64+
65+
function earlyReturnFunction(boolean condition) {
66+
if (condition) {
67+
return 1;
68+
}
69+
return 2;
70+
}
71+
72+
// Test function calling itself indirectly (through another function)
73+
assert(indirectSelfCall(3) == 6)
74+
75+
function indirectSelfCall(int n) {
76+
if (n <= 0) {
77+
return 0;
78+
}
79+
return n + helperForIndirect(n - 1);
80+
}
81+
82+
function helperForIndirect(int n) {
83+
return indirectSelfCall(n);
84+
}
85+
86+
// Test function with multiple return types based on conditions
87+
function dynamicReturn(int choice) {
88+
if (choice == 1) {
89+
return 100;
90+
} else if (choice == 2) {
91+
return "text";
92+
} else {
93+
return true;
94+
}
95+
}
96+
97+
// Test the dynamic returns
98+
assert(dynamicReturn(1) == 100)
99+
assert(dynamicReturn(2) == "text")
100+
assert(dynamicReturn(3) == true)

src/test/resources/testsuite/independent/function/function_call.ql

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,8 @@ assert(sub(3,1)==2)
1212

1313
assertErrorCode(() -> {add(1, "2")}, "INVALID_ARGUMENT")
1414

15+
assert(check(3,1)==false)
1516

17+
function check(a, b) {
18+
return a < b;
19+
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
// Test function scoping and parameter shadowing
2+
int outerVariable = 10;
3+
4+
// Test function with same parameter name as global variable
5+
assert(testScoping(5) == 15)
6+
7+
// Test that global variable is not affected
8+
assert(outerVariable == 10)
9+
10+
function testScoping(int outerVariable) {
11+
// Parameter shadows global variable
12+
return outerVariable + 10;
13+
}
14+
15+
// Test nested scoping with local variables
16+
assert(nestedScoping(3) == 18)
17+
18+
function nestedScoping(int x) {
19+
int localVar = x * 2; // 6
20+
if (localVar > 5) {
21+
int innerVar = localVar * 2; // 12
22+
return innerVar + 6; // 18
23+
}
24+
return localVar;
25+
}
26+
27+
// Test functions that call other functions with same parameter names
28+
assert(chainedScoping(2) == 14)
29+
30+
function chainedScoping(int value) {
31+
return helperFunction(value + 1);
32+
}
33+
34+
function helperFunction(int value) {
35+
// Different 'value' parameter
36+
return value * 4 + 2; // (2+1) * 4 + 2 = 14
37+
}
38+
39+
// Test function with multiple parameters having local scope
40+
assert(multipleParams(1, 2, 3) == 12)
41+
42+
function multipleParams(int a, int b, int c) {
43+
int sum = a + b + c;
44+
int doubled = sum * 2;
45+
return doubled;
46+
}
47+
48+
// Test function that modifies parameters (local copies)
49+
int originalValue = 5;
50+
assert(modifyParameter(originalValue) == 25)
51+
assert(originalValue == 5) // Original should remain unchanged
52+
53+
function modifyParameter(int param) {
54+
param = param * 5; // Modifying local copy
55+
return param;
56+
}
57+
58+
// Test function with conditional blocks and local variables
59+
assert(conditionalScoping(true, 10) == 30)
60+
assert(conditionalScoping(false, 10) == 0) // 10 - 10 = 0
61+
62+
function conditionalScoping(boolean condition, int base) {
63+
int result = base;
64+
if (condition) {
65+
int bonus = 20;
66+
result = result + bonus;
67+
} else {
68+
int penalty = 10;
69+
result = result - penalty;
70+
}
71+
return result;
72+
}
73+
74+
// Test loops with local scope
75+
assert(loopScoping(3) == 6)
76+
77+
function loopScoping(int count) {
78+
int total = 0;
79+
for (int i = 1; i <= count; i++) {
80+
int squared = i; // Local to loop iteration
81+
total += squared;
82+
}
83+
return total; // 1 + 2 + 3 = 6
84+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// Test mixed function and variable declarations with forward references
2+
int globalVar = computeInitialValue();
3+
int message = formatMessage(42, getValue());
4+
5+
assert(globalVar == 100)
6+
assert(message == 92) // 42 + 50 = 92
7+
8+
// Variables referencing functions before they're defined
9+
int result1 = doubleValue(25);
10+
assert(result1 == 50)
11+
12+
function computeInitialValue() {
13+
return 100;
14+
}
15+
16+
function formatMessage(int prefix, int value) {
17+
return prefix + value;
18+
}
19+
20+
function getValue() {
21+
return 50;
22+
}
23+
24+
function doubleValue(int x) {
25+
return x * 2;
26+
}
27+
28+
// Test functions that modify and return based on global state
29+
int counter = 0;
30+
31+
function increment() {
32+
counter = counter + 1;
33+
return counter;
34+
}
35+
36+
function getCounterValue() {
37+
return counter;
38+
}
39+
40+
// Test the counter functions
41+
int first = increment(); // Should be 1
42+
int second = increment(); // Should be 2
43+
int current = getCounterValue(); // Should be 2
44+
45+
assert(first == 1)
46+
assert(second == 2)
47+
assert(current == 2)
48+
49+
// Test simple calculation with constants
50+
function simpleCalculation() {
51+
return 10 * 30; // 300
52+
}
53+
54+
assert(simpleCalculation() == 300)

0 commit comments

Comments
 (0)