flocc 0.5.22 → 0.5.23
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/dist/flocc.es.js +362 -21
- package/dist/flocc.js +362 -21
- package/dist/helpers/Rule.d.ts +56 -1
- package/dist/main.d.ts +1 -1
- package/package.json +1 -1
package/dist/flocc.es.js
CHANGED
|
@@ -817,7 +817,37 @@ var Operators;
|
|
|
817
817
|
Operators["agent"] = "agent";
|
|
818
818
|
Operators["environment"] = "environment";
|
|
819
819
|
Operators["vector"] = "vector";
|
|
820
|
+
Operators["log"] = "log";
|
|
820
821
|
})(Operators || (Operators = {}));
|
|
822
|
+
var operatorInfo = {
|
|
823
|
+
add: { min: 2, max: 2 },
|
|
824
|
+
subtract: { min: 2, max: 2 },
|
|
825
|
+
multiply: { min: 2, max: 2 },
|
|
826
|
+
divide: { min: 2, max: 2 },
|
|
827
|
+
mod: { min: 2, max: 2 },
|
|
828
|
+
power: { min: 2, max: 2 },
|
|
829
|
+
get: { min: 1, max: 1 },
|
|
830
|
+
set: { min: 2, max: 2 },
|
|
831
|
+
enqueue: { min: 2, max: 2 },
|
|
832
|
+
local: { min: 1, max: 2 },
|
|
833
|
+
if: { min: 2, max: 3 },
|
|
834
|
+
and: { min: 2, max: 2 },
|
|
835
|
+
or: { min: 2, max: 2 },
|
|
836
|
+
gt: { min: 2, max: 2 },
|
|
837
|
+
gte: { min: 2, max: 2 },
|
|
838
|
+
lt: { min: 2, max: 2 },
|
|
839
|
+
lte: { min: 2, max: 2 },
|
|
840
|
+
eq: { min: 2, max: 2 },
|
|
841
|
+
map: { min: 2, max: 2 },
|
|
842
|
+
filter: { min: 2, max: 2 },
|
|
843
|
+
key: { min: 2, max: 2 },
|
|
844
|
+
method: { min: 2, max: Infinity },
|
|
845
|
+
agent: { min: 0, max: 0 },
|
|
846
|
+
environment: { min: 0, max: 0 },
|
|
847
|
+
vector: { min: 1, max: Infinity },
|
|
848
|
+
log: { min: 1, max: Infinity }
|
|
849
|
+
};
|
|
850
|
+
var ARITHMETIC_OPS = new Set(["add", "subtract", "multiply", "divide", "mod", "power"]);
|
|
821
851
|
var add = function (a, b) { return a + b; };
|
|
822
852
|
var subtract = function (a, b) { return a - b; };
|
|
823
853
|
var multiply = function (a, b) { return a * b; };
|
|
@@ -841,6 +871,127 @@ var method = function (obj, name) {
|
|
|
841
871
|
return null;
|
|
842
872
|
return obj[name].apply(obj, args);
|
|
843
873
|
};
|
|
874
|
+
/**
|
|
875
|
+
* Format a single value for step display. If isFirst is true and the value is
|
|
876
|
+
* a known operator string, render it bare (unquoted).
|
|
877
|
+
*/
|
|
878
|
+
function formatStepValue(val, isFirst) {
|
|
879
|
+
if (val === null || val === undefined)
|
|
880
|
+
return String(val);
|
|
881
|
+
if (typeof val === "number" || typeof val === "boolean")
|
|
882
|
+
return String(val);
|
|
883
|
+
if (typeof val === "string") {
|
|
884
|
+
if (isFirst && val in Operators)
|
|
885
|
+
return val;
|
|
886
|
+
return JSON.stringify(val);
|
|
887
|
+
}
|
|
888
|
+
if (val instanceof Array) {
|
|
889
|
+
if (val.length === 0)
|
|
890
|
+
return "[]";
|
|
891
|
+
var parts = val.map(function (s, i) { return formatStepValue(s, i === 0); });
|
|
892
|
+
return "(" + parts.join(" ") + ")";
|
|
893
|
+
}
|
|
894
|
+
if (typeof val === "object") {
|
|
895
|
+
try {
|
|
896
|
+
return JSON.stringify(val);
|
|
897
|
+
}
|
|
898
|
+
catch (_a) {
|
|
899
|
+
return String(val);
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
return String(val);
|
|
903
|
+
}
|
|
904
|
+
/**
|
|
905
|
+
* Format a step into a readable string representation for logging.
|
|
906
|
+
*/
|
|
907
|
+
function formatStep(step) {
|
|
908
|
+
return formatStepValue(step, false);
|
|
909
|
+
}
|
|
910
|
+
/**
|
|
911
|
+
* Format a value as an S-expression atom (no recursion into arrays).
|
|
912
|
+
*/
|
|
913
|
+
function formatAtom(val) {
|
|
914
|
+
if (val === null || val === undefined)
|
|
915
|
+
return "null";
|
|
916
|
+
if (typeof val === "number" || typeof val === "boolean")
|
|
917
|
+
return String(val);
|
|
918
|
+
if (typeof val === "string")
|
|
919
|
+
return JSON.stringify(val);
|
|
920
|
+
if (typeof val === "object" && !Array.isArray(val)) {
|
|
921
|
+
try {
|
|
922
|
+
return JSON.stringify(val);
|
|
923
|
+
}
|
|
924
|
+
catch (_a) {
|
|
925
|
+
return "[object]";
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
return String(val);
|
|
929
|
+
}
|
|
930
|
+
/**
|
|
931
|
+
* Format a value for S-expression display. The first element (operator) of
|
|
932
|
+
* a step is rendered as a bare identifier; other string values are quoted.
|
|
933
|
+
*/
|
|
934
|
+
function formatSexpValue(val, isOperator) {
|
|
935
|
+
if (isOperator && typeof val === "string")
|
|
936
|
+
return val;
|
|
937
|
+
return formatAtom(val);
|
|
938
|
+
}
|
|
939
|
+
/**
|
|
940
|
+
* Pretty-print a single step as an S-expression with width-based wrapping.
|
|
941
|
+
*/
|
|
942
|
+
function prettyFormatStep(step, indentUnit, maxLineWidth, currentIndent) {
|
|
943
|
+
if (!Array.isArray(step))
|
|
944
|
+
return formatAtom(step);
|
|
945
|
+
if (step.length === 0)
|
|
946
|
+
return "()";
|
|
947
|
+
// Try compact first
|
|
948
|
+
var compact = "(" + step.map(function (s, i) {
|
|
949
|
+
if (Array.isArray(s))
|
|
950
|
+
return prettyFormatStep(s, indentUnit, Infinity, "");
|
|
951
|
+
return formatSexpValue(s, i === 0);
|
|
952
|
+
}).join(" ") + ")";
|
|
953
|
+
if (currentIndent.length + compact.length <= maxLineWidth)
|
|
954
|
+
return compact;
|
|
955
|
+
// Wrap: operator on first line, each arg indented
|
|
956
|
+
var op = Array.isArray(step[0]) ? prettyFormatStep(step[0], indentUnit, maxLineWidth, currentIndent) : formatSexpValue(step[0], true);
|
|
957
|
+
var args = step.slice(1);
|
|
958
|
+
if (args.length === 0)
|
|
959
|
+
return "(" + op + ")";
|
|
960
|
+
var childIndent = currentIndent + indentUnit;
|
|
961
|
+
var formattedArgs = args.map(function (a) { return prettyFormatStep(a, indentUnit, maxLineWidth, childIndent); });
|
|
962
|
+
return "(" + op + "\n" + formattedArgs.map(function (a) { return childIndent + a; }).join("\n") + ")";
|
|
963
|
+
}
|
|
964
|
+
function validOperatorNames() {
|
|
965
|
+
return Object.keys(operatorInfo).join(", ");
|
|
966
|
+
}
|
|
967
|
+
function checkArity(op, argCount, strict) {
|
|
968
|
+
if (strict === void 0) { strict = false; }
|
|
969
|
+
var info = operatorInfo[op];
|
|
970
|
+
if (!info)
|
|
971
|
+
return null;
|
|
972
|
+
if (info.min === info.max) {
|
|
973
|
+
if (argCount < info.min) {
|
|
974
|
+
return "Rule: \"" + op + "\" expects " + info.min + " argument" + (info.min !== 1 ? "s" : "") + ", got " + argCount;
|
|
975
|
+
}
|
|
976
|
+
if (strict && argCount > info.max) {
|
|
977
|
+
return "Rule: \"" + op + "\" expects " + info.min + " argument" + (info.min !== 1 ? "s" : "") + ", got " + argCount;
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
else if (info.max === Infinity) {
|
|
981
|
+
if (argCount < info.min) {
|
|
982
|
+
return "Rule: \"" + op + "\" expects at least " + info.min + " argument" + (info.min !== 1 ? "s" : "") + ", got " + argCount;
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
else {
|
|
986
|
+
if (argCount < info.min) {
|
|
987
|
+
return "Rule: \"" + op + "\" expects " + info.min + "-" + info.max + " arguments, got " + argCount;
|
|
988
|
+
}
|
|
989
|
+
if (strict && argCount > info.max) {
|
|
990
|
+
return "Rule: \"" + op + "\" expects " + info.min + "-" + info.max + " arguments, got " + argCount;
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
return null;
|
|
994
|
+
}
|
|
844
995
|
/**
|
|
845
996
|
* The `Rule` class is an experimental interface for adding behavior to {@linkcode Agent}s. A `Rule` object may be used in place of a `tick` function to be added as `Agent` behavior using `agent.set('tick', tickRule)`. As a trivial example, consider the following `Rule`, which increments the `Agent`'s `x` value with every time step:
|
|
846
997
|
*
|
|
@@ -895,6 +1046,7 @@ var Rule = /** @class */ (function () {
|
|
|
895
1046
|
* |`"agent"`|`0`|No arguments; returns the `Agent`|
|
|
896
1047
|
* |`"environment"`|`0`|No arguments, returns the `Environment`|
|
|
897
1048
|
* |`"vector"`|`any`|Creates an n-dimensional {@linkcode Vector} from the supplied arguments|
|
|
1049
|
+
* |`"log"`|`any`|Logs expression(s) and returns the last evaluated value|
|
|
898
1050
|
*/
|
|
899
1051
|
function Rule(environment, steps) {
|
|
900
1052
|
var _this = this;
|
|
@@ -903,10 +1055,12 @@ var Rule = /** @class */ (function () {
|
|
|
903
1055
|
/** @hidden */
|
|
904
1056
|
this.locals = {};
|
|
905
1057
|
/**
|
|
906
|
-
*
|
|
907
|
-
* @since 0.3.0
|
|
908
|
-
* @hidden
|
|
1058
|
+
* When true, evaluation traces are logged and captured.
|
|
909
1059
|
*/
|
|
1060
|
+
this.trace = false;
|
|
1061
|
+
this.traceLog = [];
|
|
1062
|
+
/** @hidden */
|
|
1063
|
+
this._traceDepth = 0;
|
|
910
1064
|
this.evaluate = function (agent, step) {
|
|
911
1065
|
var first = step && step.length > 0 ? step[0] : null;
|
|
912
1066
|
if (first === undefined || first === null)
|
|
@@ -917,30 +1071,79 @@ var Rule = /** @class */ (function () {
|
|
|
917
1071
|
return _this.evaluate(agent, step.slice(1));
|
|
918
1072
|
return innerStep;
|
|
919
1073
|
}
|
|
920
|
-
if
|
|
921
|
-
|
|
922
|
-
|
|
1074
|
+
// Trace: check if this is a known operator call to trace
|
|
1075
|
+
var shouldTrace = _this.trace && typeof first === "string" && first in Operators;
|
|
1076
|
+
if (shouldTrace)
|
|
1077
|
+
_this._traceDepth++;
|
|
1078
|
+
var result = _this._evaluateOp(agent, step, first);
|
|
1079
|
+
if (shouldTrace) {
|
|
1080
|
+
_this._traceLogEntry(step, result);
|
|
1081
|
+
_this._traceDepth--;
|
|
1082
|
+
}
|
|
1083
|
+
return result;
|
|
1084
|
+
};
|
|
1085
|
+
/** @hidden */
|
|
1086
|
+
this._evaluateOp = function (agent, step, first) {
|
|
1087
|
+
if (first === Operators.log) {
|
|
1088
|
+
var args = step.slice(1);
|
|
1089
|
+
if (args.length === 0) {
|
|
1090
|
+
console.warn("Rule: \"log\" expects at least 1 argument, got 0");
|
|
1091
|
+
return null;
|
|
1092
|
+
}
|
|
1093
|
+
var lastValue = null;
|
|
1094
|
+
for (var i = 0; i < args.length; i++) {
|
|
1095
|
+
var expr = args[i];
|
|
1096
|
+
var evaluated = _this.evaluate(agent, [expr]);
|
|
1097
|
+
console.log("Rule log: " + formatStep(expr) + " \u2192 " + formatStep(evaluated));
|
|
1098
|
+
lastValue = evaluated;
|
|
1099
|
+
}
|
|
1100
|
+
return lastValue;
|
|
923
1101
|
}
|
|
924
1102
|
if (!(first in Operators) && step.length > 1) {
|
|
1103
|
+
if (typeof first === "string") {
|
|
1104
|
+
console.warn("Rule: unknown operator \"" + first + "\". Valid operators: " + validOperatorNames());
|
|
1105
|
+
}
|
|
925
1106
|
return step;
|
|
926
1107
|
}
|
|
1108
|
+
var argCount = step.length - 1;
|
|
1109
|
+
// Arity check at runtime for known operators
|
|
1110
|
+
if (typeof first === "string" && first in Operators) {
|
|
1111
|
+
var arityMsg = checkArity(first, argCount);
|
|
1112
|
+
if (arityMsg) {
|
|
1113
|
+
console.warn(arityMsg);
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
927
1116
|
var a = step.length > 1 ? [step[1]] : undefined;
|
|
928
1117
|
var b = step.length > 2 ? [step[2]] : undefined;
|
|
929
1118
|
var c = step.length > 3 ? [step[3]] : undefined;
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
1119
|
+
// Arithmetic operators with type checking
|
|
1120
|
+
if (ARITHMETIC_OPS.has(first)) {
|
|
1121
|
+
var va = _this.evaluate(agent, a);
|
|
1122
|
+
var vb = _this.evaluate(agent, b);
|
|
1123
|
+
if (typeof va !== "number" || typeof vb !== "number") {
|
|
1124
|
+
console.warn("Rule: \"" + first + "\" expects numeric arguments, got " + typeof va + " and " + typeof vb);
|
|
1125
|
+
}
|
|
1126
|
+
if (first === Operators.add)
|
|
1127
|
+
return add(va, vb);
|
|
1128
|
+
if (first === Operators.subtract)
|
|
1129
|
+
return subtract(va, vb);
|
|
1130
|
+
if (first === Operators.multiply)
|
|
1131
|
+
return multiply(va, vb);
|
|
1132
|
+
if (first === Operators.divide)
|
|
1133
|
+
return divide(va, vb);
|
|
1134
|
+
if (first === Operators.mod)
|
|
1135
|
+
return mod(va, vb);
|
|
1136
|
+
if (first === Operators.power)
|
|
1137
|
+
return power(va, vb);
|
|
1138
|
+
}
|
|
1139
|
+
if (first === Operators.get) {
|
|
1140
|
+
var keyName = _this.evaluate(agent, a);
|
|
1141
|
+
var result = get(agent, keyName);
|
|
1142
|
+
if (result === null || result === undefined) {
|
|
1143
|
+
console.warn("Rule: \"get\" returned null for key \"" + keyName + "\" \u2014 key may not exist on agent");
|
|
1144
|
+
}
|
|
1145
|
+
return result;
|
|
1146
|
+
}
|
|
944
1147
|
if (first === Operators.set)
|
|
945
1148
|
return set(agent, _this.evaluate(agent, a), _this.evaluate(agent, b));
|
|
946
1149
|
if (first === Operators.enqueue) {
|
|
@@ -1028,13 +1231,151 @@ var Rule = /** @class */ (function () {
|
|
|
1028
1231
|
this.environment = environment;
|
|
1029
1232
|
this.steps = steps;
|
|
1030
1233
|
}
|
|
1234
|
+
/**
|
|
1235
|
+
* Format arbitrary steps as pretty-printed S-expressions.
|
|
1236
|
+
*/
|
|
1237
|
+
Rule.formatSteps = function (steps, options) {
|
|
1238
|
+
var indent = (options && options.indent) || " ";
|
|
1239
|
+
var maxLineWidth = (options && options.maxLineWidth) || 60;
|
|
1240
|
+
// Check if it's multi-step (array of arrays)
|
|
1241
|
+
var isMultiStep = steps.length > 0 && Array.isArray(steps[0]);
|
|
1242
|
+
if (isMultiStep) {
|
|
1243
|
+
return steps.map(function (s) { return prettyFormatStep(s, indent, maxLineWidth, ""); }).join("\n\n");
|
|
1244
|
+
}
|
|
1245
|
+
return prettyFormatStep(steps, indent, maxLineWidth, "");
|
|
1246
|
+
};
|
|
1247
|
+
/**
|
|
1248
|
+
* Pretty-print the rule's step tree as S-expressions.
|
|
1249
|
+
*/
|
|
1250
|
+
Rule.prototype.format = function (options) {
|
|
1251
|
+
return Rule.formatSteps(this.steps, options);
|
|
1252
|
+
};
|
|
1253
|
+
/**
|
|
1254
|
+
* Returns a formatted S-expression representation of the rule.
|
|
1255
|
+
*/
|
|
1256
|
+
Rule.prototype.toString = function () {
|
|
1257
|
+
return this.format();
|
|
1258
|
+
};
|
|
1259
|
+
/**
|
|
1260
|
+
* Validate the rule's step tree and return an array of diagnostics.
|
|
1261
|
+
* Does not throw — returns diagnostics for inspection.
|
|
1262
|
+
* @since 0.6.0
|
|
1263
|
+
*/
|
|
1264
|
+
Rule.prototype.validate = function () {
|
|
1265
|
+
var diagnostics = [];
|
|
1266
|
+
if (!this.steps || (Array.isArray(this.steps) && this.steps.length === 0)) {
|
|
1267
|
+
diagnostics.push({
|
|
1268
|
+
path: "root",
|
|
1269
|
+
level: "warning",
|
|
1270
|
+
message: "Empty steps array"
|
|
1271
|
+
});
|
|
1272
|
+
return diagnostics;
|
|
1273
|
+
}
|
|
1274
|
+
// Check if steps is a single step (first element is a string operator)
|
|
1275
|
+
// or an array of steps (first element is an array)
|
|
1276
|
+
var isMultiStep = Array.isArray(this.steps[0]);
|
|
1277
|
+
if (isMultiStep) {
|
|
1278
|
+
// Multiple top-level steps
|
|
1279
|
+
for (var i = 0; i < this.steps.length; i++) {
|
|
1280
|
+
var step = this.steps[i];
|
|
1281
|
+
if (!Array.isArray(step)) {
|
|
1282
|
+
diagnostics.push({
|
|
1283
|
+
path: "[" + i + "]",
|
|
1284
|
+
level: "warning",
|
|
1285
|
+
message: "Step is a bare value (" + typeof step + ": " + JSON.stringify(step) + ") instead of an array"
|
|
1286
|
+
});
|
|
1287
|
+
}
|
|
1288
|
+
else {
|
|
1289
|
+
this._validateStep(step, "[" + i + "]", diagnostics);
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
}
|
|
1293
|
+
else {
|
|
1294
|
+
// Single step (the steps array IS the step)
|
|
1295
|
+
this._validateStep(this.steps, "root", diagnostics);
|
|
1296
|
+
}
|
|
1297
|
+
return diagnostics;
|
|
1298
|
+
};
|
|
1299
|
+
/** @hidden */
|
|
1300
|
+
Rule.prototype._validateStep = function (step, path, diagnostics) {
|
|
1301
|
+
if (step.length === 0) {
|
|
1302
|
+
diagnostics.push({
|
|
1303
|
+
path: path,
|
|
1304
|
+
level: "warning",
|
|
1305
|
+
message: "Empty step array"
|
|
1306
|
+
});
|
|
1307
|
+
return;
|
|
1308
|
+
}
|
|
1309
|
+
var first = step[0];
|
|
1310
|
+
// If first element is an array, it's a nested step sequence
|
|
1311
|
+
if (Array.isArray(first)) {
|
|
1312
|
+
this._validateStep(first, path + "[0]", diagnostics);
|
|
1313
|
+
for (var i = 1; i < step.length; i++) {
|
|
1314
|
+
if (Array.isArray(step[i])) {
|
|
1315
|
+
this._validateStep(step[i], path + ("[" + i + "]"), diagnostics);
|
|
1316
|
+
}
|
|
1317
|
+
}
|
|
1318
|
+
return;
|
|
1319
|
+
}
|
|
1320
|
+
if (typeof first === "string") {
|
|
1321
|
+
var argCount = step.length - 1;
|
|
1322
|
+
// Check if operator is known
|
|
1323
|
+
if (!(first in Operators)) {
|
|
1324
|
+
if (step.length > 1) {
|
|
1325
|
+
diagnostics.push({
|
|
1326
|
+
path: path,
|
|
1327
|
+
level: "error",
|
|
1328
|
+
message: "Unknown operator \"" + first + "\". Valid operators: " + validOperatorNames()
|
|
1329
|
+
});
|
|
1330
|
+
}
|
|
1331
|
+
return;
|
|
1332
|
+
}
|
|
1333
|
+
// Check arity (strict: also warn on too many args)
|
|
1334
|
+
var arityMsg = checkArity(first, argCount, true);
|
|
1335
|
+
if (arityMsg) {
|
|
1336
|
+
diagnostics.push({
|
|
1337
|
+
path: path,
|
|
1338
|
+
level: "error",
|
|
1339
|
+
message: arityMsg
|
|
1340
|
+
});
|
|
1341
|
+
}
|
|
1342
|
+
// Recurse into sub-steps
|
|
1343
|
+
for (var i = 1; i < step.length; i++) {
|
|
1344
|
+
if (Array.isArray(step[i])) {
|
|
1345
|
+
this._validateStep(step[i], path + ("[" + i + "]"), diagnostics);
|
|
1346
|
+
}
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1349
|
+
};
|
|
1350
|
+
/**
|
|
1351
|
+
* interpret single array step
|
|
1352
|
+
* @since 0.3.0
|
|
1353
|
+
* @hidden
|
|
1354
|
+
*/
|
|
1355
|
+
/** @hidden */
|
|
1356
|
+
Rule.prototype._traceLogEntry = function (step, result) {
|
|
1357
|
+
var indent = " ".repeat(this._traceDepth - 1);
|
|
1358
|
+
var formatted = formatStep(step);
|
|
1359
|
+
var resultFormatted = formatStep(result);
|
|
1360
|
+
var line = "Rule trace: " + indent + formatted + " \u2192 " + resultFormatted;
|
|
1361
|
+
console.log(line);
|
|
1362
|
+
this.traceLog.push(line);
|
|
1363
|
+
};
|
|
1031
1364
|
/**
|
|
1032
1365
|
* @since 0.3.0
|
|
1033
1366
|
* @hidden
|
|
1034
1367
|
*/
|
|
1035
1368
|
Rule.prototype.call = function (agent) {
|
|
1369
|
+
if (this.trace) {
|
|
1370
|
+
this.traceLog = [];
|
|
1371
|
+
this._traceDepth = 0;
|
|
1372
|
+
}
|
|
1036
1373
|
return this.evaluate(agent, this.steps);
|
|
1037
1374
|
};
|
|
1375
|
+
/**
|
|
1376
|
+
* Static operator info mapping operator names to their expected argument counts.
|
|
1377
|
+
*/
|
|
1378
|
+
Rule.operatorInfo = operatorInfo;
|
|
1038
1379
|
return Rule;
|
|
1039
1380
|
}());
|
|
1040
1381
|
|
|
@@ -5526,6 +5867,6 @@ var Heatmap = /** @class */ (function (_super) {
|
|
|
5526
5867
|
/**
|
|
5527
5868
|
* The current version of the Flocc library.
|
|
5528
5869
|
*/
|
|
5529
|
-
var version = "0.5.
|
|
5870
|
+
var version = "0.5.23";
|
|
5530
5871
|
|
|
5531
5872
|
export { ASCIIRenderer, Agent, CanvasRenderer, Colors, Environment, GridEnvironment, Heatmap, Histogram, KDTree, LineChartRenderer, Network, NumArray, Rule, TableRenderer, Terrain, version as VERSION, Vector, utils };
|
package/dist/flocc.js
CHANGED
|
@@ -823,7 +823,37 @@
|
|
|
823
823
|
Operators["agent"] = "agent";
|
|
824
824
|
Operators["environment"] = "environment";
|
|
825
825
|
Operators["vector"] = "vector";
|
|
826
|
+
Operators["log"] = "log";
|
|
826
827
|
})(Operators || (Operators = {}));
|
|
828
|
+
var operatorInfo = {
|
|
829
|
+
add: { min: 2, max: 2 },
|
|
830
|
+
subtract: { min: 2, max: 2 },
|
|
831
|
+
multiply: { min: 2, max: 2 },
|
|
832
|
+
divide: { min: 2, max: 2 },
|
|
833
|
+
mod: { min: 2, max: 2 },
|
|
834
|
+
power: { min: 2, max: 2 },
|
|
835
|
+
get: { min: 1, max: 1 },
|
|
836
|
+
set: { min: 2, max: 2 },
|
|
837
|
+
enqueue: { min: 2, max: 2 },
|
|
838
|
+
local: { min: 1, max: 2 },
|
|
839
|
+
if: { min: 2, max: 3 },
|
|
840
|
+
and: { min: 2, max: 2 },
|
|
841
|
+
or: { min: 2, max: 2 },
|
|
842
|
+
gt: { min: 2, max: 2 },
|
|
843
|
+
gte: { min: 2, max: 2 },
|
|
844
|
+
lt: { min: 2, max: 2 },
|
|
845
|
+
lte: { min: 2, max: 2 },
|
|
846
|
+
eq: { min: 2, max: 2 },
|
|
847
|
+
map: { min: 2, max: 2 },
|
|
848
|
+
filter: { min: 2, max: 2 },
|
|
849
|
+
key: { min: 2, max: 2 },
|
|
850
|
+
method: { min: 2, max: Infinity },
|
|
851
|
+
agent: { min: 0, max: 0 },
|
|
852
|
+
environment: { min: 0, max: 0 },
|
|
853
|
+
vector: { min: 1, max: Infinity },
|
|
854
|
+
log: { min: 1, max: Infinity }
|
|
855
|
+
};
|
|
856
|
+
var ARITHMETIC_OPS = new Set(["add", "subtract", "multiply", "divide", "mod", "power"]);
|
|
827
857
|
var add = function (a, b) { return a + b; };
|
|
828
858
|
var subtract = function (a, b) { return a - b; };
|
|
829
859
|
var multiply = function (a, b) { return a * b; };
|
|
@@ -847,6 +877,127 @@
|
|
|
847
877
|
return null;
|
|
848
878
|
return obj[name].apply(obj, args);
|
|
849
879
|
};
|
|
880
|
+
/**
|
|
881
|
+
* Format a single value for step display. If isFirst is true and the value is
|
|
882
|
+
* a known operator string, render it bare (unquoted).
|
|
883
|
+
*/
|
|
884
|
+
function formatStepValue(val, isFirst) {
|
|
885
|
+
if (val === null || val === undefined)
|
|
886
|
+
return String(val);
|
|
887
|
+
if (typeof val === "number" || typeof val === "boolean")
|
|
888
|
+
return String(val);
|
|
889
|
+
if (typeof val === "string") {
|
|
890
|
+
if (isFirst && val in Operators)
|
|
891
|
+
return val;
|
|
892
|
+
return JSON.stringify(val);
|
|
893
|
+
}
|
|
894
|
+
if (val instanceof Array) {
|
|
895
|
+
if (val.length === 0)
|
|
896
|
+
return "[]";
|
|
897
|
+
var parts = val.map(function (s, i) { return formatStepValue(s, i === 0); });
|
|
898
|
+
return "(" + parts.join(" ") + ")";
|
|
899
|
+
}
|
|
900
|
+
if (typeof val === "object") {
|
|
901
|
+
try {
|
|
902
|
+
return JSON.stringify(val);
|
|
903
|
+
}
|
|
904
|
+
catch (_a) {
|
|
905
|
+
return String(val);
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
return String(val);
|
|
909
|
+
}
|
|
910
|
+
/**
|
|
911
|
+
* Format a step into a readable string representation for logging.
|
|
912
|
+
*/
|
|
913
|
+
function formatStep(step) {
|
|
914
|
+
return formatStepValue(step, false);
|
|
915
|
+
}
|
|
916
|
+
/**
|
|
917
|
+
* Format a value as an S-expression atom (no recursion into arrays).
|
|
918
|
+
*/
|
|
919
|
+
function formatAtom(val) {
|
|
920
|
+
if (val === null || val === undefined)
|
|
921
|
+
return "null";
|
|
922
|
+
if (typeof val === "number" || typeof val === "boolean")
|
|
923
|
+
return String(val);
|
|
924
|
+
if (typeof val === "string")
|
|
925
|
+
return JSON.stringify(val);
|
|
926
|
+
if (typeof val === "object" && !Array.isArray(val)) {
|
|
927
|
+
try {
|
|
928
|
+
return JSON.stringify(val);
|
|
929
|
+
}
|
|
930
|
+
catch (_a) {
|
|
931
|
+
return "[object]";
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
return String(val);
|
|
935
|
+
}
|
|
936
|
+
/**
|
|
937
|
+
* Format a value for S-expression display. The first element (operator) of
|
|
938
|
+
* a step is rendered as a bare identifier; other string values are quoted.
|
|
939
|
+
*/
|
|
940
|
+
function formatSexpValue(val, isOperator) {
|
|
941
|
+
if (isOperator && typeof val === "string")
|
|
942
|
+
return val;
|
|
943
|
+
return formatAtom(val);
|
|
944
|
+
}
|
|
945
|
+
/**
|
|
946
|
+
* Pretty-print a single step as an S-expression with width-based wrapping.
|
|
947
|
+
*/
|
|
948
|
+
function prettyFormatStep(step, indentUnit, maxLineWidth, currentIndent) {
|
|
949
|
+
if (!Array.isArray(step))
|
|
950
|
+
return formatAtom(step);
|
|
951
|
+
if (step.length === 0)
|
|
952
|
+
return "()";
|
|
953
|
+
// Try compact first
|
|
954
|
+
var compact = "(" + step.map(function (s, i) {
|
|
955
|
+
if (Array.isArray(s))
|
|
956
|
+
return prettyFormatStep(s, indentUnit, Infinity, "");
|
|
957
|
+
return formatSexpValue(s, i === 0);
|
|
958
|
+
}).join(" ") + ")";
|
|
959
|
+
if (currentIndent.length + compact.length <= maxLineWidth)
|
|
960
|
+
return compact;
|
|
961
|
+
// Wrap: operator on first line, each arg indented
|
|
962
|
+
var op = Array.isArray(step[0]) ? prettyFormatStep(step[0], indentUnit, maxLineWidth, currentIndent) : formatSexpValue(step[0], true);
|
|
963
|
+
var args = step.slice(1);
|
|
964
|
+
if (args.length === 0)
|
|
965
|
+
return "(" + op + ")";
|
|
966
|
+
var childIndent = currentIndent + indentUnit;
|
|
967
|
+
var formattedArgs = args.map(function (a) { return prettyFormatStep(a, indentUnit, maxLineWidth, childIndent); });
|
|
968
|
+
return "(" + op + "\n" + formattedArgs.map(function (a) { return childIndent + a; }).join("\n") + ")";
|
|
969
|
+
}
|
|
970
|
+
function validOperatorNames() {
|
|
971
|
+
return Object.keys(operatorInfo).join(", ");
|
|
972
|
+
}
|
|
973
|
+
function checkArity(op, argCount, strict) {
|
|
974
|
+
if (strict === void 0) { strict = false; }
|
|
975
|
+
var info = operatorInfo[op];
|
|
976
|
+
if (!info)
|
|
977
|
+
return null;
|
|
978
|
+
if (info.min === info.max) {
|
|
979
|
+
if (argCount < info.min) {
|
|
980
|
+
return "Rule: \"" + op + "\" expects " + info.min + " argument" + (info.min !== 1 ? "s" : "") + ", got " + argCount;
|
|
981
|
+
}
|
|
982
|
+
if (strict && argCount > info.max) {
|
|
983
|
+
return "Rule: \"" + op + "\" expects " + info.min + " argument" + (info.min !== 1 ? "s" : "") + ", got " + argCount;
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
else if (info.max === Infinity) {
|
|
987
|
+
if (argCount < info.min) {
|
|
988
|
+
return "Rule: \"" + op + "\" expects at least " + info.min + " argument" + (info.min !== 1 ? "s" : "") + ", got " + argCount;
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
else {
|
|
992
|
+
if (argCount < info.min) {
|
|
993
|
+
return "Rule: \"" + op + "\" expects " + info.min + "-" + info.max + " arguments, got " + argCount;
|
|
994
|
+
}
|
|
995
|
+
if (strict && argCount > info.max) {
|
|
996
|
+
return "Rule: \"" + op + "\" expects " + info.min + "-" + info.max + " arguments, got " + argCount;
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
return null;
|
|
1000
|
+
}
|
|
850
1001
|
/**
|
|
851
1002
|
* The `Rule` class is an experimental interface for adding behavior to {@linkcode Agent}s. A `Rule` object may be used in place of a `tick` function to be added as `Agent` behavior using `agent.set('tick', tickRule)`. As a trivial example, consider the following `Rule`, which increments the `Agent`'s `x` value with every time step:
|
|
852
1003
|
*
|
|
@@ -901,6 +1052,7 @@
|
|
|
901
1052
|
* |`"agent"`|`0`|No arguments; returns the `Agent`|
|
|
902
1053
|
* |`"environment"`|`0`|No arguments, returns the `Environment`|
|
|
903
1054
|
* |`"vector"`|`any`|Creates an n-dimensional {@linkcode Vector} from the supplied arguments|
|
|
1055
|
+
* |`"log"`|`any`|Logs expression(s) and returns the last evaluated value|
|
|
904
1056
|
*/
|
|
905
1057
|
function Rule(environment, steps) {
|
|
906
1058
|
var _this = this;
|
|
@@ -909,10 +1061,12 @@
|
|
|
909
1061
|
/** @hidden */
|
|
910
1062
|
this.locals = {};
|
|
911
1063
|
/**
|
|
912
|
-
*
|
|
913
|
-
* @since 0.3.0
|
|
914
|
-
* @hidden
|
|
1064
|
+
* When true, evaluation traces are logged and captured.
|
|
915
1065
|
*/
|
|
1066
|
+
this.trace = false;
|
|
1067
|
+
this.traceLog = [];
|
|
1068
|
+
/** @hidden */
|
|
1069
|
+
this._traceDepth = 0;
|
|
916
1070
|
this.evaluate = function (agent, step) {
|
|
917
1071
|
var first = step && step.length > 0 ? step[0] : null;
|
|
918
1072
|
if (first === undefined || first === null)
|
|
@@ -923,30 +1077,79 @@
|
|
|
923
1077
|
return _this.evaluate(agent, step.slice(1));
|
|
924
1078
|
return innerStep;
|
|
925
1079
|
}
|
|
926
|
-
if
|
|
927
|
-
|
|
928
|
-
|
|
1080
|
+
// Trace: check if this is a known operator call to trace
|
|
1081
|
+
var shouldTrace = _this.trace && typeof first === "string" && first in Operators;
|
|
1082
|
+
if (shouldTrace)
|
|
1083
|
+
_this._traceDepth++;
|
|
1084
|
+
var result = _this._evaluateOp(agent, step, first);
|
|
1085
|
+
if (shouldTrace) {
|
|
1086
|
+
_this._traceLogEntry(step, result);
|
|
1087
|
+
_this._traceDepth--;
|
|
1088
|
+
}
|
|
1089
|
+
return result;
|
|
1090
|
+
};
|
|
1091
|
+
/** @hidden */
|
|
1092
|
+
this._evaluateOp = function (agent, step, first) {
|
|
1093
|
+
if (first === Operators.log) {
|
|
1094
|
+
var args = step.slice(1);
|
|
1095
|
+
if (args.length === 0) {
|
|
1096
|
+
console.warn("Rule: \"log\" expects at least 1 argument, got 0");
|
|
1097
|
+
return null;
|
|
1098
|
+
}
|
|
1099
|
+
var lastValue = null;
|
|
1100
|
+
for (var i = 0; i < args.length; i++) {
|
|
1101
|
+
var expr = args[i];
|
|
1102
|
+
var evaluated = _this.evaluate(agent, [expr]);
|
|
1103
|
+
console.log("Rule log: " + formatStep(expr) + " \u2192 " + formatStep(evaluated));
|
|
1104
|
+
lastValue = evaluated;
|
|
1105
|
+
}
|
|
1106
|
+
return lastValue;
|
|
929
1107
|
}
|
|
930
1108
|
if (!(first in Operators) && step.length > 1) {
|
|
1109
|
+
if (typeof first === "string") {
|
|
1110
|
+
console.warn("Rule: unknown operator \"" + first + "\". Valid operators: " + validOperatorNames());
|
|
1111
|
+
}
|
|
931
1112
|
return step;
|
|
932
1113
|
}
|
|
1114
|
+
var argCount = step.length - 1;
|
|
1115
|
+
// Arity check at runtime for known operators
|
|
1116
|
+
if (typeof first === "string" && first in Operators) {
|
|
1117
|
+
var arityMsg = checkArity(first, argCount);
|
|
1118
|
+
if (arityMsg) {
|
|
1119
|
+
console.warn(arityMsg);
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
933
1122
|
var a = step.length > 1 ? [step[1]] : undefined;
|
|
934
1123
|
var b = step.length > 2 ? [step[2]] : undefined;
|
|
935
1124
|
var c = step.length > 3 ? [step[3]] : undefined;
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
1125
|
+
// Arithmetic operators with type checking
|
|
1126
|
+
if (ARITHMETIC_OPS.has(first)) {
|
|
1127
|
+
var va = _this.evaluate(agent, a);
|
|
1128
|
+
var vb = _this.evaluate(agent, b);
|
|
1129
|
+
if (typeof va !== "number" || typeof vb !== "number") {
|
|
1130
|
+
console.warn("Rule: \"" + first + "\" expects numeric arguments, got " + typeof va + " and " + typeof vb);
|
|
1131
|
+
}
|
|
1132
|
+
if (first === Operators.add)
|
|
1133
|
+
return add(va, vb);
|
|
1134
|
+
if (first === Operators.subtract)
|
|
1135
|
+
return subtract(va, vb);
|
|
1136
|
+
if (first === Operators.multiply)
|
|
1137
|
+
return multiply(va, vb);
|
|
1138
|
+
if (first === Operators.divide)
|
|
1139
|
+
return divide(va, vb);
|
|
1140
|
+
if (first === Operators.mod)
|
|
1141
|
+
return mod(va, vb);
|
|
1142
|
+
if (first === Operators.power)
|
|
1143
|
+
return power(va, vb);
|
|
1144
|
+
}
|
|
1145
|
+
if (first === Operators.get) {
|
|
1146
|
+
var keyName = _this.evaluate(agent, a);
|
|
1147
|
+
var result = get(agent, keyName);
|
|
1148
|
+
if (result === null || result === undefined) {
|
|
1149
|
+
console.warn("Rule: \"get\" returned null for key \"" + keyName + "\" \u2014 key may not exist on agent");
|
|
1150
|
+
}
|
|
1151
|
+
return result;
|
|
1152
|
+
}
|
|
950
1153
|
if (first === Operators.set)
|
|
951
1154
|
return set(agent, _this.evaluate(agent, a), _this.evaluate(agent, b));
|
|
952
1155
|
if (first === Operators.enqueue) {
|
|
@@ -1034,13 +1237,151 @@
|
|
|
1034
1237
|
this.environment = environment;
|
|
1035
1238
|
this.steps = steps;
|
|
1036
1239
|
}
|
|
1240
|
+
/**
|
|
1241
|
+
* Format arbitrary steps as pretty-printed S-expressions.
|
|
1242
|
+
*/
|
|
1243
|
+
Rule.formatSteps = function (steps, options) {
|
|
1244
|
+
var indent = (options && options.indent) || " ";
|
|
1245
|
+
var maxLineWidth = (options && options.maxLineWidth) || 60;
|
|
1246
|
+
// Check if it's multi-step (array of arrays)
|
|
1247
|
+
var isMultiStep = steps.length > 0 && Array.isArray(steps[0]);
|
|
1248
|
+
if (isMultiStep) {
|
|
1249
|
+
return steps.map(function (s) { return prettyFormatStep(s, indent, maxLineWidth, ""); }).join("\n\n");
|
|
1250
|
+
}
|
|
1251
|
+
return prettyFormatStep(steps, indent, maxLineWidth, "");
|
|
1252
|
+
};
|
|
1253
|
+
/**
|
|
1254
|
+
* Pretty-print the rule's step tree as S-expressions.
|
|
1255
|
+
*/
|
|
1256
|
+
Rule.prototype.format = function (options) {
|
|
1257
|
+
return Rule.formatSteps(this.steps, options);
|
|
1258
|
+
};
|
|
1259
|
+
/**
|
|
1260
|
+
* Returns a formatted S-expression representation of the rule.
|
|
1261
|
+
*/
|
|
1262
|
+
Rule.prototype.toString = function () {
|
|
1263
|
+
return this.format();
|
|
1264
|
+
};
|
|
1265
|
+
/**
|
|
1266
|
+
* Validate the rule's step tree and return an array of diagnostics.
|
|
1267
|
+
* Does not throw — returns diagnostics for inspection.
|
|
1268
|
+
* @since 0.6.0
|
|
1269
|
+
*/
|
|
1270
|
+
Rule.prototype.validate = function () {
|
|
1271
|
+
var diagnostics = [];
|
|
1272
|
+
if (!this.steps || (Array.isArray(this.steps) && this.steps.length === 0)) {
|
|
1273
|
+
diagnostics.push({
|
|
1274
|
+
path: "root",
|
|
1275
|
+
level: "warning",
|
|
1276
|
+
message: "Empty steps array"
|
|
1277
|
+
});
|
|
1278
|
+
return diagnostics;
|
|
1279
|
+
}
|
|
1280
|
+
// Check if steps is a single step (first element is a string operator)
|
|
1281
|
+
// or an array of steps (first element is an array)
|
|
1282
|
+
var isMultiStep = Array.isArray(this.steps[0]);
|
|
1283
|
+
if (isMultiStep) {
|
|
1284
|
+
// Multiple top-level steps
|
|
1285
|
+
for (var i = 0; i < this.steps.length; i++) {
|
|
1286
|
+
var step = this.steps[i];
|
|
1287
|
+
if (!Array.isArray(step)) {
|
|
1288
|
+
diagnostics.push({
|
|
1289
|
+
path: "[" + i + "]",
|
|
1290
|
+
level: "warning",
|
|
1291
|
+
message: "Step is a bare value (" + typeof step + ": " + JSON.stringify(step) + ") instead of an array"
|
|
1292
|
+
});
|
|
1293
|
+
}
|
|
1294
|
+
else {
|
|
1295
|
+
this._validateStep(step, "[" + i + "]", diagnostics);
|
|
1296
|
+
}
|
|
1297
|
+
}
|
|
1298
|
+
}
|
|
1299
|
+
else {
|
|
1300
|
+
// Single step (the steps array IS the step)
|
|
1301
|
+
this._validateStep(this.steps, "root", diagnostics);
|
|
1302
|
+
}
|
|
1303
|
+
return diagnostics;
|
|
1304
|
+
};
|
|
1305
|
+
/** @hidden */
|
|
1306
|
+
Rule.prototype._validateStep = function (step, path, diagnostics) {
|
|
1307
|
+
if (step.length === 0) {
|
|
1308
|
+
diagnostics.push({
|
|
1309
|
+
path: path,
|
|
1310
|
+
level: "warning",
|
|
1311
|
+
message: "Empty step array"
|
|
1312
|
+
});
|
|
1313
|
+
return;
|
|
1314
|
+
}
|
|
1315
|
+
var first = step[0];
|
|
1316
|
+
// If first element is an array, it's a nested step sequence
|
|
1317
|
+
if (Array.isArray(first)) {
|
|
1318
|
+
this._validateStep(first, path + "[0]", diagnostics);
|
|
1319
|
+
for (var i = 1; i < step.length; i++) {
|
|
1320
|
+
if (Array.isArray(step[i])) {
|
|
1321
|
+
this._validateStep(step[i], path + ("[" + i + "]"), diagnostics);
|
|
1322
|
+
}
|
|
1323
|
+
}
|
|
1324
|
+
return;
|
|
1325
|
+
}
|
|
1326
|
+
if (typeof first === "string") {
|
|
1327
|
+
var argCount = step.length - 1;
|
|
1328
|
+
// Check if operator is known
|
|
1329
|
+
if (!(first in Operators)) {
|
|
1330
|
+
if (step.length > 1) {
|
|
1331
|
+
diagnostics.push({
|
|
1332
|
+
path: path,
|
|
1333
|
+
level: "error",
|
|
1334
|
+
message: "Unknown operator \"" + first + "\". Valid operators: " + validOperatorNames()
|
|
1335
|
+
});
|
|
1336
|
+
}
|
|
1337
|
+
return;
|
|
1338
|
+
}
|
|
1339
|
+
// Check arity (strict: also warn on too many args)
|
|
1340
|
+
var arityMsg = checkArity(first, argCount, true);
|
|
1341
|
+
if (arityMsg) {
|
|
1342
|
+
diagnostics.push({
|
|
1343
|
+
path: path,
|
|
1344
|
+
level: "error",
|
|
1345
|
+
message: arityMsg
|
|
1346
|
+
});
|
|
1347
|
+
}
|
|
1348
|
+
// Recurse into sub-steps
|
|
1349
|
+
for (var i = 1; i < step.length; i++) {
|
|
1350
|
+
if (Array.isArray(step[i])) {
|
|
1351
|
+
this._validateStep(step[i], path + ("[" + i + "]"), diagnostics);
|
|
1352
|
+
}
|
|
1353
|
+
}
|
|
1354
|
+
}
|
|
1355
|
+
};
|
|
1356
|
+
/**
|
|
1357
|
+
* interpret single array step
|
|
1358
|
+
* @since 0.3.0
|
|
1359
|
+
* @hidden
|
|
1360
|
+
*/
|
|
1361
|
+
/** @hidden */
|
|
1362
|
+
Rule.prototype._traceLogEntry = function (step, result) {
|
|
1363
|
+
var indent = " ".repeat(this._traceDepth - 1);
|
|
1364
|
+
var formatted = formatStep(step);
|
|
1365
|
+
var resultFormatted = formatStep(result);
|
|
1366
|
+
var line = "Rule trace: " + indent + formatted + " \u2192 " + resultFormatted;
|
|
1367
|
+
console.log(line);
|
|
1368
|
+
this.traceLog.push(line);
|
|
1369
|
+
};
|
|
1037
1370
|
/**
|
|
1038
1371
|
* @since 0.3.0
|
|
1039
1372
|
* @hidden
|
|
1040
1373
|
*/
|
|
1041
1374
|
Rule.prototype.call = function (agent) {
|
|
1375
|
+
if (this.trace) {
|
|
1376
|
+
this.traceLog = [];
|
|
1377
|
+
this._traceDepth = 0;
|
|
1378
|
+
}
|
|
1042
1379
|
return this.evaluate(agent, this.steps);
|
|
1043
1380
|
};
|
|
1381
|
+
/**
|
|
1382
|
+
* Static operator info mapping operator names to their expected argument counts.
|
|
1383
|
+
*/
|
|
1384
|
+
Rule.operatorInfo = operatorInfo;
|
|
1044
1385
|
return Rule;
|
|
1045
1386
|
}());
|
|
1046
1387
|
|
|
@@ -5532,7 +5873,7 @@
|
|
|
5532
5873
|
/**
|
|
5533
5874
|
* The current version of the Flocc library.
|
|
5534
5875
|
*/
|
|
5535
|
-
var version = "0.5.
|
|
5876
|
+
var version = "0.5.23";
|
|
5536
5877
|
|
|
5537
5878
|
exports.ASCIIRenderer = ASCIIRenderer;
|
|
5538
5879
|
exports.Agent = Agent;
|
package/dist/helpers/Rule.d.ts
CHANGED
|
@@ -3,6 +3,23 @@ import { Agent } from "../agents/Agent";
|
|
|
3
3
|
declare type StepToken = number | string | Step;
|
|
4
4
|
interface Step extends Array<StepToken> {
|
|
5
5
|
}
|
|
6
|
+
interface RuleDiagnostic {
|
|
7
|
+
path: string;
|
|
8
|
+
level: "error" | "warning";
|
|
9
|
+
message: string;
|
|
10
|
+
}
|
|
11
|
+
interface RuleFormatOptions {
|
|
12
|
+
indent?: string;
|
|
13
|
+
maxLineWidth?: number;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Operator info: maps operator names to their expected argument counts.
|
|
17
|
+
* `min` and `max` define the valid range. If `max` is Infinity, the operator is variadic.
|
|
18
|
+
*/
|
|
19
|
+
interface OperatorArity {
|
|
20
|
+
min: number;
|
|
21
|
+
max: number;
|
|
22
|
+
}
|
|
6
23
|
/**
|
|
7
24
|
* The `Rule` class is an experimental interface for adding behavior to {@linkcode Agent}s. A `Rule` object may be used in place of a `tick` function to be added as `Agent` behavior using `agent.set('tick', tickRule)`. As a trivial example, consider the following `Rule`, which increments the `Agent`'s `x` value with every time step:
|
|
8
25
|
*
|
|
@@ -36,6 +53,23 @@ declare class Rule {
|
|
|
36
53
|
locals: {
|
|
37
54
|
[key: string]: any;
|
|
38
55
|
};
|
|
56
|
+
/**
|
|
57
|
+
* When true, evaluation traces are logged and captured.
|
|
58
|
+
*/
|
|
59
|
+
trace: boolean;
|
|
60
|
+
traceLog: string[];
|
|
61
|
+
/** @hidden */
|
|
62
|
+
private _traceDepth;
|
|
63
|
+
/**
|
|
64
|
+
* Static operator info mapping operator names to their expected argument counts.
|
|
65
|
+
*/
|
|
66
|
+
static operatorInfo: {
|
|
67
|
+
[key: string]: OperatorArity;
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* Format arbitrary steps as pretty-printed S-expressions.
|
|
71
|
+
*/
|
|
72
|
+
static formatSteps(steps: any[], options?: RuleFormatOptions): string;
|
|
39
73
|
/**
|
|
40
74
|
* A single step may be as simple as `["get", "x"]`. This returns the `Agent`'s `"x"` value to the outer step that contains it. So, for example, the step `["add", 1, ["get", "x"]]`, working from the inside out, retrieves the `"x"` value and then adds `1` to it. More complex steps function similarly, always traversing to the deepest nested step, evaluating it, and 'unwrapping' until all steps have been executed.
|
|
41
75
|
*
|
|
@@ -67,18 +101,39 @@ declare class Rule {
|
|
|
67
101
|
* |`"agent"`|`0`|No arguments; returns the `Agent`|
|
|
68
102
|
* |`"environment"`|`0`|No arguments, returns the `Environment`|
|
|
69
103
|
* |`"vector"`|`any`|Creates an n-dimensional {@linkcode Vector} from the supplied arguments|
|
|
104
|
+
* |`"log"`|`any`|Logs expression(s) and returns the last evaluated value|
|
|
70
105
|
*/
|
|
71
106
|
constructor(environment: Environment, steps: Step[]);
|
|
107
|
+
/**
|
|
108
|
+
* Pretty-print the rule's step tree as S-expressions.
|
|
109
|
+
*/
|
|
110
|
+
format(options?: RuleFormatOptions): string;
|
|
111
|
+
/**
|
|
112
|
+
* Returns a formatted S-expression representation of the rule.
|
|
113
|
+
*/
|
|
114
|
+
toString(): string;
|
|
115
|
+
/**
|
|
116
|
+
* Validate the rule's step tree and return an array of diagnostics.
|
|
117
|
+
* Does not throw — returns diagnostics for inspection.
|
|
118
|
+
* @since 0.6.0
|
|
119
|
+
*/
|
|
120
|
+
validate(): RuleDiagnostic[];
|
|
121
|
+
/** @hidden */
|
|
122
|
+
private _validateStep;
|
|
72
123
|
/**
|
|
73
124
|
* interpret single array step
|
|
74
125
|
* @since 0.3.0
|
|
75
126
|
* @hidden
|
|
76
127
|
*/
|
|
128
|
+
/** @hidden */
|
|
129
|
+
private _traceLogEntry;
|
|
77
130
|
evaluate: (agent: Agent, step: any[]) => any;
|
|
131
|
+
/** @hidden */
|
|
132
|
+
private _evaluateOp;
|
|
78
133
|
/**
|
|
79
134
|
* @since 0.3.0
|
|
80
135
|
* @hidden
|
|
81
136
|
*/
|
|
82
137
|
call(agent: Agent): any;
|
|
83
138
|
}
|
|
84
|
-
export { Rule };
|
|
139
|
+
export { Rule, RuleDiagnostic, RuleFormatOptions };
|
package/dist/main.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { Agent } from "./agents/Agent";
|
|
2
2
|
export { Vector } from "./helpers/Vector";
|
|
3
3
|
export { Network } from "./helpers/Network";
|
|
4
|
-
export { Rule } from "./helpers/Rule";
|
|
4
|
+
export { Rule, RuleDiagnostic, RuleFormatOptions } from "./helpers/Rule";
|
|
5
5
|
export { KDTree } from "./helpers/KDTree";
|
|
6
6
|
export { NumArray } from "./helpers/NumArray";
|
|
7
7
|
export { Colors, Terrain } from "./helpers/Terrain";
|