@reidelsaltres/pureper 0.1.154 → 0.1.156

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.
@@ -1,231 +0,0 @@
1
- class PHTMLParserRule {
2
- public pattern: RegExp;
3
-
4
- // replacer can return either a string replacement OR an object with { text, end }
5
- // where `end` is a position in the source string just after the consumed range.
6
- public replacer: (parser: PHTMLParser, match: IArguments, scope?: Record<string, any>) => string | { text: string; end: number };
7
-
8
- constructor(pattern: RegExp, replacer: (parser: PHTMLParser, match: IArguments, scope?: Record<string, any>) => string | { text: string; end: number }) {
9
- this.pattern = pattern;
10
- this.replacer = replacer;
11
- }
12
- }
13
-
14
- export default class PHTMLParser {
15
- // Extracts a balanced block starting from position `start` in `content`.
16
- // Returns { block, end } where `block` is the content inside braces and `end` is the position after closing brace.
17
- private static extractBalancedBlock(content: string, start: number): { block: string; end: number } | null {
18
- if (content[start] !== '{') return null;
19
- let depth = 1;
20
- let i = start + 1;
21
- while (i < content.length && depth > 0) {
22
- if (content[i] === '{') depth++;
23
- else if (content[i] === '}') depth--;
24
- i++;
25
- }
26
- if (depth !== 0) return null;
27
- return { block: content.slice(start + 1, i - 1), end: i };
28
- }
29
-
30
- // Extracts a balanced parenthesis block starting at `start` (which must be '(')
31
- private static extractBalancedParens(content: string, start: number): { block: string; end: number } | null {
32
- if (content[start] !== '(') return null;
33
- let depth = 1;
34
- let i = start + 1;
35
- while (i < content.length && depth > 0) {
36
- if (content[i] === '(') depth++;
37
- else if (content[i] === ')') depth--;
38
- i++;
39
- }
40
- if (depth !== 0) return null;
41
- return { block: content.slice(start + 1, i - 1), end: i };
42
- }
43
-
44
- // Note: @for handling moved to a rule in `rules` so it can participate in the
45
- // ordered rules pipeline. The actual exec loop for the for-rule is implemented
46
- // inside parse() to allow balanced-brace extraction.
47
-
48
- private static rules: PHTMLParserRule[] = [
49
- // Rule for nested @for blocks. This rule's replacer computes the
50
- // replacement for one @for occurrence when given a RegExpExecArray
51
- // (it expects match.index and match.input to be present so the parser
52
- // can extract the full balanced block that follows the match).
53
- new PHTMLParserRule(/@for\s*\(([A-Za-z0-9_$]+)\s+in\s+([A-Za-z0-9_$.]+)\s*\)\s*\{/g,
54
- (parser, m, scope) => {
55
- // `m` will be a RegExpExecArray-like object when used by the
56
- // exec-based loop inside `parse()` so we can treat it accordingly.
57
- const match = m as any as RegExpExecArray;
58
- const iterVar = match[1];
59
- const iterableExpr = match[2];
60
- const offset = Number(match[match.length - 2]); // offset position
61
- const input = String(match[match.length - 1]); // source string
62
-
63
- const blockStart = offset + match[0].length - 1; // position of '{'
64
- const extracted = PHTMLParser.extractBalancedBlock(input, blockStart);
65
- if (!extracted) return match[0];
66
-
67
- const inner = extracted.block;
68
- const resolved = parser.resolveExpression(iterableExpr, scope);
69
- const arr = Array.isArray(resolved) ? resolved : [];
70
-
71
- const resultParts: string[] = [];
72
- for (const item of arr) {
73
- const fullScope = Object.assign({}, scope, { [iterVar]: item });
74
- resultParts.push(parser.parse(inner, fullScope));
75
- }
76
-
77
- // return both the replacement text and the position after the full balanced block
78
- return { text: resultParts.join('\n'), end: extracted.end };
79
- }),
80
- // Double-paren expression: @(( code )) — use exec+balanced-start extraction
81
- new PHTMLParserRule(/@\(\(/g, (parser, m, scope) => {
82
- const match = m as any as RegExpExecArray;
83
- const offset = Number(match[match.length - 2]);
84
- const input = String(match[match.length - 1]);
85
- const blockStart = offset + match[0].length - 1; // points at the inner '('
86
-
87
- // extract balanced parentheses
88
- const extracted = PHTMLParser.extractBalancedParens(input, blockStart);
89
- if (!extracted) return match[0];
90
-
91
- const code = extracted.block;
92
- // Evaluate code using parser.variables + local scope
93
- const ctx = parser.buildContext(scope);
94
- let result: any;
95
- try {
96
- // try to evaluate as expression first
97
- const fn = new Function('with(this){ return (' + code + '); }');
98
- result = fn.call(ctx);
99
- } catch (e) {
100
- try {
101
- const fn2 = new Function('with(this){ ' + code + ' }');
102
- result = fn2.call(ctx);
103
- } catch (e2) {
104
- // On error return empty string
105
- return '';
106
- }
107
- }
108
-
109
- // final end should include the outer ')' if present (for @(( ... )) constructs)
110
- let finalEnd = extracted.end;
111
- if (input[extracted.end] === ')') finalEnd = extracted.end + 1;
112
- return { text: parser.stringifyValue(result), end: finalEnd };
113
- }),
114
- // Single-paren expression: @( code ) — similar extraction, processed after double-paren rule
115
- new PHTMLParserRule(/@\(/g, (parser, m, scope) => {
116
- const match = m as any as RegExpExecArray;
117
- const offset = Number(match[match.length - 2]);
118
- const input = String(match[match.length - 1]);
119
- const blockStart = offset + match[0].length - 1; // points at '('
120
-
121
- const extracted = PHTMLParser.extractBalancedParens(input, blockStart);
122
- if (!extracted) return match[0];
123
-
124
- const code = extracted.block;
125
- const ctx = parser.buildContext(scope);
126
- let result: any;
127
- try {
128
- const fn = new Function('with(this){ return (' + code + '); }');
129
- result = fn.call(ctx);
130
- } catch (e) {
131
- try {
132
- const fn2 = new Function('with(this){ ' + code + ' }');
133
- result = fn2.call(ctx);
134
- } catch (e2) {
135
- return '';
136
- }
137
- }
138
-
139
- return { text: parser.stringifyValue(result), end: extracted.end };
140
- }),
141
- ];
142
- public variables: Record<string, unknown> = {};
143
-
144
- public addVariable(name: string, value: unknown): this {
145
- this.variables[name] = value;
146
- return this;
147
- }
148
-
149
- // Resolve a dotted expression against the parser variables and optional local scope.
150
- // Examples: "subjectChips" -> this.variables.subjectChips
151
- // "chip.Color" -> scope.chip.Color (if chip exists in scope) or this.variables.chip.Color
152
- public resolveExpression(expr: string, scope?: Record<string, any>): any {
153
- const combined = this.buildContext(scope);
154
- const parts = expr.split('.').map(p => p.trim()).filter(Boolean);
155
- let cur: any = combined;
156
- for (const part of parts) {
157
- if (cur == null) return undefined;
158
- cur = cur[part];
159
- }
160
- return cur;
161
- }
162
-
163
- // Build execution context that includes prototype methods from scope
164
- private buildContext(scope?: Record<string, any>): Record<string, any> {
165
- const ctx: Record<string, any> = Object.assign({}, this.variables);
166
- if (scope) {
167
- // Copy own properties
168
- Object.assign(ctx, scope);
169
- // Copy prototype methods (for class instances)
170
- let proto = Object.getPrototypeOf(scope);
171
- while (proto && proto !== Object.prototype) {
172
- for (const key of Object.getOwnPropertyNames(proto)) {
173
- if (key !== 'constructor' && typeof proto[key] === 'function' && !(key in ctx)) {
174
- ctx[key] = proto[key].bind(scope);
175
- }
176
- }
177
- proto = Object.getPrototypeOf(proto);
178
- }
179
- }
180
- return ctx;
181
- }
182
-
183
- private stringifyValue(val: any): string {
184
- if (val == null) return '';
185
- if (typeof val === 'string') return val;
186
- const str = String(val);
187
- return str;
188
- }
189
-
190
- public parse(content: string, scope?: Record<string, any>): string {
191
- let working = content;
192
-
193
- for (const rule of PHTMLParser.rules) {
194
- const pattern = rule.pattern;
195
- let result = '';
196
- let lastIndex = 0;
197
- let match: RegExpExecArray | null;
198
-
199
- // reset scanning position
200
- pattern.lastIndex = 0;
201
- while ((match = pattern.exec(working)) !== null) {
202
- // append text before this match
203
- result += working.slice(lastIndex, match.index);
204
-
205
- // build args array similar to replace(): [...match, offset, input]
206
- const args = [...match, match.index, working] as any as IArguments;
207
- const out = rule.replacer(this, args, scope);
208
-
209
- if (typeof out === 'string') {
210
- // simple replacement: advance by matched token length
211
- result += out;
212
- lastIndex = match.index + match[0].length;
213
- }
214
- else {
215
- // object replacement provides final end index — consume until that position
216
- result += out.text;
217
- lastIndex = out.end;
218
- }
219
-
220
- // make sure regex scanning continues from the correct place
221
- pattern.lastIndex = lastIndex;
222
- }
223
-
224
- // append tail and update working
225
- result += working.slice(lastIndex);
226
- working = result;
227
- }
228
-
229
- return working;
230
- }
231
- }
@@ -1,24 +0,0 @@
1
- export default class Rule {
2
- public static readonly PREFIX = "@";
3
- }
4
- export class RuleFor<T> extends Rule {
5
- public indexName: string;
6
- public index: number;
7
-
8
- public variableName: string;
9
- public variable: T;
10
-
11
- public iterableName: string;
12
- public iterable: Iterable<T>;
13
-
14
- public constructor(index: number, variable: T, iterable: Iterable<T>) {
15
- super();
16
- this.index = index;
17
- this.variable = variable;
18
- this.iterable = iterable;
19
- }
20
- }
21
- export type Expression<T> = () => T;
22
- export class Scope {
23
-
24
- }