Skip to content

Commit 507e0f0

Browse files
committed
Fix forward declaration procedure parsing
- Fix forward declaration procedures not parsing correctly if they are not followed by an AST term. This seems to only be an issue in interpreter mode as far as was tested. - Add comments to clarify proc keyword rewriting logic.
1 parent c6af4a4 commit 507e0f0

File tree

1 file changed

+62
-33
lines changed

1 file changed

+62
-33
lines changed

src/main/java/com/laytonsmith/core/compiler/keywords/ProcKeyword.java

Lines changed: 62 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -28,64 +28,93 @@ public int process(List<ParseTree> list, int keywordPosition) throws ConfigCompi
2828
if(list.get(keywordPosition).getData() instanceof CKeyword) {
2929
// It's a lone keyword, so we expect some function to follow, which is the proc name + variables
3030
FileOptions options = list.get(keywordPosition).getFileOptions();
31+
32+
// Validate minimal required number of nodes.
3133
if(list.size() <= keywordPosition + 1) {
3234
throw new ConfigCompileException("Unexpected keyword", list.get(keywordPosition).getTarget());
3335
}
36+
37+
/*
38+
* Parse:
39+
* "proc _procName(params) { code }" to "proc(_procName, params, code)". Params and code are optional.
40+
* "proc _procName(params)" to "proc(_procName, params, noop())". Params are optional.
41+
* "proc _procName" to "get_proc(_procName)".
42+
*/
3443
if(list.get(keywordPosition + 1).getData() instanceof CFunction) {
44+
45+
// Create proc function node.
3546
ParseTree procNode = new ParseTree(new CFunction(
3647
DataHandling.proc.NAME, list.get(keywordPosition).getTarget()), options);
3748
procNode.getNodeModifiers().merge(list.get(keywordPosition).getNodeModifiers());
49+
50+
// Add proc name to proc function node. Proc name node is currently a function node.
3851
procNode.addChild(new ParseTree(new CString(list.get(keywordPosition + 1).getData().val(),
3952
list.get(keywordPosition + 1).getTarget()), options));
40-
// Grab the functions children, and put them on the stack
53+
54+
// Move proc name function node children to proc function node.
4155
for(ParseTree child : list.get(keywordPosition + 1).getChildren()) {
4256
procNode.addChild(child);
4357
}
44-
boolean forwardDeclaration = false;
45-
if(list.size() > keywordPosition + 2) {
46-
if(list.get(keywordPosition + 2).getData() instanceof CFunction cf
47-
&& com.laytonsmith.core.functions.Compiler.__cbrace__.NAME.equals(cf.val())) {
48-
validateCodeBlock(list.get(keywordPosition + 2), "Expected braces to follow proc definition");
49-
procNode.addChild(getArgumentOrNoop(list.get(keywordPosition + 2)));
50-
} else {
51-
// Forward declaration, add a noop "implementation"
52-
forwardDeclaration = true;
53-
ParseTree statement = new ParseTree(new CFunction(Compiler.__statements__.NAME, Target.UNKNOWN),
54-
list.get(keywordPosition + 1).getFileOptions(), true);
55-
statement.addChild(new ParseTree(new CFunction(Meta.noop.NAME, Target.UNKNOWN),
56-
list.get(keywordPosition + 1).getFileOptions(), true));
57-
procNode.addChild(statement);
58-
}
58+
59+
// Get code block from __cbrace__ function node. Define as forward declaration if code block is missing.
60+
if(list.size() > keywordPosition + 2
61+
&& list.get(keywordPosition + 2).getData() instanceof CFunction cf
62+
&& com.laytonsmith.core.functions.Compiler.__cbrace__.NAME.equals(cf.val())) {
63+
64+
// Validate code block and add to proc function node.
65+
validateCodeBlock(list.get(keywordPosition + 2), "Expected braces to follow proc definition");
66+
procNode.addChild(getArgumentOrNoop(list.get(keywordPosition + 2)));
67+
68+
// Remove processed nodes from AST.
69+
list.remove(keywordPosition); // Remove keyword node.
70+
list.remove(keywordPosition); // Remove proc name function node (with proc parameters).
71+
list.remove(keywordPosition); // Remove __cbrace__ function node.
5972
} else {
60-
throw new ConfigCompileException("Expected braces to follow proc definition", list.get(keywordPosition + 1).getTarget());
61-
}
62-
list.remove(keywordPosition); // Remove the keyword
63-
list.remove(keywordPosition); // Remove the function definition
64-
if(!forwardDeclaration) {
65-
list.remove(keywordPosition); // Remove the cbrace
73+
74+
// Define as forward declaration by add a "noop()" as code block.
75+
ParseTree statement = new ParseTree(new CFunction(Compiler.__statements__.NAME, Target.UNKNOWN),
76+
list.get(keywordPosition + 1).getFileOptions(), true);
77+
statement.addChild(new ParseTree(new CFunction(Meta.noop.NAME, Target.UNKNOWN),
78+
list.get(keywordPosition + 1).getFileOptions(), true));
79+
procNode.addChild(statement);
80+
81+
// Remove processed nodes from AST.
82+
list.remove(keywordPosition); // Remove keyword node.
83+
list.remove(keywordPosition); // Remove proc name function node (with proc parameters).
6684
}
67-
list.add(keywordPosition, procNode); // Add in the new proc definition
85+
86+
// Add proc function node to AST.
87+
list.add(keywordPosition, procNode);
88+
6889
} else if(list.get(keywordPosition + 1).getData() instanceof CBareString name) {
69-
// get_proc rewrite
70-
list.remove(keywordPosition);
71-
list.remove(keywordPosition);
90+
91+
// Parse "proc _procName" to "get_proc(_procName)".
92+
list.remove(keywordPosition); // Remove keyword node.
93+
list.remove(keywordPosition); // Remove proc name node.
94+
95+
// Add get_proc function node with proc name child node to AST.
7296
ParseTree getProc = new ParseTree(new CFunction(DataHandling.get_proc.NAME, Target.UNKNOWN), options, true);
7397
getProc.addChild(new ParseTree(new CString(name.val(), name.getTarget()), options));
7498
list.add(keywordPosition, getProc);
99+
75100
} else {
101+
102+
// Proc keyword used incorrectly.
76103
throw new ConfigCompileException("Unexpected use of \"proc\" keyword", list.get(keywordPosition).getTarget());
77104
}
78105

79106
} else if(nodeIsProcFunction(list.get(keywordPosition))) {
80-
// It's the functional usage, possibly followed by a cbrace. If so, pull the cbrace in, and that's it
81-
if(list.size() > keywordPosition + 1) {
82-
if(isValidCodeBlock(list.get(keywordPosition + 1))) {
83-
list.get(keywordPosition).addChild(getArgumentOrNoop(list.get(keywordPosition + 1)));
84-
list.remove(keywordPosition + 1);
85-
}
107+
108+
// Parse "proc(_procName, params) { code }" to "proc(_procName, params, code)". Params are optional.
109+
if(list.size() > keywordPosition + 1 && isValidCodeBlock(list.get(keywordPosition + 1))) {
110+
111+
// Pull in __cbrace__ function node as proc function node code block.
112+
list.get(keywordPosition).addChild(getArgumentOrNoop(list.get(keywordPosition + 1)));
113+
list.remove(keywordPosition + 1); // Remove __cbrace__ function node.
86114
}
87115
} else {
88-
// Random keyword in the middle of nowhere
116+
117+
// Random proc keyword in the middle of nowhere.
89118
throw new ConfigCompileException("Unexpected use of \"proc\" keyword", list.get(keywordPosition).getTarget());
90119
}
91120
return keywordPosition;

0 commit comments

Comments
 (0)