@openhps/core
Version:
Open Hybrid Positioning System - Core component
256 lines (240 loc) • 7.67 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.loop = exports.default = exports.Loop = exports.Continue = exports.Break = void 0;
var _Node = _interopRequireDefault(require("../core/Node.js"));
var _ExpressionNode = require("../code/ExpressionNode.js");
var _TSLBase = require("../tsl/TSLBase.js");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* This module offers a variety of ways to implement loops in TSL. In it's basic form it's:
* ```js
* Loop( count, ( { i } ) => {
*
* } );
* ```
* However, it is also possible to define a start and end ranges, data types and loop conditions:
* ```js
* Loop( { start: int( 0 ), end: int( 10 ), type: 'int', condition: '<' }, ( { i } ) => {
*
* } );
*```
* Nested loops can be defined in a compacted form:
* ```js
* Loop( 10, 5, ( { i, j } ) => {
*
* } );
* ```
* Loops that should run backwards can be defined like so:
* ```js
* Loop( { start: 10 }, () => {} );
* ```
* It is possible to execute with boolean values, similar to the `while` syntax.
* ```js
* const value = float( 0 ).toVar();
*
* Loop( value.lessThan( 10 ), () => {
*
* value.addAssign( 1 );
*
* } );
* ```
* The module also provides `Break()` and `Continue()` TSL expression for loop control.
* @augments Node
*/
class LoopNode extends _Node.default {
static get type() {
return 'LoopNode';
}
/**
* Constructs a new loop node.
*
* @param {Array<any>} params - Depending on the loop type, array holds different parameterization values for the loop.
*/
constructor(params = []) {
super();
this.params = params;
}
/**
* Returns a loop variable name based on an index. The pattern is
* `0` = `i`, `1`= `j`, `2`= `k` and so on.
*
* @param {number} index - The index.
* @return {string} The loop variable name.
*/
getVarName(index) {
return String.fromCharCode('i'.charCodeAt(0) + index);
}
/**
* Returns properties about this node.
*
* @param {NodeBuilder} builder - The current node builder.
* @return {Object} The node properties.
*/
getProperties(builder) {
const properties = builder.getNodeProperties(this);
if (properties.stackNode !== undefined) return properties;
//
const inputs = {};
for (let i = 0, l = this.params.length - 1; i < l; i++) {
const param = this.params[i];
const name = param.isNode !== true && param.name || this.getVarName(i);
const type = param.isNode !== true && param.type || 'int';
inputs[name] = (0, _ExpressionNode.expression)(name, type);
}
const stack = builder.addStack(); // TODO: cache() it
properties.returnsNode = this.params[this.params.length - 1](inputs, stack, builder);
properties.stackNode = stack;
builder.removeStack();
return properties;
}
/**
* This method is overwritten since the node type is inferred based on the loop configuration.
*
* @param {NodeBuilder} builder - The current node builder.
* @return {string} The node type.
*/
getNodeType(builder) {
const {
returnsNode
} = this.getProperties(builder);
return returnsNode ? returnsNode.getNodeType(builder) : 'void';
}
setup(builder) {
// setup properties
this.getProperties(builder);
}
generate(builder) {
const properties = this.getProperties(builder);
const params = this.params;
const stackNode = properties.stackNode;
for (let i = 0, l = params.length - 1; i < l; i++) {
const param = params[i];
let isWhile = false,
start = null,
end = null,
name = null,
type = null,
condition = null,
update = null;
if (param.isNode) {
if (param.getNodeType(builder) === 'bool') {
isWhile = true;
type = 'bool';
end = param.build(builder, type);
} else {
type = 'int';
name = this.getVarName(i);
start = '0';
end = param.build(builder, type);
condition = '<';
}
} else {
type = param.type || 'int';
name = param.name || this.getVarName(i);
start = param.start;
end = param.end;
condition = param.condition;
update = param.update;
if (typeof start === 'number') start = builder.generateConst(type, start);else if (start && start.isNode) start = start.build(builder, type);
if (typeof end === 'number') end = builder.generateConst(type, end);else if (end && end.isNode) end = end.build(builder, type);
if (start !== undefined && end === undefined) {
start = start + ' - 1';
end = '0';
condition = '>=';
} else if (end !== undefined && start === undefined) {
start = '0';
condition = '<';
}
if (condition === undefined) {
if (Number(start) > Number(end)) {
condition = '>=';
} else {
condition = '<';
}
}
}
let loopSnippet;
if (isWhile) {
loopSnippet = `while ( ${end} )`;
} else {
const internalParam = {
start,
end,
condition
};
//
const startSnippet = internalParam.start;
const endSnippet = internalParam.end;
let declarationSnippet = '';
let conditionalSnippet = '';
let updateSnippet = '';
if (!update) {
if (type === 'int' || type === 'uint') {
if (condition.includes('<')) update = '++';else update = '--';
} else {
if (condition.includes('<')) update = '+= 1.';else update = '-= 1.';
}
}
declarationSnippet += builder.getVar(type, name) + ' = ' + startSnippet;
conditionalSnippet += name + ' ' + condition + ' ' + endSnippet;
updateSnippet += name + ' ' + update;
loopSnippet = `for ( ${declarationSnippet}; ${conditionalSnippet}; ${updateSnippet} )`;
}
builder.addFlowCode((i === 0 ? '\n' : '') + builder.tab + loopSnippet + ' {\n\n').addFlowTab();
}
const stackSnippet = stackNode.build(builder, 'void');
const returnsSnippet = properties.returnsNode ? properties.returnsNode.build(builder) : '';
builder.removeFlowTab().addFlowCode('\n' + builder.tab + stackSnippet);
for (let i = 0, l = this.params.length - 1; i < l; i++) {
builder.addFlowCode((i === 0 ? '' : builder.tab) + '}\n\n').removeFlowTab();
}
builder.addFlowTab();
return returnsSnippet;
}
}
var _default = exports.default = LoopNode;
/**
* TSL function for creating a loop node.
*
* @tsl
* @function
* @param {...any} params - A list of parameters.
* @returns {LoopNode}
*/
const Loop = (...params) => (0, _TSLBase.nodeObject)(new LoopNode((0, _TSLBase.nodeArray)(params, 'int'))).append();
/**
* TSL function for creating a `Continue()` expression.
*
* @tsl
* @function
* @returns {ExpressionNode}
*/
exports.Loop = Loop;
const Continue = () => (0, _ExpressionNode.expression)('continue').append();
/**
* TSL function for creating a `Break()` expression.
*
* @tsl
* @function
* @returns {ExpressionNode}
*/
exports.Continue = Continue;
const Break = () => (0, _ExpressionNode.expression)('break').append();
// Deprecated
/**
* @tsl
* @function
* @deprecated since r168. Use {@link Loop} instead.
*
* @param {...any} params
* @returns {LoopNode}
*/
exports.Break = Break;
const loop = (...params) => {
// @deprecated, r168
console.warn('THREE.TSL: loop() has been renamed to Loop().');
return Loop(...params);
};
exports.loop = loop;