stringent 0.0.1 → 0.0.2
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/README.md +108 -0
- package/dist/combinators/index.d.ts +57 -0
- package/dist/combinators/index.js +104 -0
- package/dist/context.d.ts +27 -0
- package/dist/context.js +13 -0
- package/dist/createParser.d.ts +76 -0
- package/dist/createParser.js +57 -0
- package/dist/grammar/index.d.ts +43 -0
- package/dist/grammar/index.js +12 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +22 -0
- package/dist/parse/index.d.ts +137 -0
- package/dist/parse/index.js +15 -0
- package/dist/primitive/index.d.ts +104 -0
- package/dist/primitive/index.js +97 -0
- package/dist/runtime/infer.d.ts +26 -0
- package/dist/runtime/infer.js +34 -0
- package/dist/runtime/parser.d.ts +34 -0
- package/dist/runtime/parser.js +271 -0
- package/dist/schema/index.d.ts +273 -0
- package/dist/schema/index.js +111 -0
- package/dist/static/infer.d.ts +26 -0
- package/dist/static/infer.js +9 -0
- package/dist/static/parser.d.ts +7 -0
- package/dist/static/parser.js +6 -0
- package/package.json +30 -7
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parse Type - Type-Level Parsing with Grammar Support
|
|
3
|
+
*
|
|
4
|
+
* Parse<Grammar, Input, Context> computes the exact result type of parsing
|
|
5
|
+
* an input string against a grammar.
|
|
6
|
+
*
|
|
7
|
+
* The grammar is a flat tuple of precedence levels:
|
|
8
|
+
* [[Level0Ops], [Level1Ops], ..., [Atoms]]
|
|
9
|
+
*
|
|
10
|
+
* Parsing proceeds:
|
|
11
|
+
* 1. Try operators at current level (index 0, lowest precedence)
|
|
12
|
+
* 2. Fall back to next level (index 1, higher precedence)
|
|
13
|
+
* 3. Continue until atoms (last element)
|
|
14
|
+
*/
|
|
15
|
+
import type { Token } from "@sinclair/parsebox";
|
|
16
|
+
import type { Context } from "../context.js";
|
|
17
|
+
import type { Grammar } from "../grammar/index.js";
|
|
18
|
+
import type { NodeSchema, PatternSchema, PatternSchemaBase, NamedSchema, NumberSchema, StringSchema, IdentSchema, ConstSchema, ExprSchema } from "../schema/index.js";
|
|
19
|
+
import type { NumberNode, StringNode, IdentNode, ConstNode } from "../primitive/index.js";
|
|
20
|
+
/** Base parse error */
|
|
21
|
+
export interface ParseError<TMessage extends string = string> {
|
|
22
|
+
readonly __error: true;
|
|
23
|
+
readonly message: TMessage;
|
|
24
|
+
}
|
|
25
|
+
/** Type mismatch error (expression has wrong type) */
|
|
26
|
+
export type TypeMismatchError<TExpected extends string, TActual extends string> = ParseError<`Type mismatch: expected ${TExpected}, got ${TActual}`>;
|
|
27
|
+
/** No match error (no grammar rule matched) */
|
|
28
|
+
export type NoMatchError = ParseError<"No grammar rule matched">;
|
|
29
|
+
/** Binary operator node */
|
|
30
|
+
export interface BinaryNode<TName extends string = string, TLeft = unknown, TRight = unknown, TOutputSchema extends string = string> {
|
|
31
|
+
readonly node: TName;
|
|
32
|
+
readonly outputSchema: TOutputSchema;
|
|
33
|
+
readonly left: TLeft;
|
|
34
|
+
readonly right: TRight;
|
|
35
|
+
}
|
|
36
|
+
type ParseNumberPrimitive<TInput extends string> = Token.TNumber<TInput> extends [infer V extends string, infer R extends string] ? [NumberNode<V>, R] : [];
|
|
37
|
+
type ParseStringPrimitive<TQuotes extends readonly string[], TInput extends string> = Token.TString<[...TQuotes], TInput> extends [
|
|
38
|
+
infer V extends string,
|
|
39
|
+
infer R extends string
|
|
40
|
+
] ? [StringNode<V>, R] : [];
|
|
41
|
+
type ParseIdentPrimitive<TInput extends string, TContext extends Context> = Token.TIdent<TInput> extends [
|
|
42
|
+
infer V extends string,
|
|
43
|
+
infer R extends string
|
|
44
|
+
] ? V extends keyof TContext["data"] ? [IdentNode<V, TContext["data"][V] & string>, R] : [IdentNode<V, "unknown">, R] : [];
|
|
45
|
+
type ParseConstPrimitive<TValue extends string, TInput extends string> = Token.TConst<TValue, TInput> extends [
|
|
46
|
+
infer _V extends string,
|
|
47
|
+
infer R extends string
|
|
48
|
+
] ? [ConstNode<TValue>, R] : [];
|
|
49
|
+
/**
|
|
50
|
+
* Parse a single pattern element (non-Expr).
|
|
51
|
+
* Works with both plain schemas and NamedSchema (intersection type).
|
|
52
|
+
*/
|
|
53
|
+
type ParseElement<TElement extends PatternSchema, TInput extends string, TContext extends Context> = TElement extends NumberSchema ? ParseNumberPrimitive<TInput> : TElement extends StringSchema<infer Q> ? ParseStringPrimitive<Q, TInput> : TElement extends IdentSchema ? ParseIdentPrimitive<TInput, TContext> : TElement extends ConstSchema<infer V> ? ParseConstPrimitive<V, TInput> : never;
|
|
54
|
+
/**
|
|
55
|
+
* Parse a tuple of pattern elements.
|
|
56
|
+
*
|
|
57
|
+
* TCurrentLevels - grammar from current level onward (for rhs)
|
|
58
|
+
* TNextLevels - grammar from next level onward (for lhs, avoids left-recursion)
|
|
59
|
+
* TFullGrammar - complete grammar (for expr role, full reset)
|
|
60
|
+
*/
|
|
61
|
+
type ParsePatternTuple<TPattern extends readonly PatternSchema[], TInput extends string, TContext extends Context, TCurrentLevels extends Grammar, TNextLevels extends Grammar, TFullGrammar extends Grammar, TAcc extends unknown[] = []> = TPattern extends readonly [
|
|
62
|
+
infer First extends PatternSchema,
|
|
63
|
+
...infer Rest extends readonly PatternSchema[]
|
|
64
|
+
] ? ParseElementWithLevel<First, TInput, TContext, TCurrentLevels, TNextLevels, TFullGrammar> extends [infer R, infer Remaining extends string] ? ParsePatternTuple<Rest, Remaining, TContext, TCurrentLevels, TNextLevels, TFullGrammar, [
|
|
65
|
+
...TAcc,
|
|
66
|
+
R
|
|
67
|
+
]> : [] : [TAcc, TInput];
|
|
68
|
+
/**
|
|
69
|
+
* Parse an expression element based on its role.
|
|
70
|
+
* Works with both plain schemas and NamedSchema (intersection type).
|
|
71
|
+
*
|
|
72
|
+
* Role determines which grammar slice is used:
|
|
73
|
+
* - "lhs": TNextLevels (avoids left-recursion)
|
|
74
|
+
* - "rhs": TCurrentLevels (maintains precedence, enables right-associativity)
|
|
75
|
+
* - "expr": TFullGrammar (full reset for delimited contexts)
|
|
76
|
+
*/
|
|
77
|
+
type ParseElementWithLevel<TElement extends PatternSchema, TInput extends string, TContext extends Context, TCurrentLevels extends Grammar, TNextLevels extends Grammar, TFullGrammar extends Grammar> = TElement extends ExprSchema<infer C, infer Role> ? Role extends "lhs" ? ParseExprWithConstraint<TNextLevels, TInput, TContext, C, TFullGrammar> : Role extends "rhs" ? ParseExprWithConstraint<TCurrentLevels, TInput, TContext, C, TFullGrammar> : ParseExprWithConstraint<TFullGrammar, TInput, TContext, C, TFullGrammar> : ParseElement<TElement, TInput, TContext>;
|
|
78
|
+
/**
|
|
79
|
+
* Parse a node's pattern and build the result node.
|
|
80
|
+
*/
|
|
81
|
+
type ParseNodePattern<TNode extends NodeSchema, TInput extends string, TContext extends Context, TCurrentLevels extends Grammar, TNextLevels extends Grammar, TFullGrammar extends Grammar> = ParsePatternTuple<TNode["pattern"], TInput, TContext, TCurrentLevels, TNextLevels, TFullGrammar> extends [infer Children extends unknown[], infer Rest extends string] ? [BuildNodeResult<TNode, Children>, Rest] : [];
|
|
82
|
+
type ExtractBindings<TPattern extends readonly PatternSchema[], TChildren extends unknown[], TAcc extends {} = {}> = TPattern extends readonly [
|
|
83
|
+
infer First extends PatternSchema,
|
|
84
|
+
...infer RestPattern extends readonly PatternSchema[]
|
|
85
|
+
] ? TChildren extends [infer Child, ...infer RestChildren] ? First extends NamedSchema<PatternSchemaBase, infer Name> ? ExtractBindings<RestPattern, RestChildren, {
|
|
86
|
+
[P in keyof TAcc | Name]: P extends Name ? Child : P extends keyof TAcc ? TAcc[P] : never;
|
|
87
|
+
}> : ExtractBindings<RestPattern, RestChildren, TAcc> : TAcc : TAcc;
|
|
88
|
+
/**
|
|
89
|
+
* Build the result node from parsed children.
|
|
90
|
+
*
|
|
91
|
+
* Uses named bindings from .as() to determine node fields.
|
|
92
|
+
* - Single unnamed child: passthrough (atom behavior)
|
|
93
|
+
* - Otherwise: bindings become node fields
|
|
94
|
+
*/
|
|
95
|
+
type BuildNodeResult<TNode extends NodeSchema, TChildren extends unknown[]> = ExtractBindings<TNode["pattern"], TChildren> extends infer Bindings ? keyof Bindings extends never ? TChildren extends [infer Only] ? Only : never : {
|
|
96
|
+
readonly node: TNode["name"];
|
|
97
|
+
readonly outputSchema: TNode["resultType"];
|
|
98
|
+
} & Bindings : never;
|
|
99
|
+
/**
|
|
100
|
+
* Parse an expression with optional type constraint.
|
|
101
|
+
*/
|
|
102
|
+
type ParseExprWithConstraint<TStartLevels extends Grammar, TInput extends string, TContext extends Context, TConstraint extends string | undefined, TFullGrammar extends Grammar> = ParseLevels<TStartLevels, TInput, TContext, TFullGrammar> extends [
|
|
103
|
+
infer Node extends {
|
|
104
|
+
outputSchema: string;
|
|
105
|
+
},
|
|
106
|
+
infer Rest extends string
|
|
107
|
+
] ? TConstraint extends string ? Node["outputSchema"] extends TConstraint ? [Node, Rest] : [] : [Node, Rest] : [];
|
|
108
|
+
/**
|
|
109
|
+
* Try parsing each node in a level.
|
|
110
|
+
*/
|
|
111
|
+
type ParseNodes<TNodes extends readonly NodeSchema[], TInput extends string, TContext extends Context, TCurrentLevels extends Grammar, TNextLevels extends Grammar, TFullGrammar extends Grammar> = TNodes extends readonly [
|
|
112
|
+
infer First extends NodeSchema,
|
|
113
|
+
...infer Rest extends readonly NodeSchema[]
|
|
114
|
+
] ? ParseNodePattern<First, TInput, TContext, TCurrentLevels, TNextLevels, TFullGrammar> extends [infer R, infer Remaining extends string] ? [R, Remaining] : ParseNodes<Rest, TInput, TContext, TCurrentLevels, TNextLevels, TFullGrammar> : [];
|
|
115
|
+
/**
|
|
116
|
+
* Parse using grammar levels (flat tuple).
|
|
117
|
+
*
|
|
118
|
+
* TLevels is the remaining levels to try, starting from current.
|
|
119
|
+
* - Try nodes at first level (TLevels[0])
|
|
120
|
+
* - If no match, fall back to rest of levels
|
|
121
|
+
* - Base case: single level (atoms) - just try those nodes
|
|
122
|
+
*/
|
|
123
|
+
type ParseLevels<TLevels extends Grammar, TInput extends string, TContext extends Context, TFullGrammar extends Grammar> = TLevels extends readonly [
|
|
124
|
+
infer CurrentNodes extends readonly NodeSchema[],
|
|
125
|
+
...infer NextNodes extends Grammar
|
|
126
|
+
] ? ParseNodes<CurrentNodes, TInput, TContext, TLevels, NextNodes, TFullGrammar> extends [infer R, infer Remaining extends string] ? [R, Remaining] : ParseLevels<NextNodes, TInput, TContext, TFullGrammar> : TLevels extends readonly [infer LastNodes extends readonly NodeSchema[]] ? ParseNodes<LastNodes, TInput, TContext, TLevels, TLevels, TFullGrammar> : [];
|
|
127
|
+
/**
|
|
128
|
+
* Parse<Grammar, Input, Context>
|
|
129
|
+
*
|
|
130
|
+
* Main entry point for type-level parsing.
|
|
131
|
+
*
|
|
132
|
+
* @example
|
|
133
|
+
* type Result = Parse<MyGrammar, "1+2", Context>;
|
|
134
|
+
* // [BinaryNode<"add", NumberNode<"1">, NumberNode<"2">, "number">, ""]
|
|
135
|
+
*/
|
|
136
|
+
export type Parse<TGrammar extends Grammar, TInput extends string, TContext extends Context> = ParseLevels<TGrammar, TInput, TContext, TGrammar>;
|
|
137
|
+
export {};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parse Type - Type-Level Parsing with Grammar Support
|
|
3
|
+
*
|
|
4
|
+
* Parse<Grammar, Input, Context> computes the exact result type of parsing
|
|
5
|
+
* an input string against a grammar.
|
|
6
|
+
*
|
|
7
|
+
* The grammar is a flat tuple of precedence levels:
|
|
8
|
+
* [[Level0Ops], [Level1Ops], ..., [Atoms]]
|
|
9
|
+
*
|
|
10
|
+
* Parsing proceeds:
|
|
11
|
+
* 1. Try operators at current level (index 0, lowest precedence)
|
|
12
|
+
* 2. Fall back to next level (index 1, higher precedence)
|
|
13
|
+
* 3. Continue until atoms (last element)
|
|
14
|
+
*/
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Primitive Factory System
|
|
3
|
+
*
|
|
4
|
+
* Creates context-aware parser primitives using Parsebox Token API.
|
|
5
|
+
* Parse methods are generic - return types computed from input literals.
|
|
6
|
+
*
|
|
7
|
+
* IMPORTANT: All parse methods MUST be generic in context:
|
|
8
|
+
* parse<TInput extends string, $ extends Context>(input: TInput, $: $)
|
|
9
|
+
*
|
|
10
|
+
* DO NOT remove the $ generic parameter - it ensures schema type information
|
|
11
|
+
* flows through the parser chain. See CLAUDE.md for details.
|
|
12
|
+
*/
|
|
13
|
+
import { Token } from "@sinclair/parsebox";
|
|
14
|
+
import type { Context } from "../context.js";
|
|
15
|
+
import type { ToNumber } from "hotscript/dist/internals/numbers/impl/utils.js";
|
|
16
|
+
export type { Context } from "../context.js";
|
|
17
|
+
/** Parse result: empty = no match, [value, rest] = matched */
|
|
18
|
+
export type ParseResult<T, R extends string = string> = [] | [T, R];
|
|
19
|
+
export interface ASTNode<TType extends string = string, TOutputSchema = unknown> {
|
|
20
|
+
node: TType;
|
|
21
|
+
outputSchema: TOutputSchema;
|
|
22
|
+
}
|
|
23
|
+
export interface IdentNode<TName extends string = string, TOutputSchema = "unknown"> extends ASTNode<"identifier", TOutputSchema> {
|
|
24
|
+
name: TName;
|
|
25
|
+
}
|
|
26
|
+
export type NumberNode<TValue extends string = string> = ASTNode<"literal", "number"> & {
|
|
27
|
+
raw: TValue;
|
|
28
|
+
value: ToNumber<TValue>;
|
|
29
|
+
};
|
|
30
|
+
export type StringNode<TValue extends string = string> = ASTNode<"literal", "string"> & {
|
|
31
|
+
raw: TValue;
|
|
32
|
+
value: TValue;
|
|
33
|
+
};
|
|
34
|
+
export type NullNode = ASTNode<"literal", "null"> & {
|
|
35
|
+
raw: "null";
|
|
36
|
+
value: null;
|
|
37
|
+
};
|
|
38
|
+
export type UndefinedNode = ASTNode<"literal", "undefined"> & {
|
|
39
|
+
raw: "undefined";
|
|
40
|
+
value: undefined;
|
|
41
|
+
};
|
|
42
|
+
export type BooleanNode<TValue extends string = string> = ASTNode<"boolean", TValue> & {
|
|
43
|
+
raw: TValue;
|
|
44
|
+
value: TValue extends "true" ? true : false;
|
|
45
|
+
};
|
|
46
|
+
export type LiteralNode = NumberNode | StringNode | NullNode | UndefinedNode | BooleanNode;
|
|
47
|
+
export type ConstNode<TValue extends string = string> = ASTNode<"const", TValue>;
|
|
48
|
+
/** Computed parse result for Number */
|
|
49
|
+
export type ParseNumber<TInput extends string> = Token.TNumber<TInput> extends [
|
|
50
|
+
infer V extends string,
|
|
51
|
+
infer R extends string
|
|
52
|
+
] ? [NumberNode<V>, R] : [];
|
|
53
|
+
/** Computed parse result for String */
|
|
54
|
+
export type ParseString<TQuotes extends string[], TInput extends string> = Token.TString<TQuotes, TInput> extends [
|
|
55
|
+
infer V extends string,
|
|
56
|
+
infer R extends string
|
|
57
|
+
] ? [StringNode<V>, R] : [];
|
|
58
|
+
/** Computed parse result for Ident - looks up value type from schema */
|
|
59
|
+
export type ParseIdent<TInput extends string, $ extends Context> = Token.TIdent<TInput> extends [
|
|
60
|
+
infer V extends string,
|
|
61
|
+
infer R extends string
|
|
62
|
+
] ? V extends keyof $["data"] ? [IdentNode<V, $["data"][V]>, R] : [IdentNode<V>, R] : [];
|
|
63
|
+
/** Computed parse result for Const */
|
|
64
|
+
export type ParseConst<TValue extends string, TInput extends string> = Token.TConst<TValue, TInput> extends [
|
|
65
|
+
infer _V extends string,
|
|
66
|
+
infer R extends string
|
|
67
|
+
] ? [ConstNode<TValue>, R] : [];
|
|
68
|
+
/**
|
|
69
|
+
* Base parser interface. All parsers implement this.
|
|
70
|
+
* Context is generic to preserve type information.
|
|
71
|
+
*/
|
|
72
|
+
export interface IParser {
|
|
73
|
+
parse<TInput extends string, $ extends Context>(input: TInput, $: $): [] | [unknown, string];
|
|
74
|
+
}
|
|
75
|
+
/** Number primitive - parses numeric literals using Token.Number */
|
|
76
|
+
declare class _Number {
|
|
77
|
+
readonly __primitive: "number";
|
|
78
|
+
parse<TInput extends string, $ extends Context>(input: TInput, _$: $): ParseNumber<TInput>;
|
|
79
|
+
}
|
|
80
|
+
/** String primitive - parses quoted string literals using Token.String */
|
|
81
|
+
declare class _String<TQuotes extends string[]> {
|
|
82
|
+
readonly __primitive: "string";
|
|
83
|
+
readonly quotes: TQuotes;
|
|
84
|
+
constructor(quotes: TQuotes);
|
|
85
|
+
parse<TInput extends string, $ extends Context>(input: TInput, _$: $): ParseString<TQuotes, TInput>;
|
|
86
|
+
}
|
|
87
|
+
/** Identifier primitive - parses identifiers using Token.Ident */
|
|
88
|
+
declare class _Ident {
|
|
89
|
+
readonly __primitive: "ident";
|
|
90
|
+
parse<TInput extends string, $ extends Context>(input: TInput, $: $): ParseIdent<TInput, $>;
|
|
91
|
+
}
|
|
92
|
+
/** Const primitive - parses exact string matches using Token.Const */
|
|
93
|
+
declare class _Const<TValue extends string> {
|
|
94
|
+
readonly __primitive: "const";
|
|
95
|
+
readonly value: TValue;
|
|
96
|
+
constructor(value: TValue);
|
|
97
|
+
parse<TInput extends string, $ extends Context>(input: TInput, _$: $): ParseConst<TValue, TInput>;
|
|
98
|
+
}
|
|
99
|
+
export declare const Number: () => _Number;
|
|
100
|
+
export declare const String: <TQuotes extends string[]>(quotes: [...TQuotes]) => _String<TQuotes>;
|
|
101
|
+
export declare const Ident: () => _Ident;
|
|
102
|
+
export declare const Const: <TValue extends string>(value: TValue) => _Const<TValue>;
|
|
103
|
+
export type { _Number, _String, _Ident, _Const };
|
|
104
|
+
export { type IParser as Primitive };
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Primitive Factory System
|
|
3
|
+
*
|
|
4
|
+
* Creates context-aware parser primitives using Parsebox Token API.
|
|
5
|
+
* Parse methods are generic - return types computed from input literals.
|
|
6
|
+
*
|
|
7
|
+
* IMPORTANT: All parse methods MUST be generic in context:
|
|
8
|
+
* parse<TInput extends string, $ extends Context>(input: TInput, $: $)
|
|
9
|
+
*
|
|
10
|
+
* DO NOT remove the $ generic parameter - it ensures schema type information
|
|
11
|
+
* flows through the parser chain. See CLAUDE.md for details.
|
|
12
|
+
*/
|
|
13
|
+
import { Token } from "@sinclair/parsebox";
|
|
14
|
+
// =============================================================================
|
|
15
|
+
// Primitive Implementations
|
|
16
|
+
// =============================================================================
|
|
17
|
+
/** Number primitive - parses numeric literals using Token.Number */
|
|
18
|
+
class _Number {
|
|
19
|
+
__primitive = "number";
|
|
20
|
+
parse(input, _$) {
|
|
21
|
+
// Runtime type is [] | [string, string], but TypeScript computes exact type
|
|
22
|
+
const result = Token.Number(input);
|
|
23
|
+
if (result.length !== 2)
|
|
24
|
+
return [];
|
|
25
|
+
return [
|
|
26
|
+
{
|
|
27
|
+
node: "literal",
|
|
28
|
+
raw: result[0],
|
|
29
|
+
value: +result[0],
|
|
30
|
+
outputSchema: "number",
|
|
31
|
+
},
|
|
32
|
+
result[1],
|
|
33
|
+
];
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/** String primitive - parses quoted string literals using Token.String */
|
|
37
|
+
class _String {
|
|
38
|
+
__primitive = "string";
|
|
39
|
+
quotes;
|
|
40
|
+
constructor(quotes) {
|
|
41
|
+
this.quotes = quotes;
|
|
42
|
+
}
|
|
43
|
+
parse(input, _$) {
|
|
44
|
+
const result = Token.String(this.quotes, input);
|
|
45
|
+
if (result.length !== 2)
|
|
46
|
+
return [];
|
|
47
|
+
return [
|
|
48
|
+
{
|
|
49
|
+
node: "literal",
|
|
50
|
+
raw: result[0],
|
|
51
|
+
value: result[0],
|
|
52
|
+
outputSchema: "string",
|
|
53
|
+
},
|
|
54
|
+
result[1],
|
|
55
|
+
];
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/** Identifier primitive - parses identifiers using Token.Ident */
|
|
59
|
+
class _Ident {
|
|
60
|
+
__primitive = "ident";
|
|
61
|
+
parse(input, $) {
|
|
62
|
+
const result = Token.Ident(input);
|
|
63
|
+
if (result.length !== 2)
|
|
64
|
+
return [];
|
|
65
|
+
const name = result[0];
|
|
66
|
+
const data = $.data;
|
|
67
|
+
const valueType = name in data ? data[name] : "unknown";
|
|
68
|
+
return [
|
|
69
|
+
{ node: "identifier", name, outputSchema: valueType },
|
|
70
|
+
result[1],
|
|
71
|
+
];
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/** Const primitive - parses exact string matches using Token.Const */
|
|
75
|
+
class _Const {
|
|
76
|
+
__primitive = "const";
|
|
77
|
+
value;
|
|
78
|
+
constructor(value) {
|
|
79
|
+
this.value = value;
|
|
80
|
+
}
|
|
81
|
+
parse(input, _$) {
|
|
82
|
+
const result = Token.Const(this.value, input);
|
|
83
|
+
if (result.length !== 2)
|
|
84
|
+
return [];
|
|
85
|
+
return [
|
|
86
|
+
{ node: "const", outputSchema: JSON.stringify(this.value) },
|
|
87
|
+
result[1],
|
|
88
|
+
];
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
// =============================================================================
|
|
92
|
+
// Exported Factories
|
|
93
|
+
// =============================================================================
|
|
94
|
+
export const Number = () => new _Number();
|
|
95
|
+
export const String = (quotes) => new _String(quotes);
|
|
96
|
+
export const Ident = () => new _Ident();
|
|
97
|
+
export const Const = (value) => new _Const(value);
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime Type Inference
|
|
3
|
+
*
|
|
4
|
+
* Infers the result type of a parsed AST at runtime.
|
|
5
|
+
*
|
|
6
|
+
* With the new architecture, nodes carry their outputSchema directly,
|
|
7
|
+
* so inference is simpler - just extract the outputSchema field.
|
|
8
|
+
*/
|
|
9
|
+
import type { Context } from "../context.js";
|
|
10
|
+
export type InferredType = string;
|
|
11
|
+
/**
|
|
12
|
+
* Runtime inference - extracts outputSchema from parsed AST nodes.
|
|
13
|
+
*
|
|
14
|
+
* @param ast - The parsed AST node
|
|
15
|
+
* @param context - Parse context (unused, kept for API compatibility)
|
|
16
|
+
* @returns The result type of the AST node
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```ts
|
|
20
|
+
* const result = parser.parse("1+2", {});
|
|
21
|
+
* if (result.length === 2) {
|
|
22
|
+
* const type = infer(result[0], {}); // "number"
|
|
23
|
+
* }
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export declare function infer(ast: unknown, _context: Context): InferredType;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime Type Inference
|
|
3
|
+
*
|
|
4
|
+
* Infers the result type of a parsed AST at runtime.
|
|
5
|
+
*
|
|
6
|
+
* With the new architecture, nodes carry their outputSchema directly,
|
|
7
|
+
* so inference is simpler - just extract the outputSchema field.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Runtime inference - extracts outputSchema from parsed AST nodes.
|
|
11
|
+
*
|
|
12
|
+
* @param ast - The parsed AST node
|
|
13
|
+
* @param context - Parse context (unused, kept for API compatibility)
|
|
14
|
+
* @returns The result type of the AST node
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```ts
|
|
18
|
+
* const result = parser.parse("1+2", {});
|
|
19
|
+
* if (result.length === 2) {
|
|
20
|
+
* const type = infer(result[0], {}); // "number"
|
|
21
|
+
* }
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export function infer(ast, _context) {
|
|
25
|
+
if (typeof ast !== "object" || ast === null) {
|
|
26
|
+
throw new Error(`Invalid AST node: ${ast}`);
|
|
27
|
+
}
|
|
28
|
+
const node = ast;
|
|
29
|
+
// Nodes with outputSchema (new architecture)
|
|
30
|
+
if ("outputSchema" in node && typeof node.outputSchema === "string") {
|
|
31
|
+
return node.outputSchema;
|
|
32
|
+
}
|
|
33
|
+
throw new Error(`AST node has no outputSchema: ${JSON.stringify(ast)}`);
|
|
34
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime Parser
|
|
3
|
+
*
|
|
4
|
+
* Mirrors the type-level Parse<Grammar, Input, Context> at runtime.
|
|
5
|
+
* Uses the same precedence-based parsing strategy:
|
|
6
|
+
* 1. Try operators at current level (lowest precedence first)
|
|
7
|
+
* 2. Fall back to next level (higher precedence)
|
|
8
|
+
* 3. Base case: try atoms (last level)
|
|
9
|
+
*/
|
|
10
|
+
import type { Context } from "../context.js";
|
|
11
|
+
import type { ComputeGrammar, Grammar } from "../grammar/index.js";
|
|
12
|
+
import type { Parse } from "../parse/index.js";
|
|
13
|
+
import type { NodeSchema } from "../schema/index.js";
|
|
14
|
+
import { ASTNode } from "../primitive/index.js";
|
|
15
|
+
/** Parse result: empty = no match, [node, rest] = matched */
|
|
16
|
+
export type ParseResult<T extends ASTNode<any, any> = ASTNode<any, any>> = [] | [T & {}, string];
|
|
17
|
+
/**
|
|
18
|
+
* Build runtime grammar from node schemas.
|
|
19
|
+
*
|
|
20
|
+
* Returns a flat tuple of levels:
|
|
21
|
+
* [[ops@prec1], [ops@prec2], ..., [atoms]]
|
|
22
|
+
*
|
|
23
|
+
* Levels are sorted by precedence ascending (lowest first).
|
|
24
|
+
* Atoms are always the last level.
|
|
25
|
+
*/
|
|
26
|
+
export declare function buildGrammar(nodes: readonly NodeSchema[]): Grammar;
|
|
27
|
+
/**
|
|
28
|
+
* Parse input string using node schemas.
|
|
29
|
+
*
|
|
30
|
+
* The return type is computed from the input types using the type-level
|
|
31
|
+
* Parse<Grammar, Input, Context> type, ensuring runtime and type-level
|
|
32
|
+
* parsing stay in sync.
|
|
33
|
+
*/
|
|
34
|
+
export declare function parse<const TNodes extends readonly NodeSchema[], const TInput extends string, const TContext extends Context>(nodes: TNodes, input: TInput, context: TContext): Parse<ComputeGrammar<TNodes>, TInput, TContext>;
|