fable 3.0.124 → 3.0.126
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 +48 -24
- package/dist/fable.compatible.js +329 -55
- package/dist/fable.compatible.min.js +2 -2
- package/dist/fable.compatible.min.js.map +1 -1
- package/dist/fable.js +304 -30
- package/dist/fable.min.js +2 -2
- package/dist/fable.min.js.map +1 -1
- package/package.json +2 -2
- package/source/Fable.js +18 -0
- package/source/services/Fable-Service-ExpressionParser/Fable-Service-ExpressionParser-Base.js +55 -0
- package/source/services/Fable-Service-ExpressionParser/Fable-Service-ExpressionParser-ExpressionTokenizer.js +158 -0
- package/source/services/Fable-Service-ExpressionParser/Fable-Service-ExpressionParser-FunctionMap.json +22 -0
- package/source/services/Fable-Service-ExpressionParser/Fable-Service-ExpressionParser-Linter.js +180 -0
- package/source/services/Fable-Service-ExpressionParser/Fable-Service-ExpressionParser-Postfix.js +626 -0
- package/source/services/Fable-Service-ExpressionParser/Fable-Service-ExpressionParser-SolvePostfixedExpression.js +125 -0
- package/source/services/Fable-Service-ExpressionParser/Fable-Service-ExpressionParser-TokenMap.json +75 -0
- package/source/services/Fable-Service-ExpressionParser.js +210 -0
- package/source/services/Fable-Service-Math.js +30 -1
- package/source/services/Fable-Service-MetaTemplate/MetaTemplate-StringParser.js +23 -14
- package/source/services/Fable-Service-MetaTemplate.js +4 -2
- package/source/services/Fable-Service-Template.js +1 -1
- package/test/ExpressionParser_tests.js +184 -0
- package/test/MetaTemplating_tests.js +41 -1
package/dist/fable.js
CHANGED
|
@@ -3017,15 +3017,15 @@ this.connectFable(this);// --> Bootstrapping of fable into the Service Manager i
|
|
|
3017
3017
|
// Initialization Phase 2: Map in the default services.
|
|
3018
3018
|
// They will then be available in the Default service provider set as well.
|
|
3019
3019
|
this.connectPreinitServiceProviderInstance(this.UUID);this.connectPreinitServiceProviderInstance(this.Logging);this.connectPreinitServiceProviderInstance(this.SettingsManager);// Initialize and instantiate the default baked-in Data Arithmatic service
|
|
3020
|
-
this.addAndInstantiateServiceType('EnvironmentData',require('./services/Fable-Service-EnvironmentData.js'));this.addServiceType('Template',require('./services/Fable-Service-Template.js'));this.addServiceType('MetaTemplate',require('./services/Fable-Service-MetaTemplate.js'));this.addServiceType('Anticipate',require('./services/Fable-Service-Anticipate.js'));this.addAndInstantiateServiceType('Dates',require('./services/Fable-Service-DateManipulation.js'));this.addAndInstantiateServiceType('DataFormat',require('./services/Fable-Service-DataFormat.js'));this.addAndInstantiateServiceType('DataGeneration',require('./services/Fable-Service-DataGeneration.js'));this.addAndInstantiateServiceType('Utility',require('./services/Fable-Service-Utility.js'));this.addAndInstantiateServiceType('Math',require('./services/Fable-Service-Math.js'));this.addServiceType('RestClient',require('./services/Fable-Service-RestClient.js'));this.addServiceType('Manifest',require('manyfest'));this.addServiceType('ObjectCache',require('cachetrax'));this.addAndInstantiateServiceType('ProgressTime',require('./services/Fable-Service-ProgressTime.js'));this.addServiceType('ProgressTrackerSet',require('./services/Fable-Service-ProgressTrackerSet.js'));this.addServiceType('Operation',require('./services/Fable-Service-Operation.js'));this.addServiceType('CSVParser',require('./services/Fable-Service-CSVParser.js'));this.addServiceType('FilePersistence',require('./services/Fable-Service-FilePersistence.js'));}/* State Accessors */get isFable(){return true;}get settings(){return this.SettingsManager.settings;}get settingsManager(){return this.SettingsManager;}// For backwards compatibility
|
|
3021
|
-
getUUID(){return this.UUID.getUUID();}newAnticipate(){return this.instantiateServiceProviderWithoutRegistration('Anticipate');}/* Service Manager Methods */addServiceType(pServiceType,pServiceClass){if(this.servicesMap.hasOwnProperty(pServiceType)){// TODO: Check if any services are running?
|
|
3020
|
+
this.addAndInstantiateServiceType('EnvironmentData',require('./services/Fable-Service-EnvironmentData.js'));this.addServiceType('Template',require('./services/Fable-Service-Template.js'));this.addServiceType('MetaTemplate',require('./services/Fable-Service-MetaTemplate.js'));this.addServiceType('Anticipate',require('./services/Fable-Service-Anticipate.js'));this.addAndInstantiateServiceType('Dates',require('./services/Fable-Service-DateManipulation.js'));this.addAndInstantiateServiceType('DataFormat',require('./services/Fable-Service-DataFormat.js'));this.addAndInstantiateServiceType('DataGeneration',require('./services/Fable-Service-DataGeneration.js'));this.addAndInstantiateServiceType('Utility',require('./services/Fable-Service-Utility.js'));this.addAndInstantiateServiceType('Math',require('./services/Fable-Service-Math.js'));this.addServiceType('ExpressionParser',require('./services/Fable-Service-ExpressionParser.js'));this.addServiceType('RestClient',require('./services/Fable-Service-RestClient.js'));this.addServiceType('Manifest',require('manyfest'));this.addServiceType('ObjectCache',require('cachetrax'));this.addAndInstantiateServiceType('ProgressTime',require('./services/Fable-Service-ProgressTime.js'));this.addServiceType('ProgressTrackerSet',require('./services/Fable-Service-ProgressTrackerSet.js'));this.addServiceType('Operation',require('./services/Fable-Service-Operation.js'));this.addServiceType('CSVParser',require('./services/Fable-Service-CSVParser.js'));this.addServiceType('FilePersistence',require('./services/Fable-Service-FilePersistence.js'));}/* State Accessors */get isFable(){return true;}get settings(){return this.SettingsManager.settings;}get settingsManager(){return this.SettingsManager;}// For backwards compatibility
|
|
3021
|
+
getUUID(){return this.UUID.getUUID();}newAnticipate(){return this.instantiateServiceProviderWithoutRegistration('Anticipate');}newManyfest(pManifestDefinition){return this.instantiateServiceProviderWithoutRegistration('Manifest',pManifestDefinition);}/* Service Manager Methods */addServiceType(pServiceType,pServiceClass){if(this.servicesMap.hasOwnProperty(pServiceType)){// TODO: Check if any services are running?
|
|
3022
3022
|
this.log.warn("Adding a service type [".concat(pServiceType,"] that already exists. This will change the default class prototype for this service."));}else{// Add the container for instantiated services to go in
|
|
3023
3023
|
this.servicesMap[pServiceType]={};// Add the type to the list of types
|
|
3024
3024
|
this.serviceTypes.push(pServiceType);}// Using the static member of the class is a much more reliable way to check if it is a service class than instanceof
|
|
3025
3025
|
if(typeof pServiceClass=='function'&&pServiceClass.isFableService){// Add the class to the list of classes
|
|
3026
3026
|
this.serviceClasses[pServiceType]=pServiceClass;}else{// Add the base class to the list of classes
|
|
3027
3027
|
this.log.error("Attempted to add service type [".concat(pServiceType,"] with an invalid class. Using base service class, which will not crash but won't provide meaningful services."));this.serviceClasses[pServiceType]=libFableServiceBase;}return this.serviceClasses[pServiceType];}addServiceTypeIfNotExists(pServiceType,pServiceClass){if(!this.servicesMap.hasOwnProperty(pServiceType)){return this.addServiceType(pServiceType,pServiceClass);}else{return this.serviceClasses[pServiceType];}}// This is for the services that are meant to run mostly single-instance so need a default at initialization
|
|
3028
|
-
addAndInstantiateServiceType(pServiceType,pServiceClass){this.addServiceType(pServiceType,pServiceClass);return this.instantiateServiceProvider(pServiceType,{},"".concat(pServiceType,"-Default"));}// Some services expect to be overloaded / customized class.
|
|
3028
|
+
addAndInstantiateServiceType(pServiceType,pServiceClass){this.addServiceType(pServiceType,pServiceClass);return this.instantiateServiceProvider(pServiceType,{},"".concat(pServiceType,"-Default"));}addAndInstantiateServiceTypeIfNotExists(pServiceType,pServiceClass){if(!this.servicesMap.hasOwnProperty(pServiceType)){return this.instantiateServiceProvider(pServiceType,{},"".concat(pServiceType,"-Default"));}else{return this.serviceClasses[pServiceType];}}// Some services expect to be overloaded / customized class.
|
|
3029
3029
|
instantiateServiceProviderFromPrototype(pServiceType,pOptions,pCustomServiceHash,pServicePrototype){// Instantiate the service
|
|
3030
3030
|
let tmpService=new pServicePrototype(this,pOptions,pCustomServiceHash);if(this.extraServiceInitialization){tmpService=this.extraServiceInitialization(tmpService);}// Add the service to the service map
|
|
3031
3031
|
this.servicesMap[pServiceType][tmpService.Hash]=tmpService;// If this is the first service of this type, make it the default
|
|
@@ -3044,7 +3044,7 @@ this.servicesMap[tmpServiceType][tmpServiceHash]=pServiceInstance;// If this is
|
|
|
3044
3044
|
if(!this.services.hasOwnProperty(tmpServiceType)){this.setDefaultServiceInstantiation(tmpServiceType,tmpServiceHash,false);}return pServiceInstance;}setDefaultServiceInstantiation(pServiceType,pServiceHash,pOverwriteService){// Overwrite services by default, unless told not to
|
|
3045
3045
|
let tmpOverwriteService=typeof pOverwriteService==='undefined'?true:pOverwriteService;// Make sure the service exists
|
|
3046
3046
|
if(this.servicesMap[pServiceType].hasOwnProperty(pServiceHash)){if(!this.hasOwnProperty(pServiceType)||tmpOverwriteService){this[pServiceType]=this.servicesMap[pServiceType][pServiceHash];}if(!this.services.hasOwnProperty(pServiceType)||tmpOverwriteService){this.services[pServiceType]=this.servicesMap[pServiceType][pServiceHash];}return true;}return false;}}// This is for backwards compatibility
|
|
3047
|
-
function autoConstruct(pSettings){return new Fable(pSettings);}module.exports=Fable;module.exports.new=autoConstruct;module.exports.LogProviderBase=libFableLog.LogProviderBase;module.exports.ServiceProviderBase=libFableServiceBase;module.exports.CoreServiceProviderBase=libFableServiceBase.CoreServiceProviderBase;module.exports.precedent=libFableSettings.precedent;},{"./services/Fable-Service-Anticipate.js":128,"./services/Fable-Service-CSVParser.js":129,"./services/Fable-Service-DataFormat.js":130,"./services/Fable-Service-DataGeneration.js":132,"./services/Fable-Service-DateManipulation.js":133,"./services/Fable-Service-EnvironmentData.js":134,"./services/Fable-Service-
|
|
3047
|
+
function autoConstruct(pSettings){return new Fable(pSettings);}module.exports=Fable;module.exports.new=autoConstruct;module.exports.LogProviderBase=libFableLog.LogProviderBase;module.exports.ServiceProviderBase=libFableServiceBase;module.exports.CoreServiceProviderBase=libFableServiceBase.CoreServiceProviderBase;module.exports.precedent=libFableSettings.precedent;},{"./services/Fable-Service-Anticipate.js":128,"./services/Fable-Service-CSVParser.js":129,"./services/Fable-Service-DataFormat.js":130,"./services/Fable-Service-DataGeneration.js":132,"./services/Fable-Service-DateManipulation.js":133,"./services/Fable-Service-EnvironmentData.js":134,"./services/Fable-Service-ExpressionParser.js":135,"./services/Fable-Service-FilePersistence.js":143,"./services/Fable-Service-Math.js":144,"./services/Fable-Service-MetaTemplate.js":145,"./services/Fable-Service-Operation.js":149,"./services/Fable-Service-ProgressTime.js":150,"./services/Fable-Service-ProgressTrackerSet.js":152,"./services/Fable-Service-RestClient.js":153,"./services/Fable-Service-Template.js":154,"./services/Fable-Service-Utility.js":155,"cachetrax":22,"fable-log":50,"fable-serviceproviderbase":51,"fable-settings":54,"fable-uuid":56,"manyfest":80}],128:[function(require,module,exports){const libFableServiceBase=require('fable-serviceproviderbase');class FableServiceAnticipate extends libFableServiceBase{constructor(pFable,pOptions,pServiceHash){super(pFable,pOptions,pServiceHash);this.serviceType='AsyncAnticipate';// The queue of operations waiting to run.
|
|
3048
3048
|
this.operationQueue=[];this.erroredOperations=[];this.executingOperationCount=0;this.completedOperationCount=0;this.callDepth=0;this.maxOperations=1;this.lastError=undefined;this.waitingFunctions=[];}checkQueue(){// This could be combined with the last else if stanza but the logic for errors and non-errors would be blended and more complex to follow so keeping it unrolled.
|
|
3049
3049
|
if(this.lastError){// If there are no operations left, and we have waiting functions, call them.
|
|
3050
3050
|
for(let i=0;i<this.waitingFunctions.length;i++){//this.log.trace('Calling waiting function.')
|
|
@@ -3340,7 +3340,272 @@ this.plugin_advancedFormat=require('dayjs/plugin/advancedFormat');this.dayJS.ext
|
|
|
3340
3340
|
// You would do the following:
|
|
3341
3341
|
// const localeDE = require('dayjs/locale/de');
|
|
3342
3342
|
// _Fable.Dates.dayJS.locale('de');
|
|
3343
|
-
}}module.exports=DateManipulation;},{"dayjs":28,"dayjs/plugin/advancedFormat":29,"dayjs/plugin/isoWeek":30,"dayjs/plugin/relativeTime":31,"dayjs/plugin/timezone":32,"dayjs/plugin/utc":33,"dayjs/plugin/weekOfYear":34,"dayjs/plugin/weekday":35,"fable-serviceproviderbase":51}],134:[function(require,module,exports){const libFableServiceBase=require('fable-serviceproviderbase');class FableServiceEnvironmentData extends libFableServiceBase{constructor(pFable,pOptions,pServiceHash){super(pFable,pOptions,pServiceHash);this.serviceType='EnvironmentData';this.Environment="node.js";}}module.exports=FableServiceEnvironmentData;},{"fable-serviceproviderbase":51}],135:[function(require,module,exports){
|
|
3343
|
+
}}module.exports=DateManipulation;},{"dayjs":28,"dayjs/plugin/advancedFormat":29,"dayjs/plugin/isoWeek":30,"dayjs/plugin/relativeTime":31,"dayjs/plugin/timezone":32,"dayjs/plugin/utc":33,"dayjs/plugin/weekOfYear":34,"dayjs/plugin/weekday":35,"fable-serviceproviderbase":51}],134:[function(require,module,exports){const libFableServiceBase=require('fable-serviceproviderbase');class FableServiceEnvironmentData extends libFableServiceBase{constructor(pFable,pOptions,pServiceHash){super(pFable,pOptions,pServiceHash);this.serviceType='EnvironmentData';this.Environment="node.js";}}module.exports=FableServiceEnvironmentData;},{"fable-serviceproviderbase":51}],135:[function(require,module,exports){const{PE}=require('big.js');const libFableServiceBase=require('fable-serviceproviderbase');/* Trying a different pattern for this service ...
|
|
3344
|
+
*
|
|
3345
|
+
* This service is a simple expression parser that can handle math expressions, with magic(tm) lookup of addresses with a manifest.
|
|
3346
|
+
*
|
|
3347
|
+
* Each method works multiple ways.
|
|
3348
|
+
*
|
|
3349
|
+
* 1. You can pass in a results object, and, it will put the state for that step outcome into the results object.
|
|
3350
|
+
* 2. It always returns the state, and works without the results object.
|
|
3351
|
+
*
|
|
3352
|
+
*
|
|
3353
|
+
* Learned a lot from this npm package: https://www.npmjs.com/package/math-expression-evaluator
|
|
3354
|
+
* And its related code at github: https://github.com/bugwheels94/math-expression-evaluator
|
|
3355
|
+
*
|
|
3356
|
+
* There were two problems with the codebase...
|
|
3357
|
+
*
|
|
3358
|
+
* First, the code was very unreadable and determining it was correct or extending it
|
|
3359
|
+
* was out of the question.
|
|
3360
|
+
*
|
|
3361
|
+
* Second, and this is a larger issue, is that we need the expressions to be parsed as
|
|
3362
|
+
* arbitrary precision. When I determined that extending the library to use string-based
|
|
3363
|
+
* numbers and an arbitrary precision library as the back-end would have taken a significantly
|
|
3364
|
+
* longer amount of time than just writing the parser from scratch, et voila.
|
|
3365
|
+
*/class FableServiceExpressionParser extends libFableServiceBase{constructor(pFable,pOptions,pServiceHash){super(pFable,pOptions,pServiceHash);this.tokenMap=require('./Fable-Service-ExpressionParser/Fable-Service-ExpressionParser-TokenMap.json');// This precedence is higher than defined in our token map
|
|
3366
|
+
this.tokenMaxPrecedence=5;this.functionMap=require('./Fable-Service-ExpressionParser/Fable-Service-ExpressionParser-FunctionMap.json');this.serviceType='ExpressionParser';this.fable.addServiceTypeIfNotExists('ExpressionParser-Tokenizer',require('./Fable-Service-ExpressionParser/Fable-Service-ExpressionParser-ExpressionTokenizer.js'));this.fable.addServiceTypeIfNotExists('ExpressionParser-Linter',require('./Fable-Service-ExpressionParser/Fable-Service-ExpressionParser-Linter.js'));this.fable.addServiceTypeIfNotExists('ExpressionParser-Postfix',require('./Fable-Service-ExpressionParser/Fable-Service-ExpressionParser-Postfix.js'));this.fable.addServiceTypeIfNotExists('ExpressionParser-Solver',require('./Fable-Service-ExpressionParser/Fable-Service-ExpressionParser-SolvePostfixedExpression.js'));this.Tokenizer=this.fable.instantiateServiceProviderWithoutRegistration('ExpressionParser-Tokenizer');this.Linter=this.fable.instantiateServiceProviderWithoutRegistration('ExpressionParser-Linter');this.Postfix=this.fable.instantiateServiceProviderWithoutRegistration('ExpressionParser-Postfix');this.Solver=this.fable.instantiateServiceProviderWithoutRegistration('ExpressionParser-Solver');// Now wire each of these up. Not in love with this pattern but better than a giant file here.
|
|
3367
|
+
this.Tokenizer.connectExpressionParser(this);this.Linter.connectExpressionParser(this);this.Postfix.connectExpressionParser(this);this.Solver.connectExpressionParser(this);}substituteValuesInTokenizedObjects(pTokenizedObjects,pDataSource,pResultObject,pManifestDefinition){let tmpResults=typeof pResultObject==='object'?pResultObject:{ExpressionParserLog:[]};if(!Array.isArray(pTokenizedObjects)){tmpResults.ExpressionParserLog.push("ERROR: ExpressionParser.substituteValuesInTokenizedObjects was passed a non-array tokenized object list.");this.log.error(tmpResults.ExpressionParserLog[tmpResults.ExpressionParserLog.length-1]);return pTokenizedObjects;}if(typeof pDataSource!=='object'){tmpResults.ExpressionParserLog.push("ERROR: ExpressionParser.substituteValuesInTokenizedObjects either was passed no data source, or was passed a non-object data source.");this.log.error(tmpResults.ExpressionParserLog[tmpResults.ExpressionParserLog.length-1]);return pTokenizedObjects;}let tmpDataSource=pDataSource;// TODO: Allow the calling API to pass in an already constructed manifest.
|
|
3368
|
+
let tmpManifest=this.fable.newManyfest(pManifestDefinition);for(let i=0;i<pTokenizedObjects.length;i++){if(typeof pTokenizedObjects[i]!=='object'){tmpResults.ExpressionParserLog.push("WARNING: ExpressionParser.substituteValuesInTokenizedObjects found a non-object tokenized object at index ".concat(i));this.log.warn(tmpResults.ExpressionParserLog[tmpResults.ExpressionParserLog.length-1]);continue;}let tmpToken=pTokenizedObjects[i];if(pTokenizedObjects[i].Type==='Token.Symbol'&&!tmpToken.Resolved){// Symbols always look up values by hash first
|
|
3369
|
+
let tmpValue=tmpManifest.getValueByHash(tmpDataSource,tmpToken.Token);// if (!tmpValue)
|
|
3370
|
+
// {
|
|
3371
|
+
// // If no hash resolves, try by address.
|
|
3372
|
+
// tmpValue = tmpManifest.getValueAtAddress(tmpToken.Token, tmpDataSource);
|
|
3373
|
+
// }
|
|
3374
|
+
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){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.StateAddress'&&!tmpToken.Resolved){// Symbols are always hashes. This gracefully works for simple shallow objects because hashes default to the address in Manyfest.
|
|
3375
|
+
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.
|
|
3376
|
+
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);}solve(pExpression,pDataSourceObject,pResultObject,pManifestDefinition,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,pManifestDefinition);return this.solvePostfixedExpression(tmpResultsObject.PostfixSolveList,tmpDataDestinationObject,tmpResultsObject,pManifestDefinition);}}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(this.ExpressionParser.tokenMap.hasOwnProperty(pToken)){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:
|
|
3377
|
+
// There is a fifth token type, VirtualSymbol
|
|
3378
|
+
// This is a value that's added during solve and looked up by address in the VirtualSymbol object.
|
|
3379
|
+
}getTokenContainerObject(pToken,pTokenType){return{Token:pToken,Type:typeof pTokenType==='undefined'?this.getTokenType(pToken):pTokenType,Descriptor:this.ExpressionParser.tokenMap.hasOwnProperty(pToken)?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
|
|
3380
|
+
*
|
|
3381
|
+
* Current token types:
|
|
3382
|
+
* - Value
|
|
3383
|
+
* : could be a symbol representation e.g. "x", "depth", "Cost", etc. --- symbol representations are looked up first as manifest hashes
|
|
3384
|
+
* : could be a number e.g. "5", "3.14159", etc.
|
|
3385
|
+
* : could be a function name e.g. sin(x), sqrt(3+5) where sin or sqrt are known function names
|
|
3386
|
+
* - StateAddress
|
|
3387
|
+
* : these are always wrapped in squiggly brackets
|
|
3388
|
+
* : e.g. {Moisture.Percentage}, {Ending.Temperature.Fahrenheit}, {AppData.Download.Size}, etc.
|
|
3389
|
+
* - Token
|
|
3390
|
+
* : could be an operator e.g. "+", "-", "*", "/"
|
|
3391
|
+
* : could be a parenthesis e.g. "(", ")"
|
|
3392
|
+
*/let tmpCurrentTokenType=false;let tmpCurrentToken='';for(let i=0;i<pExpression.length;i++){let tmpCharacter=pExpression[i];// [ WHITESPACE ]
|
|
3393
|
+
// 1. Space breaks tokens except when we're in an address that's been scoped by a {}
|
|
3394
|
+
if(tmpCharacter===' '&&tmpCurrentTokenType!=='StateAddress'){if(tmpCurrentToken.length>0){tmpResults.RawTokens.push(tmpCurrentToken);}tmpCurrentToken='';tmpCurrentTokenType=false;continue;}// [ STATE ADDRESS BLOCKS ]
|
|
3395
|
+
// 2. If we're in an address, we keep going until we hit the closing brace
|
|
3396
|
+
if(tmpCurrentTokenType==='StateAddress'&&tmpCharacter!=='}'){tmpCurrentToken+=tmpCharacter;continue;}// 3. If we're in an address and we hit the closing brace, we close the token, push it and reset
|
|
3397
|
+
if(tmpCurrentTokenType==='StateAddress'&&tmpCharacter==='}'){tmpCurrentToken+=tmpCharacter;tmpResults.RawTokens.push(tmpCurrentToken);tmpCurrentToken='';tmpCurrentTokenType=false;continue;}// 4. If we're not in an address and we hit a closing brace it's a problem
|
|
3398
|
+
// TODO: Should we just ignore it? We do at the moment.
|
|
3399
|
+
if(tmpCharacter=='}'){if(tmpCurrentToken.length>0){tmpResults.RawTokens.push(tmpCurrentToken);}tmpCurrentToken='';tmpCurrentTokenType=false;tmpResults.ExpressionParserLog.push("ExpressionParser.tokenize found a closing brace without an opening brace in the expression: ".concat(pExpression," at character index ").concat(i));this.log.warn(tmpResults.ExpressionParserLog[tmpResults.ExpressionParserLog.length-1]);continue;}// 5. If we're not in an address and we hit an opening brace, we start an address
|
|
3400
|
+
if(tmpCharacter=='{'){if(tmpCurrentToken.length>0){tmpResults.RawTokens.push(tmpCurrentToken);}tmpCurrentToken='';tmpCurrentTokenType='StateAddress';tmpCurrentToken=tmpCharacter;continue;}// [ TOKENS ]
|
|
3401
|
+
if(this.ExpressionParser.tokenMap.hasOwnProperty(tmpCharacter)){if(tmpCurrentToken.length>0){tmpResults.RawTokens.push(tmpCurrentToken);}tmpCurrentToken='';tmpCurrentTokenType=false;tmpResults.RawTokens.push(tmpCharacter);continue;}// If it's not an operator, it's a number or address.
|
|
3402
|
+
// At the moment we aren't going to gate it on whether it's a valid address or not
|
|
3403
|
+
// Just treat anything not a known token on its own as a value identifier
|
|
3404
|
+
/* Per this stack overflow article: https://stackoverflow.com/questions/4434076/best-way-to-alphanumeric-check-in-javascript
|
|
3405
|
+
* We could use a regex but it is slower than the charCodeAt method.
|
|
3406
|
+
* This also doesn't solve the problem of unicode characters, but we won't support those for now.
|
|
3407
|
+
*/ // if (pExpression.charAt(i) == '.')
|
|
3408
|
+
// {
|
|
3409
|
+
// console.log('Found a period')
|
|
3410
|
+
// }
|
|
3411
|
+
// let tmpCharCode = pExpression.charCodeAt(i);
|
|
3412
|
+
// // Match that the character code is any of...
|
|
3413
|
+
// if (
|
|
3414
|
+
// // Number [0-9]
|
|
3415
|
+
// (tmpCharCode > 47 && tmpCharCode < 58)
|
|
3416
|
+
// // Upper Case
|
|
3417
|
+
// || (tmpCharCode > 64 && tmpCharCode < 91)
|
|
3418
|
+
// // LOWER CASE
|
|
3419
|
+
// || (tmpCharCode > 96 && tmpCharCode < 123)
|
|
3420
|
+
// )
|
|
3421
|
+
// {
|
|
3422
|
+
// NOTE: Not having this guard makes a lot of interesting things possible.
|
|
3423
|
+
tmpCurrentTokenType='Value';tmpCurrentToken+=tmpCharacter;// continue;
|
|
3424
|
+
// }
|
|
3425
|
+
// tmpResults.ExpressionParserLog.push(`ExpressionParser.tokenize found an unknown character code ${tmpCharCode} character ${tmpCharacter} in the expression: ${pExpression} at index ${i}`);
|
|
3426
|
+
// this.log.warn(tmpResults.ExpressionParserLog[tmpResults.ExpressionParserLog.length-1]);
|
|
3427
|
+
}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
|
|
3428
|
+
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
|
|
3429
|
+
let tmpParenthesisDepth=0;// If it is in a state address, we don't care about the parenthesis
|
|
3430
|
+
// State addresses are between squiggly brackets
|
|
3431
|
+
let tmpInStateAddress=false;for(let i=0;i<pTokenizedExpression.length;i++){if(pTokenizedExpression[i]==='('&&!tmpInStateAddress){tmpParenthesisDepth++;}if(pTokenizedExpression[i]===')'&&!tmpInStateAddress){tmpParenthesisDepth--;}if(pTokenizedExpression[i]==='{'){tmpInStateAddress=true;}if(pTokenizedExpression[i]==='}'){tmpInStateAddress=false;}if(tmpParenthesisDepth<0){tmpResults.ExpressionParserLog.push("ERROR: ExpressionParser.lintTokenizedExpression found an unbalanced parenthesis 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]);}}if(tmpParenthesisDepth>0){// TODO: Should we add the closing parenthesis?
|
|
3432
|
+
tmpResults.ExpressionParserLog.push("WARNING: ExpressionParser.lintTokenizedExpression found an unbalanced parenthesis in the tokenized expression (ended without closing last set of parenthesis) -- appropriate closing parenthesis will be added.");tmpResults.LinterResults.push(tmpResults.ExpressionParserLog[tmpResults.ExpressionParserLog.length-1]);this.log.warn(tmpResults.ExpressionParserLog[tmpResults.ExpressionParserLog.length-1]);}// 2. Check for balanced squiggly braces
|
|
3433
|
+
tmpInStateAddress=false;for(let i=0;i<pTokenizedExpression.length;i++){if(pTokenizedExpression[i]==='{'){tmpInStateAddress=true;}if(pTokenizedExpression[i]==='}'&&tmpInStateAddress){tmpInStateAddress=false;}if(pTokenizedExpression[i]==='}'&&!tmpInStateAddress){tmpResults.ExpressionParserLog.push("ERROR: ExpressionParser.lintTokenizedExpression found an unbalanced closing squiggly brace \"}\" 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]);}if(tmpInStateAddress){tmpResults.ExpressionParserLog.push("WARNING: ExpressionParser.lintTokenizedExpression found an open squiggly brace in the tokenized expression at index ".concat(i));tmpResults.LinterResults.push(tmpResults.ExpressionParserLog[tmpResults.ExpressionParserLog.length-1]);this.log.warn(tmpResults.ExpressionParserLog[tmpResults.ExpressionParserLog.length-1]);}}// 3. Check for an equality assignment
|
|
3434
|
+
let tmpEqualityAssignmentCount=0;let tmpEqualityAssignmentIndex=false;for(let i=0;i<pTokenizedExpression.length;i++){if(pTokenizedExpression[i]==='='){tmpEqualityAssignmentCount++;tmpEqualityAssignmentIndex=i;if(tmpEqualityAssignmentCount>1){tmpResults.ExpressionParserLog.push("ERROR: ExpressionParser.lintTokenizedExpression found multiple equality assignments in the tokenized expression; equality assignment #".concat(tmpEqualityAssignmentCount," at token index ").concat(i,"."));tmpResults.LinterResults.push(tmpResults.ExpressionParserLog[tmpResults.ExpressionParserLog.length-1]);this.log.error(tmpResults.ExpressionParserLog[tmpResults.ExpressionParserLog.length-1]);}}}if(tmpEqualityAssignmentCount<1){tmpResults.ExpressionParserLog.push("WARNING: ExpressionParser.lintTokenizedExpression found no equality assignment in the tokenized expression. One called Result will be added automatically.");tmpResults.LinterResults.push(tmpResults.ExpressionParserLog[tmpResults.ExpressionParserLog.length-1]);this.log.warn(tmpResults.ExpressionParserLog[tmpResults.ExpressionParserLog.length-1]);}// 4. Make sure the equality assignment only has a single value on the other side of it
|
|
3435
|
+
// (no, this is not a magical algebraic solver, do your own simplification)
|
|
3436
|
+
// IF there is only one equality assignment (otherwise we don't even lint this because it's syntax errors all the way down)
|
|
3437
|
+
if(tmpEqualityAssignmentCount===1){// If there are exactly three tokens, make sure at least one is an assignable-ish address
|
|
3438
|
+
// This can still fail, but we aren't linting all the way into the manifest here; just the expression
|
|
3439
|
+
if(pTokenizedExpression.length===3){if(// The first token in our expression is a potentially assignable symbol
|
|
3440
|
+
this.getTokenType(pTokenizedExpression[0])==='Token.StateAddress'||this.getTokenType(pTokenizedExpression[2])==='Token.Symbol'// NOTE: For now we are only going to support assignment to the first symbol in the expression, which seems okay.
|
|
3441
|
+
// OR the last token in our expression is a potentially assignable symbol
|
|
3442
|
+
//|| (this.getTokenType(pTokenizedExpression[2]) === 'Token.StateAddress') || (this.getTokenType(pTokenizedExpression[0]) === 'Token.Symbol')
|
|
3443
|
+
){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
|
|
3444
|
+
// This is a simple lint check, not a full-blown syntax check
|
|
3445
|
+
let tmpTokenPrevious=false;for(let i=0;i<pTokenizedExpression.length-1;i++){if(this.ExpressionParser.tokenMap.hasOwnProperty(pTokenizedExpression[i])&&this.ExpressionParser.tokenMap[pTokenizedExpression[i]].Type!='Parenthesis'&&!tmpTokenPrevious){tmpTokenPrevious=true;}else if(this.ExpressionParser.tokenMap.hasOwnProperty(pTokenizedExpression[i])&&this.ExpressionParser.tokenMap[pTokenizedExpression[i]].Type!='Parenthesis'){// If this isn't a + or - positivity/negativity multiplier, it's an error.
|
|
3446
|
+
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;// // Although we have a `Token.Type == "Parenthesis"` option to check on, keeping these explicit means the solver won't
|
|
3447
|
+
// // allow users to pass in parenthesis in the wrong order.
|
|
3448
|
+
// // The linter does blow up as does the postfix, but, just in case we'll leave these explicit.
|
|
3449
|
+
// // It really doesn't hurt anything.
|
|
3450
|
+
// if (pLeftValue.Token === ')')
|
|
3451
|
+
// {
|
|
3452
|
+
// // We have found a close parenthesis which needs to pull the proper virtual symbol for the last operation on this stack.
|
|
3453
|
+
// // This ensures we are not expressing the parenthesis virtual symbols to the solver.
|
|
3454
|
+
// pLeftValue.VirtualSymbolName = pLayerStackMap[pLeftValue.SolveLayerStack];
|
|
3455
|
+
// this.log.error(`ERROR: ExpressionParser.getPosfixSolveListOperation found a close parenthesis in the left value of an operation.`);
|
|
3456
|
+
// }
|
|
3457
|
+
// else if (pRightValue.Token === '(')
|
|
3458
|
+
// {
|
|
3459
|
+
// // We have found a close parenthesis which needs to pull the proper virtual symbol for the last operation on this stack.
|
|
3460
|
+
// // This ensures we are not expressing the parenthesis virtual symbols to the solver.
|
|
3461
|
+
// pRightValue.VirtualSymbolName = pLayerStackMap[pRightValue.SolveLayerStack];
|
|
3462
|
+
// this.log.error(`ERROR: ExpressionParser.getPosfixSolveListOperation found a close parenthesis in the left value of an operation.`);
|
|
3463
|
+
// }
|
|
3464
|
+
// // Set the layer stack map virtual symbol name to the last operation performed on this stack.
|
|
3465
|
+
// pLayerStackMap[pOperation.SolveLayerStack] = pOperation.VirtualSymbolName;
|
|
3466
|
+
/* These two if blocks are very complex -- they basically provide a
|
|
3467
|
+
* way to deal with recursion that can be expressed to the user in
|
|
3468
|
+
* a meaningful way.
|
|
3469
|
+
*
|
|
3470
|
+
* The reason we are doing it as such is to show exactly how the
|
|
3471
|
+
* solver resolves the passed-in tokens into a solvable expression.
|
|
3472
|
+
*/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
|
|
3473
|
+
tmpOperation.LeftValue=this.getTokenContainerObject(tmpOperation.LeftValue.VirtualSymbolName,'Token.VirtualSymbol');// Now walk backwards and see if we need to update a previous symbol for a previously unparsed operator
|
|
3474
|
+
if(tmpDepthSolveList){for(let i=pDepthSolveIndex-1;i>=0;i--){if(tmpDepthSolveList[i].Type==='Token.Operator'&&!tmpDepthSolveList[i].Parsed&&// When walking backward, we only want to mutate if the .
|
|
3475
|
+
tmpDepthSolveList[i].hasOwnProperty('Descriptor')&&tmpOperation.Operation.hasOwnProperty('Descriptor')&&// Anything >3 does not have commutative properties
|
|
3476
|
+
tmpDepthSolveList[i].Descriptor.Precedence>3){// If the symbol to its right is not the same as this operation
|
|
3477
|
+
if(tmpDepthSolveList[i+1].VirtualSymbolName!==tmpOperation.VirtualSymbolName){// This is the recursive "shunting" being simulated
|
|
3478
|
+
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
|
|
3479
|
+
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
|
|
3480
|
+
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.
|
|
3481
|
+
tmpDepthSolveList[i].hasOwnProperty('Descriptor')&&tmpOperation.Operation.hasOwnProperty('Descriptor')&&// Anything >3 does not have commutative properties
|
|
3482
|
+
tmpDepthSolveList[i].Descriptor.Precedence>3){// If the symbol to its right is not the same as this operation
|
|
3483
|
+
if(tmpDepthSolveList[i-1].VirtualSymbolName!==tmpOperation.VirtualSymbolName){// This is the recursive "shunting" being simulated
|
|
3484
|
+
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
|
|
3485
|
+
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.
|
|
3486
|
+
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.
|
|
3487
|
+
// That is... the user entered something like "= 5 + 3" so we should just return 8, and use the default Result quietly.
|
|
3488
|
+
}else{tmpResults.PostfixedAssignmentAddress=pTokenizedExpression[0];}// 2. Categorize tokens in the expression, put them in the "expression list" as a token object
|
|
3489
|
+
for(let i=tmpEqualsIndex+1;i<pTokenizedExpression.length;i++){tmpResults.PostfixTokenObjects.push(this.getTokenContainerObject(pTokenizedExpression[i]));}// 3. Decorate mathematical parsing depth and detect functions at the same time
|
|
3490
|
+
// Having written this a few times now, it's easier to detect functions *while* parsing depth.
|
|
3491
|
+
// Especially if we want our system to be able to communicate with the user when there is an issue.
|
|
3492
|
+
let tmpDepth=0;// The virtual symbol index is used for the abstract interim values that are generated at each step of the solve
|
|
3493
|
+
let tmpVirtualParenthesisIndex=0;let tmpSolveLayerStack=[];// Kick off the solve layer stack with the first solve set identifier
|
|
3494
|
+
tmpSolveLayerStack.push("SolveSet_".concat(tmpVirtualParenthesisIndex,"_D_").concat(tmpDepth));for(let i=0;i<tmpResults.PostfixTokenObjects.length;i++){// 1. If it's an open parenthesis, set the parenthesis at the current depth and increment the depth
|
|
3495
|
+
if(tmpResults.PostfixTokenObjects[i].Token==='('){// Set the depth of the open parenthesis to the current solution depth
|
|
3496
|
+
tmpResults.PostfixTokenObjects[i].Depth=tmpDepth;// Generate the virtual symbol name for user output
|
|
3497
|
+
tmpResults.PostfixTokenObjects[i].VirtualSymbolName="Pr_".concat(tmpVirtualParenthesisIndex,"_D_").concat(tmpDepth);// 1a. Detect if this parenthesis is signaling a function
|
|
3498
|
+
// 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
|
|
3499
|
+
if(i>0){if(tmpResults.PostfixTokenObjects[i-1].Type==='Token.Symbol'){// Set the type of this to be a function
|
|
3500
|
+
tmpResults.PostfixTokenObjects[i-1].Type='Token.Function';// tmpResults.PostfixTokenObjects[i-1].Descriptor = this.ExpressionParser.tokenMap[pTokenizedExpression[i-1]];
|
|
3501
|
+
// Rename the virtual symbol n ame to include the function
|
|
3502
|
+
// tmpResults.PostfixTokenObjects[i].VirtualSymbolName = `Fn_${tmpVirtualParenthesisIndex}_D_${tmpDepth}_${this.fable.DataFormat.cleanNonAlphaCharacters(tmpResults.PostfixTokenObjects[i-1].Token)}`;
|
|
3503
|
+
// The function and the parenthesis are at the same depth and virtual symbol
|
|
3504
|
+
// tmpResults.PostfixTokenObjects[i-1].VirtualSymbolName = tmpResults.PostfixTokenObjects[i].VirtualSymbolName;
|
|
3505
|
+
}}// Parenthesis manage the solve layer stack
|
|
3506
|
+
// 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
|
|
3507
|
+
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
|
|
3508
|
+
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
|
|
3509
|
+
if(tmpDepth<0){tmpResults.ExpressionParserLog.push("ERROR: ExpressionParser.buildPostfixedSolveList found a closing parenthesis at token index ".concat(i," with no corresponding opening parenthesis."));this.log.error(tmpResults.ExpressionParserLog[tmpResults.ExpressionParserLog.length-1]);}// Parenthesis manage the solve layer stack
|
|
3510
|
+
// For closing parenthesis solve layer with a close paren, we put it in the same stack as the opening parenthesis.
|
|
3511
|
+
// Give the closing parenthesis the same virtual symbol name as the opening parenthesis
|
|
3512
|
+
// (do the both above at the same time)
|
|
3513
|
+
tmpResults.PostfixTokenObjects[i].VirtualSymbolName=tmpSolveLayerStack.pop();tmpResults.PostfixTokenObjects[i].SolveLayerStack=tmpSolveLayerStack[tmpSolveLayerStack.length-1];}// 3. If it's a state address or Symbol, set depth
|
|
3514
|
+
// It was much more complex later on solving these as virtual symbols of their own.
|
|
3515
|
+
// We are saving the value resolution for the very end.
|
|
3516
|
+
else if(tmpResults.PostfixTokenObjects[i].Type==='Token.Symbol'){// Set the depth of the current solution depth
|
|
3517
|
+
tmpResults.PostfixTokenObjects[i].Depth=tmpDepth;tmpResults.PostfixTokenObjects[i].SolveLayerStack=tmpSolveLayerStack[tmpSolveLayerStack.length-1];// Generate a virtual symbol name that's somewhat human readable
|
|
3518
|
+
//tmpResults.PostfixTokenObjects[i].VirtualSymbolName = `Sm_${tmpVirtualParenthesisIndex}_D_${tmpDepth}_${this.fable.DataFormat.cleanNonAlphaCharacters(tmpResults.PostfixTokenObjects[i].Token)}`;
|
|
3519
|
+
// We've used up this virtual symbol index so increment it
|
|
3520
|
+
// 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
|
|
3521
|
+
//tmpVirtualParenthesisIndex++;
|
|
3522
|
+
}// 4. If it's an operator or constant or comment, just set the depth
|
|
3523
|
+
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
|
|
3524
|
+
// We are going to start by creating a map of the solve layers:
|
|
3525
|
+
let tmpSolveLayerMap={};let tmpSolveLayerMaxDepth=0;for(let i=0;i<tmpResults.PostfixTokenObjects.length;i++){if(!tmpSolveLayerMap.hasOwnProperty(tmpResults.PostfixTokenObjects[i].SolveLayerStack)){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
|
|
3526
|
+
// 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.
|
|
3527
|
+
if(tmpResults.PostfixTokenObjects[i].Depth>tmpSolveLayerMaxDepth){tmpSolveLayerMaxDepth=tmpResults.PostfixTokenObjects[i].Depth;}}let tmpSolveLayerKeys=Object.keys(tmpSolveLayerMap);// Reset the virtual symbol index -- it was used above for uniquenes when creating abstract symbols for parenthesis and layer stacks.
|
|
3528
|
+
let tmpVirtualSymbolIndex=0;tmpSolveLayerKeys.sort(// Sort the solve layers by depth.
|
|
3529
|
+
(pLeftLayer,pRightLayer)=>{// It is impossible to have a layer with no entries in it.
|
|
3530
|
+
// If that ever happens, the bug is actually above and we actively want this to blow up.
|
|
3531
|
+
if(tmpSolveLayerMap[pLeftLayer][0].Depth<tmpSolveLayerMap[pRightLayer][0].Depth){return 1;}if(tmpSolveLayerMap[pLeftLayer][0].Depth>tmpSolveLayerMap[pRightLayer][0].Depth){return-1;}return 0;});// 5. Generate the postfix solve list
|
|
3532
|
+
// The most important thing is going backwards in the depth order (a la reverse polish).
|
|
3533
|
+
// Specifically not using shunting yard to provide in-depth "show your work" notes
|
|
3534
|
+
// Yes it is possible to do a somewhat similar thing with shunting yard but the code is almost unreadable
|
|
3535
|
+
// 5.1 Build the Virtual Symbol Names
|
|
3536
|
+
// This maps layer stack addresses (which match parenthesis virtual symbol names) to the resultant value for that layer stack.
|
|
3537
|
+
// These values change as it solves but the last assignment is the proper assignment because math only reads forward in a line
|
|
3538
|
+
tmpResults.PostfixLayerstackMap={};for(let tmpSolveLayerIndex=0;tmpSolveLayerIndex<tmpSolveLayerKeys.length;tmpSolveLayerIndex++){let tmpSolveLayerTokens=tmpSolveLayerMap[tmpSolveLayerKeys[tmpSolveLayerIndex]];// For each precedence (this isn't strictly required here but makes the outcome for the user more readable)
|
|
3539
|
+
for(let tmpPrecedence=0;tmpPrecedence<=this.ExpressionParser.tokenMaxPrecedence;tmpPrecedence++){// Enumerate all tokens in a layer's expression.
|
|
3540
|
+
// There is a recursive way to do this, but given the short length of even the most complex equations we're favoring readability.
|
|
3541
|
+
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.
|
|
3542
|
+
if(tmpSolveLayerTokens[i].Type==='Token.Operator'&&tmpSolveLayerTokens[i].Descriptor.Precedence===tmpPrecedence){let tmpToken=tmpSolveLayerTokens[i];// If there is a token and nothing else in this layer, then it's an error.
|
|
3543
|
+
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.
|
|
3544
|
+
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.
|
|
3545
|
+
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
|
|
3546
|
+
else if(i==0&&tmpToken.Token=='-'){tmpToken.VirtualSymbolName="V_".concat(tmpVirtualSymbolIndex);tmpResults.PostfixLayerstackMap[tmpToken.SolveLayerStack]=tmpToken.VirtualSymbolName;tmpVirtualSymbolIndex++;}// The - after an operator or an open parenthesis is also a number line orientation modifier
|
|
3547
|
+
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
|
|
3548
|
+
tmpToken.VirtualSymbolName="V_".concat(tmpVirtualSymbolIndex);tmpVirtualSymbolIndex++;}// The + at the beginning is also a number line orientation modifier ... THAT WE IGNORE
|
|
3549
|
+
else if(i==0&&tmpToken.Token=='+'){continue;}// The + after an operator or a parenthesis is also a number line orientation modifier ... THAT WE IGNORE
|
|
3550
|
+
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
|
|
3551
|
+
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.
|
|
3552
|
+
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="V_".concat(tmpVirtualSymbolIndex);tmpVirtualSymbolIndex++;tmpResults.PostfixLayerstackMap[tmpToken.SolveLayerStack]=tmpToken.VirtualSymbolName;}}}}// 5.2: Decorate the Parenthesis with Virtual Symbol Names
|
|
3553
|
+
// ... this gets funny because of precedence of operations surrounding them, parenthesis and functions.
|
|
3554
|
+
let tmpParenthesisCacheLIFOStack=[];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 in User feedback
|
|
3555
|
+
tmpPostfixTokenObject.ParenthesisStack=tmpPostfixTokenObject.VirtualSymbolName;if(tmpPostfixTokenObject.Token==='('){// It's an open parenthesis. If the previous token was an operator, get its precedence.
|
|
3556
|
+
if(i>0){if(tmpResults.PostfixTokenObjects[i-1].Type==='Token.Operator'){tmpPostfixTokenObject.PreviousPrecedence=tmpResults.PostfixTokenObjects[i-1].Descriptor.Precedence;tmpPostfixTokenObject.IsFunction=false;tmpPostfixTokenObject.PreviousVirtualSymbolName=tmpResults.PostfixTokenObjects[i-1].VirtualSymbolName;tmpPostfixTokenObject.VirtualSymbolName=tmpResults.PostfixLayerstackMap[tmpPostfixTokenObject.VirtualSymbolName];}// This is a function, we will create a virtual symbol for the discrete parenthesis
|
|
3557
|
+
else if(tmpResults.PostfixTokenObjects[i-1].Type==='Token.Function'){tmpPostfixTokenObject.PreviousPrecedence=0;tmpPostfixTokenObject.IsFunction=true;tmpPostfixTokenObject.PreviousVirtualSymbolName=tmpResults.PostfixTokenObjects[i-1].VirtualSymbolName;let tmpVirtualSymbolName=tmpResults.PostfixLayerstackMap[tmpPostfixTokenObject.VirtualSymbolName];if(!tmpVirtualSymbolName){// This is a parenthesis group with no operators in it; make a virtual symbol name.
|
|
3558
|
+
tmpVirtualSymbolName="V_".concat(tmpVirtualSymbolIndex);tmpVirtualSymbolIndex++;}tmpPostfixTokenObject.VirtualSymbolName=tmpVirtualSymbolName;if(i>1){let tmpTokenBeforeFunction=tmpResults.PostfixTokenObjects[i-2];if(tmpTokenBeforeFunction.Type==='Token.Operator'){tmpPostfixTokenObject.PreviousVirtualSymbolName=tmpResults.PostfixTokenObjects[i-2].VirtualSymbolName;}}}}else{tmpPostfixTokenObject.VirtualSymbolName=tmpResults.PostfixLayerstackMap[tmpPostfixTokenObject.VirtualSymbolName];}tmpParenthesisCacheLIFOStack.push(tmpPostfixTokenObject);}if(tmpPostfixTokenObject.Token===')'){// There are three options for assigning this:
|
|
3559
|
+
let tmpOpenParenthesis=tmpParenthesisCacheLIFOStack.pop();// It's at the end of the tokens -- use the stack's identifier
|
|
3560
|
+
if(i>=tmpResults.PostfixTokenObjects.length-1){if(tmpOpenParenthesis.IsFunction){tmpPostfixTokenObject.VirtualSymbolName=tmpOpenParenthesis.PreviousVirtualSymbolName;}else{tmpPostfixTokenObject.VirtualSymbolName=tmpOpenParenthesis.VirtualSymbolName;}}else{// Get the next token
|
|
3561
|
+
let tmpPeekedNextToken=tmpResults.PostfixTokenObjects[i+1];if(tmpPeekedNextToken.Type=='Token.Operator'&&tmpOpenParenthesis.IsFunction){// This is the most complex case -- the next token is an operator AND this is a function.
|
|
3562
|
+
// The following is just pointer math.
|
|
3563
|
+
// If the operater is at the same precedence or higher than the open parenthesis previous operator, use the previous operator's identifier
|
|
3564
|
+
// NOTE: This line of code is insanely complex
|
|
3565
|
+
tmpPostfixTokenObject.VirtualSymbolName=tmpOpenParenthesis.PreviousVirtualSymbolName;// if (tmpPeekedNextToken.Descriptor.Precedence <= tmpOpenParenthesis.PreviousPrecedence)
|
|
3566
|
+
// {
|
|
3567
|
+
// tmpPostfixTokenObject.VirtualSymbolName = tmpOpenParenthesis.PreviousVirtualSymbolName;
|
|
3568
|
+
// }
|
|
3569
|
+
// // Otherwise use this one -- it is the higher precedence. And update the previous parenthesis operator's virtual symbol to be the peeked token's virtual symbol.
|
|
3570
|
+
// else
|
|
3571
|
+
// {
|
|
3572
|
+
// tmpPostfixTokenObject.VirtualSymbolName = tmpResults.PostfixLayerstackMap[tmpPostfixTokenObject.VirtualSymbolName];
|
|
3573
|
+
// tmpOpenParenthesis.VirtualSymbolName = tmpPeekedNextToken.VirtualSymbolName;
|
|
3574
|
+
// }
|
|
3575
|
+
}else if(tmpPeekedNextToken.Type=='Token.Operator'&&tmpOpenParenthesis.hasOwnProperty('PreviousPrecedence')){// This is the second most complex case -- the next token is an operator.
|
|
3576
|
+
// If the operater is at the same precedence or higher than the open parenthesis previous operator, use the previous operator's identifier
|
|
3577
|
+
// NOTE: This line of code is insanely complex
|
|
3578
|
+
if(tmpPeekedNextToken.Descriptor.Precedence<=tmpOpenParenthesis.PreviousPrecedence){tmpPostfixTokenObject.VirtualSymbolName=tmpOpenParenthesis.PreviousVirtualSymbolName;}// Otherwise use this one -- it is the higher precedence. And update the previous parenthesis operator's virtual symbol to be the peeked token's virtual symbol.
|
|
3579
|
+
else{tmpPostfixTokenObject.VirtualSymbolName=tmpResults.PostfixLayerstackMap[tmpPostfixTokenObject.VirtualSymbolName];tmpOpenParenthesis.VirtualSymbolName=tmpPeekedNextToken.VirtualSymbolName;}}else{// If this is a function, dereference the function's previous virtual symbol name
|
|
3580
|
+
if(tmpOpenParenthesis.IsFunction){tmpPostfixTokenObject.VirtualSymbolName=tmpOpenParenthesis.PreviousVirtualSymbolName;}else{tmpPostfixTokenObject.VirtualSymbolName=tmpResults.PostfixLayerstackMap[tmpPostfixTokenObject.VirtualSymbolName];}}}// If there isn't an operator in the layer stack, push forward the assignment
|
|
3581
|
+
if(!tmpResults.PostfixLayerstackMap[tmpOpenParenthesis.ParenthesisStack]){tmpResults.PostfixLayerstackMap[tmpOpenParenthesis.ParenthesisStack]=tmpOpenParenthesis.VirtualSymbolName;}}}}for(let tmpSolveLayerIndex=0;tmpSolveLayerIndex<tmpSolveLayerKeys.length;tmpSolveLayerIndex++){let tmpSolveLayerTokens=tmpSolveLayerMap[tmpSolveLayerKeys[tmpSolveLayerIndex]];if(tmpSolveLayerTokens.length===1){// This is just a simple value assignment -- use a simple addition virtual operation.
|
|
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
|
|
3586
|
+
for(let tmpPrecedence=0;tmpPrecedence<=this.ExpressionParser.tokenMaxPrecedence;tmpPrecedence++){// Enumerate all tokens in a layer's expression.
|
|
3587
|
+
// There is a recursive way to do this, but given the short length of even the most complex equations we're favoring readability.
|
|
3588
|
+
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
|
+
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
|
+
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
|
+
// This is an abstract operation that isn't in the expression.
|
|
3592
|
+
let tmpAbstractMultiplyToken=this.getTokenContianerObject('*');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
|
+
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.getTokenContianerObject('*');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
|
+
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
|
+
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];// Not sure what to do with the other token.
|
|
3598
|
+
tmpResults.PostfixSolveList.push(this.getPosfixSolveListOperation(tmpToken,tmpSolveLayerTokens[i+1],this.getTokenContainerObject('0.0')));}}}}// Now set the assignment address.
|
|
3599
|
+
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
|
+
let tmpPassedInFable=tmpResults.hasOwnProperty('fable');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
|
+
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
|
+
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);}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
|
+
// An operator always has a left and right value.
|
|
3605
|
+
let tmpFunctionAddress=false;if(this.ExpressionParser.tokenMap.hasOwnProperty(tmpStepResultObject.ExpressionStep.Operation.Token)){tmpFunctionAddress="ResultsObject.".concat(tmpStepResultObject.ExpressionStep.Operation.Descriptor.Function);}else if(this.ExpressionParser.functionMap.hasOwnProperty(tmpStepResultObject.ExpressionStep.Operation.Token.toLowerCase())){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,"."));this.log.error(tmpResults.ExpressionParserLog[tmpResults.ExpressionParserLog.length-1]);return false;}// Equations don't always solve in virtual symbol order.
|
|
3606
|
+
tmpResults.SolverFinalVirtualSymbol=tmpStepResultObject.ExpressionStep.VirtualSymbolName;}}let tmpSolverResultValue=tmpManifest.getValueAtAddress(tmpResults,"VirtualSymbols.".concat(tmpResults.SolverFinalVirtualSymbol));// Now deal with final assignment
|
|
3607
|
+
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(!this.options.hasOwnProperty('Mode')){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
|
|
3344
3609
|
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(!tmpParameters.hasOwnProperty('Mode')){tmpParameters.Mode=this.options.Mode;}// Check if we are just starting .. if so, build the initial state for our recursive function
|
|
3345
3610
|
if(typeof tmpParameters.CurrentPathIndex==='undefined'){// Build the tools to start recursing
|
|
3346
3611
|
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.
|
|
@@ -3350,7 +3615,7 @@ tmpParameters.CurrentPathIndex++;}// Check if the path is fully complete
|
|
|
3350
3615
|
if(tmpParameters.CurrentPathIndex>=tmpParameters.ActualPathParts.length){return fCallback(null);}// Check if the path exists (and is a folder)
|
|
3351
3616
|
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.
|
|
3352
3617
|
return this.makeFolderRecursive(tmpParameters,fCallback);}else if(pCreateError.code=='EEXIST'){// The folder exists -- our dev might be running this in parallel/async/whatnot.
|
|
3353
|
-
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}],
|
|
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){const libFableServiceBase=require('fable-serviceproviderbase');/**
|
|
3354
3619
|
* Arbitrary Precision Math Operations
|
|
3355
3620
|
* @author Steven Velozo <steven@velozo.com>
|
|
3356
3621
|
* @description Simple functions that perform arbitrary precision math operations and return string resultant values. Wraps big.js
|
|
@@ -3365,7 +3630,9 @@ return this.makeFolderRecursive(tmpParameters,fCallback);}else{console.log(pCrea
|
|
|
3365
3630
|
roundHalfUp 1 ROUND_HALF_UP Rounds towards nearest neighbour. (_If equidistant, rounds away from zero._)
|
|
3366
3631
|
roundHalfEven 2 ROUND_HALF_EVEN Rounds towards nearest neighbour. (_If equidistant, rounds towards even neighbour._)
|
|
3367
3632
|
roundUp 3 ROUND_UP Rounds positively away from zero. (_Always round up._)
|
|
3368
|
-
*/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;}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();}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();}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();}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();}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();}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();}
|
|
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;}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();}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();}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();}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();}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();}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();}// Bignumber does not use precision values for power -- only javascript decimals
|
|
3634
|
+
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();}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();}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();}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();}sqrtPrecise(pValue){let tmpValue=isNaN(pValue)?0:pValue;let tmpLeftArbitraryValue=new this.fable.Utility.bigNumber(tmpValue);let tmpResult=tmpLeftArbitraryValue.sqrt();return tmpResult.toString();}absPrecise(pValue){let tmpValue=isNaN(pValue)?0:pValue;let tmpLeftArbitraryValue=new this.fable.Utility.bigNumber(tmpValue);let tmpResult=tmpLeftArbitraryValue.abs();return tmpResult.toString();}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);}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);}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);}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);}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);}radPrecise(pDegrees){let tmpDegrees=isNaN(pDegrees)?0:pDegrees;let tmpDegreesArbitraryValue=new this.fable.Utility.bigNumber(tmpDegrees);// TODO: Const for pi in arbitrary precision?
|
|
3635
|
+
let tmpResult=tmpDegreesArbitraryValue.times(Math.PI).div(180);return tmpResult.toString();}sin(pRadians){let tmpRadians=isNaN(pRadians)?0:pRadians;return Math.sin(tmpRadians);}cos(pRadians){let tmpRadians=isNaN(pRadians)?0:pRadians;return Math.cos(tmpRadians);}tan(pRadians){let tmpRadians=isNaN(pRadians)?0:pRadians;return Math.tan(tmpRadians);}}module.exports=FableServiceMath;},{"fable-serviceproviderbase":51}],145:[function(require,module,exports){const libFableServiceBase=require('fable-serviceproviderbase');/**
|
|
3369
3636
|
* Precedent Meta-Templating
|
|
3370
3637
|
* @author Steven Velozo <steven@velozo.com>
|
|
3371
3638
|
* @description Process text stream trie and postfix tree, parsing out meta-template expression functions.
|
|
@@ -3381,8 +3648,10 @@ return this.makeFolderRecursive(tmpParameters,fCallback);}else{console.log(pCrea
|
|
|
3381
3648
|
* @method parseString
|
|
3382
3649
|
* @param {string} pString - The string to parse
|
|
3383
3650
|
* @param {object} pData - Data to pass in as the second argument
|
|
3651
|
+
* @param {function} fCallback - The callback function to call when a pattern is matched
|
|
3652
|
+
* @param {array} pDataContext - The history of data objects already passed in
|
|
3384
3653
|
* @return {string} The result from the parser
|
|
3385
|
-
*/parseString(pString,pData,fCallback){if(this.LogNoisiness>4){this.fable.log.trace("Metatemplate parsing template string [".concat(pString,"] where the callback is a ").concat(typeof fCallback),{TemplateData:pData});}return this.StringParser.parseString(pString,this.ParseTree,pData,fCallback);}}module.exports=FableServiceMetaTemplate;},{"./Fable-Service-MetaTemplate/MetaTemplate-StringParser.js":
|
|
3654
|
+
*/parseString(pString,pData,fCallback,pDataContext){if(this.LogNoisiness>4){this.fable.log.trace("Metatemplate parsing template string [".concat(pString,"] where the callback is a ").concat(typeof fCallback),{TemplateData:pData});}return this.StringParser.parseString(pString,this.ParseTree,pData,fCallback,pDataContext);}}module.exports=FableServiceMetaTemplate;},{"./Fable-Service-MetaTemplate/MetaTemplate-StringParser.js":146,"./Fable-Service-MetaTemplate/MetaTemplate-WordTree.js":147,"fable-serviceproviderbase":51}],146:[function(require,module,exports){/**
|
|
3386
3655
|
* String Parser
|
|
3387
3656
|
* @author Steven Velozo <steven@velozo.com>
|
|
3388
3657
|
* @description Parse a string, properly processing each matched token in the word tree.
|
|
@@ -3414,42 +3683,45 @@ pParserState.Pattern=false;pParserState.PatternStartNode=false;pParserState.Star
|
|
|
3414
3683
|
* @param {string} pCharacter - The character to append
|
|
3415
3684
|
* @param {Object} pParserState - The state object for the current parsing task
|
|
3416
3685
|
* @private
|
|
3417
|
-
*/parseCharacter(pCharacter,pParserState,pData){// If we are already in a pattern match traversal
|
|
3686
|
+
*/parseCharacter(pCharacter,pParserState,pData,pDataContext){// If we are already in a pattern match traversal
|
|
3418
3687
|
if(pParserState.PatternMatch){// If the pattern is still matching the start and we haven't passed the buffer
|
|
3419
3688
|
if(!pParserState.StartPatternMatchComplete&&pParserState.Pattern.hasOwnProperty(pCharacter)){pParserState.Pattern=pParserState.Pattern[pCharacter];this.appendOutputBuffer(pCharacter,pParserState);}else if(pParserState.EndPatternMatchBegan){if(pParserState.Pattern.PatternEnd.hasOwnProperty(pCharacter)){// This leaf has a PatternEnd tree, so we will wait until that end is met.
|
|
3420
3689
|
pParserState.Pattern=pParserState.Pattern.PatternEnd[pCharacter];// Flush the output buffer.
|
|
3421
3690
|
this.appendOutputBuffer(pCharacter,pParserState);// If this last character is the end of the pattern, parse it.
|
|
3422
3691
|
if(pParserState.Pattern.hasOwnProperty('Parse')&&(!pParserState.Pattern.isAsync||pParserState.Pattern.isBoth)){// Run the function
|
|
3423
|
-
pParserState.OutputBuffer=pParserState.Pattern.Parse(pParserState.OutputBuffer.substr(pParserState.Pattern.PatternStartString.length,pParserState.OutputBuffer.length-(pParserState.Pattern.PatternStartString.length+pParserState.Pattern.PatternEndString.length)),pData);return this.resetOutputBuffer(pParserState);}else{this.fable.log.info("MetaTemplate: The pattern ".concat(pParserState.Pattern.PatternStartString," is asynchronous and cannot be used in a synchronous parser."));return this.resetOutputBuffer(pParserState);}}else if(pParserState.PatternStartNode.PatternEnd.hasOwnProperty(pCharacter)){// We broke out of the end -- see if this is a new start of the end.
|
|
3692
|
+
pParserState.OutputBuffer=pParserState.Pattern.Parse(pParserState.OutputBuffer.substr(pParserState.Pattern.PatternStartString.length,pParserState.OutputBuffer.length-(pParserState.Pattern.PatternStartString.length+pParserState.Pattern.PatternEndString.length)),pData,pDataContext);return this.resetOutputBuffer(pParserState);}else{this.fable.log.info("MetaTemplate: The pattern ".concat(pParserState.Pattern.PatternStartString," is asynchronous and cannot be used in a synchronous parser."));return this.resetOutputBuffer(pParserState);}}else if(pParserState.PatternStartNode.PatternEnd.hasOwnProperty(pCharacter)){// We broke out of the end -- see if this is a new start of the end.
|
|
3424
3693
|
pParserState.Pattern=pParserState.PatternStartNode.PatternEnd[pCharacter];this.appendOutputBuffer(pCharacter,pParserState);}else{pParserState.EndPatternMatchBegan=false;this.appendOutputBuffer(pCharacter,pParserState);}}else if(pParserState.Pattern.hasOwnProperty('PatternEnd')){if(!pParserState.StartPatternMatchComplete){pParserState.StartPatternMatchComplete=true;pParserState.PatternStartNode=pParserState.Pattern;}this.appendOutputBuffer(pCharacter,pParserState);if(pParserState.Pattern.PatternEnd.hasOwnProperty(pCharacter)){// This is the first character of the end pattern.
|
|
3425
3694
|
pParserState.EndPatternMatchBegan=true;// This leaf has a PatternEnd tree, so we will wait until that end is met.
|
|
3426
3695
|
pParserState.Pattern=pParserState.Pattern.PatternEnd[pCharacter];// If this last character is the end of the pattern, parse it.
|
|
3427
3696
|
if(pParserState.Pattern.hasOwnProperty('Parse')){if(pParserState.Pattern.isAsync&&!pParserState.Pattern.isBoth){this.fable.log.info("MetaTemplate: The pattern ".concat(pParserState.Pattern.PatternStartString," is asynchronous and cannot be used in a synchronous parser."));this.resetOutputBuffer(pParserState);}else{// Run the t*mplate function
|
|
3428
|
-
pParserState.OutputBuffer=pParserState.Pattern.Parse(pParserState.OutputBuffer.substr(pParserState.Pattern.PatternStartString.length,pParserState.OutputBuffer.length-(pParserState.Pattern.PatternStartString.length+pParserState.Pattern.PatternEndString.length)),pData);return this.resetOutputBuffer(pParserState);}}}}else{// We are in a pattern start but didn't match one; reset and start trying again from this character.
|
|
3697
|
+
pParserState.OutputBuffer=pParserState.Pattern.Parse(pParserState.OutputBuffer.substr(pParserState.Pattern.PatternStartString.length,pParserState.OutputBuffer.length-(pParserState.Pattern.PatternStartString.length+pParserState.Pattern.PatternEndString.length)),pData,pDataContext);return this.resetOutputBuffer(pParserState);}}}}else{// We are in a pattern start but didn't match one; reset and start trying again from this character.
|
|
3429
3698
|
this.resetOutputBuffer(pParserState);}}// If we aren't in a pattern match or pattern, and this isn't the start of a new pattern (RAW mode)....
|
|
3430
3699
|
if(!pParserState.PatternMatch){// This may be the start of a new pattern....
|
|
3431
3700
|
if(pParserState.ParseTree.hasOwnProperty(pCharacter)){// ... assign the root node as the matched node.
|
|
3432
|
-
this.resetOutputBuffer(pParserState);this.appendOutputBuffer(pCharacter,pParserState);pParserState.Pattern=pParserState.ParseTree[pCharacter];pParserState.PatternMatch=true;return true;}else{this.appendOutputBuffer(pCharacter,pParserState);}}return false;}executePatternAsync(pParserState,pData,fCallback){// ... this is the end of a pattern, cut off the end tag and parse it.
|
|
3701
|
+
this.resetOutputBuffer(pParserState);this.appendOutputBuffer(pCharacter,pParserState);pParserState.Pattern=pParserState.ParseTree[pCharacter];pParserState.PatternMatch=true;return true;}else{this.appendOutputBuffer(pCharacter,pParserState);}}return false;}executePatternAsync(pParserState,pData,fCallback,pDataContext){// ... this is the end of a pattern, cut off the end tag and parse it.
|
|
3433
3702
|
// Trim the start and end tags off the output buffer now
|
|
3434
3703
|
if(pParserState.Pattern.isAsync&&!pParserState.Pattern.isBoth){// Run the function
|
|
3435
|
-
return pParserState.Pattern.Parse(pParserState.OutputBuffer.substr(pParserState.Pattern.PatternStartString.length,pParserState.OutputBuffer.length-(pParserState.Pattern.PatternStartString.length+pParserState.Pattern.PatternEndString.length)),pData,(pError,pAsyncOutput)=>{if(pError){this.fable.log.info("Precedent ERROR: Async template error happened parsing ".concat(pParserState.Pattern.PatternStart," ... ").concat(pParserState.Pattern.PatternEnd,": ").concat(pError));}pParserState.OutputBuffer=pAsyncOutput;this.resetOutputBuffer(pParserState);return fCallback();});}else if(pParserState.Pattern.isAsync&&pParserState.Pattern.isBoth){// Run the function when both async and non async were provided with the pattern
|
|
3436
|
-
return pParserState.Pattern.ParseAsync(pParserState.OutputBuffer.substr(pParserState.Pattern.PatternStartString.length,pParserState.OutputBuffer.length-(pParserState.Pattern.PatternStartString.length+pParserState.Pattern.PatternEndString.length)),pData,(pError,pAsyncOutput)=>{if(pError){this.fable.log.info("Precedent ERROR: Async template error happened parsing ".concat(pParserState.Pattern.PatternStart," ... ").concat(pParserState.Pattern.PatternEnd,": ").concat(pError));}pParserState.OutputBuffer=pAsyncOutput;this.resetOutputBuffer(pParserState);return fCallback();});}else{// Run the t*mplate function
|
|
3437
|
-
pParserState.OutputBuffer=pParserState.Pattern.Parse(pParserState.OutputBuffer.substr(pParserState.Pattern.PatternStartString.length,pParserState.OutputBuffer.length-(pParserState.Pattern.PatternStartString.length+pParserState.Pattern.PatternEndString.length)),pData);this.resetOutputBuffer(pParserState);return fCallback();}}/**
|
|
3704
|
+
return pParserState.Pattern.Parse(pParserState.OutputBuffer.substr(pParserState.Pattern.PatternStartString.length,pParserState.OutputBuffer.length-(pParserState.Pattern.PatternStartString.length+pParserState.Pattern.PatternEndString.length)),pData,(pError,pAsyncOutput)=>{if(pError){this.fable.log.info("Precedent ERROR: Async template error happened parsing ".concat(pParserState.Pattern.PatternStart," ... ").concat(pParserState.Pattern.PatternEnd,": ").concat(pError));}pParserState.OutputBuffer=pAsyncOutput;this.resetOutputBuffer(pParserState);return fCallback();},pDataContext);}else if(pParserState.Pattern.isAsync&&pParserState.Pattern.isBoth){// Run the function when both async and non async were provided with the pattern
|
|
3705
|
+
return pParserState.Pattern.ParseAsync(pParserState.OutputBuffer.substr(pParserState.Pattern.PatternStartString.length,pParserState.OutputBuffer.length-(pParserState.Pattern.PatternStartString.length+pParserState.Pattern.PatternEndString.length)),pData,(pError,pAsyncOutput)=>{if(pError){this.fable.log.info("Precedent ERROR: Async template error happened parsing ".concat(pParserState.Pattern.PatternStart," ... ").concat(pParserState.Pattern.PatternEnd,": ").concat(pError));}pParserState.OutputBuffer=pAsyncOutput;this.resetOutputBuffer(pParserState);return fCallback();},pDataContext);}else{// Run the t*mplate function
|
|
3706
|
+
pParserState.OutputBuffer=pParserState.Pattern.Parse(pParserState.OutputBuffer.substr(pParserState.Pattern.PatternStartString.length,pParserState.OutputBuffer.length-(pParserState.Pattern.PatternStartString.length+pParserState.Pattern.PatternEndString.length)),pData,pDataContext);this.resetOutputBuffer(pParserState);return fCallback();}}/**
|
|
3438
3707
|
* Parse a character in the buffer.
|
|
3439
|
-
* @method
|
|
3708
|
+
* @method parseCharacterAsync
|
|
3440
3709
|
* @param {string} pCharacter - The character to append
|
|
3441
3710
|
* @param {Object} pParserState - The state object for the current parsing task
|
|
3711
|
+
* @param {Object} pData - The data to pass to the function as a second parameter
|
|
3712
|
+
* @param {function} fCallback - The callback function to call when the parse is complete
|
|
3713
|
+
* @param {array} pDataContext - The history of data objects/context already passed in
|
|
3442
3714
|
* @private
|
|
3443
|
-
*/parseCharacterAsync(pCharacter,pParserState,pData,fCallback){// If we are already in a pattern match traversal
|
|
3715
|
+
*/parseCharacterAsync(pCharacter,pParserState,pData,fCallback,pDataContext){// If we are already in a pattern match traversal
|
|
3444
3716
|
if(pParserState.PatternMatch){// If the pattern is still matching the start and we haven't passed the buffer
|
|
3445
3717
|
if(!pParserState.StartPatternMatchComplete&&pParserState.Pattern.hasOwnProperty(pCharacter)){pParserState.Pattern=pParserState.Pattern[pCharacter];this.appendOutputBuffer(pCharacter,pParserState);}else if(pParserState.EndPatternMatchBegan){if(pParserState.Pattern.PatternEnd.hasOwnProperty(pCharacter)){// This leaf has a PatternEnd tree, so we will wait until that end is met.
|
|
3446
3718
|
pParserState.Pattern=pParserState.Pattern.PatternEnd[pCharacter];// Flush the output buffer.
|
|
3447
3719
|
this.appendOutputBuffer(pCharacter,pParserState);// If this last character is the end of the pattern, parse it.
|
|
3448
|
-
if(pParserState.Pattern.hasOwnProperty('Parse')){return this.executePatternAsync(pParserState,pData,fCallback);}}else if(pParserState.PatternStartNode.PatternEnd.hasOwnProperty(pCharacter)){// We broke out of the end -- see if this is a new start of the end.
|
|
3720
|
+
if(pParserState.Pattern.hasOwnProperty('Parse')){return this.executePatternAsync(pParserState,pData,fCallback,pDataContext);}}else if(pParserState.PatternStartNode.PatternEnd.hasOwnProperty(pCharacter)){// We broke out of the end -- see if this is a new start of the end.
|
|
3449
3721
|
pParserState.Pattern=pParserState.PatternStartNode.PatternEnd[pCharacter];this.appendOutputBuffer(pCharacter,pParserState);}else{pParserState.EndPatternMatchBegan=false;this.appendOutputBuffer(pCharacter,pParserState);}}else if(pParserState.Pattern.hasOwnProperty('PatternEnd')){if(!pParserState.StartPatternMatchComplete){pParserState.StartPatternMatchComplete=true;pParserState.PatternStartNode=pParserState.Pattern;}this.appendOutputBuffer(pCharacter,pParserState);if(pParserState.Pattern.PatternEnd.hasOwnProperty(pCharacter)){// This is the first character of the end pattern.
|
|
3450
3722
|
pParserState.EndPatternMatchBegan=true;// This leaf has a PatternEnd tree, so we will wait until that end is met.
|
|
3451
3723
|
pParserState.Pattern=pParserState.Pattern.PatternEnd[pCharacter];// If this last character is the end of the pattern, parse it.
|
|
3452
|
-
if(pParserState.Pattern.hasOwnProperty('Parse')){return this.executePatternAsync(pParserState,pData,fCallback);}}}else{// We are in a pattern start but didn't match one; reset and start trying again from this character.
|
|
3724
|
+
if(pParserState.Pattern.hasOwnProperty('Parse')){return this.executePatternAsync(pParserState,pData,fCallback,pDataContext);}}}else{// We are in a pattern start but didn't match one; reset and start trying again from this character.
|
|
3453
3725
|
this.resetOutputBuffer(pParserState);}}// If we aren't in a pattern match or pattern, and this isn't the start of a new pattern (RAW mode)....
|
|
3454
3726
|
else{// This may be the start of a new pattern....
|
|
3455
3727
|
if(pParserState.ParseTree.hasOwnProperty(pCharacter)){// ... assign the root node as the matched node.
|
|
@@ -3462,10 +3734,12 @@ return fCallback();}/**
|
|
|
3462
3734
|
* @param {Object} pParseTree - The parse tree to begin parsing from (usually root)
|
|
3463
3735
|
* @param {Object} pData - The data to pass to the function as a second parameter
|
|
3464
3736
|
* @param {function} fCallback - The callback function to call when the parse is complete
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
let
|
|
3468
|
-
this.flushOutputBuffer(tmpParserState);return
|
|
3737
|
+
* @param {array} pDataContext - The history of data objects/context already passed in
|
|
3738
|
+
*/parseString(pString,pParseTree,pData,fCallback,pDataContext){// TODO: There is danger here if a template function attempts to functionally recurse and doesn't pass this in.
|
|
3739
|
+
let tmpPreviousDataContext=Array.isArray(pDataContext)?pDataContext:[];let tmpDataContext=Array.from(tmpPreviousDataContext);tmpDataContext.push(pData);if(typeof fCallback!=='function'){let tmpParserState=this.newParserState(pParseTree);for(var i=0;i<pString.length;i++){// TODO: This is not fast.
|
|
3740
|
+
this.parseCharacter(pString[i],tmpParserState,pData,tmpDataContext);}this.flushOutputBuffer(tmpParserState);return tmpParserState.Output;}else{// This is the async mode
|
|
3741
|
+
let tmpParserState=this.newParserState(pParseTree);tmpParserState.Asynchronous=true;let tmpAnticipate=this.fable.instantiateServiceProviderWithoutRegistration('Anticipate');for(let i=0;i<pString.length;i++){tmpAnticipate.anticipate(fCallback=>{this.parseCharacterAsync(pString[i],tmpParserState,pData,fCallback,tmpDataContext);});}tmpAnticipate.wait(pError=>{// Flush the remaining data
|
|
3742
|
+
this.flushOutputBuffer(tmpParserState);return fCallback(pError,tmpParserState.Output);});}}}module.exports=StringParser;},{}],147:[function(require,module,exports){/**
|
|
3469
3743
|
* Word Tree
|
|
3470
3744
|
* @author Steven Velozo <steven@velozo.com>
|
|
3471
3745
|
* @description Create a tree (directed graph) of Javascript objects, one character per object.
|
|
@@ -3505,7 +3779,7 @@ for(var i=0;i<pPatternStart.length;i++){tmpLeaf=this.addChild(tmpLeaf,pPatternSt
|
|
|
3505
3779
|
* @param {function} fParser - The function to parse if this is the matched pattern, once the Pattern End is met. If this is a string, a simple replacement occurs.
|
|
3506
3780
|
* @return {bool} True if adding the pattern was successful
|
|
3507
3781
|
*/addPatternBoth(pPatternStart,pPatternEnd,fParser,fParserAsync){let tmpLeaf=this.addPattern(pPatternStart,pPatternEnd,fParser);if(tmpLeaf){tmpLeaf.isAsync=true;tmpLeaf.isBoth=true;// When a leaf has both async and non-async versions of the functions, we store the async in fParserAsync.
|
|
3508
|
-
tmpLeaf.ParseAsync=fParserAsync;}}}module.exports=WordTree;},{}],
|
|
3782
|
+
tmpLeaf.ParseAsync=fParserAsync;}}}module.exports=WordTree;},{}],148:[function(require,module,exports){module.exports={"Metadata":{"UUID":false,"Hash":false,"Name":"","Summary":"","Version":0},"Status":{"Completed":false,"StepCount":0},"Steps":[],"Errors":[],"Log":[]};},{}],149:[function(require,module,exports){const{PE}=require('big.js');const libFableServiceBase=require('fable-serviceproviderbase');const _OperationStatePrototypeString=JSON.stringify(require('./Fable-Service-Operation-DefaultSettings.js'));class FableOperation extends libFableServiceBase{constructor(pFable,pOptions,pServiceHash){super(pFable,pOptions,pServiceHash);// Timestamps will just be the long ints
|
|
3509
3783
|
this.timeStamps={};this.serviceType='PhasedOperation';this.state=JSON.parse(_OperationStatePrototypeString);this.stepMap={};this.stepFunctions={};// Match the service instantiation to the operation.
|
|
3510
3784
|
this.state.Metadata.Hash=this.Hash;this.state.Metadata.UUID=this.UUID;this.state.Metadata.Name=typeof this.options.Name=='string'?this.options.Name:"Unnamed Operation ".concat(this.state.Metadata.UUID);this.name=this.state.Metadata.Name;this.progressTrackerSet=this.fable.instantiateServiceProviderWithoutRegistration('ProgressTrackerSet');this.state.OverallProgressTracker=this.progressTrackerSet.createProgressTracker("Overall-".concat(this.state.Metadata.UUID));// This is here to use the pass-through logging functions in the operation itself.
|
|
3511
3785
|
this.log=this;}execute(fExecutionCompleteCallback){// TODO: Should the same operation be allowed to execute more than one time?
|
|
@@ -3520,9 +3794,9 @@ tmpAnticipate.wait(pError=>{if(pError){this.fable.log.error("Operation [".concat
|
|
|
3520
3794
|
addStep(fStepFunction,pStepMetadata,pStepName,pStepDescription,pGUIDStep){let tmpStep={};// GUID is optional
|
|
3521
3795
|
tmpStep.GUIDStep=typeof pGUIDStep!=='undefined'?pGUIDStep:"STEP-".concat(this.state.Steps.length,"-").concat(this.fable.DataGeneration.randomNumericString());// Name is optional
|
|
3522
3796
|
tmpStep.Name=typeof pStepName!=='undefined'?pStepName:"Step [".concat(tmpStep.GUIDStep,"]");tmpStep.Description=typeof pStepDescription!=='undefined'?pStepDescription:"Step execution of ".concat(tmpStep.Name,".");tmpStep.ProgressTracker=this.progressTrackerSet.createProgressTracker(tmpStep.GUIDStep);tmpStep.Metadata=typeof pStepMetadata==='object'?pStepMetadata:{};// There is an array of steps, in the Operation State itself ... push a step there
|
|
3523
|
-
this.state.Steps.push(tmpStep);this.stepMap[tmpStep.GUIDStep]=tmpStep;this.stepFunctions[tmpStep.GUIDStep]=typeof fStepFunction=='function'?fStepFunction:function(fDone){return fDone();};this.state.Status.StepCount++;return tmpStep;}setStepTotalOperations(pGUIDStep,pTotalOperationCount){if(!this.stepMap.hasOwnProperty(pGUIDStep)){return new Error("Step [".concat(pGUIDStep,"] does not exist in operation [").concat(this.state.Metadata.UUID,"] ").concat(this.state.Metadata.Name," when attempting to set total operations to ").concat(pTotalOperationCount,"."));}this.progressTrackerSet.setProgressTrackerTotalOperations(this.stepMap[pGUIDStep].ProgressTracker.Hash,pTotalOperationCount);}writeOperationLog(pLogLevel,pLogText,pLogObject){this.state.Log.push("[".concat(new Date().toUTCString(),"]-[").concat(pLogLevel,"]: ").concat(pLogText));if(typeof pLogObject=='object'){this.state.Log.push(JSON.stringify(pLogObject));}}writeOperationErrors(pLogText,pLogObject){this.state.Errors.push("".concat(pLogText));if(typeof pLogObject=='object'){this.state.Errors.push(JSON.stringify(pLogObject));}}trace(pLogText,pLogObject){this.writeOperationLog('TRACE',pLogText,pLogObject);this.fable.log.trace(pLogText,pLogObject);}debug(pLogText,pLogObject){this.writeOperationLog('DEBUG',pLogText,pLogObject);this.fable.log.debug(pLogText,pLogObject);}info(pLogText,pLogObject){this.writeOperationLog('INFO',pLogText,pLogObject);this.fable.log.info(pLogText,pLogObject);}warn(pLogText,pLogObject){this.writeOperationLog('WARN',pLogText,pLogObject);this.fable.log.warn(pLogText,pLogObject);}error(pLogText,pLogObject){this.writeOperationLog('ERROR',pLogText,pLogObject);this.writeOperationErrors(pLogText,pLogObject);this.fable.log.error(pLogText,pLogObject);}fatal(pLogText,pLogObject){this.writeOperationLog('FATAL',pLogText,pLogObject);this.writeOperationErrors(pLogText,pLogObject);this.fable.log.fatal(pLogText,pLogObject);}}module.exports=FableOperation;},{"./Fable-Service-Operation-DefaultSettings.js":
|
|
3797
|
+
this.state.Steps.push(tmpStep);this.stepMap[tmpStep.GUIDStep]=tmpStep;this.stepFunctions[tmpStep.GUIDStep]=typeof fStepFunction=='function'?fStepFunction:function(fDone){return fDone();};this.state.Status.StepCount++;return tmpStep;}setStepTotalOperations(pGUIDStep,pTotalOperationCount){if(!this.stepMap.hasOwnProperty(pGUIDStep)){return new Error("Step [".concat(pGUIDStep,"] does not exist in operation [").concat(this.state.Metadata.UUID,"] ").concat(this.state.Metadata.Name," when attempting to set total operations to ").concat(pTotalOperationCount,"."));}this.progressTrackerSet.setProgressTrackerTotalOperations(this.stepMap[pGUIDStep].ProgressTracker.Hash,pTotalOperationCount);}writeOperationLog(pLogLevel,pLogText,pLogObject){this.state.Log.push("[".concat(new Date().toUTCString(),"]-[").concat(pLogLevel,"]: ").concat(pLogText));if(typeof pLogObject=='object'){this.state.Log.push(JSON.stringify(pLogObject));}}writeOperationErrors(pLogText,pLogObject){this.state.Errors.push("".concat(pLogText));if(typeof pLogObject=='object'){this.state.Errors.push(JSON.stringify(pLogObject));}}trace(pLogText,pLogObject){this.writeOperationLog('TRACE',pLogText,pLogObject);this.fable.log.trace(pLogText,pLogObject);}debug(pLogText,pLogObject){this.writeOperationLog('DEBUG',pLogText,pLogObject);this.fable.log.debug(pLogText,pLogObject);}info(pLogText,pLogObject){this.writeOperationLog('INFO',pLogText,pLogObject);this.fable.log.info(pLogText,pLogObject);}warn(pLogText,pLogObject){this.writeOperationLog('WARN',pLogText,pLogObject);this.fable.log.warn(pLogText,pLogObject);}error(pLogText,pLogObject){this.writeOperationLog('ERROR',pLogText,pLogObject);this.writeOperationErrors(pLogText,pLogObject);this.fable.log.error(pLogText,pLogObject);}fatal(pLogText,pLogObject){this.writeOperationLog('FATAL',pLogText,pLogObject);this.writeOperationErrors(pLogText,pLogObject);this.fable.log.fatal(pLogText,pLogObject);}}module.exports=FableOperation;},{"./Fable-Service-Operation-DefaultSettings.js":148,"big.js":17,"fable-serviceproviderbase":51}],150:[function(require,module,exports){const libFableServiceBase=require('fable-serviceproviderbase');class FableServiceProgressTime extends libFableServiceBase{constructor(pFable,pOptions,pServiceHash){super(pFable,pOptions,pServiceHash);this.serviceType='ProgressTime';this.timeStamps={};}formatTimeDuration(pTimeDurationInMilliseconds){let tmpTimeDuration=typeof pTimeDurationInMilliseconds=='number'?pTimeDurationInMilliseconds:0;if(pTimeDurationInMilliseconds<0){return'unknown';}let tmpTimeDurationString='';if(tmpTimeDuration>3600000){tmpTimeDurationString+=Math.floor(tmpTimeDuration/3600000)+'h ';tmpTimeDuration=tmpTimeDuration%3600000;}if(tmpTimeDuration>60000){tmpTimeDurationString+=Math.floor(tmpTimeDuration/60000)+'m ';tmpTimeDuration=tmpTimeDuration%60000;}if(tmpTimeDuration>1000){tmpTimeDurationString+=Math.floor(tmpTimeDuration/1000)+'s ';tmpTimeDuration=tmpTimeDuration%1000;}tmpTimeDurationString+=Math.round(tmpTimeDuration)+'ms';return tmpTimeDurationString;}createTimeStamp(pTimeStampHash){let tmpTimeStampHash=typeof pTimeStampHash=='string'?pTimeStampHash:'Default';this.timeStamps[tmpTimeStampHash]=+new Date();return this.timeStamps[tmpTimeStampHash];}getTimeStampValue(pTimeStampHash){let tmpTimeStampHash=typeof pTimeStampHash=='string'?pTimeStampHash:'Default';return this.timeStamps.hasOwnProperty(tmpTimeStampHash)?this.timeStamps[tmpTimeStampHash]:-1;}updateTimeStampValue(pTimeStampHash,pReferenceTime){let tmpTimeStampHash=typeof pTimeStampHash=='string'?pTimeStampHash:'Default';let tmpReferenceTime=false;// This function allows the user to pass in either a reference time in ms, or, a hash of a timestamp.
|
|
3524
3798
|
if(typeof pReferenceTime=='string'){tmpReferenceTime=this.timeStamps.hasOwnProperty(tmpReference)?this.timeStamps[tmpReference]:false;}else if(typeof pReferenceTime=='number'){tmpReferenceTime=pReferenceTime;}else{tmpReferenceTime=+new Date();}if(this.timeStamps.hasOwnProperty(tmpTimeStampHash)&&tmpReferenceTime){this.timeStamps[tmpTimeStampHash]=tmpReferenceTime;return this.timeStamps[tmpTimeStampHash];}else{return-1;}}removeTimeStamp(pTimeStampHash){let tmpTimeStampHash=typeof pTimeStampHash=='string'?pTimeStampHash:'Default';if(this.timeStamps.hasOwnProperty(tmpTimeStampHash)){delete this.timeStamps[tmpTimeStampHash];return true;}else{return false;}}getTimeStampDelta(pTimeStampHash,pReferenceTime){let tmpTimeStampHash=typeof pTimeStampHash=='string'?pTimeStampHash:'Default';let tmpReferenceTime=false;// This function allows the user to pass in either a reference time in ms, or, a hash of a timestamp.
|
|
3525
|
-
if(typeof pReferenceTime=='string'){tmpReferenceTime=this.timeStamps.hasOwnProperty(tmpReference)?this.timeStamps[tmpReference]:false;}else if(typeof pReferenceTime=='number'){tmpReferenceTime=pReferenceTime;}else{tmpReferenceTime=+new Date();}if(this.timeStamps.hasOwnProperty(tmpTimeStampHash)&&tmpReferenceTime){return tmpReferenceTime-this.timeStamps[tmpTimeStampHash];}else{return-1;}}getDurationBetweenTimestamps(pTimeStampHashStart,pTimeStampHashEnd){let tmpTimeStampHashStart=typeof pTimeStampHashStart=='string'?pTimeStampHashStart:'Default';let tmpTimeStampHashEnd=typeof pTimeStampHashEnd=='string'?pTimeStampHashEnd:'Default';if(this.timeStamps.hasOwnProperty(tmpTimeStampHashStart)&&this.timeStamps.hasOwnProperty(tmpTimeStampHashEnd)){return this.timeStamps[tmpTimeStampHashEnd]-this.timeStamps[tmpTimeStampHashStart];}else{return-1;}}getTimeStampDeltaMessage(pTimeStampHash,pMessage,pReferenceTime){let tmpTimeStampHash=typeof pTimeStampHash=='string'?pTimeStampHash:'Default';let tmpMessage=typeof pMessage!=='undefined'?pMessage:"Elapsed for ".concat(tmpTimeStampHash,": ");let tmpOperationTime=this.getTimeStampDelta(tmpTimeStampHash,pReferenceTime);return"".concat(tmpMessage," ").concat(this.formatTimeDuration(tmpOperationTime));}logTimeStampDelta(pTimeStampHash,pMessage,pReferenceTime){this.fable.log.info(this.getTimeStampDeltaMessage(pTimeStampHash,pMessage,pReferenceTime));}}module.exports=FableServiceProgressTime;},{"fable-serviceproviderbase":51}],
|
|
3799
|
+
if(typeof pReferenceTime=='string'){tmpReferenceTime=this.timeStamps.hasOwnProperty(tmpReference)?this.timeStamps[tmpReference]:false;}else if(typeof pReferenceTime=='number'){tmpReferenceTime=pReferenceTime;}else{tmpReferenceTime=+new Date();}if(this.timeStamps.hasOwnProperty(tmpTimeStampHash)&&tmpReferenceTime){return tmpReferenceTime-this.timeStamps[tmpTimeStampHash];}else{return-1;}}getDurationBetweenTimestamps(pTimeStampHashStart,pTimeStampHashEnd){let tmpTimeStampHashStart=typeof pTimeStampHashStart=='string'?pTimeStampHashStart:'Default';let tmpTimeStampHashEnd=typeof pTimeStampHashEnd=='string'?pTimeStampHashEnd:'Default';if(this.timeStamps.hasOwnProperty(tmpTimeStampHashStart)&&this.timeStamps.hasOwnProperty(tmpTimeStampHashEnd)){return this.timeStamps[tmpTimeStampHashEnd]-this.timeStamps[tmpTimeStampHashStart];}else{return-1;}}getTimeStampDeltaMessage(pTimeStampHash,pMessage,pReferenceTime){let tmpTimeStampHash=typeof pTimeStampHash=='string'?pTimeStampHash:'Default';let tmpMessage=typeof pMessage!=='undefined'?pMessage:"Elapsed for ".concat(tmpTimeStampHash,": ");let tmpOperationTime=this.getTimeStampDelta(tmpTimeStampHash,pReferenceTime);return"".concat(tmpMessage," ").concat(this.formatTimeDuration(tmpOperationTime));}logTimeStampDelta(pTimeStampHash,pMessage,pReferenceTime){this.fable.log.info(this.getTimeStampDeltaMessage(pTimeStampHash,pMessage,pReferenceTime));}}module.exports=FableServiceProgressTime;},{"fable-serviceproviderbase":51}],151:[function(require,module,exports){class ProgressTracker{constructor(pProgressTrackerSet,pProgressTrackerHash){this.progressTrackerSet=pProgressTrackerSet;this.progressTrackerHash=pProgressTrackerHash;this.data=this.progressTrackerSet.getProgressTrackerData(this.progressTrackerHash);}updateProgressTracker(pProgressAmount){return this.progressTrackerSet.updateProgressTracker(this.progressTrackerHash,pProgressAmount);}incrementProgressTracker(pProgressIncrementAmount){return this.progressTrackerSet.incrementProgressTracker(this.progressTrackerHash,pProgressIncrementAmount);}setProgressTrackerTotalOperations(pTotalOperationCount){return this.progressTrackerSet.setProgressTrackerTotalOperations(this.progressTrackerHash,pTotalOperationCount);}getProgressTrackerStatusString(){return this.progressTrackerSet.getProgressTrackerStatusString(this.progressTrackerHash);}}module.exports=ProgressTracker;},{}],152:[function(require,module,exports){const libFableServiceBase=require('fable-serviceproviderbase');const libProgressTrackerClass=require('./Fable-Service-ProgressTracker/ProgressTracker.js');class FableServiceProgressTrackerSet extends libFableServiceBase{constructor(pFable,pOptions,pServiceHash){super(pFable,pOptions,pServiceHash);this.serviceType='ProgressTrackerSet';this.progressTrackers={};// Create an internal PorgressTime service to track timestamps
|
|
3526
3800
|
this.progressTimes=this.fable.instantiateServiceProviderWithoutRegistration('ProgressTime');// This timestamp is used and updated by *all* progress trackers.
|
|
3527
3801
|
this.progressTimes.createTimeStamp('CurrentTime');}getProgressTracker(pProgressTrackerHash){let tmpProgressTrackerHash=typeof pProgressTrackerHash=='string'?pProgressTrackerHash:'Default';if(!this.progressTrackers.hasOwnProperty(tmpProgressTrackerHash)){this.fable.log.warn("ProgressTracker ".concat(tmpProgressTrackerHash," does not exist! Creating a new tracker..."));this.createProgressTracker(tmpProgressTrackerHash,100);}return new libProgressTrackerClass(this,pProgressTrackerHash);}getProgressTrackerData(pProgressTrackerHash){let tmpProgressTrackerHash=typeof pProgressTrackerHash=='string'?pProgressTrackerHash:'Default';if(!this.progressTrackers.hasOwnProperty(tmpProgressTrackerHash)){this.fable.log.warn("ProgressTracker ".concat(tmpProgressTrackerHash," does not exist! Creating a new tracker..."));this.createProgressTracker(tmpProgressTrackerHash,100);}return this.progressTrackers[tmpProgressTrackerHash];}createProgressTracker(pProgressTrackerHash,pTotalOperations){let tmpProgressTrackerHash=typeof pProgressTrackerHash=='string'?pProgressTrackerHash:'Default';let tmpTotalOperations=typeof pTotalOperations=='number'?pTotalOperations:100;let tmpProgressTracker={Hash:tmpProgressTrackerHash,StartTimeHash:"".concat(tmpProgressTrackerHash,"-Start"),StartTimeStamp:-1,CurrentTimeStamp:-1,EndTimeHash:"".concat(tmpProgressTrackerHash,"-End"),EndTimeStamp:-1,PercentComplete:-1,// If this is set to true, PercentComplete will be calculated as CurrentCount / TotalCount even if it goes over 100%
|
|
3528
3802
|
AllowTruePercentComplete:false,ElapsedTime:-1,AverageOperationTime:-1,EstimatedCompletionTime:-1,TotalCount:tmpTotalOperations,CurrentCount:-1};if(this.progressTrackers.hasOwnProperty(tmpProgressTrackerHash)){this.fable.log.warn("ProgressTracker ".concat(tmpProgressTrackerHash," already exists! Overwriting with a new tracker..."));this.progressTimes.removeTimeStamp(tmpProgressTracker.StartTimeHash);this.progressTimes.removeTimeStamp(tmpProgressTracker.EndTimeHash);}this.progressTrackers[tmpProgressTrackerHash]=tmpProgressTracker;return tmpProgressTracker;}setProgressTrackerTotalOperations(pProgressTrackerHash,pTotalOperations){let tmpProgressTrackerHash=typeof pProgressTrackerHash=='string'?pProgressTrackerHash:'Default';let tmpTotalOperations=typeof pTotalOperations=='number'?pTotalOperations:100;if(!this.progressTrackers.hasOwnProperty(tmpProgressTrackerHash)){this.fable.log.warn("Attempted to set the total operations of ProgressTracker ".concat(tmpProgressTrackerHash," but it does not exist! Creating a new tracker..."));this.createProgressTracker(tmpProgressTrackerHash,tmpTotalOperations);}this.progressTrackers[tmpProgressTrackerHash].TotalCount=tmpTotalOperations;return this.progressTrackers[tmpProgressTrackerHash];}startProgressTracker(pProgressTrackerHash){let tmpProgressTrackerHash=typeof pProgressTrackerHash=='string'?pProgressTrackerHash:'Default';// This is the only method to lazily create ProgressTrackers now
|
|
@@ -3545,7 +3819,7 @@ this.solveProgressTrackerStatus(tmpProgressTrackerHash);if(!this.progressTracker
|
|
|
3545
3819
|
if(tmpProgressTracker.StartTimeStamp<1){return"ProgressTracker ".concat(tmpProgressTracker.Hash," has not been started yet.");}// 2. Started, but no operations completed
|
|
3546
3820
|
if(tmpProgressTracker.CurrentCount<1&&tmpProgressTracker.EndTimeStamp<1){return"ProgressTracker ".concat(tmpProgressTracker.Hash," has no completed operations. ").concat(this.progressTimes.formatTimeDuration(tmpProgressTracker.ElapsedTime)," have elapsed since it was started.");}// 3. Started, some operations completed
|
|
3547
3821
|
else if(tmpProgressTracker.EndTimeStamp<1){return"ProgressTracker ".concat(tmpProgressTracker.Hash," is ").concat(tmpProgressTracker.PercentComplete.toFixed(3),"% completed - ").concat(tmpProgressTracker.CurrentCount," / ").concat(tmpProgressTracker.TotalCount," operations over ").concat(this.progressTimes.formatTimeDuration(tmpProgressTracker.ElapsedTime)," (median ").concat(this.progressTimes.formatTimeDuration(tmpProgressTracker.AverageOperationTime)," per). Estimated completion: ").concat(this.progressTimes.formatTimeDuration(tmpProgressTracker.EstimatedCompletionTime));}// 4. Done
|
|
3548
|
-
else{return"ProgressTracker ".concat(tmpProgressTracker.Hash," is done. ").concat(tmpProgressTracker.CurrentCount," / ").concat(tmpProgressTracker.TotalCount," operations were completed in ").concat(this.progressTimes.formatTimeDuration(tmpProgressTracker.ElapsedTime)," (median ").concat(this.progressTimes.formatTimeDuration(tmpProgressTracker.AverageOperationTime)," per).");}}}logProgressTrackerStatus(pProgressTrackerHash){this.fable.log.info(this.getProgressTrackerStatusString(pProgressTrackerHash));}}module.exports=FableServiceProgressTrackerSet;},{"./Fable-Service-ProgressTracker/ProgressTracker.js":
|
|
3822
|
+
else{return"ProgressTracker ".concat(tmpProgressTracker.Hash," is done. ").concat(tmpProgressTracker.CurrentCount," / ").concat(tmpProgressTracker.TotalCount," operations were completed in ").concat(this.progressTimes.formatTimeDuration(tmpProgressTracker.ElapsedTime)," (median ").concat(this.progressTimes.formatTimeDuration(tmpProgressTracker.AverageOperationTime)," per).");}}}logProgressTrackerStatus(pProgressTrackerHash){this.fable.log.info(this.getProgressTrackerStatusString(pProgressTrackerHash));}}module.exports=FableServiceProgressTrackerSet;},{"./Fable-Service-ProgressTracker/ProgressTracker.js":151,"fable-serviceproviderbase":51}],153:[function(require,module,exports){(function(Buffer){(function(){const libFableServiceBase=require('fable-serviceproviderbase');const libSimpleGet=require('simple-get');const libCookie=require('cookie');class FableServiceRestClient extends libFableServiceBase{constructor(pFable,pOptions,pServiceHash){super(pFable,pOptions,pServiceHash);this.TraceLog=false;if(this.options.TraceLog||this.fable.TraceLog){this.TraceLog=true;}this.dataFormat=this.fable.services.DataFormat;this.serviceType='RestClient';this.cookie=false;// This is a function that can be overridden, to allow the management
|
|
3549
3823
|
// of the request options before they are passed to the request library.
|
|
3550
3824
|
this.prepareRequestOptions=pOptions=>{return pOptions;};}get simpleGet(){return libSimpleGet;}prepareCookies(pRequestOptions){if(this.cookie){let tmpCookieObject=this.cookie;if(!pRequestOptions.hasOwnProperty('headers')){pRequestOptions.headers={};}let tmpCookieKeys=Object.keys(tmpCookieObject);if(tmpCookieKeys.length>0){// Only grab the first for now.
|
|
3551
3825
|
pRequestOptions.headers.cookie=libCookie.serialize(tmpCookieKeys[0],tmpCookieObject[tmpCookieKeys[0]]);}}return pRequestOptions;}preRequest(pOptions){// Validate the options object
|
|
@@ -3558,7 +3832,7 @@ if(!tmpDataBuffer){tmpDataBuffer=Buffer.from(pChunk);}else{tmpDataBuffer=Buffer.
|
|
|
3558
3832
|
{
|
|
3559
3833
|
tmpOptions.headers['Content-Type'] = 'application/json';
|
|
3560
3834
|
}
|
|
3561
|
-
*/tmpOptions.RequestStartTime=this.fable.log.getTimeStamp();if(this.TraceLog){this.fable.log.debug("Beginning ".concat(tmpOptions.method," JSON request to ").concat(tmpOptions.url," at ").concat(tmpOptions.RequestStartTime));}return libSimpleGet(tmpOptions,(pError,pResponse)=>{if(pError){return fCallback(pError,pResponse);}if(this.TraceLog){let tmpConnectTime=this.fable.log.getTimeStamp();this.fable.log.debug("--> JSON ".concat(tmpOptions.method," connected in ").concat(this.dataFormat.formatTimeDelta(tmpOptions.RequestStartTime,tmpConnectTime),"ms code ").concat(pResponse.statusCode));}let tmpJSONData='';pResponse.on('data',pChunk=>{if(this.TraceLog){let tmpChunkTime=this.fable.log.getTimeStamp();this.fable.log.debug("--> JSON ".concat(tmpOptions.method," data chunk size ").concat(pChunk.length,"b received in ").concat(this.dataFormat.formatTimeDelta(tmpOptions.RequestStartTime,tmpChunkTime),"ms"));}tmpJSONData+=pChunk;});pResponse.on('end',()=>{if(this.TraceLog){let tmpCompletionTime=this.fable.log.getTimeStamp();this.fable.log.debug("==> JSON ".concat(tmpOptions.method," completed - received in ").concat(this.dataFormat.formatTimeDelta(tmpOptions.RequestStartTime,tmpCompletionTime),"ms"));}return fCallback(pError,pResponse,JSON.parse(tmpJSONData));});});}getJSON(pOptionsOrURL,fCallback){let tmpRequestOptions=typeof pOptionsOrURL=='object'?pOptionsOrURL:{};if(typeof pOptionsOrURL=='string'){tmpRequestOptions.url=pOptionsOrURL;}tmpRequestOptions.method='GET';return this.executeJSONRequest(tmpRequestOptions,fCallback);}putJSON(pOptions,fCallback){if(typeof pOptions.body!='object'){return fCallback(new Error("PUT JSON Error Invalid options object"));}pOptions.method='PUT';return this.executeJSONRequest(pOptions,fCallback);}postJSON(pOptions,fCallback){if(typeof pOptions.body!='object'){return fCallback(new Error("POST JSON Error Invalid options object"));}pOptions.method='POST';return this.executeJSONRequest(pOptions,fCallback);}patchJSON(pOptions,fCallback){if(typeof pOptions.body!='object'){return fCallback(new Error("PATCH JSON Error Invalid options object"));}pOptions.method='PATCH';return this.executeJSONRequest(pOptions,fCallback);}headJSON(pOptions,fCallback){if(typeof pOptions.body!='object'){return fCallback(new Error("HEAD JSON Error Invalid options object"));}pOptions.method='HEAD';return this.executeJSONRequest(pOptions,fCallback);}delJSON(pOptions,fCallback){pOptions.method='DELETE';return this.executeJSONRequest(pOptions,fCallback);}getRawText(pOptionsOrURL,fCallback){let tmpRequestOptions=typeof pOptionsOrURL=='object'?pOptionsOrURL:{};if(typeof pOptionsOrURL=='string'){tmpRequestOptions.url=pOptionsOrURL;}tmpRequestOptions.method='GET';return this.executeChunkedRequest(tmpRequestOptions,fCallback);}}module.exports=FableServiceRestClient;}).call(this);}).call(this,require("buffer").Buffer);},{"buffer":20,"cookie":27,"fable-serviceproviderbase":51,"simple-get":101}],
|
|
3835
|
+
*/tmpOptions.RequestStartTime=this.fable.log.getTimeStamp();if(this.TraceLog){this.fable.log.debug("Beginning ".concat(tmpOptions.method," JSON request to ").concat(tmpOptions.url," at ").concat(tmpOptions.RequestStartTime));}return libSimpleGet(tmpOptions,(pError,pResponse)=>{if(pError){return fCallback(pError,pResponse);}if(this.TraceLog){let tmpConnectTime=this.fable.log.getTimeStamp();this.fable.log.debug("--> JSON ".concat(tmpOptions.method," connected in ").concat(this.dataFormat.formatTimeDelta(tmpOptions.RequestStartTime,tmpConnectTime),"ms code ").concat(pResponse.statusCode));}let tmpJSONData='';pResponse.on('data',pChunk=>{if(this.TraceLog){let tmpChunkTime=this.fable.log.getTimeStamp();this.fable.log.debug("--> JSON ".concat(tmpOptions.method," data chunk size ").concat(pChunk.length,"b received in ").concat(this.dataFormat.formatTimeDelta(tmpOptions.RequestStartTime,tmpChunkTime),"ms"));}tmpJSONData+=pChunk;});pResponse.on('end',()=>{if(this.TraceLog){let tmpCompletionTime=this.fable.log.getTimeStamp();this.fable.log.debug("==> JSON ".concat(tmpOptions.method," completed - received in ").concat(this.dataFormat.formatTimeDelta(tmpOptions.RequestStartTime,tmpCompletionTime),"ms"));}return fCallback(pError,pResponse,JSON.parse(tmpJSONData));});});}getJSON(pOptionsOrURL,fCallback){let tmpRequestOptions=typeof pOptionsOrURL=='object'?pOptionsOrURL:{};if(typeof pOptionsOrURL=='string'){tmpRequestOptions.url=pOptionsOrURL;}tmpRequestOptions.method='GET';return this.executeJSONRequest(tmpRequestOptions,fCallback);}putJSON(pOptions,fCallback){if(typeof pOptions.body!='object'){return fCallback(new Error("PUT JSON Error Invalid options object"));}pOptions.method='PUT';return this.executeJSONRequest(pOptions,fCallback);}postJSON(pOptions,fCallback){if(typeof pOptions.body!='object'){return fCallback(new Error("POST JSON Error Invalid options object"));}pOptions.method='POST';return this.executeJSONRequest(pOptions,fCallback);}patchJSON(pOptions,fCallback){if(typeof pOptions.body!='object'){return fCallback(new Error("PATCH JSON Error Invalid options object"));}pOptions.method='PATCH';return this.executeJSONRequest(pOptions,fCallback);}headJSON(pOptions,fCallback){if(typeof pOptions.body!='object'){return fCallback(new Error("HEAD JSON Error Invalid options object"));}pOptions.method='HEAD';return this.executeJSONRequest(pOptions,fCallback);}delJSON(pOptions,fCallback){pOptions.method='DELETE';return this.executeJSONRequest(pOptions,fCallback);}getRawText(pOptionsOrURL,fCallback){let tmpRequestOptions=typeof pOptionsOrURL=='object'?pOptionsOrURL:{};if(typeof pOptionsOrURL=='string'){tmpRequestOptions.url=pOptionsOrURL;}tmpRequestOptions.method='GET';return this.executeChunkedRequest(tmpRequestOptions,fCallback);}}module.exports=FableServiceRestClient;}).call(this);}).call(this,require("buffer").Buffer);},{"buffer":20,"cookie":27,"fable-serviceproviderbase":51,"simple-get":101}],154:[function(require,module,exports){const libFableServiceBase=require('fable-serviceproviderbase');class FableServiceTemplate extends libFableServiceBase{// Underscore and lodash have a behavior, _.template, which compiles a
|
|
3562
3836
|
// string-based template with code snippets into simple executable pieces,
|
|
3563
3837
|
// with the added twist of returning a precompiled function ready to go.
|
|
3564
3838
|
//
|
|
@@ -3574,11 +3848,11 @@ GuaranteedNonMatch:/.^/};// This is a helper for the escaper and unescaper funct
|
|
|
3574
3848
|
// Right now we are going to keep what underscore is doing, but, not forever.
|
|
3575
3849
|
this.templateEscapes={'\\':'\\',"'":"'",'r':'\r','\r':'r','n':'\n','\n':'n','t':'\t','\t':'t','u2028':'\u2028','\u2028':'u2028','u2029':'\u2029','\u2029':'u2029'};// This is defined as such to underscore that it is a dynamic programming
|
|
3576
3850
|
// function on this class.
|
|
3577
|
-
this.renderFunction=false;this.templateString=false;}renderTemplate(pData){return this.renderFunction(pData);}templateFunction(
|
|
3851
|
+
this.renderFunction=false;this.templateString=false;}renderTemplate(pData){return this.renderFunction(pData);}templateFunction(){let fRenderTemplateBound=this.renderTemplate.bind(this);return fRenderTemplateBound;}buildTemplateFunction(pTemplateText,pData){// For now this is being kept in a weird form ... this is to mimic the old
|
|
3578
3852
|
// underscore code until this is rewritten using precedent.
|
|
3579
3853
|
this.TemplateSource="__p+='"+pTemplateText.replace(this.Matchers.Escaper,pMatch=>{return"\\".concat(this.templateEscapes[pMatch]);}).replace(this.Matchers.Interpolate||this.Matchers.GuaranteedNonMatch,(pMatch,pCode)=>{return"'+\n(".concat(decodeURIComponent(pCode),")+\n'");}).replace(this.Matchers.Evaluate||this.Matchers.GuaranteedNonMatch,(pMatch,pCode)=>{return"';\n".concat(decodeURIComponent(pCode),"\n;__p+='");})+"';\n";this.TemplateSource="with(pTemplateDataObject||{}){\n".concat(this.TemplateSource,"}\n");this.TemplateSource="var __p='';var print=function(){__p+=Array.prototype.join.call(arguments, '')};\n".concat(this.TemplateSource,"return __p;\n");this.renderFunction=new Function('pTemplateDataObject',this.TemplateSource);if(typeof pData!='undefined'){return this.renderFunction(pData);}// Provide the compiled function source as a convenience for build time
|
|
3580
3854
|
// precompilation.
|
|
3581
|
-
this.TemplateSourceCompiled='function(obj){\n'+this.TemplateSource+'}';return this.templateFunction();}}module.exports=FableServiceTemplate;},{"fable-serviceproviderbase":51}],
|
|
3855
|
+
this.TemplateSourceCompiled='function(obj){\n'+this.TemplateSource+'}';return this.templateFunction();}}module.exports=FableServiceTemplate;},{"fable-serviceproviderbase":51}],155:[function(require,module,exports){const libFableServiceBase=require('fable-serviceproviderbase');// TODO: These are still pretty big -- consider the smaller polyfills
|
|
3582
3856
|
const libAsyncWaterfall=require('async.waterfall');const libAsyncEachLimit=require('async.eachlimit');const libBigNumber=require('big.js');class FableServiceUtility extends libFableServiceBase{// Underscore and lodash have a behavior, _.template, which compiles a
|
|
3583
3857
|
// string-based template with code snippets into simple executable pieces,
|
|
3584
3858
|
// with the added twist of returning a precompiled function ready to go.
|