Skip to content

Commit bb3c331

Browse files
committed
getOutVarAttrs
1 parent 4f6c502 commit bb3c331

File tree

5 files changed

+237
-2
lines changed

5 files changed

+237
-2
lines changed

README-EN-source.adoc

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,10 @@ QLExpress4 provides a method to parse all variables that need to be passed in fr
170170
include::./src/test/java/com/alibaba/qlexpress4/Express4RunnerTest.java[tag=getOutVarNames]
171171
----
172172

173-
To parse all functions that require external input, call the `getOutFunctions` method.
173+
More script-dependency parsing tools:
174+
175+
* `getOutFunctions` – parses all functions that must be defined externally
176+
* `getOutVarAttrs` – parses all variables (and the attributes they reference) that must be supplied from outside; an enhanced version of `getOutVarNames`
174177

175178
=== High-Precision Calculation
176179

README-source.adoc

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,10 @@ QLExpress4 提供了一个方法,可以解析出脚本中所有需要从外部
172172
include::./src/test/java/com/alibaba/qlexpress4/Express4RunnerTest.java[tag=getOutVarNames]
173173
----
174174

175-
如果想解析所有需要从外部传入的函数,则可以调用 `getOutFunctions` 方法。
175+
更多脚本依赖解析工具:
176+
177+
* `getOutFunctions`: 解析所有需要从外部定义的函数
178+
* `getOutVarAttrs`:解析所有需要从外部传入变量及其涉及的属性,`getOutVarNames` 的增强版本
176179

177180
=== 高精度计算
178181

src/main/java/com/alibaba/qlexpress4/Express4Runner.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import com.alibaba.qlexpress4.aparser.ImportManager;
2020
import com.alibaba.qlexpress4.aparser.MacroDefine;
2121
import com.alibaba.qlexpress4.aparser.OutFunctionVisitor;
22+
import com.alibaba.qlexpress4.aparser.OutVarAttrsVisitor;
2223
import com.alibaba.qlexpress4.aparser.OutVarNamesVisitor;
2324
import com.alibaba.qlexpress4.aparser.QCompileCache;
2425
import com.alibaba.qlexpress4.aparser.QLParser;
@@ -197,6 +198,18 @@ public Set<String> getOutVarNames(String script) {
197198
return outVarNamesVisitor.getOutVars();
198199
}
199200

201+
/**
202+
* get out var attrs in script
203+
* @param script
204+
* @return out var attrs
205+
*/
206+
public Set<List<String>> getOutVarAttrs(String script) {
207+
QLParser.ProgramContext programContext = parseToSyntaxTree(script);
208+
OutVarAttrsVisitor outVarAttrsVisitor = new OutVarAttrsVisitor(inheritDefaultImport());
209+
programContext.accept(outVarAttrsVisitor);
210+
return outVarAttrsVisitor.getOutVarAttrs();
211+
}
212+
200213
/**
201214
* get out functions(Functions that need to be passed from outside the script through context) in script
202215
* @param script
Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
package com.alibaba.qlexpress4.aparser;
2+
3+
import com.alibaba.qlexpress4.utils.QLStringUtils;
4+
import org.antlr.v4.runtime.Token;
5+
import org.antlr.v4.runtime.tree.TerminalNode;
6+
7+
import java.util.ArrayList;
8+
import java.util.HashSet;
9+
import java.util.List;
10+
import java.util.Set;
11+
import java.util.stream.Collectors;
12+
13+
public class OutVarAttrsVisitor extends ScopeStackVisitor {
14+
15+
private final Set<List<String>> outVarAttrs = new HashSet<>();
16+
17+
private final ImportManager importManager;
18+
19+
public OutVarAttrsVisitor(ImportManager importManager) {
20+
super(new ExistVarStack(null));
21+
this.importManager = importManager;
22+
}
23+
24+
private static class ExistVarStack implements ExistStack {
25+
private final ExistVarStack parent;
26+
27+
private final Set<String> existVars = new HashSet<>();
28+
29+
private ExistVarStack(ExistVarStack parent) {
30+
this.parent = parent;
31+
}
32+
33+
public void add(String varName) {
34+
existVars.add(varName);
35+
}
36+
37+
public boolean exist(String varName) {
38+
if (existVars.contains(varName)) {
39+
return true;
40+
}
41+
return parent != null && parent.exist(varName);
42+
}
43+
44+
public ExistVarStack push() {
45+
return new ExistVarStack(this);
46+
}
47+
48+
public ExistVarStack pop() {
49+
return parent;
50+
}
51+
}
52+
53+
// handle import
54+
55+
@Override
56+
public Void visitImportCls(QLParser.ImportClsContext ctx) {
57+
String importClsPath = ctx.varId()
58+
.stream()
59+
.map(QLParser.VarIdContext::getStart)
60+
.map(Token::getText)
61+
.collect(Collectors.joining("."));
62+
importManager.addImport(ImportManager.importCls(importClsPath));
63+
return null;
64+
}
65+
66+
@Override
67+
public Void visitImportPack(QLParser.ImportPackContext ctx) {
68+
List<QLParser.VarIdContext> importPackPathTokens = ctx.varId();
69+
boolean isInnerCls =
70+
!Character.isLowerCase(importPackPathTokens.get(importPackPathTokens.size() - 1).getText().charAt(0));
71+
String importPath = importPackPathTokens.stream()
72+
.map(QLParser.VarIdContext::getStart)
73+
.map(Token::getText)
74+
.collect(Collectors.joining("."));
75+
importManager
76+
.addImport(isInnerCls ? ImportManager.importInnerCls(importPath) : ImportManager.importPack(importPath));
77+
return null;
78+
}
79+
80+
// collect exist variable name
81+
82+
/**
83+
* @param ctx int a = 10;
84+
* @return a
85+
*/
86+
@Override
87+
public Void visitVariableDeclaratorId(QLParser.VariableDeclaratorIdContext ctx) {
88+
QLParser.VarIdContext varIdContext = ctx.varId();
89+
getStack().add(varIdContext.getText());
90+
return null;
91+
}
92+
93+
@Override
94+
public Void visitLeftHandSide(QLParser.LeftHandSideContext ctx) {
95+
List<QLParser.PathPartContext> pathPartContexts = ctx.pathPart();
96+
String leftVarName = ctx.varId().getText();
97+
if (pathPartContexts.isEmpty()) {
98+
getStack().add(leftVarName);
99+
}
100+
else if (!getStack().exist(leftVarName)) {
101+
addAttrs(leftVarName, pathPartContexts);
102+
}
103+
return null;
104+
}
105+
106+
// exclude function call
107+
108+
@Override
109+
public Void visitPrimary(QLParser.PrimaryContext ctx) {
110+
QLParser.PrimaryNoFixPathableContext primaryNoFixPathableContext = ctx.primaryNoFixPathable();
111+
if (primaryNoFixPathableContext != null) {
112+
List<QLParser.PathPartContext> pathPartContexts = ctx.pathPart();
113+
if (primaryNoFixPathableContext instanceof QLParser.VarIdExprContext && !pathPartContexts.isEmpty()
114+
&& pathPartContexts.get(0) instanceof QLParser.CallExprContext) {
115+
// function call
116+
for (QLParser.PathPartContext pathPartContext : pathPartContexts) {
117+
pathPartContext.accept(this);
118+
}
119+
return null;
120+
}
121+
if (primaryNoFixPathableContext instanceof QLParser.VarIdExprContext) {
122+
int restIndex = parseOutVarAttrInPath(((QLParser.VarIdExprContext)primaryNoFixPathableContext).varId(),
123+
pathPartContexts);
124+
for (int i = restIndex; i < pathPartContexts.size(); i++) {
125+
pathPartContexts.get(i).accept(this);
126+
}
127+
return null;
128+
}
129+
}
130+
131+
return super.visitPrimary(ctx);
132+
}
133+
134+
private int parseOutVarAttrInPath(QLParser.VarIdContext idContext,
135+
List<QLParser.PathPartContext> pathPartContexts) {
136+
List<String> headPartIds = new ArrayList<>();
137+
String primaryId = idContext.getText();
138+
headPartIds.add(primaryId);
139+
for (QLParser.PathPartContext pathPartContext : pathPartContexts) {
140+
if (pathPartContext instanceof QLParser.FieldAccessContext) {
141+
headPartIds.add(parseFieldId(((QLParser.FieldAccessContext)pathPartContext).fieldId()));
142+
}
143+
else {
144+
break;
145+
}
146+
}
147+
ImportManager.LoadPartQualifiedResult loadPartQualifiedResult = importManager.loadPartQualified(headPartIds);
148+
if (loadPartQualifiedResult.getCls() != null) {
149+
return loadPartQualifiedResult.getRestIndex() - 1;
150+
}
151+
else {
152+
return getStack().exist(primaryId) ? 0 : addAttrs(primaryId, pathPartContexts);
153+
}
154+
}
155+
156+
private String parseFieldId(QLParser.FieldIdContext ctx) {
157+
TerminalNode quoteStringLiteral = ctx.QuoteStringLiteral();
158+
if (quoteStringLiteral != null) {
159+
return QLStringUtils.parseStringEscape(quoteStringLiteral.getText());
160+
}
161+
return ctx.getStart().getText();
162+
}
163+
164+
private String getFieldId(QLParser.PathPartContext pathPartContext) {
165+
if (pathPartContext instanceof QLParser.FieldAccessContext) {
166+
return parseFieldId(((QLParser.FieldAccessContext)pathPartContext).fieldId());
167+
}
168+
else if (pathPartContext instanceof QLParser.OptionalFieldAccessContext) {
169+
return parseFieldId(((QLParser.OptionalFieldAccessContext)pathPartContext).fieldId());
170+
}
171+
else if (pathPartContext instanceof QLParser.SpreadFieldAccessContext) {
172+
return parseFieldId(((QLParser.SpreadFieldAccessContext)pathPartContext).fieldId());
173+
}
174+
else {
175+
return null;
176+
}
177+
}
178+
179+
private int addAttrs(String primaryId, List<QLParser.PathPartContext> pathPartContexts) {
180+
List<String> attrs = new ArrayList<>();
181+
attrs.add(primaryId);
182+
183+
int i = 0;
184+
for (; i < pathPartContexts.size(); i++) {
185+
String fieldId = getFieldId(pathPartContexts.get(i));
186+
if (fieldId == null) {
187+
break;
188+
}
189+
attrs.add(fieldId);
190+
}
191+
192+
outVarAttrs.add(attrs);
193+
return i;
194+
}
195+
196+
public Set<List<String>> getOutVarAttrs() {
197+
return outVarAttrs;
198+
}
199+
}

src/test/java/com/alibaba/qlexpress4/Express4RunnerTest.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -992,6 +992,23 @@ public void getOutVarNamesTest() {
992992
Assert.assertEquals(expectOutVarNamesWithClsMethodCall, outVarNamesWithClsMethodCall);
993993
}
994994

995+
@Test
996+
public void getOutVarAttrs() {
997+
Express4Runner express4Runner = new Express4Runner(InitOptions.DEFAULT_OPTIONS);
998+
Assert.assertEquals(Arrays.asList("a.b.c", "a.b.d", "c.m"),
999+
flatOutVarAttrs(express4Runner.getOutVarAttrs("a.b.c+a.b.c-a.b.d*c.m")));
1000+
Assert.assertEquals(Collections.singletonList("c.m"),
1001+
flatOutVarAttrs(express4Runner.getOutVarAttrs("a=2;test(a.b.c,c.m)")));
1002+
Assert.assertEquals(Arrays.asList("a.b", "c.m"),
1003+
flatOutVarAttrs(express4Runner.getOutVarAttrs("a.b=2;test(c.m)")));
1004+
Assert.assertEquals(Collections.singletonList("c"),
1005+
flatOutVarAttrs(express4Runner.getOutVarAttrs("java.lang.Math.abs(c)")));
1006+
}
1007+
1008+
private List<String> flatOutVarAttrs(Set<List<String>> outVarAttrs) {
1009+
return outVarAttrs.stream().map(l -> String.join(".", l)).sorted().collect(Collectors.toList());
1010+
}
1011+
9951012
@Test
9961013
public void getOutFunctions() {
9971014
Express4Runner express4Runner = new Express4Runner(InitOptions.DEFAULT_OPTIONS);

0 commit comments

Comments
 (0)