Skip to content
Permalink
Browse files
JEXL-369: const variables require assignment at declaration time;
- simplified code accordingly;
  • Loading branch information
henrib committed May 18, 2022
1 parent 67d2510 commit be7e4cb50a63e2dde647f983c45411315b5827a3
Showing 6 changed files with 37 additions and 87 deletions.
@@ -34,7 +34,7 @@ public class LexicalScope {
* Bits per symbol.
* Declared, const, defined.
*/
protected static final int BITS_PER_SYMBOL = 3;
protected static final int BITS_PER_SYMBOL = 2;
/**
* Number of symbols.
*/
@@ -133,18 +133,6 @@ public boolean isConstant(final int symbol) {
return isSet(bit);
}

/**
* Checks whether a const symbol has been defined, ie has a value.
*
* @param symbol the symbol
* @return true if defined, false otherwise
*/
public boolean isDefined(final int symbol) {
final int bit = (symbol << BITS_PER_SYMBOL) | 2;
return isSet(bit);

}

/**
* Adds a symbol in this scope.
*
@@ -171,17 +159,6 @@ public boolean addConstant(final int symbol) {
return set(bit);
}

/**
* Defines a constant in this scope.
*
* @param symbol the symbol
* @return true if registered, false if symbol was already registered
*/
public boolean defineSymbol(final int symbol) {
final int bit = (symbol << BITS_PER_SYMBOL) | 2;
return set(bit);
}

/**
* Clear all symbols.
*
@@ -34,8 +34,6 @@ public class ASTIdentifier extends JexlNode {
private static final int LEXICAL = 3;
/** The const variable flag. */
private static final int CONST = 4;
/** The defined variable flag. */
private static final int DEFINED = 5;

ASTIdentifier(final int id) {
super(id);
@@ -127,14 +125,6 @@ public void setConstant(final boolean f) {
flags = set(CONST, flags, f);
}

public boolean isDefined() {
return isSet(DEFINED, flags);
}

public void setDefined(final boolean f) {
flags = set(DEFINED, flags, f);
}

public String getName() {
return name;
}
@@ -46,21 +46,11 @@ public boolean isConstant(final int symbol) {
return lexicalScope != null && lexicalScope.isConstant(symbol);
}

@Override
public boolean isDefined(final int symbol) {
return lexicalScope != null && lexicalScope.isDefined(symbol);
}

@Override
public void setConstant(int symbol) {
lexicalScope.addConstant(symbol);
}

@Override
public void setDefined(int symbol) {
lexicalScope.defineSymbol(symbol);
}

@Override
public int getSymbolCount() {
return lexicalScope == null? 0 : lexicalScope.getSymbolCount();
@@ -99,7 +99,6 @@ public interface LexicalUnit {
*/
boolean declareSymbol(int symbol);
void setConstant(int symbol);
void setDefined(int symbol);

/**
* Checks whether a symbol is declared in this lexical unit.
@@ -108,7 +107,6 @@ public interface LexicalUnit {
*/
boolean hasSymbol(int symbol);
boolean isConstant(int symbol);
boolean isDefined(int symbol);

/**
* @return the number of local variables declared in this unit
@@ -337,7 +335,6 @@ protected String checkVariable(final ASTIdentifier identifier, final String name
// track if const is defined or not
if (unit.isConstant(symbol)) {
identifier.setConstant(true);
identifier.setDefined(unit.isDefined(symbol));
}
} else if (info instanceof JexlNode.Info) {
declared = isSymbolDeclared((JexlNode.Info) info, symbol);
@@ -412,7 +409,6 @@ protected void declareFunction(final ASTVar variable, final Token token) {
if (declareSymbol(symbol)) {
scope.addLexical(symbol);
block.setConstant(symbol);
block.setDefined(symbol);
} else {
if (getFeatures().isLexical()) {
throw new JexlException(variable, name + ": variable is already declared");
@@ -488,7 +484,6 @@ protected void declareParameter(final Token token, boolean lexical, boolean cons
scope.addLexical(symbol);
if (constant) {
block.setConstant(symbol);
block.setDefined(symbol); // worst case is no argument, parameter will bind to null
}
}
}
@@ -620,32 +615,12 @@ protected void jjtreeCloseNodeScope(final JexlNode node) {
if (!lv.isLeftValue()) {
throw new JexlException.Assignment(lv.jexlInfo(), null).clean();
}
if (lv instanceof ASTIdentifier) {
if (lv instanceof ASTIdentifier && !(lv instanceof ASTVar)) {
ASTIdentifier var = (ASTIdentifier) lv;
int symbol = var.getSymbol();
boolean isconst = symbol >= 0 && block != null && block.isConstant(symbol);
if (isconst) {
if (!(var instanceof ASTVar)) { // if not a declaration...
if (block.isDefined(symbol)) {
throw new JexlException.Assignment(var.jexlInfo(), var.getName()).clean();
} else {
block.setDefined(symbol);
}
} else {
block.setDefined(symbol);
}
}
}
} else {
// control that a const is defined before usage
int nchildren = node.jjtGetNumChildren();
for(int c = 0; c < nchildren; ++c) {
JexlNode child = node.jjtGetChild(c);
if (child instanceof ASTIdentifier) {
ASTIdentifier var = (ASTIdentifier) child;
if (var.isConstant() && !var.isDefined()) {
throw new JexlException.Parsing(info, var.getName() + ": constant is not defined").clean();
}
if (isconst) { // if not a declaration...
throw new JexlException.Parsing(var.jexlInfo(), var.getName() +": const assignment.").clean();
}
}
}
@@ -445,7 +445,7 @@ void Var() #void : {}
|
<LET> DeclareVar(true, false) (LOOKAHEAD(1) <assign> Expression() #Assignment(2))?
|
<CONST> DeclareVar(true, true) (LOOKAHEAD(1) <assign> Expression() #Assignment(2))?
<CONST> DeclareVar(true, true) <assign> Expression() #Assignment(2)
}

void DeclareVar(boolean lexical, boolean constant) #Var :
@@ -438,7 +438,7 @@ public void test271e() {
}

@Test
public void testConst0() {
public void testConst0a() {
final JexlFeatures f = new JexlFeatures();
final JexlEngine jexl = new JexlBuilder().strict(true).create();
final JexlScript script = jexl.createScript(
@@ -448,37 +448,55 @@ public void testConst0() {
Assert.assertEquals(22, result);
}

@Test
public void testConstb0() {
final JexlFeatures f = new JexlFeatures();
final JexlEngine jexl = new JexlBuilder().strict(true).create();
final JexlScript script = jexl.createScript(
"{ const x = 10; }{ const x = 20; }");
final JexlContext jc = new MapContext();
final Object result = script.execute(jc);
Assert.assertEquals(20, result);
}

@Test
public void testConst1() {
final JexlFeatures f = new JexlFeatures();
final JexlEngine jexl = new JexlBuilder().strict(true).create();
try {
final JexlScript script = jexl.createScript(
"const x; x + 1");
Assert.fail("should fail, x is not defined");
"const foo; foo");
Assert.fail("should fail, const foo must be followed by assign.");
} catch(JexlException.Parsing xparse) {
Assert.assertTrue(xparse.getMessage().contains("x"));
Assert.assertTrue(xparse.getMessage().contains("const"));
}
}

@Test
public void testConst2() {
public void testConst2a() {
final JexlFeatures f = new JexlFeatures();
final JexlEngine jexl = new JexlBuilder().strict(true).create();
final JexlScript script = jexl.createScript( "const x; x = 1");
Object result = script.execute(null);
Assert.assertEquals(1, result);
for(String op : Arrays.asList("=", "+=", "-=", "/=", "*=", "%=", "<<=", ">>=", ">>>=", "^=", "&=", "|=")) {
try {
final JexlScript script = jexl.createScript("const foo = 42; foo "+op+" 1;");
Assert.fail("should fail, const precludes assignment");
} catch (JexlException.Parsing xparse) {
Assert.assertTrue(xparse.getMessage().contains("foo"));
}
}
}

@Test
public void testConst3() {
public void testConst2b() {
final JexlFeatures f = new JexlFeatures();
final JexlEngine jexl = new JexlBuilder().strict(true).create();
try {
final JexlScript script = jexl.createScript( "const x; x = 1; x = 2;");
Assert.fail("should fail, x is not defined");
} catch(JexlException.Parsing xparse) {
Assert.assertTrue(xparse.getMessage().contains("x"));
for(String op : Arrays.asList("=", "+=", "-=", "/=", "*=", "%=", "<<=", ">>=", ">>>=", "^=", "&=", "|=")) {
try {
final JexlScript script = jexl.createScript("{ const foo = 42; } { let foo = 0; foo " + op + " 1; }");
Assert.assertNotNull(script);
} catch(JexlException.Parsing xparse) {
Assert.fail(xparse.toString());
}
}
}

0 comments on commit be7e4cb

Please sign in to comment.