Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed compiling of constructor and type reference. Fixed unary plus/minus. Added node.getRaw(). #14

Merged
merged 8 commits into from
Jan 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/SpelExpressionParser.js
Original file line number Diff line number Diff line change
Expand Up @@ -714,7 +714,7 @@ export var SpelExpressionParser = function () {
return true;
}
var value = node.stringValue();
return (value.length && VALID_QUALIFIED_ID_PATTERN.test(value));
return (value && value.length && VALID_QUALIFIED_ID_PATTERN.test(value));
}

// This is complicated due to the support for dollars in identifiers. Dollars are normally separate tokens but
Expand Down
4 changes: 2 additions & 2 deletions src/ast/BeanReference.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ import {SpelNode} from './SpelNode';
*
* @author Andy Clement
*/
function createNode(position, left, right) {
var node = SpelNode.create('beanref', position, left, right);
function createNode(position, beanName) {
var node = SpelNode.create('beanref', position);

node.getValue = function (state) {
throw {
Expand Down
45 changes: 42 additions & 3 deletions src/ast/ConstructorReference.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/

import {SpelNode} from './SpelNode';
import {Stack} from '../lib/Stack';

/**
* Represents the invocation of a constructor. Either a constructor on a regular type or
Expand All @@ -29,13 +30,51 @@ import {SpelNode} from './SpelNode';
* @author Juergen Hoeller
* @since 3.0
*/
function createNode(position, left, right) {
var node = SpelNode.create('constructorref', position, left, right);
function createNode(position, dimensions, nodes) {
var isArray = nodes !== undefined;
var dimension;
if (isArray) {
dimension = dimensions.length && dimensions[0] && dimensions[0].getType() === 'number' ? dimensions[0].getValue() : null;
} else {
nodes = dimensions;
dimensions = undefined;
}
const [_qualifiedIdentifier, ...args] = nodes;

var node = SpelNode.create('constructorref', position, ...nodes);

node.getRaw = function () {
return dimension;
};

node.getValue = function (state) {
if (isArray && args.length <= 1) {
var compiledArgs = [];

//populate arguments
args.forEach(function (arg) {
// reset the active context to root context for evaluating argument
const currentActiveContext = state.activeContext
state.activeContext = new Stack();
state.activeContext.push(state.rootContext);

// evaluate argument
compiledArgs.push(arg.getValue(state));

// reset the active context
state.activeContext = currentActiveContext;
});

if (args.length === 1) {
return compiledArgs[0];
} else {
return dimension ? new Array(dimension) : [];
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return dimension ? new Array(dimension) : [];
return dimension ? [dimension] : [];

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line would create an empty array with dimension, not an array with 1 element.

Example in console:

> new Array(4)
 Array(4) [ <4 empty slots> ]
  length: 4

> [3]
 Array [ 3 ]
  0: 3
  length: 1

See test for evaluating new int[3]:

it('should create new empty array with dimension', ()=>{
//given
let context = {};
//when
let newArray = evaluator.eval('new int[3]', context);
//then
expect(newArray.length).toEqual(3);
});
});
});

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, right, thanks

}
}

throw {
name: 'MethodNotImplementedException',
message: 'BeanReference: Not implemented'
message: 'ConstructorReference: Not implemented'
}
Comment on lines 75 to 78
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this throw be removed now since it's implemented? It looks like it's unreachable anyway.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's implemented only for array construction:

if (isArray && args.length <= 1) {

For other classes it will throw ConstructorReference: Not implemented (just renamed typo here BeanReference -> ConstructorReference )

};

Expand Down
7 changes: 7 additions & 0 deletions src/ast/FunctionReference.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,13 @@ import {Stack} from '../lib/Stack';
function createNode(functionName, position, args) {
var node = SpelNode.create('function', position);

node.getRaw = function () {
return {
functionName,
args
};
};

node.getValue = function (state) {
var locals = state.locals || {},
context = state.rootContext,
Expand Down
8 changes: 6 additions & 2 deletions src/ast/Identifier.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,12 @@ import {SpelNode} from './SpelNode';
* @author Andy Clement
* @since 3.0
*/
function createNode(position, left, right) {
var node = SpelNode.create('identifier', position, left, right);
function createNode(identifierName, position) {
var node = SpelNode.create('identifier', position);

node.getRaw = function () {
return identifierName;
};

node.getValue = function (state) {
throw {
Expand Down
4 changes: 4 additions & 0 deletions src/ast/InlineList.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ function createNode(position, elements) {
var node = SpelNode.create('list', position),
list = [].concat(elements || []);

node.getRaw = function () {
return list;
};

node.getValue = function (state) {
return list.map(function (element) {
return element.getValue(state);
Expand Down
7 changes: 7 additions & 0 deletions src/ast/MethodReference.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,13 @@ import {Stack} from '../lib/Stack'
function createNode(nullSafeNavigation, methodName, position, args) {
var node = SpelNode.create('method', position);

node.getRaw = function () {
return {
methodName,
args
};
};

node.getValue = function (state) {
var context = state.activeContext.peek(),
compiledArgs = [],
Expand Down
3 changes: 3 additions & 0 deletions src/ast/OpMinus.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ function createNode(position, left, right) {
var node = SpelNode.create('op-minus', position, left, right);

node.getValue = function (state) {
if (!right) {
return - left.getValue(state);
}
return left.getValue(state) - right.getValue(state);
};

Expand Down
3 changes: 3 additions & 0 deletions src/ast/OpPlus.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ function createNode(position, left, right) {
var node = SpelNode.create('op-plus', position, left, right);

node.getValue = function (state) {
if (!right) {
return + left.getValue(state);
}
//javascript will handle string concatenation or addition depending on types
return left.getValue(state) + right.getValue(state);
};
Expand Down
4 changes: 4 additions & 0 deletions src/ast/PropertyReference.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ import {SpelNode} from './SpelNode';
function createNode(nullSafeNavigation, propertyName, position) {
var node = SpelNode.create('property', position);

node.getRaw = function () {
return propertyName;
};

node.getValue = function (state) {
var context = state.activeContext.peek();

Expand Down
8 changes: 6 additions & 2 deletions src/ast/QualifiedIdentifier.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,12 @@ import {SpelNode} from './SpelNode';
* @author Andy Clement
* @since 3.0
*/
function createNode(position, left, right) {
var node = SpelNode.create('qualifiedidentifier', position, left, right);
function createNode(position, pieces) {
var node = SpelNode.create('qualifiedidentifier', position, ...pieces);

node.getRaw = function () {
return pieces.map(p => p.getRaw());
};

node.getValue = function (state) {
throw {
Expand Down
10 changes: 10 additions & 0 deletions src/ast/SpelNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,16 @@ function createSpelNode(nodeType, position, ...operands) {
return children;
};
node.addChild = function (childNode) {
if (!childNode) {
// See OpMinus and OpPlus: right node can be null for unary mode
return;
}
if (!childNode.setParent) {
throw {
name: 'Error',
message: 'Trying to add a child which is not a node: ' + JSON.stringify(childNode)
};
}
childNode.setParent(node);
children.push(childNode);
};
Expand Down
4 changes: 2 additions & 2 deletions src/ast/TypeReference.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ import {SpelNode} from './SpelNode';
*
* @author Andy Clement
*/
function createNode(position, left, right) {
var node = SpelNode.create('typeref', position, left, right);
function createNode(position, node, _dims) {
var node = SpelNode.create('typeref', position, node);

node.getValue = function (state) {
throw {
Expand Down
4 changes: 4 additions & 0 deletions src/ast/VariableReference.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ import {SpelNode} from './SpelNode';
function createNode(variableName, position) {
var node = SpelNode.create('variable', position);

node.getRaw = function () {
return variableName;
};

node.getValue = function (state) {
var context = state.activeContext.peek(),
locals = state.locals;
Expand Down
82 changes: 82 additions & 0 deletions test/spec/SpelExpressionEvaluator.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,38 @@ describe('spel expression evaluator', ()=>{
expect(compiledExpression.eval).toBeDefined();
});

it('should compile expression with constructor', ()=>{
//when
let compiledExpression = evaluator.compile('new java.text.SimpleDateFormat("yyyy-MM-dd").parse("2022-01-01")');

//then
expect(compiledExpression.eval).toBeDefined();
});

it('should compile expression with array constructor without dimensions', ()=>{
//when
let compiledExpression = evaluator.compile('new int[]{1,2,3}');

//then
expect(compiledExpression.eval).toBeDefined();
});

it('should compile expression with array constructor with dimensions', ()=>{
//when
let compiledExpression = evaluator.compile('new int[3]{1,2,3}');

//then
expect(compiledExpression.eval).toBeDefined();
});

it('should compile expression with type reference', ()=>{
//when
let compiledExpression = evaluator.compile('T(java.time.LocalTime).parse("11:22")');

//then
expect(compiledExpression.eval).toBeDefined();
});

});


Expand All @@ -28,10 +60,14 @@ describe('spel expression evaluator', ()=>{
//when
let numberInt = evaluator.eval('123');
let numberFloat = evaluator.eval('123.4');
let negativeNumberInt = evaluator.eval('-123');
let negativeNumberFloat = evaluator.eval('-123.4');

//then
expect(numberInt).toBe(123);
expect(numberFloat).toBe(123.4);
expect(negativeNumberInt).toBe(-123);
expect(negativeNumberFloat).toBe(-123.4);
});

it('should evaluate a string', ()=>{
Expand Down Expand Up @@ -833,6 +869,52 @@ describe('spel expression evaluator', ()=>{

});

describe('constructor', ()=>{
it('should create new int array', ()=>{
//given
let context = {};

//when
let newArray = evaluator.eval('new int[]{1, 2, 3}', context);

//then
expect(newArray).toEqual([1, 2, 3]);
});

it('should create new int array with dimension', ()=>{
//given
let context = {};

//when
let newArray = evaluator.eval('new int[3]{1, 2, 3}', context);

//then
expect(newArray).toEqual([1, 2, 3]);
});

it('should create new empty array', ()=>{
//given
let context = {};

//when
let newArray = evaluator.eval('new int[]', context);

//then
expect(newArray).toEqual([]);
});

it('should create new empty array with dimension', ()=>{
//given
let context = {};

//when
let newArray = evaluator.eval('new int[3]', context);

//then
expect(newArray.length).toEqual(3);
});
});

});

});