diff --git a/proparse/src/main/antlr4/org/prorefactor/proparse/antlr4/Proparse.g4 b/proparse/src/main/antlr4/org/prorefactor/proparse/antlr4/Proparse.g4 index bcd3811e9..61adb5a0b 100644 --- a/proparse/src/main/antlr4/org/prorefactor/proparse/antlr4/Proparse.g4 +++ b/proparse/src/main/antlr4/org/prorefactor/proparse/antlr4/Proparse.g4 @@ -1329,7 +1329,7 @@ datatypeVar: | UNSIGNEDBYTE | UNSIGNEDSHORT | UNSIGNEDINTEGER - | { support.abbrevDatatype(_input.LT(1).getText()) !=0 }? id=ID // Like 'i' for INTEGER or 'de' for DECIMAL + | { ABLNodeType.abbrevDatatype(_input.LT(1).getText()) != ABLNodeType.INVALID_NODE }? id=ID // Like 'i' for INTEGER or 'de' for DECIMAL | { !support.isDataTypeVariable(_input.LT(1)) }? typeName ; diff --git a/proparse/src/main/java/org/prorefactor/core/ABLNodeType.java b/proparse/src/main/java/org/prorefactor/core/ABLNodeType.java index 1e0113519..03c80f9a6 100644 --- a/proparse/src/main/java/org/prorefactor/core/ABLNodeType.java +++ b/proparse/src/main/java/org/prorefactor/core/ABLNodeType.java @@ -2081,6 +2081,43 @@ public static DataType getDataType(int nodeType) { } } + /** + * An AS phrase allows further abbreviations on the datatype names. Input a token's text, this returns 0 if it is not + * a datatype abbreviation, otherwise returns the integer token type for the abbreviation. Here's the normal keyword + * abbreviation, with what AS phrase allows: + * + */ + public static ABLNodeType abbrevDatatype(String text) { + String s = text.toLowerCase(); + if ("cha".startsWith(s)) + return ABLNodeType.CHARACTER; + if ("da".equals(s) || "dat".equals(s)) + return ABLNodeType.DATE; + if ("de".equals(s)) + return ABLNodeType.DECIMAL; + if ("i".equals(s) || "in".equals(s)) + return ABLNodeType.INTEGER; + if ("logical".startsWith(s)) + return ABLNodeType.LOGICAL; + if ("rec".equals(s) || "reci".equals(s)) + return ABLNodeType.RECID; + if ("rowi".equals(s)) + return ABLNodeType.ROWID; + if ("widget-h".startsWith(s) && s.length() >= 4) + return ABLNodeType.WIDGETHANDLE; + + return ABLNodeType.INVALID_NODE; + } + private static void generateKeywordsG4(final PrintStream out) { out.println("// Generated file - Do not manually edit"); out.println(); diff --git a/proparse/src/main/java/org/prorefactor/proparse/JPNodeVisitor.java b/proparse/src/main/java/org/prorefactor/proparse/JPNodeVisitor.java index 1b8f74154..cbba20e16 100644 --- a/proparse/src/main/java/org/prorefactor/proparse/JPNodeVisitor.java +++ b/proparse/src/main/java/org/prorefactor/proparse/JPNodeVisitor.java @@ -1163,7 +1163,7 @@ else if (builder.getNodeType() == ABLNodeType.ROW) else if (builder.getNodeType() == ABLNodeType.WIDGET) builder.changeType(ABLNodeType.WIDGETHANDLE); else if (ctx.id != null) - builder.changeType(ABLNodeType.getNodeType(ParserSupport.abbrevDatatype(ctx.id.getText()))); + builder.changeType(ABLNodeType.abbrevDatatype(ctx.id.getText())); return builder.setRuleNode(ctx); } @@ -2086,7 +2086,10 @@ public Builder visitNullPhrase(NullPhraseContext ctx) { @Override public Builder visitOnStatement(OnStatementContext ctx) { - return createStatementTreeFromFirstNode(ctx).setBlock(true); + support.visitorEnterScope(ctx); + Builder builder = createStatementTreeFromFirstNode(ctx).setBlock(true); + support.visitorExitScope(ctx); + return builder; } @Override @@ -2724,7 +2727,7 @@ public Builder visitTriggerOfSub1(TriggerOfSub1Context ctx) { @Override public Builder visitTriggerOfSub2(TriggerOfSub2Context ctx) { - support.defVar(ctx.id.getText()); + support.defVar(ctx.id.getText()); // FIXME Is that really needed at this stage ? return createTreeFromFirstNode(ctx).setRuleNode(ctx); } @@ -2736,7 +2739,7 @@ public Builder visitTriggerTableLabel(TriggerTableLabelContext ctx) { @Override public Builder visitTriggerOld(TriggerOldContext ctx) { Builder node = createTreeFromFirstNode(ctx).setRuleNode(ctx); - support.defVar(ctx.id.getText()); + support.defVar(ctx.id.getText()); // FIXME Is that really needed at this stage ? return node; } diff --git a/proparse/src/main/java/org/prorefactor/proparse/support/ParserSupport.java b/proparse/src/main/java/org/prorefactor/proparse/support/ParserSupport.java index 57c06c74c..041172854 100644 --- a/proparse/src/main/java/org/prorefactor/proparse/support/ParserSupport.java +++ b/proparse/src/main/java/org/prorefactor/proparse/support/ParserSupport.java @@ -27,7 +27,6 @@ import org.antlr.v4.runtime.tree.ParseTreeProperty; import org.prorefactor.core.ABLNodeType; import org.prorefactor.core.JPNode; -import org.prorefactor.proparse.antlr4.Proparse; import org.prorefactor.proparse.support.SymbolScope.FieldType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -92,59 +91,6 @@ public IProparseEnvironment getProparseSession() { // Functions triggered from proparse.g4 // ************************************ - /** - * An AS phrase allows further abbreviations on the datatype names. Input a token's text, this returns 0 if it is not - * a datatype abbreviation, otherwise returns the integer token type for the abbreviation. Here's the normal keyword - * abbreviation, with what AS phrase allows: - * - */ - public static int abbrevDatatype(String text) { - String s = text.toLowerCase(); - if ("cha".startsWith(s)) - return Proparse.CHARACTER; - if ("da".equals(s) || "dat".equals(s)) - return Proparse.DATE; - if ("de".equals(s)) - return Proparse.DECIMAL; - if ("i".equals(s) || "in".equals(s)) - return Proparse.INTEGER; - if ("logical".startsWith(s)) - return Proparse.LOGICAL; - if ("rec".equals(s) || "reci".equals(s)) - return Proparse.RECID; - if ("rowi".equals(s)) - return Proparse.ROWID; - if ("widget-h".startsWith(s) && s.length() >= 4) - return Proparse.WIDGETHANDLE; - return 0; - } - - // TEMP-ANTLR4 - public void visitorEnterScope(RuleContext ctx) { - SymbolScope scope = innerScopesMap.get(ctx); - if (scope != null) { - currentScope = scope; - } - } - - // TEMP-ANTLR4 - public void visitorExitScope(RuleContext ctx) { - SymbolScope scope = innerScopesMap.get(ctx); - if (scope != null) { - currentScope = currentScope.getSuperScope(); - } - } - - // TEMP-ANTLR4 public void addInnerScope(RuleContext ctx) { currentScope = new SymbolScope(session, currentScope); innerScopes.add(currentScope); @@ -235,39 +181,11 @@ public boolean recordSemanticPredicate(Token lt1, Token lt2, Token lt3) { return (schemaTablePriority ? isTableSchemaFirst(recname.toLowerCase()) : isTable(recname.toLowerCase())) != null; } - // ******************************************* - // End of functions triggered from proparse.g4 - // ******************************************* - - public void pushNode(ParseTree ctx, JPNode node) { - nodes.put(ctx, node); - } - - public JPNode getNode(ParseTree ctx) { - return nodes.get(ctx); - } - public void pushRecordExpression(RuleContext ctx, String recName) { recordExpressions.put(ctx, schemaTablePriority ? currentScope.isTableSchemaFirst(recName.toLowerCase()) : currentScope.isTable(recName.toLowerCase())); } - public FieldType getRecordExpression(RuleContext ctx) { - return recordExpressions.get(ctx); - } - - public void clearRecordExpressions() { - recordExpressions = new ParseTreeProperty<>(); - } - - public FieldType isTable(String inName) { - return currentScope.isTable(inName); - } - - public FieldType isTableSchemaFirst(String inName) { - return currentScope.isTableSchemaFirst(inName); - } - /** Returns true if the lookahead is a table name, and not a var name. */ public boolean isTableName(Token token) { int numDots = CharMatcher.is('.').countIn(token.getText()); @@ -286,44 +204,12 @@ public boolean isVar(String name) { return currentScope.isVariable(name); } - public boolean isInlineVar(String name) { - return currentScope.isInlineVariable(name); - } - - public int isMethodOrFunc(String name) { - // Methods and user functions are only at the "unit" (class) scope. - // Methods can also be inherited from superclasses. - return unitScope.isMethodOrFunction(name); - } - public int isMethodOrFunc(Token token) { if (token == null) return 0; return unitScope.isMethodOrFunction(token.getText()); } - /** - * @return True if parsing a class or interface - */ - public boolean isClass() { - return !Strings.isNullOrEmpty(className); - } - - /** - * @return True if parsing an interface - */ - public boolean isInterface() { - return unitIsInterface; - } - - public boolean isEnum() { - return unitIsEnum; - } - - public boolean isSchemaTablePriority() { - return schemaTablePriority; - } - public void setSchemaTablePriority(boolean priority) { this.schemaTablePriority = priority; } @@ -344,11 +230,6 @@ public boolean unknownMethodCallsAllowed() { return allowUnknownMethodCalls; } - // TODO Speed issue in this function, multiplied JPNode tree generation time by a factor 10 - public String lookupClassName(String text) { - return classFinder.lookup(text); - } - public boolean hasHiddenBefore(TokenStream stream) { int currIndex = stream.index(); // Obviously no hidden token for first token @@ -366,4 +247,80 @@ public boolean hasHiddenAfter(TokenStream stream) { // Otherwise see if token is in different channel return stream.get(currIndex + 1).getChannel() != Token.DEFAULT_CHANNEL; } + + /** + * @return True if parsing a class or interface + */ + public boolean isClass() { + return !Strings.isNullOrEmpty(className); + } + + /** + * @return True if parsing an interface + */ + public boolean isInterface() { + return unitIsInterface; + } + + public boolean isEnum() { + return unitIsEnum; + } + + private FieldType isTable(String inName) { + return currentScope.isTable(inName); + } + + private FieldType isTableSchemaFirst(String inName) { + return currentScope.isTableSchemaFirst(inName); + } + + // ******************************************* + // End of functions triggered from proparse.g4 + // ******************************************* + + public void pushNode(ParseTree ctx, JPNode node) { + nodes.put(ctx, node); + } + + public JPNode getNode(ParseTree ctx) { + return nodes.get(ctx); + } + + public void visitorEnterScope(RuleContext ctx) { + SymbolScope scope = innerScopesMap.get(ctx); + if (scope != null) { + currentScope = scope; + } + } + + public void visitorExitScope(RuleContext ctx) { + SymbolScope scope = innerScopesMap.get(ctx); + if (scope != null) { + currentScope = currentScope.getSuperScope(); + } + } + + public FieldType getRecordExpression(RuleContext ctx) { + return recordExpressions.get(ctx); + } + + public void clearRecordExpressions() { + recordExpressions = new ParseTreeProperty<>(); + } + + public boolean isInlineVar(String name) { + return currentScope.isInlineVariable(name); + } + + public int isMethodOrFunc(String name) { + // Methods and user functions are only at the "unit" (class) scope. + // Methods can also be inherited from superclasses. + return unitScope.isMethodOrFunction(name); + } + + // TODO Speed issue in this function, multiplied JPNode tree generation time by a factor 10 + public String lookupClassName(String text) { + return classFinder.lookup(text); + } + } diff --git a/proparse/src/test/java/org/prorefactor/proparse/ExpressionEngineTest.java b/proparse/src/test/java/org/prorefactor/proparse/ExpressionEngineTest.java index a4f68f45d..c2b1f3322 100644 --- a/proparse/src/test/java/org/prorefactor/proparse/ExpressionEngineTest.java +++ b/proparse/src/test/java/org/prorefactor/proparse/ExpressionEngineTest.java @@ -481,6 +481,22 @@ public void testInlineVariable03() { assertEquals(exp.getDataType().getPrimitive(), PrimitiveDataType.DECIMAL); } + @Test + public void testInlineVariable04() { + // Not a good test case, this has to be removed. The way inline variables are handled has to be rewritten, + // as it is the only reason why we have to maintain the ParseSupport.currentScope object from JPNodeVisitor + // This test case just ensures that the scope is set correctly, as a bug was detected late in the dev cycle + // that broke the parser with NPE in some inline variables cases + ParseUnit unit01 = new ParseUnit("procedure p1: message 'xx' update lVar as log. end.", session); + unit01.treeParser01(); + ParseUnit unit02 = new ParseUnit("function f1 returns char(): message 'xx' update lVar as log. end.", session); + unit02.treeParser01(); + ParseUnit unit03 = new ParseUnit("on choose of btn1 do: message 'xx' update lVar as log. end.", session); + unit03.treeParser01(); + ParseUnit unit04 = new ParseUnit("class cls1: method public void m1(): message 'xx' update lVar as log. end. end.", session); + unit04.treeParser01(); + } + private void testSimpleExpression(String code, DataType expected) { ParseUnit unit01 = new ParseUnit(code, session); unit01.treeParser01();