Skip to content

Commit 1b0b930

Browse files
committed
Allow global variables in modules to use context
1 parent e5eddd7 commit 1b0b930

12 files changed

+100
-49
lines changed

src/main/java/com/schibsted/spt/data/jslt/Parser.java

+1
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,7 @@ public Parser withNamedModules(Map<String, Module> thisModules) {
187187
*/
188188
public Expression compile() {
189189
ParseContext ctx = new ParseContext(functions, source, resolver, modules,
190+
new ArrayList(),
190191
new PreparationContext());
191192
return ParserImpl.compileExpression(ctx, new JsltParser(reader));
192193
}

src/main/java/com/schibsted/spt/data/jslt/impl/ExpressionImpl.java

+13-11
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public class ExpressionImpl implements Expression {
3535
private Map<String, Function> functions;
3636
private ExpressionNode actual;
3737
private int stackFrameSize;
38-
private JsonNode[] globalStackFrame;
38+
private JstlFile[] fileModules;
3939

4040
// contains the mapping from external parameters (variables set from
4141
// outside at query-time) to slots, so that we can put the
@@ -79,12 +79,15 @@ public JsonNode apply(Scope scope, JsonNode input) {
7979
if (input == null)
8080
input = NullNode.instance;
8181

82-
// if imported modules have global variables we need to set those
83-
// before we start
84-
if (globalStackFrame != null)
85-
scope.insertModuleGlobals(globalStackFrame);
82+
// evaluate lets in global modules
83+
if (fileModules != null) {
84+
for (int ix = 0; ix < fileModules.length; ix++)
85+
fileModules[ix].evaluateLetsOnly(scope, input);
86+
}
8687

88+
// evaluate own lets
8789
NodeUtils.evalLets(scope, input, lets);
90+
8891
return actual.apply(scope, input);
8992
}
9093

@@ -112,10 +115,8 @@ public void prepare(PreparationContext ctx) {
112115
* ExpressionImpl is a module. Called once during compilation.
113116
* The values are then remembered forever.
114117
*/
115-
public void evaluateLetsOnly(Scope scope) {
116-
// the context node is null: all references to it in modules are
117-
// verboten, anyway
118-
NodeUtils.evalLets(scope, null, lets);
118+
public void evaluateLetsOnly(Scope scope, JsonNode input) {
119+
NodeUtils.evalLets(scope, input, lets);
119120
}
120121

121122
public void optimize() {
@@ -149,7 +150,8 @@ public int getStackFrameSize() {
149150
return stackFrameSize;
150151
}
151152

152-
public void setInitialScope(Scope startScope) {
153-
this.globalStackFrame = startScope.getGlobalStackFrame();
153+
public void setGlobalModules(List<JstlFile> fileModules) {
154+
this.fileModules = new JstlFile[fileModules.size()];
155+
this.fileModules = fileModules.toArray(this.fileModules);
154156
}
155157
}

src/main/java/com/schibsted/spt/data/jslt/impl/JstlFile.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ public JsonNode call(JsonNode input, JsonNode[] arguments) {
6464
return body.apply(arguments[0]);
6565
}
6666

67-
public void evaluateLetsOnly(Scope scope) {
68-
body.evaluateLetsOnly(scope);
67+
public void evaluateLetsOnly(Scope scope, JsonNode input) {
68+
body.evaluateLetsOnly(scope, input);
6969
}
7070
}

src/main/java/com/schibsted/spt/data/jslt/impl/ParseContext.java

+20-9
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
package com.schibsted.spt.data.jslt.impl;
1717

1818
import java.util.Map;
19+
import java.util.List;
1920
import java.util.HashMap;
2021
import java.util.ArrayList;
2122
import java.util.Collection;
@@ -39,10 +40,18 @@ public class ParseContext {
3940
*/
4041
private String source;
4142
/**
42-
* Imported modules listed under their prefixes.
43+
* Imported modules listed under their prefixes. This is scoped per
44+
* source file, since each has a different name-module mapping.
4345
*/
4446
private Map<String, Module> modules;
45-
private Collection<FunctionExpression> funcalls; // delayed function resolution
47+
/**
48+
* Tracks all loaded JSLT files. Shared between all contexts.
49+
*/
50+
private List<JstlFile> files;
51+
/**
52+
* Function expressions, used for delayed name-to-function resolution.
53+
*/
54+
private Collection<FunctionExpression> funcalls;
4655
private ParseContext parent;
4756
private ResourceResolver resolver;
4857
/**
@@ -57,13 +66,15 @@ public class ParseContext {
5766
public ParseContext(Collection<Function> extensions, String source,
5867
ResourceResolver resolver,
5968
Map<String, Module> namedModules,
69+
List<JstlFile> files,
6070
PreparationContext preparationContext) {
6171
this.extensions = extensions;
6272
this.functions = new HashMap();
6373
for (Function func : extensions)
6474
functions.put(func.getName(), func);
6575

6676
this.source = source;
77+
this.files = files;
6778
this.funcalls = new ArrayList();
6879
this.modules = new HashMap();
6980
this.resolver = resolver;
@@ -75,7 +86,7 @@ public ParseContext(Collection<Function> extensions, String source,
7586

7687
public ParseContext(String source) {
7788
this(Collections.EMPTY_SET, source, new ClasspathResourceResolver(),
78-
new HashMap(), new PreparationContext());
89+
new HashMap(), new ArrayList(), new PreparationContext());
7990
}
8091

8192
public void setParent(ParseContext parent) {
@@ -165,11 +176,11 @@ public ResourceResolver getResolver() {
165176
return resolver;
166177
}
167178

168-
public Scope evaluateGlobalModuleVariables(int stackFrameSize) {
169-
Scope scope = Scope.getRoot(stackFrameSize);
170-
for (Module m : modules.values())
171-
if (m instanceof JstlFile)
172-
((JstlFile) m).evaluateLetsOnly(scope);
173-
return scope;
179+
public List<JstlFile> getFiles() {
180+
return files;
181+
}
182+
183+
public void registerJsltFile(JstlFile file) {
184+
files.add(file);
174185
}
175186
}

src/main/java/com/schibsted/spt/data/jslt/impl/Scope.java

-17
Original file line numberDiff line numberDiff line change
@@ -66,21 +66,4 @@ public void setValue(int slot, JsonNode value) {
6666
else
6767
localStackFrames.peek()[slot] = value;
6868
}
69-
70-
public void insertModuleGlobals(JsonNode[] globals) {
71-
for (int ix = 0; ix < globals.length; ix++)
72-
if (globals[ix] != null)
73-
globalStackFrame[ix] = globals[ix];
74-
}
75-
76-
public JsonNode[] getGlobalStackFrame() {
77-
return globalStackFrame;
78-
}
79-
80-
public boolean hasGlobalValuesSet() {
81-
for (int ix = 0; ix < globalStackFrame.length; ix++)
82-
if (globalStackFrame[ix] != null)
83-
return true;
84-
return false;
85-
}
8669
}

src/main/java/com/schibsted/spt/data/jslt/parser/ParserImpl.java

+4-8
Original file line numberDiff line numberDiff line change
@@ -52,14 +52,9 @@ public static Expression compileExpression(ParseContext ctx, JsltParser parser)
5252
try {
5353
parser.Start();
5454
ExpressionImpl expr = compile(ctx, (SimpleNode) parser.jjtree.rootNode());
55-
56-
// we need to evaluate the global variables in all the modules,
57-
// if there are any, once and for all, and remember their values
58-
Scope scope = ctx.evaluateGlobalModuleVariables(expr.getStackFrameSize());
59-
if (scope.hasGlobalValuesSet())
60-
expr.setInitialScope(scope);
61-
55+
expr.setGlobalModules(ctx.getFiles());
6256
return expr;
57+
6358
} catch (ParseException e) {
6459
throw new JsltException("Parse error: " + e.getMessage(),
6560
makeLocation(ctx, e.currentToken));
@@ -72,7 +67,7 @@ private static ExpressionImpl compileImport(Collection<Function> functions,
7267
ParseContext parent,
7368
String jslt) {
7469
try (Reader reader = parent.getResolver().resolve(jslt)) {
75-
ParseContext ctx = new ParseContext(functions, jslt, parent.getResolver(), parent.getNamedModules(), parent.getPreparationContext());
70+
ParseContext ctx = new ParseContext(functions, jslt, parent.getResolver(), parent.getNamedModules(), parent.getFiles(), parent.getPreparationContext());
7671
ctx.setParent(parent);
7772
return compileModule(ctx, new JsltParser(reader));
7873
} catch (IOException e) {
@@ -552,6 +547,7 @@ private static void processImports(ParseContext ctx, SimpleNode parent) {
552547
JstlFile file = doImport(ctx, source, node, prefix);
553548
ctx.registerModule(prefix, file);
554549
ctx.addDeclaredFunction(prefix, file);
550+
ctx.registerJsltFile(file);
555551
}
556552
}
557553
}

src/test/resources/function-declaration-tests.yaml

+38
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,30 @@ tests:
124124
m:variable($global)
125125
output: "\"variable is global\""
126126

127+
# in this one we don't define the global variable in the query
128+
-
129+
input: {}
130+
query: >
131+
import "module-with-global-var.jslt" as m
132+
m:variable("variable is")
133+
output: "\"variable is global\""
134+
135+
-
136+
input: {}
137+
query: >
138+
import "module-as-function-with-global.jslt" as m
139+
m("variable is")
140+
output: "\"variable is global\""
141+
142+
-
143+
input: {}
144+
query: >
145+
import "module-with-global-var.jslt" as m
146+
import "module-also-with-global-var.jslt" as m2
147+
let global = "variable is"
148+
m:variable($global) + " " + m2:andfunc($global) + " " + $global
149+
output: "\"variable is global and variable is variable is\""
150+
127151
-
128152
input: {}
129153
query: >
@@ -145,6 +169,20 @@ tests:
145169
m:foo(2)
146170
output: 6
147171

172+
-
173+
input: "{\"foo\" : 22}"
174+
query: >
175+
import "module-with-global-var-dot.jslt" as m
176+
m:foo()
177+
output: 22
178+
179+
-
180+
input: "{\"foo\" : 22}"
181+
query: >
182+
import "module-imports-other-module.jslt" as m
183+
m:foo()
184+
output: 22
185+
148186
-
149187
input: {}
150188
query: >
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
2+
// Test that we can use global variables in modules
3+
def andfunc(text)
4+
$global + $text
5+
6+
let global = "and "
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
2+
let global = "global"
3+
4+
. + " " + $global
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
2+
import "module-with-global-var-dot.jslt" as m
3+
4+
def foo()
5+
m:foo()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
2+
let foo = .foo
3+
4+
def foo()
5+
$foo
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11

2+
let global = "global"
3+
24
// Test that we can use global variables in modules
35
def variable(text)
46
$text + " " + $global
5-
6-
let global = "global"

0 commit comments

Comments
 (0)