PageSourceSearch

https://metaphorofitself.net/js/EveXLEvaluator.js

js metaphorofitself.net collected 2026-10-03 18:51:06 UTC 6,445 bytes, 177 lines download raw bytes

1(function (global) {
2  "use strict";
3
4  const MAX_SEARCH_STEPS = 250000;
5
6  function compare(left, operator, right) {
7    if (operator === "=") return left === right;
8    if (operator === "!=") return left !== right;
9    if (operator === "<") return left < right;
10    if (operator === "<=") return left <= right;
11    if (operator === ">") return left > right;
12    if (operator === ">=") return left >= right;
13    return false;
14  }
15
16  function invariantMatches(node, reading) {
17    if (!node || !node.TYPE) return true;
18    if (node.TYPE === "COMPARISON") {
19      return compare(String(reading[node.OPERAND1]), node.OPERATOR, String(node.OPERAND2));
20    }
21    if (node.TYPE === "BOOLEAN-OPERATION") {
22      const left = invariantMatches(node.OPERAND1, reading);
23      const right = invariantMatches(node.OPERAND2, reading);
24      return node.OPERATOR === "AND" ? left && right : left || right;
25    }
26    return false;
27  }
28
29  function allRanges(length) {
30    const ranges = [];
31    for (let end = 0; end < length; end += 1) {
32      for (let start = end; start >= 0; start -= 1) {
33        ranges.push({ start: start, end: end, duration: end - start + 1 });
34      }
35    }
36    return ranges;
37  }
38
39  function matchingRanges(declaration, stream) {
40    if (declaration.TYPE === "PURE-TIME-INTERVAL") return allRanges(stream.length);
41
42    const runs = [];
43    let start = null;
44    for (let index = 0; index <= stream.length; index += 1) {
45      const matches = index < stream.length && invariantMatches(declaration["DATA-INVARIANTS"], stream[index]);
46      if (matches && start === null) start = index;
47      if (!matches && start !== null) {
48        runs.push({ start: start, end: index - 1, duration: index - start });
49        start = null;
50      }
51    }
52
53    if (declaration.MODIFIER !== "PARTIAL") return runs;
54
55    const partials = [];
56    runs.forEach(function (run) {
57      for (let end = run.start; end <= run.end; end += 1) {
58        for (let rangeStart = end; rangeStart >= run.start; rangeStart -= 1) {
59          partials.push({ start: rangeStart, end: end, duration: end - rangeStart + 1 });
60        }
61      }
62    });
63    return partials;
64  }
65
66  function intervalRelation(node, assignments) {
67    const left = assignments[node.OPERAND1.ID];
68    const right = assignments[node.OPERAND2.ID];
69    if (!left || !right) return null;
70
71    const relation = node.OPERATORS[0];
72    if (relation === "PRECEDES") return left.end + 1 < right.start;
73    if (relation === "MEETS") return left.end + 1 === right.start;
74    if (relation === "OVERLAPS") {
75      return left.start < right.start && right.start <= left.end && left.end < right.end;
76    }
77    if (relation === "STARTS") return left.start === right.start && left.end < right.end;
78    if (relation === "CONTAINS") return left.start < right.start && left.end > right.end;
79    if (relation === "ENDS") return left.start > right.start && left.end === right.end;
80    if (relation === "EQUALS") return left.start === right.start && left.end === right.end;
81    if (relation === "DIFFERS") return left.start !== right.start || left.end !== right.end;
82    return false;
83  }
84
85  function durationValue(operand, assignments) {
86    if (operand.TYPE === "INTEGER") return Number(operand.VALUE);
87    const interval = assignments[operand.ID];
88    return interval ? interval.duration : null;
89  }
90
91  function evaluateNode(node, assignments) {
92    if (!node || !node.TYPE) return true;
93
94    if (node.TYPE === "BOOLEAN-OPERATION") {
95      const left = evaluateNode(node.OPERAND1, assignments);
96      const right = evaluateNode(node.OPERAND2, assignments);
97      if (node.OPERATOR === "AND") {
98        if (left === false || right === false) return false;
99        return left === true && right === true ? true : null;
100      }
101      if (left === true || right === true) return true;
102      return left === false && right === false ? false : null;
103    }
104
105    if (node.TYPE === "INTERVAL-RELATION") return intervalRelation(node, assignments);
106
107    if (node.TYPE === "DURATION-RELATION") {
108      const left = durationValue(node.OPERAND1, assignments);
109      const right = durationValue(node.OPERAND2, assignments);
110      if (left === null || right === null) return null;
111      return compare(left, node.OPERATOR, right);
112    }
113
114    if (node.TYPE === "NON-EXISTENCE") {
115      const nested = evaluateNode(node.STATEMENT && node.STATEMENT["INTERVAL-RELATIONS"], assignments);
116      return nested === null ? null : !nested;
117    }
118
119    return false;
120  }
121
122  function evaluate(ast, stream) {
123    const declarations = (ast && ast["INTERVAL-DECLARATIONS"]) || [];
124    if (!declarations.length || !stream.length) return { result: false, intervals: {} };
125
126    const candidates = {};
127    declarations.forEach(function (declaration) {
128      candidates[declaration.ID] = matchingRanges(declaration, stream);
129    });
130    if (declarations.some(function (declaration) { return !candidates[declaration.ID].length; })) {
131      return { result: false, intervals: {} };
132    }
133
134    const ordered = declarations.slice().sort(function (left, right) {
135      return candidates[left.ID].length - candidates[right.ID].length;
136    });
137    const assignments = {};
138    const relation = ast["INTERVAL-RELATIONS"] || {};
139    let steps = 0;
140    let solution = null;
141
142    function search(index) {
143      if (solution || steps >= MAX_SEARCH_STEPS) return;
144      steps += 1;
145
146      const status = evaluateNode(relation, assignments);
147      if (status === false) return;
148      if (index === ordered.length) {
149        if (status === true) solution = Object.assign({}, assignments);
150        return;
151      }
152
153      const declaration = ordered[index];
154      for (const candidate of candidates[declaration.ID]) {
155        assignments[declaration.ID] = candidate;
156        search(index + 1);
157        if (solution) return;
158      }
159      delete assignments[declaration.ID];
160    }
161
162    search(0);
163    if (!solution) return { result: false, intervals: {} };
164
165    const intervals = {};
166    declarations.forEach(function (declaration) {
167      const interval = solution[declaration.ID];
168      intervals[declaration.ID] = interval
169        ? { start: interval.start, duration: interval.duration }
170        : null;
171    });
172    return { result: true, intervals: intervals };
173  }
174
175  global.EveXLEvaluator = { evaluate: evaluate };
176  if (typeof module !== "undefined" && module.exports) module.exports = global.EveXLEvaluator;
177})(typeof window !== "undefined" ? window : globalThis);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.