Skip to content

Commit

Permalink
support add alias
Browse files Browse the repository at this point in the history
  • Loading branch information
DQinYuan committed Jan 14, 2025
1 parent daf9893 commit 3b387e5
Show file tree
Hide file tree
Showing 10 changed files with 296 additions and 14 deletions.
2 changes: 1 addition & 1 deletion README-source.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ QLExpress4 作为 QLExpress 的最新演进版本,基于 Antlr4 重写了解
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>QLExpress4</artifactId>
<version>4.0.0-beta.2</version>
<version>4.0.0-beta.3</version>
</dependency>
----

Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<groupId>com.alibaba</groupId>
<artifactId>QLExpress4</artifactId>
<packaging>jar</packaging>
<version>4.0.0-beta.2</version>
<version>4.0.0-beta.3</version>
<name>QLExpress</name>
<description>QLExpress is a powerful, lightweight, dynamic language for the Java platform aimed at improving
developers’ productivity in different business scenes.
Expand Down
2 changes: 1 addition & 1 deletion src/main/antlr4/QLParser.g4
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ primaryNoFix
| '[' listItems? ']' # listExpr
| '{' mapEntries RBRACE # mapExpr
| '{' blockStatements? RBRACE # blockExpr
| IF '(' condition=expression ')' THEN? thenBody=ifBody ('else' elseBody=ifBody)? # ifExpr
| IF '(' condition=expression ')' THEN? thenBody=ifBody (ELSE elseBody=ifBody)? # ifExpr
| TRY '{' blockStatements? RBRACE tryCatches? tryFinally? # tryCatchExpr
| SELECTOR_START SelectorVariable_VANME RBRACE # contextSelectExpr
;
Expand Down
24 changes: 23 additions & 1 deletion src/main/java/com/alibaba/qlexpress4/Express4Runner.java
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ private BatchAddFunctionResult addFunctionByAnnotation(Class<?> clazz, Object ob
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
if (!BasicUtil.isPublic(method)) {
result.getSucc().add(method.getName());
result.getFail().add(method.getName());
continue;
}
if (QLFunctionUtil.containsQLFunctionForMethod(method)) {
Expand Down Expand Up @@ -339,4 +339,26 @@ public boolean addOperator(String operator, CustomBinaryOperator customBinaryOpe
public boolean replaceDefaultOperator(String operator, CustomBinaryOperator customBinaryOperator) {
return operatorManager.replaceDefaultOperator(operator, customBinaryOperator);
}

/**
* add alias for keyWord, operator and function
* @param alias must be a valid id
* @param originToken key word in qlexpress
* @return true if add alias successfully
*/
public boolean addAlias(String alias, String originToken) {
boolean addKeyWordAliasResult = operatorManager.addKeyWordAlias(alias, originToken);
boolean addOperatorAliasResult = operatorManager.addOperatorAlias(alias, originToken);
boolean addFunctionAliasResult = addFunctionAlias(alias, originToken);

return addKeyWordAliasResult || addOperatorAliasResult || addFunctionAliasResult;
}

private boolean addFunctionAlias(String alias, String originToken) {
CustomFunction customFunction = userDefineFunction.get(originToken);
if (customFunction != null) {
return userDefineFunction.putIfAbsent(alias, customFunction) == null;
}
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ enum OpType {
}

/**
* judge lexeme is opType or not
* determine whether lexeme is opType or not
* @param lexeme lexeme
* @param opType type of operator
* @return true if lexeme is opType
Expand All @@ -26,4 +26,10 @@ enum OpType {
*/
Integer precedence(String lexeme);

/**
* get alias token type of lexeme
* @param lexeme
* @return alias token type
*/
Integer getAlias(String lexeme);
}
160 changes: 153 additions & 7 deletions src/main/java/com/alibaba/qlexpress4/aparser/SyntaxTreeFactory.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
package com.alibaba.qlexpress4.aparser;

import com.alibaba.qlexpress4.runtime.operator.OperatorManager;
import org.antlr.v4.runtime.CharStream;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.Parser;
import org.antlr.v4.runtime.RuleContext;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.TokenFactory;
import org.antlr.v4.runtime.TokenSource;
import org.antlr.v4.runtime.TokenStream;
import org.antlr.v4.runtime.WritableToken;
import org.antlr.v4.runtime.atn.DecisionInfo;
import org.antlr.v4.runtime.atn.DecisionState;
import org.antlr.v4.runtime.atn.PredictionMode;
import org.antlr.v4.runtime.misc.Interval;

import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;
Expand All @@ -20,16 +27,11 @@ public class SyntaxTreeFactory {

private static final AtomicBoolean IS_WARM_UP = new AtomicBoolean();

public static void warmUp () {
public static void warmUp() {
if (IS_WARM_UP.compareAndSet(false, true)) {
// warm up
warmUpExpress("1+1");
warmUpExpress("a = b + c");
warmUpExpress("int v = 100;");
warmUpExpress("TEST(1,2)");
warmUpExpress("if (true) {return 10;} else {return 20;}");
warmUpExpress("for (int i = 0; i < 10; i++) {i}");
warmUpExpress("for (a:[1,2,3]) {a}");
}
}

Expand All @@ -43,7 +45,8 @@ public static QLParser.ProgramContext buildTree(String script, ParserOperatorMan
InterpolationMode interpolationMode) {
QLexer lexer = new QLexer(CharStreams.fromString(script), interpolationMode);
CommonTokenStream tokens = new CommonTokenStream(lexer);
QLParser qlGrammarParser = new QLParser(tokens, operatorManager, interpolationMode);
QLParser qlGrammarParser = new QLParser(new AliasTokenStream(tokens, operatorManager),
operatorManager, interpolationMode);
qlGrammarParser.setErrorHandler(new QLErrorStrategy(script));
qlGrammarParser.getInterpreter().setPredictionMode(PredictionMode.SLL);
if (profile) {
Expand Down Expand Up @@ -87,4 +90,147 @@ private static void profileParser(Parser parser) {
}
}
}

private static Token preHandleToken(Token originToken, ParserOperatorManager operatorManager) {
if (originToken instanceof WritableToken && originToken.getType() == QLexer.ID) {
Integer aliasId = operatorManager.getAlias(originToken.getText());
if (aliasId != null && originToken.getType() != aliasId) {
((WritableToken) originToken).setType(aliasId);
}
}
return originToken;
}

private static class AliasTokenSource implements TokenSource {

private final TokenSource tokenSource;

private final ParserOperatorManager operatorManager;

private AliasTokenSource(TokenSource tokenSource, ParserOperatorManager operatorManager) {
this.tokenSource = tokenSource;
this.operatorManager = operatorManager;
}

@Override
public Token nextToken() {
return preHandleToken(tokenSource.nextToken(), operatorManager);
}

@Override
public int getLine() {
return tokenSource.getLine();
}

@Override
public int getCharPositionInLine() {
return tokenSource.getCharPositionInLine();
}

@Override
public CharStream getInputStream() {
return tokenSource.getInputStream();
}

@Override
public String getSourceName() {
return tokenSource.getSourceName();
}

@Override
public void setTokenFactory(TokenFactory<?> factory) {
tokenSource.setTokenFactory(factory);
}

@Override
public TokenFactory<?> getTokenFactory() {
return tokenSource.getTokenFactory();
}
}

private static class AliasTokenStream implements TokenStream {

private final TokenStream stream;
private final ParserOperatorManager operatorManager;

private AliasTokenStream(TokenStream stream, ParserOperatorManager operatorManager) {
this.stream = stream;
this.operatorManager = operatorManager;
}

@Override
public Token LT(int k) {
return preHandleToken(stream.LT(k), operatorManager);
}

@Override
public Token get(int index) {
return preHandleToken(stream.get(index), operatorManager);
}

@Override
public TokenSource getTokenSource() {
return new AliasTokenSource(stream.getTokenSource(), operatorManager);
}

@Override
public String getText(Interval interval) {
return stream.getText(interval);
}

@Override
public String getText() {
return stream.getText();
}

@Override
public String getText(RuleContext ctx) {
return stream.getText(ctx);
}

@Override
public String getText(Token start, Token stop) {
return stream.getText(start, stop);
}

@Override
public void consume() {
stream.consume();
}

@Override
public int LA(int i) {
return preHandleToken(LT(i), operatorManager).getType();
}

@Override
public int mark() {
return stream.mark();
}

@Override
public void release(int marker) {
stream.release(marker);
}

@Override
public int index() {
return stream.index();
}

@Override
public void seek(int index) {
stream.seek(index);
}

@Override
public int size() {
return stream.size();
}

@Override
public String getSourceName() {
return stream.getSourceName();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import com.alibaba.qlexpress4.QLPrecedences;
import com.alibaba.qlexpress4.aparser.OperatorFactory;
import com.alibaba.qlexpress4.aparser.ParserOperatorManager;
import com.alibaba.qlexpress4.aparser.QLexer;
import com.alibaba.qlexpress4.exception.ErrorReporter;
import com.alibaba.qlexpress4.exception.UserDefineException;
import com.alibaba.qlexpress4.runtime.QRuntime;
Expand Down Expand Up @@ -130,8 +131,29 @@ public class OperatorManager implements OperatorFactory, ParserOperatorManager {
}
}

private static final Map<String, Integer> ALIASABLE_KEYWORDS = new HashMap<>();

static {
ALIASABLE_KEYWORDS.put("if", QLexer.IF);
ALIASABLE_KEYWORDS.put("then", QLexer.THEN);
ALIASABLE_KEYWORDS.put("else", QLexer.ELSE);
ALIASABLE_KEYWORDS.put("for", QLexer.FOR);
ALIASABLE_KEYWORDS.put("while", QLexer.WHILE);
ALIASABLE_KEYWORDS.put("break", QLexer.BREAK);
ALIASABLE_KEYWORDS.put("continue", QLexer.CONTINUE);
ALIASABLE_KEYWORDS.put("return", QLexer.RETURN);
ALIASABLE_KEYWORDS.put("function", QLexer.FUNCTION);
ALIASABLE_KEYWORDS.put("macro", QLexer.MACRO);
ALIASABLE_KEYWORDS.put("new", QLexer.NEW);
ALIASABLE_KEYWORDS.put("null", QLexer.NULL);
ALIASABLE_KEYWORDS.put("true", QLexer.TRUE);
ALIASABLE_KEYWORDS.put("false", QLexer.FALSE);
}

private final Map<String, BinaryOperator> customBinaryOperatorMap = new ConcurrentHashMap<>();

private final Map<String, Integer> keyWordAliases = new ConcurrentHashMap<>();

/**
* @param operatorName
* @param customBinaryOperator
Expand Down Expand Up @@ -236,4 +258,53 @@ public boolean isOpType(String lexeme, OpType opType) {
public Integer precedence(String lexeme) {
return getBinaryOperator(lexeme).getPriority();
}

@Override
public Integer getAlias(String lexeme) {
return keyWordAliases.get(lexeme);
}

public boolean addKeyWordAlias(String lexeme, String keyWord) {
Integer keyWordId = ALIASABLE_KEYWORDS.get(keyWord);
if (keyWordId == null) {
return false;
}
keyWordAliases.put(lexeme, keyWordId);
return true;
}

public boolean addOperatorAlias(String lexeme, String operator) {
BinaryOperator originDefaultOp = DEFAULT_BINARY_OPERATOR_MAP.get(operator);
if (originDefaultOp != null) {
BinaryOperator newOperator = adaptOriginOperator(originDefaultOp, lexeme);
BinaryOperator prev = customBinaryOperatorMap.putIfAbsent(lexeme, newOperator);
return prev == null;
}
BinaryOperator originCusOp = customBinaryOperatorMap.get(operator);
if (originCusOp != null) {
BinaryOperator newOperator = adaptOriginOperator(originCusOp, lexeme);
BinaryOperator prev = customBinaryOperatorMap.putIfAbsent(lexeme, newOperator);
return prev == null;
}
return false;
}

private BinaryOperator adaptOriginOperator(BinaryOperator originOperator, String lexeme) {
return new BinaryOperator() {
@Override
public Object execute(Value left, Value right, QRuntime qRuntime, QLOptions qlOptions, ErrorReporter errorReporter) {
return originOperator.execute(left, right, qRuntime, qlOptions, errorReporter);
}

@Override
public String getOperator() {
return lexeme;
}

@Override
public int getPriority() {
return originOperator.getPriority();
}
};
}
}
Loading

0 comments on commit 3b387e5

Please sign in to comment.