fable 3.0.132 → 3.0.134
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/debug/Harness.js +27 -36
- package/dist/fable.compatible.js +336 -165
- package/dist/fable.compatible.min.js +2 -2
- package/dist/fable.compatible.min.js.map +1 -1
- package/dist/fable.js +277 -106
- package/dist/fable.min.js +2 -2
- package/dist/fable.min.js.map +1 -1
- package/example_applications/data/Fruit-Data.json +694 -0
- package/example_applications/data/Fruit-Manyfest.json +56 -0
- package/example_applications/mathematical_playground/AppData.json +8 -0
- package/example_applications/mathematical_playground/Equations.json +12 -0
- package/example_applications/mathematical_playground/Math-Solver-Harness.js +89 -0
- package/package.json +3 -3
- package/source/services/Fable-Service-ExpressionParser/Fable-Service-ExpressionParser-FunctionMap.json +80 -20
- package/source/services/Fable-Service-ExpressionParser/Fable-Service-ExpressionParser-Postfix.js +218 -200
- package/source/services/Fable-Service-ExpressionParser/Fable-Service-ExpressionParser-SolvePostfixedExpression.js +11 -1
- package/source/services/Fable-Service-ExpressionParser/Fable-Service-ExpressionParser-TokenMap.json +1 -0
- package/source/services/Fable-Service-ExpressionParser.js +22 -3
- package/source/services/Fable-Service-Math.js +570 -19
- package/test/ExpressionParser_tests.js +20 -1
- package/test/Math_test.js +54 -3
- package/test/data/chocodata.json +248 -0
package/dist/fable.js
CHANGED
|
@@ -1549,7 +1549,7 @@ let tmpDescriptorAddresses=Object.keys(tmpSource);tmpDescriptorAddresses.forEach
|
|
|
1549
1549
|
* @class Manyfest
|
|
1550
1550
|
*/class Manyfest extends libFableServiceProviderBase{constructor(pFable,pManifest,pServiceHash){if(pFable===undefined){super({});}else{super(pFable,pManifest,pServiceHash);}this.serviceType='Manifest';// Wire in logging
|
|
1551
1551
|
this.logInfo=libSimpleLog;this.logError=libSimpleLog;// Create an object address resolver and map in the functions
|
|
1552
|
-
this.objectAddressCheckAddressExists=new libObjectAddressCheckAddressExists(this.logInfo,this.logError);this.objectAddressGetValue=new libObjectAddressGetValue(this.logInfo,this.logError);this.objectAddressSetValue=new libObjectAddressSetValue(this.logInfo,this.logError);this.objectAddressDeleteValue=new libObjectAddressDeleteValue(this.logInfo,this.logError);if(!('defaultValues'in this.options)){this.options.defaultValues={"String":"","Number":0,"Float":0.0,"Integer":0,"Boolean":false,"Binary":0,"DateTime":0,"Array":[],"Object":{},"Null":null};}if(!('strict'in this.options)){this.options.strict=false;}this.scope=undefined;this.elementAddresses=undefined;this.elementHashes=undefined;this.elementDescriptors=undefined;this.reset();if(typeof this.options==='object'){this.loadManifest(this.options);}this.schemaManipulations=new libSchemaManipulation(this.logInfo,this.logError);this.objectAddressGeneration=new libObjectAddressGeneration(this.logInfo,this.logError);this.hashTranslations=new libHashTranslation(this.logInfo,this.logError);}/*************************************************************************
|
|
1552
|
+
this.objectAddressCheckAddressExists=new libObjectAddressCheckAddressExists(this.logInfo,this.logError);this.objectAddressGetValue=new libObjectAddressGetValue(this.logInfo,this.logError);this.objectAddressSetValue=new libObjectAddressSetValue(this.logInfo,this.logError);this.objectAddressDeleteValue=new libObjectAddressDeleteValue(this.logInfo,this.logError);if(!('defaultValues'in this.options)){this.options.defaultValues={"String":"","Number":0,"Float":0.0,"Integer":0,"PreciseNumber":"0.0","Boolean":false,"Binary":0,"DateTime":0,"Array":[],"Object":{},"Null":null};}if(!('strict'in this.options)){this.options.strict=false;}this.scope=undefined;this.elementAddresses=undefined;this.elementHashes=undefined;this.elementDescriptors=undefined;this.reset();if(typeof this.options==='object'){this.loadManifest(this.options);}this.schemaManipulations=new libSchemaManipulation(this.logInfo,this.logError);this.objectAddressGeneration=new libObjectAddressGeneration(this.logInfo,this.logError);this.hashTranslations=new libHashTranslation(this.logInfo,this.logError);this.numberRegex=/^[+-]?(\d+(\.\d*)?|\.\d+)([eE][+-]?\d+)?$/;}/*************************************************************************
|
|
1553
1553
|
* Schema Manifest Loading, Reading, Manipulation and Serialization Functions
|
|
1554
1554
|
*/ // Reset critical manifest properties
|
|
1555
1555
|
reset(){this.scope='DEFAULT';this.elementAddresses=[];this.elementHashes={};this.elementDescriptors={};}clone(){// Make a copy of the options in-place
|
|
@@ -1589,8 +1589,8 @@ validate(pObject){let tmpValidationData={Error:null,Errors:[],MissingElements:[]
|
|
|
1589
1589
|
for(let i=0;i<this.elementAddresses.length;i++){let tmpDescriptor=this.getDescriptor(this.elementAddresses[i]);let tmpValueExists=this.checkAddressExists(pObject,tmpDescriptor.Address);let tmpValue=this.getValueAtAddress(pObject,tmpDescriptor.Address);if(typeof tmpValue=='undefined'||!tmpValueExists){// This will technically mean that `Object.Some.Value = undefined` will end up showing as "missing"
|
|
1590
1590
|
// TODO: Do we want to do a different message based on if the property exists but is undefined?
|
|
1591
1591
|
tmpValidationData.MissingElements.push(tmpDescriptor.Address);if(tmpDescriptor.Required||this.options.strict){addValidationError(tmpDescriptor.Address,'is flagged REQUIRED but is not set in the object');}}// Now see if there is a data type specified for this element
|
|
1592
|
-
if(tmpDescriptor.DataType){let tmpElementType=typeof tmpValue;switch(tmpDescriptor.DataType.toString().trim().toLowerCase()){case'string':if(tmpElementType!='string'){addValidationError(tmpDescriptor.Address,"has a DataType ".concat(tmpDescriptor.DataType," but is of the type ").concat(tmpElementType));}break;case'number':if(tmpElementType!='number'){addValidationError(tmpDescriptor.Address,"has a DataType ".concat(tmpDescriptor.DataType," but is of the type ").concat(tmpElementType));}break;case'integer':if(tmpElementType!='number'){addValidationError(tmpDescriptor.Address,"has a DataType ".concat(tmpDescriptor.DataType," but is of the type ").concat(tmpElementType));}else{let tmpValueString=tmpValue.toString();if(tmpValueString.indexOf('.')>-1){// TODO: Is this an error?
|
|
1593
|
-
addValidationError(tmpDescriptor.Address,"has a DataType ".concat(tmpDescriptor.DataType," but has a decimal point in the number."));}}break;case'float':if(tmpElementType!='number'){addValidationError(tmpDescriptor.Address,"has a DataType ".concat(tmpDescriptor.DataType," but is of the type ").concat(tmpElementType));}break;case'
|
|
1592
|
+
if(tmpDescriptor.DataType){let tmpElementType=typeof tmpValue;switch(tmpDescriptor.DataType.toString().trim().toLowerCase()){case'string':if(tmpElementType!='string'){addValidationError(tmpDescriptor.Address,"has a DataType ".concat(tmpDescriptor.DataType," but is of the type ").concat(tmpElementType));}break;case"precisenumber":if(tmpElementType!='string'){addValidationError(tmpDescriptor.Address,"has a DataType ".concat(tmpDescriptor.DataType," but is of the type ").concat(tmpElementType));}else if(!this.numberRegex.test(tmpValue)){addValidationError(tmpDescriptor.Address,"has a DataType ".concat(tmpDescriptor.DataType," but is not a valid number"));}break;case'number':if(tmpElementType!='number'){addValidationError(tmpDescriptor.Address,"has a DataType ".concat(tmpDescriptor.DataType," but is of the type ").concat(tmpElementType));}break;case'integer':if(tmpElementType!='number'){addValidationError(tmpDescriptor.Address,"has a DataType ".concat(tmpDescriptor.DataType," but is of the type ").concat(tmpElementType));}else{let tmpValueString=tmpValue.toString();if(tmpValueString.indexOf('.')>-1){// TODO: Is this an error?
|
|
1593
|
+
addValidationError(tmpDescriptor.Address,"has a DataType ".concat(tmpDescriptor.DataType," but has a decimal point in the number."));}}break;case'float':if(tmpElementType!='number'){addValidationError(tmpDescriptor.Address,"has a DataType ".concat(tmpDescriptor.DataType," but is of the type ").concat(tmpElementType));}break;case'datetime':let tmpValueDate=new Date(tmpValue);if(tmpValueDate.toString()=='Invalid Date'){addValidationError(tmpDescriptor.Address,"has a DataType ".concat(tmpDescriptor.DataType," but is not parsable as a Date by Javascript"));}default:// Check if this is a string, in the default case
|
|
1594
1594
|
// Note this is only when a DataType is specified and it is an unrecognized data type.
|
|
1595
1595
|
if(tmpElementType!='string'){addValidationError(tmpDescriptor.Address,"has a DataType ".concat(tmpDescriptor.DataType," (which auto-converted to String because it was unrecognized) but is of the type ").concat(tmpElementType));}break;}}}return tmpValidationData;}// Returns a default value, or, the default value for the data type (which is overridable with configuration)
|
|
1596
1596
|
getDefaultValue(pDescriptor){if(typeof pDescriptor!='object'){return undefined;}if('Default'in pDescriptor){return pDescriptor.Default;}else{// Default to a null if it doesn't have a type specified.
|
|
@@ -3376,9 +3376,19 @@ let tmpValue=tmpManifest.getValueByHash(tmpDataSource,tmpToken.Token);// if (!tm
|
|
|
3376
3376
|
// // If no hash resolves, try by address.
|
|
3377
3377
|
// tmpValue = tmpManifest.getValueAtAddress(tmpToken.Token, tmpDataSource);
|
|
3378
3378
|
// }
|
|
3379
|
-
if(!tmpValue){tmpResults.ExpressionParserLog.push("WARNING: ExpressionParser.substituteValuesInTokenizedObjects found no value for the symbol hash or address ".concat(tmpToken.Token," at index ").concat(i));this.log.warn(tmpResults.ExpressionParserLog[tmpResults.ExpressionParserLog.length-1]);continue;}else{tmpResults.ExpressionParserLog.push("INFO: ExpressionParser.substituteValuesInTokenizedObjects found a value [".concat(tmpValue,"] for the state address ").concat(tmpToken.Token," at index ").concat(i));this.log.info(tmpResults.ExpressionParserLog[tmpResults.ExpressionParserLog.length-1]);try{let tmpValueParsed=new this.fable.Utility.bigNumber(tmpValue);tmpToken.Resolved=true;tmpToken.Value=tmpValueParsed.toString();}catch(pError){
|
|
3379
|
+
if(!tmpValue){tmpResults.ExpressionParserLog.push("WARNING: ExpressionParser.substituteValuesInTokenizedObjects found no value for the symbol hash or address ".concat(tmpToken.Token," at index ").concat(i));this.log.warn(tmpResults.ExpressionParserLog[tmpResults.ExpressionParserLog.length-1]);continue;}else{tmpResults.ExpressionParserLog.push("INFO: ExpressionParser.substituteValuesInTokenizedObjects found a value [".concat(tmpValue,"] for the state address ").concat(tmpToken.Token," at index ").concat(i));this.log.info(tmpResults.ExpressionParserLog[tmpResults.ExpressionParserLog.length-1]);try{let tmpValueParsed=new this.fable.Utility.bigNumber(tmpValue);tmpToken.Resolved=true;tmpToken.Value=tmpValueParsed.toString();}catch(pError){// TODO: Should we allow this to be a function? Good god the complexity and beauty of that...
|
|
3380
|
+
if(Array.isArray(tmpValue)||typeof tmpValue==='object'){tmpToken.Resolved=true;tmpToken.Value=tmpValue;}else{tmpResults.ExpressionParserLog.push("INFO: ExpressionParser.substituteValuesInTokenizedObjects found a non-numeric, non-set value for the state address ".concat(tmpToken.Token," at index ").concat(i));this.log.error(tmpResults.ExpressionParserLog[tmpResults.ExpressionParserLog.length-1]);tmpToken.Resolved=false;}}}}if(pTokenizedObjects[i].Type==='Token.StateAddress'&&!tmpToken.Resolved){// Symbols are always hashes. This gracefully works for simple shallow objects because hashes default to the address in Manyfest.
|
|
3380
3381
|
let tmpValue=tmpManifest.getValueAtAddress(tmpDataSource,tmpToken.Token);if(!tmpValue){tmpResults.ExpressionParserLog.push("WARNING: ExpressionParser.substituteValuesInTokenizedObjects found no value for the state address ".concat(tmpToken.Token," at index ").concat(i));this.log.warn(tmpResults.ExpressionParserLog[tmpResults.ExpressionParserLog.length-1]);continue;}else{tmpResults.ExpressionParserLog.push("INFO: ExpressionParser.substituteValuesInTokenizedObjects found a value [".concat(tmpValue,"] for the state address ").concat(tmpToken.Token," at index ").concat(i));this.log.info(tmpResults.ExpressionParserLog[tmpResults.ExpressionParserLog.length-1]);try{let tmpValueParsed=new this.fable.Utility.bigNumber(tmpValue);tmpToken.Resolved=true;tmpToken.Value=tmpValueParsed.toString();}catch(pError){tmpResults.ExpressionParserLog.push("ERROR: ExpressionParser.substituteValuesInTokenizedObjects found a non-numeric value for the state address ".concat(tmpToken.Token," at index ").concat(i));this.log.error(tmpResults.ExpressionParserLog[tmpResults.ExpressionParserLog.length-1]);tmpToken.Resolved=false;}}}if(pTokenizedObjects[i].Type==='Token.Constant'&&!tmpToken.Resolved){tmpResults.ExpressionParserLog.push("INFO: ExpressionParser.substituteValuesInTokenizedObjects found a value [".concat(tmpToken.Token,"] for the constant ").concat(tmpToken.Token," at index ").concat(i));this.log.info(tmpResults.ExpressionParserLog[tmpResults.ExpressionParserLog.length-1]);try{let tmpValueParsed=new this.fable.Utility.bigNumber(tmpToken.Token);tmpToken.Resolved=true;tmpToken.Value=tmpValueParsed.toString();}catch(pError){// This constant has the right symbols but apparently isn't a parsable number.
|
|
3381
|
-
tmpResults.ExpressionParserLog.push("ERROR: ExpressionParser.substituteValuesInTokenizedObjects found a non-numeric value for the state address ".concat(tmpToken.Token," at index ").concat(i));this.log.error(tmpResults.ExpressionParserLog[tmpResults.ExpressionParserLog.length-1]);tmpToken.Resolved=false;}}}return pTokenizedObjects;}tokenize(pExpression,pResultObject){return this.Tokenizer.tokenize(pExpression,pResultObject);}lintTokenizedExpression(pTokenizedExpression,pResultObject){return this.Linter.lintTokenizedExpression(pTokenizedExpression,pResultObject);}buildPostfixedSolveList(pTokenizedExpression,pResultObject){return this.Postfix.buildPostfixedSolveList(pTokenizedExpression,pResultObject);}solvePostfixedExpression(pPostfixedExpression,pDataDestinationObject,pResultObject,pManifest){return this.Solver.solvePostfixedExpression(pPostfixedExpression,pDataDestinationObject,pResultObject,pManifest);}
|
|
3382
|
+
tmpResults.ExpressionParserLog.push("ERROR: ExpressionParser.substituteValuesInTokenizedObjects found a non-numeric value for the state address ".concat(tmpToken.Token," at index ").concat(i));this.log.error(tmpResults.ExpressionParserLog[tmpResults.ExpressionParserLog.length-1]);tmpToken.Resolved=false;}}}return pTokenizedObjects;}tokenize(pExpression,pResultObject){return this.Tokenizer.tokenize(pExpression,pResultObject);}lintTokenizedExpression(pTokenizedExpression,pResultObject){return this.Linter.lintTokenizedExpression(pTokenizedExpression,pResultObject);}buildPostfixedSolveList(pTokenizedExpression,pResultObject){return this.Postfix.buildPostfixedSolveList(pTokenizedExpression,pResultObject);}solvePostfixedExpression(pPostfixedExpression,pDataDestinationObject,pResultObject,pManifest){return this.Solver.solvePostfixedExpression(pPostfixedExpression,pDataDestinationObject,pResultObject,pManifest);}/**
|
|
3383
|
+
* Solves the given expression using the provided data and manifest.
|
|
3384
|
+
*
|
|
3385
|
+
* @param {string} pExpression - The expression to solve.
|
|
3386
|
+
* @param {object} pDataSourceObject - (optional) The data source object (e.g. AppData).
|
|
3387
|
+
* @param {object} pResultObject - (optional) The result object containing the full postfix expression list, internal variables and solver history.
|
|
3388
|
+
* @param {object} pManifest - (optional) The manifest object for dereferencing variables.
|
|
3389
|
+
* @param {object} pDataDestinationObject - (optional) The data destination object for where to marshal the result into.
|
|
3390
|
+
* @returns {any} - The result of solving the expression.
|
|
3391
|
+
*/solve(pExpression,pDataSourceObject,pResultObject,pManifest,pDataDestinationObject){let tmpResultsObject=typeof pResultObject==='object'?pResultObject:{};let tmpDataSourceObject=typeof pDataSourceObject==='object'?pDataSourceObject:{};let tmpDataDestinationObject=typeof pDataDestinationObject==='object'?pDataDestinationObject:{};this.tokenize(pExpression,tmpResultsObject);this.lintTokenizedExpression(tmpResultsObject.RawTokens,tmpResultsObject);this.buildPostfixedSolveList(tmpResultsObject.RawTokens,tmpResultsObject);this.substituteValuesInTokenizedObjects(tmpResultsObject.PostfixTokenObjects,tmpDataSourceObject,tmpResultsObject,pManifest);return this.solvePostfixedExpression(tmpResultsObject.PostfixSolveList,tmpDataDestinationObject,tmpResultsObject,pManifest);}}module.exports=FableServiceExpressionParser;},{"./Fable-Service-ExpressionParser/Fable-Service-ExpressionParser-ExpressionTokenizer.js":137,"./Fable-Service-ExpressionParser/Fable-Service-ExpressionParser-FunctionMap.json":138,"./Fable-Service-ExpressionParser/Fable-Service-ExpressionParser-Linter.js":139,"./Fable-Service-ExpressionParser/Fable-Service-ExpressionParser-Postfix.js":140,"./Fable-Service-ExpressionParser/Fable-Service-ExpressionParser-SolvePostfixedExpression.js":141,"./Fable-Service-ExpressionParser/Fable-Service-ExpressionParser-TokenMap.json":142,"big.js":17,"fable-serviceproviderbase":51}],136:[function(require,module,exports){const libFableServiceProviderBase=require('fable-serviceproviderbase');class ExpressionParserOperationBase extends libFableServiceProviderBase{constructor(pFable,pOptions,pServiceHash){super(pFable,pOptions,pServiceHash);this.serviceType='ExpressionParserOperationBase';this.numberTest=/^-{0,1}\d*\.{0,1}\d+$/;this.ExpressionParser=false;}connectExpressionParser(pExpressionParser){this.ExpressionParser=pExpressionParser;}getTokenType(pToken){if(pToken in this.ExpressionParser.tokenMap){return"Token.".concat(this.ExpressionParser.tokenMap[pToken].Type);}else if(pToken.length>2&&pToken[0]==='{'&&pToken[pToken.length-1]==='}'){return'Token.StateAddress';}else if(this.numberTest.test(pToken)){return'Token.Constant';}else{return'Token.Symbol';}// Just for documentation sake:
|
|
3382
3392
|
// There is a fifth token type, VirtualSymbol
|
|
3383
3393
|
// This is a value that's added during solve and looked up by address in the VirtualSymbol object.
|
|
3384
3394
|
}getTokenContainerObject(pToken,pTokenType){return{Token:pToken,Type:typeof pTokenType==='undefined'?this.getTokenType(pToken):pTokenType,Descriptor:pToken in this.ExpressionParser.tokenMap?this.ExpressionParser.tokenMap[pToken]:false};}}module.exports=ExpressionParserOperationBase;},{"fable-serviceproviderbase":51}],137:[function(require,module,exports){const libExpressionParserOperationBase=require('./Fable-Service-ExpressionParser-Base.js');class ExpressionTokenizer extends libExpressionParserOperationBase{constructor(pFable,pOptions,pServiceHash){super(pFable,pOptions,pServiceHash);this.serviceType='ExpressionParser-Tokenizer';}tokenize(pExpression,pResultObject){let tmpResults=typeof pResultObject==='object'?pResultObject:{ExpressionParserLog:[]};tmpResults.RawExpression=pExpression;tmpResults.RawTokens=[];tmpResults.ExpressionParserLog=[];if(typeof pExpression!=='string'){this.log.warn('ExpressionParser.tokenize was passed a non-string expression.');return tmpResults.RawTokens;}/* Tokenize the expression
|
|
@@ -3429,7 +3439,7 @@ tmpCurrentTokenType='Value';tmpCurrentToken+=tmpCharacter;// continue;
|
|
|
3429
3439
|
// }
|
|
3430
3440
|
// tmpResults.ExpressionParserLog.push(`ExpressionParser.tokenize found an unknown character code ${tmpCharCode} character ${tmpCharacter} in the expression: ${pExpression} at index ${i}`);
|
|
3431
3441
|
// this.log.warn(tmpResults.ExpressionParserLog[tmpResults.ExpressionParserLog.length-1]);
|
|
3432
|
-
}if(tmpCurrentTokenType&&tmpCurrentToken.length>0){tmpResults.RawTokens.push(tmpCurrentToken);}return tmpResults.RawTokens;}}module.exports=ExpressionTokenizer;},{"./Fable-Service-ExpressionParser-Base.js":136}],138:[function(require,module,exports){module.exports={"sqrt":{"Name":"Square Root","Address":"fable.Math.sqrtPrecise"},"abs":{"Name":"Absolute Value","Address":"fable.Math.absPrecise"},"rad":{"Name":"Degrees to Radians","Address":"fable.Math.radPrecise"},"sin":{"Name":"Sine","Address":"fable.Math.sin"}};},{}],139:[function(require,module,exports){const libExpressionParserOperationBase=require('./Fable-Service-ExpressionParser-Base.js');class ExpressionParserLinter extends libExpressionParserOperationBase{constructor(pFable,pOptions,pServiceHash){super(pFable,pOptions,pServiceHash);this.serviceType='ExpressionParser-Linter';}lintTokenizedExpression(pTokenizedExpression,pResultObject){let tmpResults=typeof pResultObject==='object'?pResultObject:{ExpressionParserLog:[]};tmpResults.LinterResults=[];// Guard against bad data being passed in
|
|
3442
|
+
}if(tmpCurrentTokenType&&tmpCurrentToken.length>0){tmpResults.RawTokens.push(tmpCurrentToken);}return tmpResults.RawTokens;}}module.exports=ExpressionTokenizer;},{"./Fable-Service-ExpressionParser-Base.js":136}],138:[function(require,module,exports){module.exports={"sqrt":{"Name":"Square Root","Address":"fable.Math.sqrtPrecise"},"abs":{"Name":"Absolute Value","Address":"fable.Math.absPrecise"},"rad":{"Name":"Degrees to Radians","Address":"fable.Math.radPrecise"},"pi":{"Name":"Pi","Address":"fable.Math.piPrecise"},"sin":{"Name":"Sine","Address":"fable.Math.sin"},"cos":{"Name":"Cosine","Address":"fable.Math.cos"},"tan":{"Name":"Tangent","Address":"fable.Math.tan"},"count":{"Name":"Count Set Elements","Address":"fable.Math.countSetElements"},"sortset":{"Name":"Sort Set","Address":"fable.Math.sortSetPrecise"},"bucketset":{"Name":"Bucket Set","Address":"fable.Math.bucketSetPrecise"},"sorthistogram":{"Name":"Sort Histogram","Address":"fable.Math.sortHistogramPrecise"},"max":{"Name":"Maximum","Address":"fable.Math.maxPrecise"},"min":{"Name":"Minimum","Address":"fable.Math.minPrecise"},"sum":{"Name":"Sum","Address":"fable.Math.sumPrecise"},"avg":{"Name":"Average","Address":"fable.Math.averagePrecise"},"mean":{"Name":"Mean","Address":"fable.Math.meanPrecise"},"median":{"Name":"Median","Address":"fable.Math.medianPrecise"},"mode":{"Name":"Mode","Address":"fable.Math.modePrecise"}};},{}],139:[function(require,module,exports){const libExpressionParserOperationBase=require('./Fable-Service-ExpressionParser-Base.js');class ExpressionParserLinter extends libExpressionParserOperationBase{constructor(pFable,pOptions,pServiceHash){super(pFable,pOptions,pServiceHash);this.serviceType='ExpressionParser-Linter';}lintTokenizedExpression(pTokenizedExpression,pResultObject){let tmpResults=typeof pResultObject==='object'?pResultObject:{ExpressionParserLog:[]};tmpResults.LinterResults=[];// Guard against bad data being passed in
|
|
3433
3443
|
if(!Array.isArray(pTokenizedExpression)){tmpResults.ExpressionParserLog.push("ERROR: ExpressionParser.lintTokenizedExpression was passed a non-array tokenized expression.");tmpResults.LinterResults.push(tmpResults.ExpressionParserLog[tmpResults.ExpressionParserLog.length-1]);this.log.error(tmpResults.ExpressionParserLog[tmpResults.ExpressionParserLog.length-1]);return pTokenizedExpression;}if(pTokenizedExpression.length<1){tmpResults.ExpressionParserLog.push("ERROR: ExpressionParser.lintTokenizedExpression was passed an empty tokenized expression.");tmpResults.LinterResults.push(tmpResults.ExpressionParserLog[tmpResults.ExpressionParserLog.length-1]);this.log.error(tmpResults.ExpressionParserLog[tmpResults.ExpressionParserLog.length-1]);return pTokenizedExpression;}// 1. Check for balanced parenthesis
|
|
3434
3444
|
let tmpParenthesisDepth=0;// If it is in a state address, we don't care about the parenthesis
|
|
3435
3445
|
// State addresses are between squiggly brackets
|
|
@@ -3448,45 +3458,16 @@ this.getTokenType(pTokenizedExpression[0])==='Token.StateAddress'||this.getToken
|
|
|
3448
3458
|
){tmpResults.ExpressionParserLog.push("WARNING: ExpressionParser.lintTokenizedExpression found a single equality assignment in the tokenized expression with no assignable address on the left side of the assignment.");tmpResults.LinterResults.push(tmpResults.ExpressionParserLog[tmpResults.ExpressionParserLog.length-1]);this.log.warn(tmpResults.ExpressionParserLog[tmpResults.ExpressionParserLog.length-1]);}}}// 5. Check that there are no operators adjacent to each other
|
|
3449
3459
|
// This is a simple lint check, not a full-blown syntax check
|
|
3450
3460
|
let tmpTokenPrevious=false;for(let i=0;i<pTokenizedExpression.length-1;i++){if(pTokenizedExpression[i]in this.ExpressionParser.tokenMap&&this.ExpressionParser.tokenMap[pTokenizedExpression[i]].Type!='Parenthesis'&&!tmpTokenPrevious){tmpTokenPrevious=true;}else if(pTokenizedExpression[i]in this.ExpressionParser.tokenMap&&this.ExpressionParser.tokenMap[pTokenizedExpression[i]].Type!='Parenthesis'){// If this isn't a + or - positivity/negativity multiplier, it's an error.
|
|
3451
|
-
if(pTokenizedExpression[i]!=='+'&&pTokenizedExpression[i]!=='-'){tmpResults.ExpressionParserLog.push("ERROR: ExpressionParser.lintTokenizedExpression found an ".concat(pTokenizedExpression[i]," operator adjacent to another operator in the tokenized expression at token index ").concat(i));tmpResults.LinterResults.push(tmpResults.ExpressionParserLog[tmpResults.ExpressionParserLog.length-1]);this.log.error(tmpResults.ExpressionParserLog[tmpResults.ExpressionParserLog.length-1]);}}else{tmpTokenPrevious=false;}}return tmpResults.LinterResults;}}module.exports=ExpressionParserLinter;},{"./Fable-Service-ExpressionParser-Base.js":136}],140:[function(require,module,exports){const libExpressionParserOperationBase=require('./Fable-Service-ExpressionParser-Base.js');class ExpressionParserPostfix extends libExpressionParserOperationBase{constructor(pFable,pOptions,pServiceHash){super(pFable,pOptions,pServiceHash);this.serviceType='ExpressionParser-Postfix';}getPosfixSolveListOperation(pOperation,pLeftValue,pRightValue,pDepthSolveList,pDepthSolveIndex){let tmpOperation={VirtualSymbolName:pOperation.VirtualSymbolName,Operation:pOperation,LeftValue:pLeftValue,RightValue:pRightValue};let tmpDepthSolveList=Array.isArray(pDepthSolveList)?pDepthSolveList:false
|
|
3452
|
-
// // allow users to pass in parenthesis in the wrong order.
|
|
3453
|
-
// // The linter does blow up as does the postfix, but, just in case we'll leave these explicit.
|
|
3454
|
-
// // It really doesn't hurt anything.
|
|
3455
|
-
// if (pLeftValue.Token === ')')
|
|
3456
|
-
// {
|
|
3457
|
-
// // We have found a close parenthesis which needs to pull the proper virtual symbol for the last operation on this stack.
|
|
3458
|
-
// // This ensures we are not expressing the parenthesis virtual symbols to the solver.
|
|
3459
|
-
// pLeftValue.VirtualSymbolName = pLayerStackMap[pLeftValue.SolveLayerStack];
|
|
3460
|
-
// this.log.error(`ERROR: ExpressionParser.getPosfixSolveListOperation found a close parenthesis in the left value of an operation.`);
|
|
3461
|
-
// }
|
|
3462
|
-
// else if (pRightValue.Token === '(')
|
|
3463
|
-
// {
|
|
3464
|
-
// // We have found a close parenthesis which needs to pull the proper virtual symbol for the last operation on this stack.
|
|
3465
|
-
// // This ensures we are not expressing the parenthesis virtual symbols to the solver.
|
|
3466
|
-
// pRightValue.VirtualSymbolName = pLayerStackMap[pRightValue.SolveLayerStack];
|
|
3467
|
-
// this.log.error(`ERROR: ExpressionParser.getPosfixSolveListOperation found a close parenthesis in the left value of an operation.`);
|
|
3468
|
-
// }
|
|
3469
|
-
// // Set the layer stack map virtual symbol name to the last operation performed on this stack.
|
|
3470
|
-
// pLayerStackMap[pOperation.SolveLayerStack] = pOperation.VirtualSymbolName;
|
|
3471
|
-
/* These two if blocks are very complex -- they basically provide a
|
|
3461
|
+
if(pTokenizedExpression[i]!=='+'&&pTokenizedExpression[i]!=='-'){tmpResults.ExpressionParserLog.push("ERROR: ExpressionParser.lintTokenizedExpression found an ".concat(pTokenizedExpression[i]," operator adjacent to another operator in the tokenized expression at token index ").concat(i));tmpResults.LinterResults.push(tmpResults.ExpressionParserLog[tmpResults.ExpressionParserLog.length-1]);this.log.error(tmpResults.ExpressionParserLog[tmpResults.ExpressionParserLog.length-1]);}}else{tmpTokenPrevious=false;}}return tmpResults.LinterResults;}}module.exports=ExpressionParserLinter;},{"./Fable-Service-ExpressionParser-Base.js":136}],140:[function(require,module,exports){const libExpressionParserOperationBase=require('./Fable-Service-ExpressionParser-Base.js');class ExpressionParserPostfix extends libExpressionParserOperationBase{constructor(pFable,pOptions,pServiceHash){super(pFable,pOptions,pServiceHash);this.serviceType='ExpressionParser-Postfix';}getPosfixSolveListOperation(pOperation,pLeftValue,pRightValue,pDepthSolveList,pDepthSolveIndex){let tmpOperation={VirtualSymbolName:pOperation.VirtualSymbolName,Operation:pOperation,LeftValue:pLeftValue,RightValue:pRightValue};let tmpDepthSolveList=Array.isArray(pDepthSolveList)?pDepthSolveList:false;/* These two if blocks are very complex -- they basically provide a
|
|
3472
3462
|
* way to deal with recursion that can be expressed to the user in
|
|
3473
3463
|
* a meaningful way.
|
|
3474
3464
|
*
|
|
3475
3465
|
* The reason we are doing it as such is to show exactly how the
|
|
3476
3466
|
* solver resolves the passed-in tokens into a solvable expression.
|
|
3477
3467
|
*/if(!tmpOperation.LeftValue.VirtualSymbolName){tmpOperation.LeftValue.VirtualSymbolName=tmpOperation.VirtualSymbolName;}else{// We need to set the left value to a virtual symbol instead of the looked up value if it's already used in another operation
|
|
3478
|
-
tmpOperation.LeftValue=this.getTokenContainerObject(tmpOperation.LeftValue.VirtualSymbolName,'Token.VirtualSymbol')
|
|
3479
|
-
if
|
|
3480
|
-
'
|
|
3481
|
-
tmpDepthSolveList[i].Descriptor.Precedence>3){// If the symbol to its right is not the same as this operation
|
|
3482
|
-
if(tmpDepthSolveList[i+1].VirtualSymbolName!==tmpOperation.VirtualSymbolName){// This is the recursive "shunting" being simulated
|
|
3483
|
-
tmpDepthSolveList[i+1].VirtualSymbolName=tmpOperation.VirtualSymbolName;}break;}}}}if(!tmpOperation.RightValue.VirtualSymbolName){tmpOperation.RightValue.VirtualSymbolName=tmpOperation.VirtualSymbolName;}else{// We need to set the right value to a virtual symbol instead of the looked up value if it's already used in another operation
|
|
3484
|
-
tmpOperation.RightValue=this.getTokenContainerObject(tmpOperation.RightValue.VirtualSymbolName,'Token.VirtualSymbol');// Now walk forwards and see if we need to update an upcoming symbol for a previously unparsed operator
|
|
3485
|
-
if(tmpDepthSolveList){for(let i=pDepthSolveIndex+1;i<tmpDepthSolveList.length;i++){if(tmpDepthSolveList[i].Type==='Token.Operator'&&!tmpDepthSolveList[i].Parsed&&// When walking forward, we only want to mutate if the precedence hasn't been solved.
|
|
3486
|
-
'Descriptor'in tmpDepthSolveList[i]&&'Descriptor'in tmpOperation.Operation&&// Anything >3 does not have commutative properties
|
|
3487
|
-
tmpDepthSolveList[i].Descriptor.Precedence>3){// If the symbol to its right is not the same as this operation
|
|
3488
|
-
if(tmpDepthSolveList[i-1].VirtualSymbolName!==tmpOperation.VirtualSymbolName){// This is the recursive "shunting" being simulated
|
|
3489
|
-
tmpDepthSolveList[i-1].VirtualSymbolName=tmpOperation.VirtualSymbolName;}break;}else if(tmpDepthSolveList[i].Type==='Token.Operator'&&!tmpDepthSolveList[i].Parsed){break;}}}}tmpOperation.Operation.Parsed=true;return tmpOperation;}buildPostfixedSolveList(pTokenizedExpression,pResultObject){let tmpResults=typeof pResultObject==='object'?pResultObject:{ExpressionParserLog:[]};tmpResults.PostfixedAssignmentAddress='Result';tmpResults.PostfixTokenObjects=[];tmpResults.PostfixSolveList=[];if(pTokenizedExpression.length<3){tmpResults.ExpressionParserLog.push("ERROR: ExpressionParser.buildPostfixedSolveList was passed a tokenized expression with less than three tokens.");this.log.error(tmpResults.ExpressionParserLog[tmpResults.ExpressionParserLog.length-1]);return tmpResults.PostfixTokenObjects;}// 1. Figure out the Equality Assignment
|
|
3468
|
+
if('LeftVirtualSymbolName'in tmpOperation.Operation){tmpOperation.LeftValue=this.getTokenContainerObject(tmpOperation.Operation.LeftVirtualSymbolName,'Token.VirtualSymbol');}else{tmpOperation.LeftValue=this.getTokenContainerObject(tmpOperation.LeftValue.VirtualSymbolName,'Token.VirtualSymbol');}}if(!tmpOperation.RightValue.VirtualSymbolName){tmpOperation.RightValue.VirtualSymbolName=tmpOperation.VirtualSymbolName;}else{// We need to set the right value to a virtual symbol instead of the looked up value if it's already used in another operation
|
|
3469
|
+
//if ('LeftVirtualSymbolName' in tmpOperation.RightValue)
|
|
3470
|
+
if('RightVirtualSymbolName'in tmpOperation.Operation){tmpOperation.RightValue=this.getTokenContainerObject(tmpOperation.Operation.RightVirtualSymbolName,'Token.VirtualSymbol');}else{tmpOperation.RightValue=this.getTokenContainerObject(tmpOperation.RightValue.VirtualSymbolName,'Token.VirtualSymbol');}}tmpOperation.Operation.Parsed=true;return tmpOperation;}buildPostfixedSolveList(pTokenizedExpression,pResultObject){let tmpResults=typeof pResultObject==='object'?pResultObject:{ExpressionParserLog:[]};tmpResults.PostfixedAssignmentAddress='Result';tmpResults.PostfixTokenObjects=[];tmpResults.PostfixSolveList=[];if(pTokenizedExpression.length<3){tmpResults.ExpressionParserLog.push("ERROR: ExpressionParser.buildPostfixedSolveList was passed a tokenized expression with less than three tokens.");this.log.error(tmpResults.ExpressionParserLog[tmpResults.ExpressionParserLog.length-1]);return tmpResults.PostfixTokenObjects;}// 1. Figure out the Equality Assignment
|
|
3490
3471
|
let tmpEqualsIndex=-1;for(let i=0;i<pTokenizedExpression.length;i++){if(pTokenizedExpression[i]==='='&&tmpEqualsIndex<0){tmpEqualsIndex=i;}// If there are two equality assignments, error and bail out.
|
|
3491
3472
|
else if(pTokenizedExpression[i]==='='){tmpResults.ExpressionParserLog.push("ERROR: ExpressionParser.buildPostfixedSolveList found multiple equality assignments in the tokenized expression; equality assignment #".concat(tmpEqualsIndex," at token index ").concat(i,"."));this.log.error(tmpResults.ExpressionParserLog[tmpResults.ExpressionParserLog.length-1]);return tmpResults.PostfixTokenObjects;}}if(tmpEqualsIndex==-1){tmpResults.ExpressionParserLog.push("WARNING: ExpressionParser.buildPostfixedSolveList found no equality assignment in the tokenized expression; defaulting to Result");this.log.warn(tmpResults.ExpressionParserLog[tmpResults.ExpressionParserLog.length-1]);}else if(tmpEqualsIndex>1){tmpResults.ExpressionParserLog.push("WARNING: ExpressionParser.buildPostfixedSolveList found an equality assignment in the tokenized expression at an unexpected location (token index ".concat(tmpEqualsIndex,"); the expression cannot be parsed."));this.log.warn(tmpResults.ExpressionParserLog[tmpResults.ExpressionParserLog.length-1]);}else if(tmpEqualsIndex===0){// This is an implicit function -- just go to result and return the value.
|
|
3492
3473
|
// That is... the user entered something like "= 5 + 3" so we should just return 8, and use the default Result quietly.
|
|
@@ -3502,12 +3483,7 @@ tmpResults.PostfixTokenObjects[i].Depth=tmpDepth;// Generate the virtual symbol
|
|
|
3502
3483
|
tmpResults.PostfixTokenObjects[i].VirtualSymbolName="Pr_".concat(tmpVirtualParenthesisIndex,"_D_").concat(tmpDepth);// 1a. Detect if this parenthesis is signaling a function
|
|
3503
3484
|
// If the previous token is a Symbol (e.g. it say sin(x) or sqrt(3+5) or something) then the parser will interpret it as a function
|
|
3504
3485
|
if(i>0){if(tmpResults.PostfixTokenObjects[i-1].Type==='Token.Symbol'){// Set the type of this to be a function
|
|
3505
|
-
tmpResults.PostfixTokenObjects[i-1].Type='Token.Function'
|
|
3506
|
-
// Rename the virtual symbol n ame to include the function
|
|
3507
|
-
// tmpResults.PostfixTokenObjects[i].VirtualSymbolName = `Fn_${tmpVirtualParenthesisIndex}_D_${tmpDepth}_${this.fable.DataFormat.cleanNonAlphaCharacters(tmpResults.PostfixTokenObjects[i-1].Token)}`;
|
|
3508
|
-
// The function and the parenthesis are at the same depth and virtual symbol
|
|
3509
|
-
// tmpResults.PostfixTokenObjects[i-1].VirtualSymbolName = tmpResults.PostfixTokenObjects[i].VirtualSymbolName;
|
|
3510
|
-
}}// Parenthesis manage the solve layer stack
|
|
3486
|
+
tmpResults.PostfixTokenObjects[i-1].Type='Token.Function';}}// Parenthesis manage the solve layer stack
|
|
3511
3487
|
// For adding a new parenthesis solve layer, we put the parenthesis in the stack we are in and then make all the contained tokens be within the stack of the parenthesis
|
|
3512
3488
|
tmpResults.PostfixTokenObjects[i].SolveLayerStack=tmpSolveLayerStack[tmpSolveLayerStack.length-1];tmpSolveLayerStack.push(tmpResults.PostfixTokenObjects[i].VirtualSymbolName);tmpVirtualParenthesisIndex++;tmpDepth++;}// 2. If it's a closed parenthesis, decrease the depth
|
|
3513
3489
|
else if(tmpResults.PostfixTokenObjects[i].Token===')'){tmpDepth--;tmpResults.PostfixTokenObjects[i].Depth=tmpDepth;// Check to see that the depth of the closed parenthesis is greater than 0
|
|
@@ -3515,17 +3491,7 @@ if(tmpDepth<0){tmpResults.ExpressionParserLog.push("ERROR: ExpressionParser.buil
|
|
|
3515
3491
|
// For closing parenthesis solve layer with a close paren, we put it in the same stack as the opening parenthesis.
|
|
3516
3492
|
// Give the closing parenthesis the same virtual symbol name as the opening parenthesis
|
|
3517
3493
|
// (do the both above at the same time)
|
|
3518
|
-
tmpResults.PostfixTokenObjects[i].VirtualSymbolName=tmpSolveLayerStack.pop();tmpResults.PostfixTokenObjects[i].SolveLayerStack=tmpSolveLayerStack[tmpSolveLayerStack.length-1];}//
|
|
3519
|
-
// It was much more complex later on solving these as virtual symbols of their own.
|
|
3520
|
-
// We are saving the value resolution for the very end.
|
|
3521
|
-
else if(tmpResults.PostfixTokenObjects[i].Type==='Token.Symbol'){// Set the depth of the current solution depth
|
|
3522
|
-
tmpResults.PostfixTokenObjects[i].Depth=tmpDepth;tmpResults.PostfixTokenObjects[i].SolveLayerStack=tmpSolveLayerStack[tmpSolveLayerStack.length-1];// Generate a virtual symbol name that's somewhat human readable
|
|
3523
|
-
//tmpResults.PostfixTokenObjects[i].VirtualSymbolName = `Sm_${tmpVirtualParenthesisIndex}_D_${tmpDepth}_${this.fable.DataFormat.cleanNonAlphaCharacters(tmpResults.PostfixTokenObjects[i].Token)}`;
|
|
3524
|
-
// We've used up this virtual symbol index so increment it
|
|
3525
|
-
// The reason we only use these once is to make sure if we use, say, sin(x) twice at the same depth we still have unique names for each virtual solution
|
|
3526
|
-
//tmpVirtualParenthesisIndex++;
|
|
3527
|
-
}// 4. If it's an operator or constant or comment, just set the depth
|
|
3528
|
-
else{tmpResults.PostfixTokenObjects[i].Depth=tmpDepth;tmpResults.PostfixTokenObjects[i].SolveLayerStack=tmpSolveLayerStack[tmpSolveLayerStack.length-1];}}// 4. Walk through the decorated symbols and generate the postfix solve list
|
|
3494
|
+
tmpResults.PostfixTokenObjects[i].VirtualSymbolName=tmpSolveLayerStack.pop();tmpResults.PostfixTokenObjects[i].SolveLayerStack=tmpSolveLayerStack[tmpSolveLayerStack.length-1];}else{tmpResults.PostfixTokenObjects[i].Depth=tmpDepth;tmpResults.PostfixTokenObjects[i].SolveLayerStack=tmpSolveLayerStack[tmpSolveLayerStack.length-1];}}// 4. Walk through the decorated symbols and generate the postfix solve list
|
|
3529
3495
|
// We are going to start by creating a map of the solve layers:
|
|
3530
3496
|
let tmpSolveLayerMap={};let tmpSolveLayerMaxDepth=0;for(let i=0;i<tmpResults.PostfixTokenObjects.length;i++){if(!(tmpResults.PostfixTokenObjects[i].SolveLayerStack in tmpSolveLayerMap)){tmpSolveLayerMap[tmpResults.PostfixTokenObjects[i].SolveLayerStack]=[];}tmpSolveLayerMap[tmpResults.PostfixTokenObjects[i].SolveLayerStack].push(tmpResults.PostfixTokenObjects[i]);// See what our max depth is. This is super important to the postfix operation
|
|
3531
3497
|
// The programmer in me thinks it would be funny to not track this and just use the map key length as the max size, which would work (logically impossible to have a depth > key length) but it would be quite a bit more confusing to grok the algorithm.
|
|
@@ -3548,64 +3514,60 @@ if(tmpSolveLayerTokens[i].Type==='Token.Operator'&&tmpSolveLayerTokens[i].Descri
|
|
|
3548
3514
|
if(tmpSolveLayerTokens.length===1){tmpResults.ExpressionParserLog.push("ERROR: ExpressionParser.buildPostfixedSolveList found a single operator in a solve layer expression at token index ".concat(i));this.log.error(tmpResults.ExpressionParserLog[tmpResults.ExpressionParserLog.length-1]);return tmpResults.PostfixSolveList;}// If the token is at the beginning of the expression and not a number line orientation modifier, it's an error.
|
|
3549
3515
|
else if(i==0&&(tmpToken.Token!='+'||tmpToken.Token!='-')){tmpResults.ExpressionParserLog.push("ERROR: ExpressionParser.buildPostfixedSolveList found an operator at the beginning of a solve layer expression at token index ".concat(i));this.log.error(tmpResults.ExpressionParserLog[tmpResults.ExpressionParserLog.length-1]);return tmpResults.PostfixSolveList;}// If the token is at the end of the expression, it is an error.
|
|
3550
3516
|
else if(i==tmpSolveLayerTokens.length-1){tmpResults.ExpressionParserLog.push("ERROR: ExpressionParser.buildPostfixedSolveList found an operator at the end of a solve layer expression at token index ".concat(i));this.log.error(tmpResults.ExpressionParserLog[tmpResults.ExpressionParserLog.length-1]);return tmpResults.PostfixSolveList;}// The - at the beginning of an expression is a number line orientation modifier
|
|
3551
|
-
else if(i==0&&tmpToken.Token=='-'){tmpToken.VirtualSymbolName="
|
|
3517
|
+
else if(i==0&&tmpToken.Token=='-'){tmpToken.VirtualSymbolName="VNLO_".concat(tmpVirtualSymbolIndex);tmpResults.PostfixLayerstackMap[tmpToken.SolveLayerStack]=tmpToken.VirtualSymbolName;tmpVirtualSymbolIndex++;}// The - after an operator or an open parenthesis is also a number line orientation modifier
|
|
3552
3518
|
else if(i>0&&tmpToken.Token=='-'&&(tmpSolveLayerTokens[i-1].Type==='Token.Operator'||tmpSolveLayerTokens[i-1].Token==='(')){// The number line negation operator is a special case that generates a virtual constant (-1.0) and multiplies it by the next token
|
|
3553
|
-
tmpToken.VirtualSymbolName="
|
|
3519
|
+
tmpToken.VirtualSymbolName="VNLO_".concat(tmpVirtualSymbolIndex);tmpVirtualSymbolIndex++;}// The + at the beginning is also a number line orientation modifier ... THAT WE IGNORE
|
|
3554
3520
|
else if(i==0&&tmpToken.Token=='+'){continue;}// The + after an operator or a parenthesis is also a number line orientation modifier ... THAT WE IGNORE
|
|
3555
3521
|
else if(i>0&&tmpToken.Token=='+'&&(tmpSolveLayerTokens[i-1].Type==='Token.Operator'||tmpSolveLayerTokens[i-1].Token==='(')){continue;}// If the token is next to another operator it's a parsing error
|
|
3556
3522
|
else if(tmpSolveLayerTokens[i-1].Type==='Token.Operator'||tmpSolveLayerTokens[i+1].Type==='Token.Operator'){tmpResults.ExpressionParserLog.push("ERROR: ExpressionParser.buildPostfixedSolveList found an operator at token index ".concat(i," that is not surrounded by two values."));this.log.error(tmpResults.ExpressionParserLog[tmpResults.ExpressionParserLog.length-1]);return tmpResults.PostfixSolveList;}// Finally add a virtual symbol name to the dang thing.
|
|
3557
|
-
else{tmpToken.VirtualSymbolName="V_".concat(tmpVirtualSymbolIndex);tmpResults.PostfixLayerstackMap[tmpToken.SolveLayerStack]=tmpToken.VirtualSymbolName;tmpVirtualSymbolIndex++;}}else if(tmpSolveLayerTokens[i].Type==='Token.Function'&&tmpPrecedence===0){let tmpToken=tmpSolveLayerTokens[i];tmpToken.VirtualSymbolName="
|
|
3523
|
+
else{tmpToken.VirtualSymbolName="V_".concat(tmpVirtualSymbolIndex);tmpResults.PostfixLayerstackMap[tmpToken.SolveLayerStack]=tmpToken.VirtualSymbolName;tmpVirtualSymbolIndex++;}}else if(tmpSolveLayerTokens[i].Type==='Token.Function'&&tmpPrecedence===0){let tmpToken=tmpSolveLayerTokens[i];tmpToken.VirtualSymbolName="VFE_".concat(tmpVirtualSymbolIndex);tmpVirtualSymbolIndex++;tmpResults.PostfixLayerstackMap[tmpToken.SolveLayerStack]=tmpToken.VirtualSymbolName;}}}}// 5.15 Generate Virtual Symbol Clusters for Functions and Parenthesis
|
|
3558
3524
|
// ... this gets funny because of precedence of operations surrounding them, parenthesis and functions.
|
|
3559
|
-
let
|
|
3560
|
-
tmpPostfixTokenObject.ParenthesisStack=tmpPostfixTokenObject.VirtualSymbolName
|
|
3561
|
-
if(i
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
let
|
|
3566
|
-
if(
|
|
3567
|
-
let
|
|
3568
|
-
//
|
|
3569
|
-
|
|
3570
|
-
//
|
|
3571
|
-
|
|
3572
|
-
//
|
|
3573
|
-
|
|
3574
|
-
|
|
3575
|
-
|
|
3576
|
-
|
|
3577
|
-
//
|
|
3578
|
-
if(
|
|
3579
|
-
|
|
3580
|
-
if(
|
|
3581
|
-
if(!
|
|
3582
|
-
// We often see these inside functions.
|
|
3583
|
-
let tmpAbstractAddToken=this.getTokenContainerObject('+');//let tmpVirtualSymbolName = tmpResults.PostfixLayerstackMap[tmpSolveLayerTokens[0].SolveLayerStack];
|
|
3584
|
-
tmpAbstractAddToken.VirtualSymbolName=tmpResults.PostfixLayerstackMap[tmpSolveLayerTokens[0].SolveLayerStack];tmpResults.PostfixSolveList.push(this.getPosfixSolveListOperation(tmpAbstractAddToken,this.getTokenContainerObject('0.0'),tmpSolveLayerTokens[0]));}}// 5.3: Generate the Postfix Solve List
|
|
3585
|
-
for(let tmpSolveLayerIndex=0;tmpSolveLayerIndex<tmpSolveLayerKeys.length;tmpSolveLayerIndex++){let tmpSolveLayerTokens=tmpSolveLayerMap[tmpSolveLayerKeys[tmpSolveLayerIndex]];// For each precedence level in the layer
|
|
3525
|
+
let tmpFunctionCacheLIFOStack=[];for(let i=0;i<tmpResults.PostfixTokenObjects.length;i++){let tmpPostfixTokenObject=tmpResults.PostfixTokenObjects[i];if(tmpPostfixTokenObject.Type==='Token.Parenthesis'){// This is just to track the parenthesis stack level for User feedback
|
|
3526
|
+
tmpPostfixTokenObject.ParenthesisStack=tmpPostfixTokenObject.VirtualSymbolName;// At the beginning of the expression, this must be an open parenthesis to be legal.
|
|
3527
|
+
if(i==0){tmpPostfixTokenObject.IsFunction=false;let tmpVirtualSymbolName=tmpResults.PostfixLayerstackMap[tmpPostfixTokenObject.VirtualSymbolName];if(!tmpVirtualSymbolName){// ... this parenthesis group has no operators in it; make a virtual symbol name.
|
|
3528
|
+
tmpVirtualSymbolName="VP_".concat(tmpVirtualSymbolIndex);tmpVirtualSymbolIndex++;}tmpPostfixTokenObject.VirtualSymbolName=tmpVirtualSymbolName;tmpFunctionCacheLIFOStack.push(tmpPostfixTokenObject);}// If it's an open parenthesis
|
|
3529
|
+
else if(tmpPostfixTokenObject.Token==='('){// ... supporting a function
|
|
3530
|
+
if(tmpResults.PostfixTokenObjects[i-1].Type==='Token.Function'){tmpPostfixTokenObject.IsFunction=true;tmpPostfixTokenObject.Function=tmpResults.PostfixTokenObjects[i-1];let tmpVirtualSymbolName=tmpResults.PostfixLayerstackMap[tmpPostfixTokenObject.VirtualSymbolName];if(!tmpVirtualSymbolName){// ... this parenthesis group has no operators in it; make a virtual symbol name.
|
|
3531
|
+
tmpVirtualSymbolName="VFP_".concat(tmpVirtualSymbolIndex);tmpVirtualSymbolIndex++;}tmpPostfixTokenObject.VirtualSymbolName=tmpVirtualSymbolName;}else{tmpPostfixTokenObject.IsFunction=false;let tmpVirtualSymbolName=tmpResults.PostfixLayerstackMap[tmpPostfixTokenObject.VirtualSymbolName];if(!tmpVirtualSymbolName){// This is a parenthesis group with no operators in it; make a virtual symbol name.
|
|
3532
|
+
tmpVirtualSymbolName="VP_".concat(tmpVirtualSymbolIndex);tmpVirtualSymbolIndex++;}tmpPostfixTokenObject.VirtualSymbolName=tmpVirtualSymbolName;}tmpFunctionCacheLIFOStack.push(tmpPostfixTokenObject);}if(tmpPostfixTokenObject.Token===')'){let tmpOpenParenthesis=tmpFunctionCacheLIFOStack.pop();if(tmpOpenParenthesis.IsFunction){tmpPostfixTokenObject.IsFunction=true;tmpPostfixTokenObject.VirtualSymbolName=tmpOpenParenthesis.Function.VirtualSymbolName;}else{tmpPostfixTokenObject.IsFunction=false;tmpPostfixTokenObject.VirtualSymbolName=tmpOpenParenthesis.VirtualSymbolName;}}}}// X. Postprocess the parenthesis groups to ensure they respect the order of operations for their boundaries
|
|
3533
|
+
for(let tmpSolveLayerIndex=0;tmpSolveLayerIndex<tmpSolveLayerKeys.length;tmpSolveLayerIndex++){let tmpParenthesisStack=[];let tmpLastOperator=false;let tmpSolveLayerTokens=tmpSolveLayerMap[tmpSolveLayerKeys[tmpSolveLayerIndex]];for(let i=0;i<tmpSolveLayerTokens.length;i++){let tmpPostfixTokenObject=tmpSolveLayerTokens[i];// Keep track of the last operator
|
|
3534
|
+
if(tmpPostfixTokenObject.Type==='Token.Operator'){tmpLastOperator=tmpPostfixTokenObject;}// This is only important to do at the close parenthesis.
|
|
3535
|
+
if(tmpPostfixTokenObject.Type==='Token.Function'){tmpPostfixTokenObject.PreviousOperator=tmpLastOperator;}else if(tmpPostfixTokenObject.Type==='Token.Parenthesis'&&tmpPostfixTokenObject.Token==='('&&tmpPostfixTokenObject.IsFunction){tmpParenthesisStack.push(tmpPostfixTokenObject);if(tmpPostfixTokenObject.Function.PreviousOperator){tmpPostfixTokenObject.PreviousOperator=tmpPostfixTokenObject.Function.PreviousOperator;}}else if(tmpPostfixTokenObject.Type==='Token.Parenthesis'&&tmpPostfixTokenObject.Token==='('){tmpPostfixTokenObject.PreviousOperator=tmpLastOperator;tmpParenthesisStack.push(tmpPostfixTokenObject);}else if(tmpPostfixTokenObject.Type==='Token.Parenthesis'&&tmpPostfixTokenObject.Token===')'){// This is ultra complex, and binds the order of operations logic to the open parenthesis for the group
|
|
3536
|
+
let tmpOpenParenthesis=tmpParenthesisStack.pop();if(i<tmpSolveLayerTokens.length-1){for(let j=i+1;j<tmpSolveLayerTokens.length;j++){if(tmpSolveLayerTokens[j].Type==='Token.Operator'){tmpOpenParenthesis.NextOperator=tmpSolveLayerTokens[j];break;}}}if(tmpOpenParenthesis.PreviousOperator&&tmpOpenParenthesis.NextOperator){if(tmpOpenParenthesis.PreviousOperator.Descriptor.Precedence<=tmpOpenParenthesis.NextOperator.Descriptor.Precedence){tmpOpenParenthesis.NextOperator.LeftVirtualSymbolName=tmpOpenParenthesis.PreviousOperator.VirtualSymbolName;}else{tmpOpenParenthesis.PreviousOperator.RightVirtualSymbolName=tmpOpenParenthesis.NextOperator.VirtualSymbolName;}}}else{if(!('SolveLayerStack'in tmpPostfixTokenObject)){// Decorate the solve layer stack for the token
|
|
3537
|
+
if(tmpParenthesisStack.length>0){tmpPostfixTokenObject.SolveLayerStack=tmpParenthesisStack[tmpParenthesisStack.length-1].SolveLayerStack;}else{tmpPostfixTokenObject.SolveLayerStack='Expression_Root';}}}}}// 5.2.9: Make sure the affinity of operators is respecting order of operations.
|
|
3538
|
+
// Walk backwards and forwards, hoisting same value precedence backwards/forwards
|
|
3539
|
+
// across each layer... the precedence change needs to be decreasing to matter
|
|
3540
|
+
for(let tmpSolveLayerIndex=0;tmpSolveLayerIndex<tmpSolveLayerKeys.length;tmpSolveLayerIndex++){let tmpLastPrecedence=false;let tmpFinalChainToken=false;let tmpSolveLayerTokens=tmpSolveLayerMap[tmpSolveLayerKeys[tmpSolveLayerIndex]];for(let i=tmpSolveLayerTokens.length-1;i>=0;i--){let tmpToken=tmpSolveLayerTokens[i];if(tmpToken.Type==='Token.Operator'){if(!tmpFinalChainToken){tmpFinalChainToken=tmpToken;}else if(tmpToken.Descriptor.Precedence>tmpLastPrecedence){// This is less imporant than the last precedence, so hoist back the virtual value
|
|
3541
|
+
tmpToken.RightVirtualSymbolName=tmpFinalChainToken.VirtualSymbolName;//console.log(`Hoisting ${tmpToken.Token} back to ${tmpFinalChainToken.Token}`);
|
|
3542
|
+
tmpFinalChainToken=tmpToken;}else if(tmpToken.Descriptor.Precedence<tmpLastPrecedence){tmpFinalChainToken=tmpToken;}tmpLastPrecedence=tmpToken.Descriptor.Precedence;}}let tmpDecreasingPrecedenceStack=[];let tmpLastToken=false;for(let i=tmpSolveLayerTokens.length-1;i>=0;i--){let tmpToken=tmpSolveLayerTokens[i];if(tmpToken.Type==='Token.Operator'){if(!tmpLastToken){tmpLastToken=tmpToken;}else if(tmpToken.Descriptor.Precedence>tmpLastPrecedence){// Check and see if this needs to be resolved in the stack
|
|
3543
|
+
if(tmpDecreasingPrecedenceStack.length>0){for(let j=tmpDecreasingPrecedenceStack.length-1;j>=0;j--){if(tmpDecreasingPrecedenceStack[j].Descriptor.Precedence>=tmpToken.Descriptor.Precedence){//console.log(`Hoisting ${tmpDecreasingPrecedenceStack[j].Token} up to ${tmpToken.Token}`);
|
|
3544
|
+
tmpDecreasingPrecedenceStack[j].LeftVirtualSymbolName=tmpToken.VirtualSymbolName;tmpDecreasingPrecedenceStack.slice(j,1);break;}}}tmpLastToken=tmpToken;}else if(tmpToken.Descriptor.Precedence<tmpLastPrecedence){tmpDecreasingPrecedenceStack.push(tmpLastToken);tmpLastToken=tmpToken;}tmpLastPrecedence=tmpToken.Descriptor.Precedence;}}}// 5.3: Generate the Postfix Solve List
|
|
3545
|
+
for(let tmpSolveLayerIndex=0;tmpSolveLayerIndex<tmpSolveLayerKeys.length;tmpSolveLayerIndex++){let tmpSolveLayerTokens=tmpSolveLayerMap[tmpSolveLayerKeys[tmpSolveLayerIndex]];// If this is a layer with one value, presume it's an assignment.
|
|
3546
|
+
if(tmpSolveLayerTokens.length===1){let tmpAbstractAssignToken=this.getTokenContainerObject('=');tmpAbstractAssignToken.VirtualSymbolName=tmpResults.PostfixLayerstackMap[tmpSolveLayerTokens[0].SolveLayerStack];// If this doesn't have a matching solvelayerstack, get the virtual symbol name from the parenthesis group it's in
|
|
3547
|
+
if(!tmpAbstractAssignToken.VirtualSymbolName){for(let i=0;i<tmpResults.PostfixTokenObjects.length;i++){if(tmpResults.PostfixTokenObjects[i].ParenthesisStack===tmpSolveLayerTokens[0].SolveLayerStack){tmpAbstractAssignToken.VirtualSymbolName=tmpResults.PostfixTokenObjects[i].VirtualSymbolName;break;}}}tmpResults.PostfixSolveList.push(this.getPosfixSolveListOperation(tmpAbstractAssignToken,tmpSolveLayerTokens[0],this.getTokenContainerObject('0.0')));continue;}// For each precedence level in the layer
|
|
3586
3548
|
for(let tmpPrecedence=0;tmpPrecedence<=this.ExpressionParser.tokenMaxPrecedence;tmpPrecedence++){// Enumerate all tokens in a layer's expression.
|
|
3587
3549
|
// There is a recursive way to do this, but given the short length of even the most complex equations we're favoring readability.
|
|
3588
3550
|
for(let i=0;i<tmpSolveLayerTokens.length;i++){// If the token is an operator and at the current precedence, add it to the postfix solve list and mutate the array.
|
|
3589
3551
|
if(tmpSolveLayerTokens[i].Type==='Token.Operator'&&tmpSolveLayerTokens[i].Descriptor.Precedence===tmpPrecedence){let tmpToken=tmpSolveLayerTokens[i];// The - at the beginning of an expression is a number line orientation modifier
|
|
3590
3552
|
if(i==0&&tmpToken.Token=='-'){// The number line negation operator is a special case that generates a virtual constant (-1.0) and multiplies it by the next token
|
|
3591
3553
|
// This is an abstract operation that isn't in the expression.
|
|
3592
|
-
let tmpAbstractMultiplyToken=this.
|
|
3554
|
+
let tmpAbstractMultiplyToken=this.getTokenContainerObject('*');tmpAbstractMultiplyToken.VirtualSymbolName=tmpToken.VirtualSymbolName;tmpResults.PostfixSolveList.push(this.getPosfixSolveListOperation(tmpAbstractMultiplyToken,this.getTokenContainerObject('-1.0'),tmpSolveLayerTokens[i+1]));}// The - after an operator or an open parenthesis is also a number line orientation modifier
|
|
3593
3555
|
else if(i>0&&tmpToken.Token=='-'&&(tmpSolveLayerTokens[i-1].Type==='Token.Operator'||tmpSolveLayerTokens[i-1].Token==='(')){// The number line negation operator is a special case that generates a virtual constant (-1.0) and multiplies it by the next token
|
|
3594
|
-
let tmpAbstractMultiplyToken=this.
|
|
3556
|
+
let tmpAbstractMultiplyToken=this.getTokenContainerObject('*');tmpAbstractMultiplyToken.VirtualSymbolName=tmpToken.VirtualSymbolName;tmpResults.PostfixSolveList.push(this.getPosfixSolveListOperation(tmpAbstractMultiplyToken,this.getTokenContainerObject('-1.0'),tmpSolveLayerTokens[i+1]));}// The + at the beginning is also a number line orientation modifier ... THAT WE IGNORE
|
|
3595
3557
|
else if(i==0&&tmpToken.Token=='+'){continue;}// The + after an operator or a parenthesis is also a number line orientation modifier ... THAT WE IGNORE
|
|
3596
3558
|
else if(i>0&&tmpToken.Token=='+'&&(tmpSolveLayerTokens[i-1].Type==='Token.Operator'||tmpSolveLayerTokens[i-1].Token==='(')){continue;}// Finally add the dang thing.
|
|
3597
|
-
else{tmpResults.PostfixSolveList.push(this.getPosfixSolveListOperation(tmpToken,tmpSolveLayerTokens[i-1],tmpSolveLayerTokens[i+1],tmpSolveLayerTokens,i));}}else if(tmpSolveLayerTokens[i].Type==='Token.Function'&&tmpPrecedence===0){let tmpToken=tmpSolveLayerTokens[i]
|
|
3598
|
-
tmpResults.PostfixSolveList.push(this.getPosfixSolveListOperation(tmpToken,tmpSolveLayerTokens[i+1],this.getTokenContainerObject('0.0')));}}}}// Now set the assignment address.
|
|
3559
|
+
else{tmpResults.PostfixSolveList.push(this.getPosfixSolveListOperation(tmpToken,tmpSolveLayerTokens[i-1],tmpSolveLayerTokens[i+1],tmpSolveLayerTokens,i));}}else if(tmpSolveLayerTokens[i].Type==='Token.Function'&&tmpPrecedence===0){let tmpToken=tmpSolveLayerTokens[i];tmpResults.PostfixSolveList.push(this.getPosfixSolveListOperation(tmpToken,tmpSolveLayerTokens[i+1],this.getTokenContainerObject('0.0')));}}}}// 7. Lastly set the assignment address.
|
|
3599
3560
|
let tmpAssignmentInstruction=this.getPosfixSolveListOperation(this.getTokenContainerObject('Assign','Token.SolverInstruction'),this.getTokenContainerObject('DestinationHash','Token.SolverInstruction'),this.getTokenContainerObject('Resulting','Token.SolverInstruction'));tmpAssignmentInstruction.VirtualSymbolName=tmpResults.PostfixedAssignmentAddress;tmpResults.PostfixSolveList.push(tmpAssignmentInstruction);return tmpResults.PostfixSolveList;}}module.exports=ExpressionParserPostfix;},{"./Fable-Service-ExpressionParser-Base.js":136}],141:[function(require,module,exports){const libExpressionParserOperationBase=require('./Fable-Service-ExpressionParser-Base.js');class ExpressionParserSolver extends libExpressionParserOperationBase{constructor(pFable,pOptions,pServiceHash){super(pFable,pOptions,pServiceHash);this.serviceType='ExpressionParser-Solver';}solvePostfixedExpression(pPostfixedExpression,pDataDestinationObject,pResultObject,pManifest){let tmpResults=typeof pResultObject==='object'?pResultObject:{ExpressionParserLog:[]};let tmpManifest=typeof pManifest==='object'?pManifest:this.fable.newManyfest();let tmpDataDestinationObject=typeof pDataDestinationObject==='object'?pDataDestinationObject:{};// If there was a fable passed in (e.g. the results object was a service or such), we won't decorate
|
|
3600
3561
|
let tmpPassedInFable=('fable'in tmpResults);if(!tmpPassedInFable){tmpResults.fable=this.fable;}if(!Array.isArray(pPostfixedExpression)){tmpResults.ExpressionParserLog.push("ERROR: ExpressionParser.solvePostfixedExpression was passed a non-array postfixed expression.");this.log.error(tmpResults.ExpressionParserLog[tmpResults.ExpressionParserLog.length-1]);return false;}if(pPostfixedExpression.length<1){tmpResults.ExpressionParserLog.push("ERROR: ExpressionParser.solvePostfixedExpression was passed an empty postfixed expression.");this.log.error(tmpResults.ExpressionParserLog[tmpResults.ExpressionParserLog.length-1]);return false;}// This is how the user communication magic happens.
|
|
3601
3562
|
tmpResults.VirtualSymbols={};for(let i=0;i<pPostfixedExpression.length;i++){if(pPostfixedExpression[i].Operation.Type==='Token.SolverInstruction'){continue;}let tmpStepResultObject={ExpressionStep:pPostfixedExpression[i],ExpressionStepIndex:i,ResultsObject:tmpResults,Manifest:tmpManifest};// Resolve the virtual symbols to their actual values
|
|
3602
3563
|
if(tmpStepResultObject.ExpressionStep.LeftValue.Type==='Token.VirtualSymbol'){tmpStepResultObject.ExpressionStep.LeftValue.Value=tmpManifest.getValueAtAddress(tmpResults.VirtualSymbols,tmpStepResultObject.ExpressionStep.LeftValue.Token);}if(tmpStepResultObject.ExpressionStep.RightValue.Type==='Token.VirtualSymbol'){tmpStepResultObject.ExpressionStep.RightValue.Value=tmpManifest.getValueAtAddress(tmpResults.VirtualSymbols,tmpStepResultObject.ExpressionStep.RightValue.Token);}// Resolve the parenthesis to their actual values
|
|
3603
|
-
if(tmpStepResultObject.ExpressionStep.LeftValue.Type==='Token.Parenthesis'){tmpStepResultObject.ExpressionStep.LeftValue.Value=tmpManifest.getValueAtAddress(tmpResults.VirtualSymbols,tmpStepResultObject.ExpressionStep.LeftValue.VirtualSymbolName);}if(tmpStepResultObject.ExpressionStep.RightValue.Type==='Token.Parenthesis'){tmpStepResultObject.ExpressionStep.RightValue.Value=tmpManifest.getValueAtAddress(tmpResults.VirtualSymbols,tmpStepResultObject.ExpressionStep.RightValue.VirtualSymbolName);}
|
|
3564
|
+
if(tmpStepResultObject.ExpressionStep.LeftValue.Type==='Token.Parenthesis'){tmpStepResultObject.ExpressionStep.LeftValue.Value=tmpManifest.getValueAtAddress(tmpResults.VirtualSymbols,tmpStepResultObject.ExpressionStep.LeftValue.VirtualSymbolName);}if(tmpStepResultObject.ExpressionStep.RightValue.Type==='Token.Parenthesis'){tmpStepResultObject.ExpressionStep.RightValue.Value=tmpManifest.getValueAtAddress(tmpResults.VirtualSymbols,tmpStepResultObject.ExpressionStep.RightValue.VirtualSymbolName);}// Virtual Constants
|
|
3565
|
+
if(tmpStepResultObject.ExpressionStep.LeftValue.Type==='Token.Constant'&&!('Value'in tmpStepResultObject.ExpressionStep.LeftValue)){tmpStepResultObject.ExpressionStep.LeftValue.Value=tmpStepResultObject.ExpressionStep.LeftValue.Token;}if(tmpStepResultObject.ExpressionStep.RightValue.Type==='Token.Constant'&&!('Value'in tmpStepResultObject.ExpressionStep.RightValue)){tmpStepResultObject.ExpressionStep.RightValue.Value=tmpStepResultObject.ExpressionStep.RightValue.Token;}if(tmpStepResultObject.ExpressionStep.Operation.Type='Operator'){// TODO: This can be optimized. A lot. If necessary. Seems pretty fast honestly for even thousands of operations. Slowest part is arbitrary precision.
|
|
3604
3566
|
// An operator always has a left and right value.
|
|
3605
|
-
let tmpFunctionAddress=false;if(tmpStepResultObject.ExpressionStep.Operation.Token in this.ExpressionParser.tokenMap){tmpFunctionAddress="ResultsObject.".concat(tmpStepResultObject.ExpressionStep.Operation.Descriptor.Function);}else if(tmpStepResultObject.ExpressionStep.Operation.Token.toLowerCase()in this.ExpressionParser.functionMap){tmpFunctionAddress="ResultsObject.".concat(this.ExpressionParser.functionMap[tmpStepResultObject.ExpressionStep.Operation.Token.toLowerCase()].Address);}try{this.log.trace("Solving Step ".concat(i," [").concat(tmpStepResultObject.ExpressionStep.VirtualSymbolName,"] --> [").concat(tmpStepResultObject.ExpressionStep.Operation.Token,"]: ( ").concat(tmpStepResultObject.ExpressionStep.LeftValue.Value," , ").concat(tmpStepResultObject.ExpressionStep.RightValue.Value," )"));tmpResults.VirtualSymbols[tmpStepResultObject.ExpressionStep.VirtualSymbolName]=tmpManifest.getValueAtAddress(tmpStepResultObject,"".concat(tmpFunctionAddress,"(ExpressionStep.LeftValue.Value,ExpressionStep.RightValue.Value)"));this.log.trace(" ---> Step ".concat(i,": ").concat(tmpResults.VirtualSymbols[tmpStepResultObject.ExpressionStep.VirtualSymbolName]));}catch(pError){tmpResults.ExpressionParserLog.push("ERROR: ExpressionParser.solvePostfixedExpression failed to solve step ".concat(i," with function ").concat(tmpStepResultObject.ExpressionStep.Operation.Token,"
|
|
3567
|
+
let tmpFunctionAddress=false;if(tmpStepResultObject.ExpressionStep.Operation.Token in this.ExpressionParser.tokenMap){tmpFunctionAddress="ResultsObject.".concat(tmpStepResultObject.ExpressionStep.Operation.Descriptor.Function);}else if(tmpStepResultObject.ExpressionStep.Operation.Token.toLowerCase()in this.ExpressionParser.functionMap){tmpFunctionAddress="ResultsObject.".concat(this.ExpressionParser.functionMap[tmpStepResultObject.ExpressionStep.Operation.Token.toLowerCase()].Address);}try{this.log.trace("Solving Step ".concat(i," [").concat(tmpStepResultObject.ExpressionStep.VirtualSymbolName,"] --> [").concat(tmpStepResultObject.ExpressionStep.Operation.Token,"]: ( ").concat(tmpStepResultObject.ExpressionStep.LeftValue.Value," , ").concat(tmpStepResultObject.ExpressionStep.RightValue.Value," )"));tmpResults.VirtualSymbols[tmpStepResultObject.ExpressionStep.VirtualSymbolName]=tmpManifest.getValueAtAddress(tmpStepResultObject,"".concat(tmpFunctionAddress,"(ExpressionStep.LeftValue.Value,ExpressionStep.RightValue.Value)"));this.log.trace(" ---> Step ".concat(i,": ").concat(tmpResults.VirtualSymbols[tmpStepResultObject.ExpressionStep.VirtualSymbolName]));}catch(pError){tmpResults.ExpressionParserLog.push("ERROR: ExpressionParser.solvePostfixedExpression failed to solve step ".concat(i," with function ").concat(tmpStepResultObject.ExpressionStep.Operation.Token,": ").concat(pError));this.log.error(tmpResults.ExpressionParserLog[tmpResults.ExpressionParserLog.length-1]);return false;}// Equations don't always solve in virtual symbol order.
|
|
3606
3568
|
tmpResults.SolverFinalVirtualSymbol=tmpStepResultObject.ExpressionStep.VirtualSymbolName;}}let tmpSolverResultValue=tmpManifest.getValueAtAddress(tmpResults,"VirtualSymbols.".concat(tmpResults.SolverFinalVirtualSymbol));// Now deal with final assignment
|
|
3607
3569
|
for(let i=0;i<pPostfixedExpression.length;i++){if(pPostfixedExpression[i].Operation.Type==='Token.SolverInstruction'&&pPostfixedExpression[i].Operation.Token=='Assign'){tmpManifest.setValueAtAddress(tmpResults.VirtualSymbols,pPostfixedExpression[i].VirtualSymbolName,tmpSolverResultValue);tmpManifest.setValueByHash(tmpDataDestinationObject,pPostfixedExpression[i].VirtualSymbolName,tmpSolverResultValue);}}// Clean up the reference if we added it to the object.
|
|
3608
|
-
if(!tmpPassedInFable){delete tmpResults.fable;}return tmpSolverResultValue.toString();}}module.exports=ExpressionParserSolver;},{"./Fable-Service-ExpressionParser-Base.js":136}],142:[function(require,module,exports){module.exports={"=":{"Name":"Equals","Token":"=","Precedence":0,"Type":"Assignment"},"(":{"Name":"Left Parenthesis","Token":"(","Precedence":0,"Type":"Parenthesis"},")":{"Name":"Right Parenthesis","Token":")","Precedence":0,"Type":"Parenthesis"},"*":{"Name":"Multiply","Token":"*","Function":"fable.Math.multiplyPrecise","Precedence":3,"Type":"Operator"},"/":{"Name":"Divide","Token":"/","Function":"fable.Math.dividePrecise","Precedence":3,"Type":"Operator"},"^":{"Name":"Exponent","Token":"^","Function":"fable.Math.powerPrecise","Precedence":1,"Type":"Operator"},"%":{"Name":"Modulus","Token":"%","Function":"fable.Math.modPrecise","Precedence":3,"Type":"Operator"},"+":{"Name":"Add","Token":"+","Function":"fable.Math.addPrecise","Precedence":4,"Type":"Operator"},"-":{"Name":"Subtract","Token":"-","Function":"fable.Math.subtractPrecise","Precedence":4,"Type":"Operator"}};},{}],143:[function(require,module,exports){(function(process){(function(){const libFableServiceBase=require('fable-serviceproviderbase');const libFS=require('fs');const libPath=require('path');const libReadline=require('readline');class FableServiceFilePersistence extends libFableServiceBase{constructor(pFable,pOptions,pServiceHash){super(pFable,pOptions,pServiceHash);this.serviceType='FilePersistence';if(!('Mode'in this.options)){this.options.Mode=parseInt('0777',8)&~process.umask();}this.libFS=libFS;this.libPath=libPath;this.libReadline=libReadline;}joinPath(){return libPath.resolve(...arguments);}existsSync(pPath){return libFS.existsSync(pPath);}exists(pPath,fCallback){let tmpFileExists=this.existsSync(pPath);;return fCallback(null,tmpFileExists);}appendFileSync(pFileName,pAppendContent,pOptions){let tmpOptions=typeof pOptions==='undefined'?'utf8':pOptions;return libFS.appendFileSync(pFileName,pAppendContent,tmpOptions);}deleteFileSync(pFileName){return libFS.unlinkSync(pFileName);}deleteFolderSync(pFileName){return libFS.rmdirSync(pFileName);}readFileSync(pFilePath,pOptions){let tmpOptions=typeof pOptions==='undefined'?'utf8':pOptions;return libFS.readFileSync(pFilePath,tmpOptions);}readFile(pFilePath,pOptions,fCallback){let tmpOptions=typeof pOptions==='undefined'?'utf8':pOptions;return libFS.readFile(pFilePath,tmpOptions,fCallback);}writeFileSync(pFileName,pFileContent,pOptions){let tmpOptions=typeof pOptions==='undefined'?'utf8':pOptions;return libFS.writeFileSync(pFileName,pFileContent,tmpOptions);}writeFileSyncFromObject(pFileName,pObject){return this.writeFileSync(pFileName,JSON.stringify(pObject,null,4));}writeFileSyncFromArray(pFileName,pFileArray){if(!Array.isArray(pFileArray)){this.log.error("File Persistence Service attempted to write ".concat(pFileName," from array but the expected array was not an array (it was a ").concat(typeof pFileArray,")."));return Error('Attempted to write ${pFileName} from array but the expected array was not an array (it was a ${typeof(pFileArray)}).');}else{for(let i=0;i<pFileArray.length;i++){return this.appendFileSync(pFileName,"".concat(pFileArray[i],"\n"));}}}writeFile(pFileName,pFileContent,pOptions,fCallback){let tmpOptions=typeof pOptions==='undefined'?'utf8':pOptions;return libFS.writeFile(pFileName,pFileContent,tmpOptions,fCallback);}lineReaderFactory(pFilePath,fOnLine,fOnComplete,fOnError){let tmpLineReader={};if(typeof pFilePath!='string'){return false;}tmpLineReader.filePath=pFilePath;tmpLineReader.fileStream=libFS.createReadStream(tmpLineReader.filePath);tmpLineReader.reader=libReadline.createInterface({input:tmpLineReader.fileStream,crlfDelay:Infinity});if(typeof fOnError==='function'){tmpLineReader.reader.on('error',fOnError);}tmpLineReader.reader.on('line',typeof fOnLine==='function'?fOnLine:()=>{});if(typeof fOnComplete==='function'){tmpLineReader.reader.on('close',fOnComplete);}return tmpLineReader;}// Folder management
|
|
3570
|
+
if(!tmpPassedInFable){delete tmpResults.fable;}return tmpSolverResultValue.toString();}}module.exports=ExpressionParserSolver;},{"./Fable-Service-ExpressionParser-Base.js":136}],142:[function(require,module,exports){module.exports={"=":{"Name":"Equals","Token":"=","Function":"fable.Math.assignValue","Precedence":0,"Type":"Assignment"},"(":{"Name":"Left Parenthesis","Token":"(","Precedence":0,"Type":"Parenthesis"},")":{"Name":"Right Parenthesis","Token":")","Precedence":0,"Type":"Parenthesis"},"*":{"Name":"Multiply","Token":"*","Function":"fable.Math.multiplyPrecise","Precedence":3,"Type":"Operator"},"/":{"Name":"Divide","Token":"/","Function":"fable.Math.dividePrecise","Precedence":3,"Type":"Operator"},"^":{"Name":"Exponent","Token":"^","Function":"fable.Math.powerPrecise","Precedence":1,"Type":"Operator"},"%":{"Name":"Modulus","Token":"%","Function":"fable.Math.modPrecise","Precedence":3,"Type":"Operator"},"+":{"Name":"Add","Token":"+","Function":"fable.Math.addPrecise","Precedence":4,"Type":"Operator"},"-":{"Name":"Subtract","Token":"-","Function":"fable.Math.subtractPrecise","Precedence":4,"Type":"Operator"}};},{}],143:[function(require,module,exports){(function(process){(function(){const libFableServiceBase=require('fable-serviceproviderbase');const libFS=require('fs');const libPath=require('path');const libReadline=require('readline');class FableServiceFilePersistence extends libFableServiceBase{constructor(pFable,pOptions,pServiceHash){super(pFable,pOptions,pServiceHash);this.serviceType='FilePersistence';if(!('Mode'in this.options)){this.options.Mode=parseInt('0777',8)&~process.umask();}this.libFS=libFS;this.libPath=libPath;this.libReadline=libReadline;}joinPath(){return libPath.resolve(...arguments);}existsSync(pPath){return libFS.existsSync(pPath);}exists(pPath,fCallback){let tmpFileExists=this.existsSync(pPath);;return fCallback(null,tmpFileExists);}appendFileSync(pFileName,pAppendContent,pOptions){let tmpOptions=typeof pOptions==='undefined'?'utf8':pOptions;return libFS.appendFileSync(pFileName,pAppendContent,tmpOptions);}deleteFileSync(pFileName){return libFS.unlinkSync(pFileName);}deleteFolderSync(pFileName){return libFS.rmdirSync(pFileName);}readFileSync(pFilePath,pOptions){let tmpOptions=typeof pOptions==='undefined'?'utf8':pOptions;return libFS.readFileSync(pFilePath,tmpOptions);}readFile(pFilePath,pOptions,fCallback){let tmpOptions=typeof pOptions==='undefined'?'utf8':pOptions;return libFS.readFile(pFilePath,tmpOptions,fCallback);}writeFileSync(pFileName,pFileContent,pOptions){let tmpOptions=typeof pOptions==='undefined'?'utf8':pOptions;return libFS.writeFileSync(pFileName,pFileContent,tmpOptions);}writeFileSyncFromObject(pFileName,pObject){return this.writeFileSync(pFileName,JSON.stringify(pObject,null,4));}writeFileSyncFromArray(pFileName,pFileArray){if(!Array.isArray(pFileArray)){this.log.error("File Persistence Service attempted to write ".concat(pFileName," from array but the expected array was not an array (it was a ").concat(typeof pFileArray,")."));return Error('Attempted to write ${pFileName} from array but the expected array was not an array (it was a ${typeof(pFileArray)}).');}else{for(let i=0;i<pFileArray.length;i++){return this.appendFileSync(pFileName,"".concat(pFileArray[i],"\n"));}}}writeFile(pFileName,pFileContent,pOptions,fCallback){let tmpOptions=typeof pOptions==='undefined'?'utf8':pOptions;return libFS.writeFile(pFileName,pFileContent,tmpOptions,fCallback);}lineReaderFactory(pFilePath,fOnLine,fOnComplete,fOnError){let tmpLineReader={};if(typeof pFilePath!='string'){return false;}tmpLineReader.filePath=pFilePath;tmpLineReader.fileStream=libFS.createReadStream(tmpLineReader.filePath);tmpLineReader.reader=libReadline.createInterface({input:tmpLineReader.fileStream,crlfDelay:Infinity});if(typeof fOnError==='function'){tmpLineReader.reader.on('error',fOnError);}tmpLineReader.reader.on('line',typeof fOnLine==='function'?fOnLine:()=>{});if(typeof fOnComplete==='function'){tmpLineReader.reader.on('close',fOnComplete);}return tmpLineReader;}// Folder management
|
|
3609
3571
|
makeFolderRecursive(pParameters,fCallback){let tmpParameters=pParameters;if(typeof pParameters=='string'){tmpParameters={Path:pParameters};}else if(typeof pParameters!=='object'){fCallback(new Error('Parameters object or string not properly passed to recursive folder create.'));return false;}if(typeof tmpParameters.Path!=='string'){fCallback(new Error('Parameters object needs a path to run the folder create operation.'));return false;}if(!('Mode'in tmpParameters)){tmpParameters.Mode=this.options.Mode;}// Check if we are just starting .. if so, build the initial state for our recursive function
|
|
3610
3572
|
if(typeof tmpParameters.CurrentPathIndex==='undefined'){// Build the tools to start recursing
|
|
3611
3573
|
tmpParameters.ActualPath=libPath.normalize(tmpParameters.Path);tmpParameters.ActualPathParts=tmpParameters.ActualPath.split(libPath.sep);tmpParameters.CurrentPathIndex=0;tmpParameters.CurrentPath='';}else{// This is not our first run, so we will continue the recursion.
|
|
@@ -3615,24 +3577,233 @@ tmpParameters.CurrentPathIndex++;}// Check if the path is fully complete
|
|
|
3615
3577
|
if(tmpParameters.CurrentPathIndex>=tmpParameters.ActualPathParts.length){return fCallback(null);}// Check if the path exists (and is a folder)
|
|
3616
3578
|
libFS.open(tmpParameters.CurrentPath+libPath.sep+tmpParameters.ActualPathParts[tmpParameters.CurrentPathIndex],'r',(pError,pFileDescriptor)=>{if(pFileDescriptor){libFS.closeSync(pFileDescriptor);}if(pError&&pError.code=='ENOENT'){/* Path doesn't exist, create it */libFS.mkdir(tmpParameters.CurrentPath+libPath.sep+tmpParameters.ActualPathParts[tmpParameters.CurrentPathIndex],tmpParameters.Mode,pCreateError=>{if(!pCreateError){// We have now created our folder and there was no error -- continue.
|
|
3617
3579
|
return this.makeFolderRecursive(tmpParameters,fCallback);}else if(pCreateError.code=='EEXIST'){// The folder exists -- our dev might be running this in parallel/async/whatnot.
|
|
3618
|
-
return this.makeFolderRecursive(tmpParameters,fCallback);}else{console.log(pCreateError.code);return fCallback(pCreateError);}});}else{return this.makeFolderRecursive(tmpParameters,fCallback);}});}}module.exports=FableServiceFilePersistence;}).call(this);}).call(this,require('_process'));},{"_process":87,"fable-serviceproviderbase":51,"fs":19,"path":83,"readline":19}],144:[function(require,module,exports){
|
|
3580
|
+
return this.makeFolderRecursive(tmpParameters,fCallback);}else{console.log(pCreateError.code);return fCallback(pCreateError);}});}else{return this.makeFolderRecursive(tmpParameters,fCallback);}});}}module.exports=FableServiceFilePersistence;}).call(this);}).call(this,require('_process'));},{"_process":87,"fable-serviceproviderbase":51,"fs":19,"path":83,"readline":19}],144:[function(require,module,exports){/**
|
|
3581
|
+
* @file Fable-Service-Math.js
|
|
3582
|
+
* @description This file contains the implementation of the FableServiceMath class, which provides simple functions for performing arbitrary precision math operations.
|
|
3583
|
+
* @module FableServiceMath
|
|
3584
|
+
* @extends libFableServiceBase
|
|
3585
|
+
*/const libFableServiceBase=require('fable-serviceproviderbase');/**
|
|
3619
3586
|
* Arbitrary Precision Math Operations
|
|
3620
3587
|
* @author Steven Velozo <steven@velozo.com>
|
|
3621
3588
|
* @description Simple functions that perform arbitrary precision math operations and return string resultant values. Wraps big.js
|
|
3622
3589
|
* @class FableServiceMath
|
|
3623
3590
|
* @extends libFableServiceBase
|
|
3624
|
-
*/class FableServiceMath extends libFableServiceBase{constructor(pFable,pOptions,pServiceHash){super(pFable,pOptions,pServiceHash);this.serviceType='Math';}/*
|
|
3625
|
-
|
|
3626
|
-
|
|
3627
|
-
|
|
3628
|
-
|
|
3629
|
-
|
|
3630
|
-
|
|
3631
|
-
|
|
3632
|
-
|
|
3633
|
-
*/get roundDown(){return this.fable.Utility.bigNumber.roundDown;}get roundHalfUp(){return this.fable.Utility.bigNumber.roundHalfUp;}get roundHalfEven(){return this.fable.Utility.bigNumber.roundHalfEven;}get roundUp(){return this.fable.Utility.bigNumber.roundUp;}
|
|
3634
|
-
|
|
3635
|
-
|
|
3591
|
+
*/class FableServiceMath extends libFableServiceBase{constructor(pFable,pOptions,pServiceHash){super(pFable,pOptions,pServiceHash);this.serviceType='Math';this.pi='3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679';}/*
|
|
3592
|
+
Pass-through Rounding Method Constants
|
|
3593
|
+
|
|
3594
|
+
Property Value BigDecimal Equiv Description
|
|
3595
|
+
---------- ----- ---------------- -----------
|
|
3596
|
+
roundDown 0 ROUND_DOWN Rounds towards zero. (_I.e. truncate, no rounding._)
|
|
3597
|
+
roundHalfUp 1 ROUND_HALF_UP Rounds towards nearest neighbour. (_If equidistant, rounds away from zero._)
|
|
3598
|
+
roundHalfEven 2 ROUND_HALF_EVEN Rounds towards nearest neighbour. (_If equidistant, rounds towards even neighbour._)
|
|
3599
|
+
roundUp 3 ROUND_UP Rounds positively away from zero. (_Always round up._)
|
|
3600
|
+
*/get roundDown(){return this.fable.Utility.bigNumber.roundDown;}get roundHalfUp(){return this.fable.Utility.bigNumber.roundHalfUp;}get roundHalfEven(){return this.fable.Utility.bigNumber.roundHalfEven;}get roundUp(){return this.fable.Utility.bigNumber.roundUp;}/**
|
|
3601
|
+
* Parses a precise number value.
|
|
3602
|
+
*
|
|
3603
|
+
* @param {number} pValue - The value to parse.
|
|
3604
|
+
* @param {any} pNonNumberValue - The value to use if parsing fails.
|
|
3605
|
+
* @returns {string} - The parsed number as a string.
|
|
3606
|
+
*/parsePrecise(pValue,pNonNumberValue){let tmpNumber;try{tmpNumber=new this.fable.Utility.bigNumber(pValue);}catch(pError){this.log.warn("Error parsing number (type ".concat(typeof pValue,"): ").concat(pError));tmpNumber=typeof pNonNumberValue==='undefined'?"0.0":pNonNumberValue;}return tmpNumber.toString();}/**
|
|
3607
|
+
* Assigns the given value. For equals operations in the solver.
|
|
3608
|
+
* @param {*} pValue - The value to be assigned.
|
|
3609
|
+
* @returns {*} The assigned value.
|
|
3610
|
+
*/assignValue(pValue){return pValue;}/**
|
|
3611
|
+
* Calculates the precise percentage of a given value compared to another value.
|
|
3612
|
+
*
|
|
3613
|
+
* @param {number} pIs - The value to calculate the percentage of.
|
|
3614
|
+
* @param {number} pOf - The value to calculate the percentage against.
|
|
3615
|
+
* @returns {string} The precise percentage as a string.
|
|
3616
|
+
*/percentagePrecise(pIs,pOf){let tmpLeftValue=isNaN(pIs)?0:pIs;let tmpRightValue=isNaN(pOf)?0:pOf;if(tmpRightValue==0){return'0';}let tmpLeftArbitraryValue=new this.fable.Utility.bigNumber(tmpLeftValue);let tmpResult=tmpLeftArbitraryValue.div(tmpRightValue);tmpResult=tmpResult.times(100);return tmpResult.toString();}/**
|
|
3617
|
+
* Rounds a value to a specified number of decimal places using a specified rounding method.
|
|
3618
|
+
*
|
|
3619
|
+
* @param {number} pValue - The value to be rounded.
|
|
3620
|
+
* @param {number} pDecimals - The number of decimal places to round to.
|
|
3621
|
+
* @param {function} [pRoundingMethod] - The rounding method to use. Defaults to `this.roundHalfUp`.
|
|
3622
|
+
* @returns {string} - The rounded value as a string.
|
|
3623
|
+
*/roundPrecise(pValue,pDecimals,pRoundingMethod){let tmpValue=isNaN(pValue)?0:pValue;let tmpDecimals=isNaN(pDecimals)?0:pDecimals;let tmpRoundingMethod=typeof pRoundingMethod==='undefined'?this.roundHalfUp:pRoundingMethod;let tmpArbitraryValue=new this.fable.Utility.bigNumber(tmpValue);let tmpResult=tmpArbitraryValue.round(tmpDecimals,tmpRoundingMethod);return tmpResult.toString();}/**
|
|
3624
|
+
* Returns a string representation of a number with a specified number of decimals.
|
|
3625
|
+
*
|
|
3626
|
+
* @param {number} pValue - The number to be formatted.
|
|
3627
|
+
* @param {number} pDecimals - The number of decimals to include in the formatted string.
|
|
3628
|
+
* @param {string} [pRoundingMethod] - The rounding method to use. Defaults to 'roundHalfUp'.
|
|
3629
|
+
* @returns {string} - The formatted number as a string.
|
|
3630
|
+
*/toFixedPrecise(pValue,pDecimals,pRoundingMethod){let tmpValue=isNaN(pValue)?0:pValue;let tmpDecimals=isNaN(pDecimals)?0:pDecimals;let tmpRoundingMethod=typeof pRoundingMethod==='undefined'?this.roundHalfUp:pRoundingMethod;let tmpArbitraryValue=new this.fable.Utility.bigNumber(tmpValue);let tmpResult=tmpArbitraryValue.toFixed(tmpDecimals,tmpRoundingMethod);return tmpResult.toString();}/**
|
|
3631
|
+
* Adds two values precisely.
|
|
3632
|
+
* @param {number} pLeftValue - The left value to be added.
|
|
3633
|
+
* @param {number} pRightValue - The right value to be added.
|
|
3634
|
+
* @returns {string} - The result of adding the two values as a string.
|
|
3635
|
+
*/addPrecise(pLeftValue,pRightValue){let tmpLeftValue=isNaN(pLeftValue)?0:pLeftValue;let tmpRightValue=isNaN(pRightValue)?0:pRightValue;let tmpLeftArbitraryValue=new this.fable.Utility.bigNumber(tmpLeftValue);let tmpResult=tmpLeftArbitraryValue.plus(tmpRightValue);return tmpResult.toString();}/**
|
|
3636
|
+
* Subtracts two values precisely.
|
|
3637
|
+
*
|
|
3638
|
+
* @param {number} pLeftValue - The left value to subtract.
|
|
3639
|
+
* @param {number} pRightValue - The right value to subtract.
|
|
3640
|
+
* @returns {string} The result of the subtraction as a string.
|
|
3641
|
+
*/subtractPrecise(pLeftValue,pRightValue){let tmpLeftValue=isNaN(pLeftValue)?0:pLeftValue;let tmpRightValue=isNaN(pRightValue)?0:pRightValue;let tmpLeftArbitraryValue=new this.fable.Utility.bigNumber(tmpLeftValue);let tmpResult=tmpLeftArbitraryValue.minus(tmpRightValue);return tmpResult.toString();}/**
|
|
3642
|
+
* Calculates the precise power of two numbers.
|
|
3643
|
+
*
|
|
3644
|
+
* @param {number} pLeftValue - The base value.
|
|
3645
|
+
* @param {number} pRightValue - The exponent value.
|
|
3646
|
+
* @returns {string} The result of raising the base value to the exponent value.
|
|
3647
|
+
*/powerPrecise(pLeftValue,pRightValue){let tmpLeftValue=isNaN(pLeftValue)?0:pLeftValue;let tmpRightValue=isNaN(pRightValue)?0:parseInt(pRightValue);let tmpLeftArbitraryValue=new this.fable.Utility.bigNumber(tmpLeftValue);let tmpResult=tmpLeftArbitraryValue.pow(tmpRightValue);return tmpResult.toString();}/**
|
|
3648
|
+
* Multiplies two values precisely.
|
|
3649
|
+
*
|
|
3650
|
+
* @param {number} pLeftValue - The left value to multiply.
|
|
3651
|
+
* @param {number} pRightValue - The right value to multiply.
|
|
3652
|
+
* @returns {string} The result of the multiplication as a string.
|
|
3653
|
+
*/multiplyPrecise(pLeftValue,pRightValue){let tmpLeftValue=isNaN(pLeftValue)?0:pLeftValue;let tmpRightValue=isNaN(pRightValue)?0:pRightValue;let tmpLeftArbitraryValue=new this.fable.Utility.bigNumber(tmpLeftValue);let tmpResult=tmpLeftArbitraryValue.times(tmpRightValue);return tmpResult.toString();}/**
|
|
3654
|
+
* Divides two values precisely.
|
|
3655
|
+
*
|
|
3656
|
+
* @param {number} pLeftValue - The left value to be divided.
|
|
3657
|
+
* @param {number} pRightValue - The right value to divide by.
|
|
3658
|
+
* @returns {string} The result of the division as a string.
|
|
3659
|
+
*/dividePrecise(pLeftValue,pRightValue){let tmpLeftValue=isNaN(pLeftValue)?0:pLeftValue;let tmpRightValue=isNaN(pRightValue)?0:pRightValue;let tmpLeftArbitraryValue=new this.fable.Utility.bigNumber(tmpLeftValue);let tmpResult=tmpLeftArbitraryValue.div(tmpRightValue);return tmpResult.toString();}/**
|
|
3660
|
+
* Calculates the modulus of two values with precision.
|
|
3661
|
+
*
|
|
3662
|
+
* @param {number} pLeftValue - The left value.
|
|
3663
|
+
* @param {number} pRightValue - The right value.
|
|
3664
|
+
* @returns {string} The result of the modulus operation as a string.
|
|
3665
|
+
*/modPrecise(pLeftValue,pRightValue){let tmpLeftValue=isNaN(pLeftValue)?0:pLeftValue;let tmpRightValue=isNaN(pRightValue)?0:pRightValue;let tmpLeftArbitraryValue=new this.fable.Utility.bigNumber(tmpLeftValue);let tmpResult=tmpLeftArbitraryValue.mod(tmpRightValue);return tmpResult.toString();}/**
|
|
3666
|
+
* Calculates the square root of a number with precise decimal places.
|
|
3667
|
+
*
|
|
3668
|
+
* @param {number} pValue - The number to calculate the square root of.
|
|
3669
|
+
* @returns {string} The square root of the input number as a string.
|
|
3670
|
+
*/sqrtPrecise(pValue){let tmpValue=isNaN(pValue)?0:pValue;let tmpLeftArbitraryValue=new this.fable.Utility.bigNumber(tmpValue);let tmpResult=tmpLeftArbitraryValue.sqrt();return tmpResult.toString();}/**
|
|
3671
|
+
* Calculates the absolute value of a number precisely.
|
|
3672
|
+
*
|
|
3673
|
+
* @param {number} pValue - The number to calculate the absolute value of.
|
|
3674
|
+
* @returns {string} The absolute value of the input number as a string.
|
|
3675
|
+
*/absPrecise(pValue){let tmpValue=isNaN(pValue)?0:pValue;let tmpLeftArbitraryValue=new this.fable.Utility.bigNumber(tmpValue);let tmpResult=tmpLeftArbitraryValue.abs();return tmpResult.toString();}/**
|
|
3676
|
+
* Compares two values precisely.
|
|
3677
|
+
*
|
|
3678
|
+
* @param {number} pLeftValue - The left value to compare.
|
|
3679
|
+
* @param {number} pRightValue - The right value to compare.
|
|
3680
|
+
* @returns {number} - Returns the result of the comparison.
|
|
3681
|
+
*/comparePrecise(pLeftValue,pRightValue){let tmpLeftValue=isNaN(pLeftValue)?0:pLeftValue;let tmpRightValue=isNaN(pRightValue)?0:pRightValue;let tmpLeftArbitraryValue=new this.fable.Utility.bigNumber(tmpLeftValue);return tmpLeftArbitraryValue.cmp(tmpRightValue);}/**
|
|
3682
|
+
* Determines if the left value is greater than the right value precisely.
|
|
3683
|
+
*
|
|
3684
|
+
* @param {number} pLeftValue - The left value to compare.
|
|
3685
|
+
* @param {number} pRightValue - The right value to compare.
|
|
3686
|
+
* @returns {boolean} - Returns true if the left value is greater than the right value, otherwise returns false.
|
|
3687
|
+
*/gtPrecise(pLeftValue,pRightValue){let tmpLeftValue=isNaN(pLeftValue)?0:pLeftValue;let tmpRightValue=isNaN(pRightValue)?0:pRightValue;let tmpLeftArbitraryValue=new this.fable.Utility.bigNumber(tmpLeftValue);return tmpLeftArbitraryValue.gt(tmpRightValue);}/**
|
|
3688
|
+
* Checks if the left value is greater than or equal to the right value.
|
|
3689
|
+
* If either value is not a number, it is treated as 0.
|
|
3690
|
+
*
|
|
3691
|
+
* @param {number} pLeftValue - The left value to compare.
|
|
3692
|
+
* @param {number} pRightValue - The right value to compare.
|
|
3693
|
+
* @returns {boolean} - True if the left value is greater than or equal to the right value, false otherwise.
|
|
3694
|
+
*/gtePrecise(pLeftValue,pRightValue){let tmpLeftValue=isNaN(pLeftValue)?0:pLeftValue;let tmpRightValue=isNaN(pRightValue)?0:pRightValue;let tmpLeftArbitraryValue=new this.fable.Utility.bigNumber(tmpLeftValue);return tmpLeftArbitraryValue.gte(tmpRightValue);}/**
|
|
3695
|
+
* Determines if the left value is less than the right value precisely.
|
|
3696
|
+
*
|
|
3697
|
+
* @param {number} pLeftValue - The left value to compare.
|
|
3698
|
+
* @param {number} pRightValue - The right value to compare.
|
|
3699
|
+
* @returns {boolean} - Returns true if the left value is less than the right value, otherwise returns false.
|
|
3700
|
+
*/ltPrecise(pLeftValue,pRightValue){let tmpLeftValue=isNaN(pLeftValue)?0:pLeftValue;let tmpRightValue=isNaN(pRightValue)?0:pRightValue;let tmpLeftArbitraryValue=new this.fable.Utility.bigNumber(tmpLeftValue);return tmpLeftArbitraryValue.lt(tmpRightValue);}/**
|
|
3701
|
+
* Determines if the left value is less than or equal to the right value.
|
|
3702
|
+
*
|
|
3703
|
+
* @param {number} pLeftValue - The left value to compare.
|
|
3704
|
+
* @param {number} pRightValue - The right value to compare.
|
|
3705
|
+
* @returns {boolean} - Returns true if the left value is less than or equal to the right value, otherwise returns false.
|
|
3706
|
+
*/ltePrecise(pLeftValue,pRightValue){let tmpLeftValue=isNaN(pLeftValue)?0:pLeftValue;let tmpRightValue=isNaN(pRightValue)?0:pRightValue;let tmpLeftArbitraryValue=new this.fable.Utility.bigNumber(tmpLeftValue);return tmpLeftArbitraryValue.lt(tmpRightValue);}/**
|
|
3707
|
+
* Converts degrees to radians with arbitrary precision.
|
|
3708
|
+
*
|
|
3709
|
+
* @param {number} pDegrees - The degrees to convert to radians.
|
|
3710
|
+
* @returns {string} - The converted radians as a string.
|
|
3711
|
+
*/radPrecise(pDegrees){let tmpDegrees=isNaN(pDegrees)?0:pDegrees;let tmpDegreesArbitraryValue=new this.fable.Utility.bigNumber(tmpDegrees);// TODO: Const for pi in arbitrary precision?
|
|
3712
|
+
let tmpResult=tmpDegreesArbitraryValue.times(Math.PI).div(180);return tmpResult.toString();}/**
|
|
3713
|
+
* Calculates the value of pi with the specified precision.
|
|
3714
|
+
* If no precision is provided, returns 100 digits after the decimal.
|
|
3715
|
+
*
|
|
3716
|
+
* @param {number} [pPrecision] - The precision to use for calculating pi.
|
|
3717
|
+
* @returns {number} - The calculated value of pi.
|
|
3718
|
+
*/piPrecise(pPrecision){if(typeof pPrecision==='undefined'){return this.pi;}else{return this.roundPrecise(this.pi,pPrecision);}}/**
|
|
3719
|
+
* Calculates the sine of the given angle in radians.
|
|
3720
|
+
*
|
|
3721
|
+
* @param {number} pRadians - The angle in radians.
|
|
3722
|
+
* @returns {number} The sine of the angle.
|
|
3723
|
+
*/sin(pRadians){let tmpRadians=isNaN(pRadians)?0:pRadians;return Math.sin(tmpRadians);}/**
|
|
3724
|
+
* Calculates the cosine of the given angle in radians.
|
|
3725
|
+
*
|
|
3726
|
+
* @param {number} pRadians - The angle in radians.
|
|
3727
|
+
* @returns {number} The cosine of the angle.
|
|
3728
|
+
*/cos(pRadians){let tmpRadians=isNaN(pRadians)?0:pRadians;return Math.cos(tmpRadians);}/**
|
|
3729
|
+
* Calculates the tangent of an angle in radians.
|
|
3730
|
+
*
|
|
3731
|
+
* @param {number} pRadians - The angle in radians.
|
|
3732
|
+
* @returns {number} The tangent of the angle.
|
|
3733
|
+
*/tan(pRadians){let tmpRadians=isNaN(pRadians)?0:pRadians;return Math.tan(tmpRadians);}/* * * * * * * * * * * * * * * *
|
|
3734
|
+
* Set functions
|
|
3735
|
+
* These are meant to work fine with arrays and more complex set descriptions returned by Manyfest.
|
|
3736
|
+
* Manyfest sometimes returns values as arrays and sometimes as a map of addresses with values depending
|
|
3737
|
+
* on what was requested.
|
|
3738
|
+
*
|
|
3739
|
+
* The following functions will likely be broken into their own service.
|
|
3740
|
+
*/ /**
|
|
3741
|
+
* Counts the number of elements in a set.
|
|
3742
|
+
*
|
|
3743
|
+
* @param {Array|Object|any} pValueSet - The set to count the elements of.
|
|
3744
|
+
* @returns {number} The number of elements in the set.
|
|
3745
|
+
*/countSetElements(pValueSet){if(Array.isArray(pValueSet)){return pValueSet.length;}else if(typeof pValueSet==='object'){return Object.keys(pValueSet).length;}else if(pValueSet){// This is controversial. Discuss with colleagues!
|
|
3746
|
+
return 1;}return 0;}/**
|
|
3747
|
+
* Sorts the elements in the given value set in ascending order using the precise parsing and comparison.
|
|
3748
|
+
*
|
|
3749
|
+
* @param {Array|Object} pValueSet - The value set to be sorted.
|
|
3750
|
+
* @returns {Array} - The sorted value set.
|
|
3751
|
+
*/sortSetPrecise(pValueSet){let tmpSortedSet=[];if(Array.isArray(pValueSet)){for(let i=0;i<pValueSet.length;i++){tmpSortedSet.push(this.parsePrecise(pValueSet[i],NaN));}}else if(typeof pValueSet==='object'){let tmpKeys=Object.keys(pValueSet);for(let i=0;i<tmpKeys.length;i++){tmpSortedSet.push(this.parsePrecise(pValueSet[tmpKeys[i]],NaN));}}tmpSortedSet.sort((pLeft,pRight)=>{return this.comparePrecise(pLeft,pRight);});return tmpSortedSet;}/**
|
|
3752
|
+
* Bucketizes a set of values based on a specified bucket size.
|
|
3753
|
+
*
|
|
3754
|
+
* @param {Array|Object} pValueSet - The set of values to be bucketized.
|
|
3755
|
+
* @param {number} pBucketSize - The size of each bucket. Optional - If NaN, the values will be bucketized by their value.
|
|
3756
|
+
* @returns {Object} - The bucketized set of values.
|
|
3757
|
+
*/bucketSetPrecise(pValueSet,pBucketSize){let tmpBucketedSet={};let tmpBucketSize=this.parsePrecise(pBucketSize,NaN);if(Array.isArray(pValueSet)){for(let i=0;i<pValueSet.length;i++){let tmpValue=this.parsePrecise(pValueSet[i],NaN);let tmpBucket=tmpValue.toString();if(!isNaN(tmpBucketSize)){tmpBucket=this.dividePrecise(pValueSet[i],tmpBucketSize);}if(!(tmpBucket in tmpBucketedSet)){tmpBucketedSet[tmpBucket]=0;}tmpBucketedSet[tmpBucket]=tmpBucketedSet[tmpBucket]+1;}}else if(typeof pValueSet==='object'){let tmpKeys=Object.keys(pValueSet);for(let i=0;i<tmpKeys.length;i++){let tmpValue=this.parsePrecise(pValueSet[tmpKeys[i]],NaN);let tmpBucket=tmpValue.toString();if(!isNaN(tmpBucketSize)){tmpBucket=this.dividePrecise(pValueSet[i],tmpBucketSize);}if(!(tmpBucket in tmpBucketedSet)){tmpBucketedSet[tmpBucket]=0;}tmpBucketedSet[tmpBucket]=tmpBucketedSet[tmpBucket]+1;}}return tmpBucketedSet;}/**
|
|
3758
|
+
* Calculates the histogram using precise bucket set for the given pValueSet.
|
|
3759
|
+
*
|
|
3760
|
+
* @param {Array<number>} pValueSet - The array of p-values.
|
|
3761
|
+
* @returns {Array<number>} The histogram of the p-values.
|
|
3762
|
+
*/histogramPrecise(pValueSet){return this.bucketSetPrecise(pValueSet);}/**
|
|
3763
|
+
* Sorts the histogram object in ascending order based on the frequencies of the buckets.
|
|
3764
|
+
*
|
|
3765
|
+
* @param {Object} pHistogram - The histogram object to be sorted.
|
|
3766
|
+
* @returns {Object} - The sorted histogram object.
|
|
3767
|
+
*/sortHistogramPrecise(pHistogram){let tmpSortedHistogram={};let tmpKeys=Object.keys(pHistogram);tmpKeys.sort((pLeft,pRight)=>{return pHistogram[pLeft]-pHistogram[pRight];});for(let i=0;i<tmpKeys.length;i++){tmpSortedHistogram[tmpKeys[i]]=pHistogram[tmpKeys[i]];}return tmpSortedHistogram;}/**
|
|
3768
|
+
* Finds the maximum value from a set of precise values.
|
|
3769
|
+
*
|
|
3770
|
+
* @param {Array|Object} pValueSet - The set of values to find the maximum from.
|
|
3771
|
+
* @returns {number} - The maximum value from the set.
|
|
3772
|
+
*/maxPrecise(pValueSet){let tmpMaxValue=NaN;if(Array.isArray(pValueSet)){for(let i=0;i<pValueSet.length;i++){if(!tmpMaxValue){tmpMaxValue=this.parsePrecise(pValueSet[i],NaN);}else{let tmpComparisonValue=this.parsePrecise(pValueSet[i],NaN);if(this.gtPrecise(tmpComparisonValue,tmpMaxValue)){tmpMaxValue=tmpComparisonValue;}}}}else if(typeof pValueSet==='object'){let tmpKeys=Object.keys(pValueSet);for(let i=0;i<tmpKeys.length;i++){if(!tmpMaxValue){tmpMaxValue=this.parsePrecise(pValueSet[tmpKeys[i]],NaN);}else{let tmpComparisonValue=this.parsePrecise(pValueSet[tmpKeys[i]],NaN);if(this.gtPrecise(tmpComparisonValue,tmpMaxValue)){tmpMaxValue=tmpComparisonValue;}}}}return tmpMaxValue;}/**
|
|
3773
|
+
* Finds the minimum value from a set of values.
|
|
3774
|
+
*
|
|
3775
|
+
* @param {Array|Object} pValueSet - The set of values to find the minimum from.
|
|
3776
|
+
* @returns {number} The minimum value from the set.
|
|
3777
|
+
*/minPrecise(pValueSet){let tmpMinValue=NaN;if(Array.isArray(pValueSet)){for(let i=0;i<pValueSet.length;i++){if(!tmpMinValue){tmpMinValue=this.parsePrecise(pValueSet[i],NaN);}else{let tmpComparisonValue=this.parsePrecise(pValueSet[i],NaN);if(!isNaN(tmpComparisonValue)&&this.ltPrecise(tmpComparisonValue,tmpMinValue)){tmpMinValue=tmpComparisonValue;}}}}else if(typeof pValueSet==='object'){let tmpKeys=Object.keys(pValueSet);for(let i=0;i<tmpKeys.length;i++){if(!tmpMinValue){tmpMinValue=this.parsePrecise(pValueSet[tmpKeys[i]],NaN);}else{let tmpComparisonValue=this.parsePrecise(pValueSet[tmpKeys[i]],NaN);if(!isNaN(tmpComparisonValue)&&this.ltPrecise(tmpComparisonValue,tmpMinValue)){tmpMinValue=tmpComparisonValue;}}}}return tmpMinValue;}/**
|
|
3778
|
+
* Calculates the precise sum of values in the given value set.
|
|
3779
|
+
*
|
|
3780
|
+
* @param {Array|Object} pValueSet - The value set to calculate the sum from.
|
|
3781
|
+
* @returns {string} The precise sum value as a string.
|
|
3782
|
+
*/sumPrecise(pValueSet){let tmpSumValue="0.0";if(Array.isArray(pValueSet)){for(let i=0;i<pValueSet.length;i++){let tmpComparisonValue=this.parsePrecise(pValueSet[i],NaN);if(!isNaN(tmpComparisonValue)){tmpSumValue=this.addPrecise(tmpSumValue,tmpComparisonValue);}}}else if(typeof pValueSet==='object'){let tmpKeys=Object.keys(pValueSet);for(let i=0;i<tmpKeys.length;i++){let tmpComparisonValue=this.parsePrecise(pValueSet[tmpKeys[i]],NaN);if(!isNaN(tmpComparisonValue)){tmpSumValue=this.addPrecise(tmpSumValue,tmpComparisonValue);}}}return tmpSumValue;}/**
|
|
3783
|
+
* Calculates the precise mean of a given value set.
|
|
3784
|
+
*
|
|
3785
|
+
* @param {Array<number>} pValueSet - The array of values to calculate the mean.
|
|
3786
|
+
* @returns {string} The precise mean value as a string.
|
|
3787
|
+
*/meanPrecise(pValueSet){let tmpSumValue=this.sumPrecise(pValueSet);let tmpCount=this.countSetElements(pValueSet);if(tmpCount==0){return'0.0';}return this.dividePrecise(tmpSumValue,tmpCount);}/**
|
|
3788
|
+
* Calculates the average of an array of values precisely.
|
|
3789
|
+
*
|
|
3790
|
+
* @param {Array<number>} pValueSet - The array of values to calculate the average of.
|
|
3791
|
+
* @returns {number} The precise average of the values.
|
|
3792
|
+
*/averagePrecise(pValueSet){return this.meanPrecise(pValueSet);}/**
|
|
3793
|
+
* Calculates the precise median value of a given value set.
|
|
3794
|
+
*
|
|
3795
|
+
* @param {Array<number>} pValueSet - The array of values to calculate the median from.
|
|
3796
|
+
* @returns {number|string} - The median value of the given value set. If the value set is empty, returns '0.0'.
|
|
3797
|
+
*/medianPrecise(pValueSet){let tmpCount=this.countSetElements(pValueSet);// If there are no elements, return 0 ... should this be NaN?
|
|
3798
|
+
if(tmpCount==0){return'0.0';}let tmpSortedValueSet=this.sortSetPrecise(pValueSet);let tmpMiddleElement=Math.floor(tmpCount/2);// If the count is odd, return the middle element
|
|
3799
|
+
if(tmpCount%2==1){return tmpSortedValueSet[tmpMiddleElement];}// If the count is even, return the average of the two middle elements
|
|
3800
|
+
else{let tmpLeftMiddleValue=tmpSortedValueSet[tmpMiddleElement-1];let tmpRightMiddleValue=tmpSortedValueSet[tmpMiddleElement];return this.dividePrecise(this.addPrecise(tmpLeftMiddleValue,tmpRightMiddleValue),2);}}/**
|
|
3801
|
+
* Calculates the mode (most frequently occurring value) of a given value set using precise mode calculation.
|
|
3802
|
+
*
|
|
3803
|
+
* @param {Array} pValueSet - The array of values to calculate the mode from.
|
|
3804
|
+
* @returns {Array} - An array containing the mode value(s) from the given value set.
|
|
3805
|
+
*/modePrecise(pValueSet){let tmpHistogram=this.bucketSetPrecise(pValueSet);let tmpMaxCount=0;// Philosophical question about whether the values should be returned sorted.
|
|
3806
|
+
let tmpHistogramValueSet=Object.keys(tmpHistogram);let tmpModeValueSet=[];for(let i=0;i<tmpHistogramValueSet.length;i++){if(tmpHistogram[tmpHistogramValueSet[i]]>tmpMaxCount){tmpMaxCount=tmpHistogram[tmpHistogramValueSet[i]];tmpModeValueSet=[tmpHistogramValueSet[i]];}else if(tmpHistogram[tmpHistogramValueSet[i]]==tmpMaxCount){tmpModeValueSet.push(tmpHistogramValueSet[i]);}}return tmpModeValueSet;}}module.exports=FableServiceMath;},{"fable-serviceproviderbase":51}],145:[function(require,module,exports){const libFableServiceBase=require('fable-serviceproviderbase');/**
|
|
3636
3807
|
* Precedent Meta-Templating
|
|
3637
3808
|
* @author Steven Velozo <steven@velozo.com>
|
|
3638
3809
|
* @description Process text stream trie and postfix tree, parsing out meta-template expression functions.
|