Skip to content

Commit b8ca993

Browse files
committed
Added the try/catch keyword. Still need to add the function.
1 parent c8852bb commit b8ca993

File tree

3 files changed

+146
-1
lines changed

3 files changed

+146
-1
lines changed
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package com.laytonsmith.core.compiler.keywords;
2+
3+
import com.laytonsmith.core.ParseTree;
4+
import com.laytonsmith.core.compiler.Keyword;
5+
import com.laytonsmith.core.exceptions.ConfigCompileException;
6+
import java.util.List;
7+
8+
/**
9+
*
10+
*/
11+
@Keyword.keyword("catch")
12+
public class CatchKeyword extends Keyword {
13+
14+
@Override
15+
public int process(List<ParseTree> list, int keywordPosition) throws ConfigCompileException {
16+
// While catch is a keyword, and it should generally be treated as such by text editors and
17+
// such, if a standalone catch is present, it is always an error. Since catch comes after
18+
// the try keyword, this is never something we have to worry about.
19+
throw new ConfigCompileException("Unexpected \"catch\" keyword", list.get(keywordPosition).getTarget());
20+
}
21+
22+
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ && nodeIsIfFunction(list.get(keywordPosition + 3))){
4343
} catch(IndexOutOfBoundsException ex){
4444
// Doesn't matter, we're apparently at the end of the stream
4545
}
46-
node.addChild(this.getArgumentOrNull(list.get(keywordPosition + 1)));
46+
node.addChild(getArgumentOrNull(list.get(keywordPosition + 1)));
4747
list.remove(keywordPosition + 1);
4848
}
4949

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
package com.laytonsmith.core.compiler.keywords;
2+
3+
import com.laytonsmith.core.ParseTree;
4+
import com.laytonsmith.core.compiler.Keyword;
5+
import com.laytonsmith.core.constructs.CClassType;
6+
import com.laytonsmith.core.constructs.CFunction;
7+
import com.laytonsmith.core.constructs.CKeyword;
8+
import com.laytonsmith.core.constructs.Construct;
9+
import com.laytonsmith.core.constructs.IVariable;
10+
import com.laytonsmith.core.constructs.Target;
11+
import com.laytonsmith.core.exceptions.ConfigCompileException;
12+
import java.util.List;
13+
14+
/**
15+
*
16+
*/
17+
@Keyword.keyword("try")
18+
public class TryKeyword extends Keyword {
19+
20+
// TODO dynamically name this once the class exists.
21+
private static final String COMPLEX_TRY = "complex_try";
22+
private static final String DEFAULT_EXCEPTION_TYPE = "Exception";
23+
24+
@Override
25+
public int process(List<ParseTree> list, int keywordPosition) throws ConfigCompileException {
26+
/**
27+
* There are several different cases we need to consider. try/catch blocks are fairly complex, and we must carefully
28+
* consider each case.
29+
*
30+
* The simplest case:
31+
* try { } catch { }
32+
* In this case, both try and catch will be CKeywords.
33+
*
34+
* A case with a specific exception type. In this case, catch will be a CFunction
35+
* try { } catch(ExceptionType @e) { }
36+
*
37+
* A multi catch
38+
* try { } catch(ExceptionType1 @e) { } catch(ExceptionType2 @e) { }
39+
* As many blocks as desired can be added here.
40+
*
41+
* TODO: Consider in the future allowing just try { }
42+
*
43+
* We also must keep in mind that try() is a function, and while this keyword internally rewrites
44+
* to complex_try, we must test for the old functional use as well.
45+
*/
46+
47+
// If it's the old version, and a function
48+
if(list.get(keywordPosition).getData() instanceof CFunction && list.get(keywordPosition).getData().val().equals("try")){
49+
return keywordPosition;
50+
}
51+
// Otherwise it's not, and we can continue on, assuming keyword usage.
52+
this.validateCodeBlock(list.get(keywordPosition + 1), "Expecting braces after try keyword");
53+
54+
ParseTree complex_try = new ParseTree(new CFunction(COMPLEX_TRY, list.get(keywordPosition).getTarget()), list.get(keywordPosition).getFileOptions());
55+
complex_try.addChild(getArgumentOrNull(list.get(keywordPosition + 1)));
56+
// Remove the keyword and the try block
57+
list.remove(keywordPosition);
58+
list.remove(keywordPosition);
59+
60+
// For now, we won't allow try {}, so this must be followed by a catch keyword. This restriction is somewhat artificial, and
61+
// if we want to remove it in the future, we can do so by removing this code block.
62+
{
63+
if(!(list.size() > keywordPosition && nodeIsCatchKeyword(list.get(keywordPosition)))){
64+
throw new ConfigCompileException("Expecting \"catch\" keyword to follow try, but none found", list.get(keywordPosition + 1).getTarget());
65+
}
66+
}
67+
68+
// We can have any number of catch statements after the try, so we loop through until we run out.
69+
for(int i = keywordPosition; i < list.size(); i++){
70+
if(!nodeIsCatchKeyword(list.get(i))){
71+
// End of the chain, stop processing.
72+
break;
73+
}
74+
if(list.size() > i + 1){
75+
this.validateCodeBlock(list.get(i + 1), "Expecting code block after catch, but none found");
76+
} else {
77+
throw new ConfigCompileException("catch must be followed by a code block, but none was found", list.get(i).getTarget());
78+
}
79+
if(list.get(i).getData() instanceof CFunction){
80+
// We have something like catch(Exception @e) { }
81+
ParseTree n = list.get(i);
82+
if(n.getChildren().size() != 1){
83+
throw new ConfigCompileException("Unexpected parameters passed to the \"catch\" clause."
84+
+ " Exactly one argument must be passed.", n.getTarget());
85+
}
86+
complex_try.addChild(n.getChildAt(0));
87+
complex_try.addChild(getArgumentOrNull(list.get(i + 1)));
88+
} else {
89+
// We have something like catch { }. In this case, this must be the final
90+
// catch statement, and we need to verify that there isn't a catch following it.
91+
if(list.size() > i + 2){
92+
if(nodeIsCatchKeyword(list.get(i + 2))){
93+
throw new ConfigCompileException("A catch with no datatype (try { } ... catch { }) must be the final"
94+
+ " catch clause in the try/catch statement", list.get(i + 2).getTarget());
95+
}
96+
}
97+
// Passed the inspection.
98+
complex_try.addChild(getArgumentOrNull(list.get(i + 1)));
99+
}
100+
// remove the catch keyword and the code block
101+
list.remove(i);
102+
list.remove(i);
103+
--i;
104+
}
105+
106+
// Set the new function into place
107+
list.add(keywordPosition, complex_try);
108+
109+
return keywordPosition;
110+
}
111+
112+
private Construct getData(List<ParseTree> list, int index){
113+
return list.get(index).getData();
114+
}
115+
116+
private boolean nodeIsCatchKeyword(ParseTree node){
117+
return
118+
(node.getData() instanceof CKeyword ||
119+
node.getData() instanceof CFunction)
120+
&& node.getData().val().equals("catch");
121+
}
122+
123+
}

0 commit comments

Comments
 (0)