stringent 0.0.1 → 0.0.4

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.
Files changed (54) hide show
  1. package/README.md +96 -0
  2. package/dist/context.d.ts +45 -0
  3. package/dist/context.d.ts.map +1 -0
  4. package/dist/context.js +14 -0
  5. package/dist/context.js.map +1 -0
  6. package/dist/createParser.d.ts +159 -0
  7. package/dist/createParser.d.ts.map +1 -0
  8. package/dist/createParser.js +118 -0
  9. package/dist/createParser.js.map +1 -0
  10. package/dist/errors.d.ts +121 -0
  11. package/dist/errors.d.ts.map +1 -0
  12. package/dist/errors.js +186 -0
  13. package/dist/errors.js.map +1 -0
  14. package/dist/grammar/index.d.ts +48 -0
  15. package/dist/grammar/index.d.ts.map +1 -0
  16. package/dist/grammar/index.js +13 -0
  17. package/dist/grammar/index.js.map +1 -0
  18. package/dist/index.d.ts +27 -0
  19. package/dist/index.d.ts.map +1 -0
  20. package/dist/index.js +31 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/parse/index.d.ts +211 -0
  23. package/dist/parse/index.d.ts.map +1 -0
  24. package/dist/parse/index.js +16 -0
  25. package/dist/parse/index.js.map +1 -0
  26. package/dist/performance.bench.d.ts +10 -0
  27. package/dist/performance.bench.d.ts.map +1 -0
  28. package/dist/performance.bench.js +379 -0
  29. package/dist/performance.bench.js.map +1 -0
  30. package/dist/primitive/index.d.ts +96 -0
  31. package/dist/primitive/index.d.ts.map +1 -0
  32. package/dist/primitive/index.js +102 -0
  33. package/dist/primitive/index.js.map +1 -0
  34. package/dist/runtime/eval.d.ts +157 -0
  35. package/dist/runtime/eval.d.ts.map +1 -0
  36. package/dist/runtime/eval.js +206 -0
  37. package/dist/runtime/eval.js.map +1 -0
  38. package/dist/runtime/infer.d.ts +27 -0
  39. package/dist/runtime/infer.d.ts.map +1 -0
  40. package/dist/runtime/infer.js +35 -0
  41. package/dist/runtime/infer.js.map +1 -0
  42. package/dist/runtime/parser.d.ts +115 -0
  43. package/dist/runtime/parser.d.ts.map +1 -0
  44. package/dist/runtime/parser.js +746 -0
  45. package/dist/runtime/parser.js.map +1 -0
  46. package/dist/schema/index.d.ts +476 -0
  47. package/dist/schema/index.d.ts.map +1 -0
  48. package/dist/schema/index.js +137 -0
  49. package/dist/schema/index.js.map +1 -0
  50. package/dist/static/infer.d.ts +27 -0
  51. package/dist/static/infer.d.ts.map +1 -0
  52. package/dist/static/infer.js +10 -0
  53. package/dist/static/infer.js.map +1 -0
  54. package/package.json +62 -8
package/README.md ADDED
@@ -0,0 +1,96 @@
1
+ # Stringent
2
+
3
+ Type-safe expression parser for TypeScript with compile-time validation.
4
+
5
+ > **Warning:** Under active development. APIs may change.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install stringent
11
+ ```
12
+
13
+ ## Quick Start
14
+
15
+ ```typescript
16
+ import { createParser, defineNode, constVal, lhs, rhs } from 'stringent';
17
+
18
+ // Define operators
19
+ const add = defineNode({
20
+ name: 'add',
21
+ pattern: [lhs('number').as('left'), constVal('+'), rhs('number').as('right')],
22
+ precedence: 1,
23
+ resultType: 'number',
24
+ eval: ({ left, right }) => left + right,
25
+ });
26
+
27
+ const mul = defineNode({
28
+ name: 'mul',
29
+ pattern: [lhs('number').as('left'), constVal('*'), rhs('number').as('right')],
30
+ precedence: 2,
31
+ resultType: 'number',
32
+ eval: ({ left, right }) => left * right,
33
+ });
34
+
35
+ // Create parser
36
+ const parser = createParser([add, mul]);
37
+
38
+ // Parse and evaluate
39
+ const [evaluator, err] = parser.parse('x + 2 * 3', { x: 'number' });
40
+ if (!err) {
41
+ const result = evaluator({ x: 1 }); // 7
42
+ // ^? number
43
+ }
44
+ ```
45
+
46
+ ## Key Features
47
+
48
+ - **Compile-time validation** - Invalid expressions fail TypeScript compilation
49
+ - **Type inference** - Return types flow through parsing to evaluation
50
+ - **ArkType integration** - Schema types validated at compile-time and runtime
51
+ - **Operator precedence** - Configurable precedence for correct parsing
52
+
53
+ ## Pattern Elements
54
+
55
+ | Element | Description |
56
+ | --------------- | ----------------------------------- |
57
+ | `lhs(type?)` | Left operand (higher precedence) |
58
+ | `rhs(type?)` | Right operand (same precedence) |
59
+ | `expr(type?)` | Full expression (resets precedence) |
60
+ | `constVal(str)` | Exact string match |
61
+
62
+ ## API
63
+
64
+ ### `createParser(nodes)`
65
+
66
+ Creates a parser from node definitions.
67
+
68
+ ### `defineNode(config)`
69
+
70
+ Defines a grammar node:
71
+
72
+ - `name` - Unique identifier
73
+ - `pattern` - Array of pattern elements
74
+ - `precedence` - Lower binds looser
75
+ - `resultType` - Output type (e.g., `'number'`, `'boolean'`)
76
+ - `eval` - Evaluation function
77
+
78
+ ### `parser.parse(input, schema)`
79
+
80
+ Returns `[evaluator, null]` on success or `[null, error]` on failure.
81
+
82
+ The evaluator is callable with data matching the schema and has `ast` and `schema` properties.
83
+
84
+ ## Examples
85
+
86
+ See [`examples/`](./examples) for more:
87
+
88
+ - Basic arithmetic with precedence
89
+ - Comparison operators
90
+ - Ternary expressions
91
+ - Custom domain operators
92
+ - Form validation
93
+
94
+ ## License
95
+
96
+ MIT
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Context - carries schema data for identifier type resolution
3
+ *
4
+ * The context maps variable names to their types, enabling type-safe
5
+ * parsing of expressions like `x + y` where x and y come from a schema.
6
+ *
7
+ * Grammar is now computed from node schemas via ComputeGrammar<Nodes>.
8
+ */
9
+ /**
10
+ * Schema value type: either a valid arktype type string or a nested object schema.
11
+ * This recursive type allows for nested object schemas like `{ x: { y: 'boolean' } }`.
12
+ */
13
+ export type SchemaValue = string | {
14
+ readonly [key: string]: SchemaValue;
15
+ };
16
+ /**
17
+ * A schema record maps variable names to their type specifications.
18
+ * Values can be arktype type strings or nested object schemas.
19
+ */
20
+ export type SchemaRecord = {
21
+ readonly [key: string]: SchemaValue;
22
+ };
23
+ /**
24
+ * Parse context with schema data.
25
+ *
26
+ * @typeParam TData - Schema mapping variable names to their types (strings or nested objects)
27
+ *
28
+ * @example
29
+ * ```ts
30
+ * type Ctx = Context<{ x: "number"; y: "string" }>;
31
+ * // x resolves to type "number", y resolves to type "string"
32
+ *
33
+ * type NestedCtx = Context<{ user: { name: "string"; age: "number" } }>;
34
+ * // user resolves to type { name: string; age: number }
35
+ * ```
36
+ */
37
+ export interface Context<TData extends SchemaRecord = SchemaRecord> {
38
+ /** Schema types for identifier resolution */
39
+ readonly data: TData;
40
+ }
41
+ /** Empty context (no schema variables) */
42
+ export declare const emptyContext: Context<{}>;
43
+ /** Type alias for empty context */
44
+ export type EmptyContext = Context<{}>;
45
+ //# sourceMappingURL=context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH;;;GAGG;AACH,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG;IAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,WAAW,CAAA;CAAE,CAAC;AAE3E;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG;IAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,WAAW,CAAA;CAAE,CAAC;AAMnE;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,OAAO,CAAC,KAAK,SAAS,YAAY,GAAG,YAAY;IAChE,6CAA6C;IAC7C,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC;CACtB;AAMD,0CAA0C;AAC1C,eAAO,MAAM,YAAY,EAAE,OAAO,CAAC,EAAE,CAAgB,CAAC;AAEtD,mCAAmC;AACnC,MAAM,MAAM,YAAY,GAAG,OAAO,CAAC,EAAE,CAAC,CAAC"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Context - carries schema data for identifier type resolution
3
+ *
4
+ * The context maps variable names to their types, enabling type-safe
5
+ * parsing of expressions like `x + y` where x and y come from a schema.
6
+ *
7
+ * Grammar is now computed from node schemas via ComputeGrammar<Nodes>.
8
+ */
9
+ // =============================================================================
10
+ // Default Context
11
+ // =============================================================================
12
+ /** Empty context (no schema variables) */
13
+ export const emptyContext = { data: {} };
14
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAyCH,gFAAgF;AAChF,kBAAkB;AAClB,gFAAgF;AAEhF,0CAA0C;AAC1C,MAAM,CAAC,MAAM,YAAY,GAAgB,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC"}
@@ -0,0 +1,159 @@
1
+ /**
2
+ * createParser Entry Point
3
+ *
4
+ * Creates a type-safe parser from node schemas.
5
+ * The returned parser has:
6
+ * - Type-level parsing via Parse<Grammar, Input, Context>
7
+ * - Runtime parsing that mirrors the type structure
8
+ * - Bound evaluator with type-safe data requirements
9
+ */
10
+ import type { NodeSchema, SchemaToType } from './schema/index.js';
11
+ import type { ComputeGrammar, Grammar } from './grammar/index.js';
12
+ import type { Parse } from './parse/index.js';
13
+ import type { Context, SchemaRecord } from './context.js';
14
+ import { type } from 'arktype';
15
+ export type { SchemaValue, SchemaRecord } from './context.js';
16
+ /**
17
+ * Extract the outputSchema from an AST node type.
18
+ * Returns the literal schema string if present, otherwise 'unknown'.
19
+ */
20
+ type ExtractOutputSchema<T> = T extends {
21
+ outputSchema: infer S;
22
+ } ? S : 'unknown';
23
+ /**
24
+ * Convert a SchemaRecord to its corresponding TypeScript data type.
25
+ * Maps each schema value to its runtime type using arktype.
26
+ *
27
+ * @example
28
+ * type Data = SchemaRecordToData<{ x: 'number'; y: 'string' }>;
29
+ * // { x: number; y: string }
30
+ */
31
+ export type SchemaRecordToData<TSchema extends SchemaRecord> = {
32
+ [K in keyof TSchema]: TSchema[K] extends string ? SchemaToType<TSchema[K]> : TSchema[K] extends SchemaRecord ? SchemaRecordToData<TSchema[K]> : unknown;
33
+ };
34
+ /**
35
+ * A bound evaluator function returned by parser.parse().
36
+ *
37
+ * The evaluator:
38
+ * - Has the nodes pre-bound from the parser
39
+ * - Has the schema captured from the parse call
40
+ * - Requires data that matches the schema types
41
+ * - Returns a value with the type inferred from the AST's outputSchema
42
+ *
43
+ * @typeParam TAST - The parsed AST type
44
+ * @typeParam TSchema - The schema record type
45
+ *
46
+ * @example
47
+ * ```ts
48
+ * const [evaluator, err] = parser.parse('x + 1', { x: 'number' });
49
+ * if (!err) {
50
+ * const value = evaluator({ x: 5 }); // value is number
51
+ * const ast = evaluator.ast; // Access the parsed AST
52
+ * }
53
+ * ```
54
+ */
55
+ export interface Evaluator<TAST, TSchema extends SchemaRecord> {
56
+ /**
57
+ * Evaluate the parsed expression with the given data.
58
+ *
59
+ * @param data - Variable values matching the schema types
60
+ * @returns The evaluated result with type inferred from outputSchema
61
+ */
62
+ (data: SchemaRecordToData<TSchema>): SchemaToType<ExtractOutputSchema<TAST>>;
63
+ /** The parsed AST */
64
+ readonly ast: TAST;
65
+ /** The schema used during parsing */
66
+ readonly schema: TSchema;
67
+ }
68
+ /**
69
+ * Result of parsing an expression.
70
+ *
71
+ * Returns a tuple of [evaluator, null] on success, or [null, error] on failure.
72
+ *
73
+ * @typeParam TAST - The parsed AST type
74
+ * @typeParam TSchema - The schema record type
75
+ */
76
+ export type ParseResult<TAST, TSchema extends SchemaRecord> = [Evaluator<TAST, TSchema>, null] | [null, Error];
77
+ /**
78
+ * Parser interface with type-safe parse method.
79
+ *
80
+ * TGrammar: The computed grammar type from node schemas
81
+ * TNodes: The tuple of node schemas
82
+ */
83
+ export interface Parser<TGrammar extends Grammar, TNodes extends readonly NodeSchema[]> {
84
+ /**
85
+ * Parse an input string and return a bound evaluator.
86
+ *
87
+ * Schema values are validated at compile time using arktype.
88
+ * Invalid type strings like 'garbage' will cause TypeScript errors.
89
+ *
90
+ * @param input - The input string to parse
91
+ * @param schema - Schema mapping field names to valid arktype type strings or nested object schemas
92
+ * @returns A tuple of [evaluator, null] on success, or [null, error] on failure
93
+ *
94
+ * @example
95
+ * ```ts
96
+ * const [evaluator, err] = parser.parse("x + 1", { x: 'number' });
97
+ * if (!err) {
98
+ * const value = evaluator({ x: 5 }); // value is number
99
+ * const ast = evaluator.ast;
100
+ * }
101
+ *
102
+ * // Valid schema types:
103
+ * parser.parse("x + 1", { x: 'number' }); // primitive
104
+ * parser.parse("x + 1", { x: 'string.email' }); // subtype
105
+ * parser.parse("x + 1", { x: 'number >= 0' }); // constraint
106
+ * parser.parse("x + 1", { x: 'string | number' }); // union
107
+ *
108
+ * // Nested object schemas:
109
+ * parser.parse("user", { user: { name: 'string', age: 'number' } });
110
+ *
111
+ * // Invalid - causes compile error:
112
+ * // parser.parse("x + 1", { x: 'garbage' });
113
+ * ```
114
+ */
115
+ parse<TInput extends string, const TSchema extends SchemaRecord>(input: ValidatedInput<TGrammar, TInput, Context<TSchema>>, schema: type.validate<TSchema>): ParseResult<ExtractAST<Parse<TGrammar, TInput, Context<TSchema>>>, TSchema>;
116
+ /** The node schemas used to create this parser */
117
+ readonly nodes: TNodes;
118
+ }
119
+ /**
120
+ * Extract the AST from a Parse result type.
121
+ * Parse returns [AST, remaining] where remaining is '' on success.
122
+ */
123
+ type ExtractAST<T> = T extends [infer AST, ''] ? AST : never;
124
+ type ValidatedInput<TGrammar extends Grammar, TInput extends string, $ extends Context> = Parse<TGrammar, TInput, $> extends [unknown, ''] ? TInput : never;
125
+ /**
126
+ * Create a type-safe parser from node schemas.
127
+ *
128
+ * The returned parser has both:
129
+ * - Compile-time type inference via Parse<Grammar, Input, Context>
130
+ * - Runtime parsing that matches the type structure
131
+ * - Bound evaluator with type-safe data requirements
132
+ *
133
+ * @param nodes - Tuple of node schemas defining the grammar
134
+ * @returns Parser instance with type-safe parse method
135
+ *
136
+ * @example
137
+ * ```ts
138
+ * import { defineNode, number, expr, constVal, createParser } from "stringent";
139
+ *
140
+ * const add = defineNode({
141
+ * name: "add",
142
+ * pattern: [lhs("number").as("left"), constVal("+"), rhs("number").as("right")],
143
+ * precedence: 1,
144
+ * resultType: "number",
145
+ * eval: ({ left, right }) => left + right,
146
+ * });
147
+ *
148
+ * const parser = createParser([add] as const);
149
+ *
150
+ * // Type-safe parsing with bound evaluator!
151
+ * const [evaluator, err] = parser.parse("x + 1", { x: 'number' });
152
+ * if (!err) {
153
+ * const value = evaluator({ x: 5 }); // value is number, equals 6
154
+ * const ast = evaluator.ast;
155
+ * }
156
+ * ```
157
+ */
158
+ export declare function createParser<const TNodes extends readonly NodeSchema[]>(nodes: TNodes): Parser<ComputeGrammar<TNodes>, TNodes>;
159
+ //# sourceMappingURL=createParser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createParser.d.ts","sourceRoot":"","sources":["../src/createParser.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,118 @@
1
+ /**
2
+ * createParser Entry Point
3
+ *
4
+ * Creates a type-safe parser from node schemas.
5
+ * The returned parser has:
6
+ * - Type-level parsing via Parse<Grammar, Input, Context>
7
+ * - Runtime parsing that mirrors the type structure
8
+ * - Bound evaluator with type-safe data requirements
9
+ */
10
+ import { parse as runtimeParse } from './runtime/parser.js';
11
+ import { type } from 'arktype';
12
+ import { evaluate } from './runtime/eval.js';
13
+ // =============================================================================
14
+ // Runtime Validation
15
+ // =============================================================================
16
+ /**
17
+ * Cache for arktype validators to avoid re-creating them for each validation.
18
+ */
19
+ const validatorCache = new Map();
20
+ /**
21
+ * Get or create an arktype validator for a schema.
22
+ */
23
+ function getValidator(schema) {
24
+ // For nested objects, create a composite key
25
+ const key = typeof schema === 'string' ? schema : JSON.stringify(schema);
26
+ let validator = validatorCache.get(key);
27
+ if (!validator) {
28
+ validator = type(schema);
29
+ validatorCache.set(key, validator);
30
+ }
31
+ return validator;
32
+ }
33
+ /**
34
+ * Validate data against a schema at runtime.
35
+ * Throws an error if validation fails.
36
+ */
37
+ function validateData(data, schema) {
38
+ const validator = getValidator(schema);
39
+ const result = validator(data);
40
+ if (result instanceof type.errors) {
41
+ throw new Error(`Data validation failed: ${result.summary}`);
42
+ }
43
+ }
44
+ // =============================================================================
45
+ // createParser Factory
46
+ // =============================================================================
47
+ /**
48
+ * Create a type-safe parser from node schemas.
49
+ *
50
+ * The returned parser has both:
51
+ * - Compile-time type inference via Parse<Grammar, Input, Context>
52
+ * - Runtime parsing that matches the type structure
53
+ * - Bound evaluator with type-safe data requirements
54
+ *
55
+ * @param nodes - Tuple of node schemas defining the grammar
56
+ * @returns Parser instance with type-safe parse method
57
+ *
58
+ * @example
59
+ * ```ts
60
+ * import { defineNode, number, expr, constVal, createParser } from "stringent";
61
+ *
62
+ * const add = defineNode({
63
+ * name: "add",
64
+ * pattern: [lhs("number").as("left"), constVal("+"), rhs("number").as("right")],
65
+ * precedence: 1,
66
+ * resultType: "number",
67
+ * eval: ({ left, right }) => left + right,
68
+ * });
69
+ *
70
+ * const parser = createParser([add] as const);
71
+ *
72
+ * // Type-safe parsing with bound evaluator!
73
+ * const [evaluator, err] = parser.parse("x + 1", { x: 'number' });
74
+ * if (!err) {
75
+ * const value = evaluator({ x: 5 }); // value is number, equals 6
76
+ * const ast = evaluator.ast;
77
+ * }
78
+ * ```
79
+ */
80
+ export function createParser(nodes) {
81
+ // Implementation function with explicit typing to avoid deep instantiation
82
+ const parse = (input, schema) => {
83
+ try {
84
+ // type.validate ensures all string values are valid arktype types
85
+ const context = { data: schema };
86
+ const result = runtimeParse(nodes, input, context);
87
+ // Check for parse failure (empty result or non-empty remaining)
88
+ if (!Array.isArray(result) || result.length !== 2) {
89
+ return [null, new Error(`Parse failed: unexpected result format`)];
90
+ }
91
+ const [ast, remaining] = result;
92
+ if (remaining !== '') {
93
+ return [null, new Error(`Parse failed: unexpected input at '${remaining}'`)];
94
+ }
95
+ // Create the bound evaluator
96
+ const evaluatorFn = (data) => {
97
+ // Validate data against schema at runtime
98
+ validateData(data, schema);
99
+ // Evaluate with the bound nodes - cast for internal use
100
+ // The type system ensures data matches the schema at compile time
101
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
102
+ return evaluate(ast, { data, nodes });
103
+ };
104
+ // Create the evaluator object with call signature and properties
105
+ const evaluator = Object.assign(evaluatorFn, {
106
+ ast: ast,
107
+ schema: schema,
108
+ });
109
+ return [evaluator, null];
110
+ }
111
+ catch (error) {
112
+ return [null, error instanceof Error ? error : new Error(String(error))];
113
+ }
114
+ };
115
+ // Return the parser object - cast to avoid deep type checking
116
+ return { parse, nodes };
117
+ }
118
+ //# sourceMappingURL=createParser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createParser.js","sourceRoot":"","sources":["../src/createParser.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Error Types and Utilities
3
+ *
4
+ * Provides rich error information for parse failures including:
5
+ * - Position tracking (offset, line, column)
6
+ * - Source snippets showing where errors occurred
7
+ * - Descriptive messages for different error types
8
+ */
9
+ /**
10
+ * Source position in the input string.
11
+ */
12
+ export interface SourcePosition {
13
+ /** Zero-based character offset from start of input */
14
+ readonly offset: number;
15
+ /** One-based line number */
16
+ readonly line: number;
17
+ /** One-based column number (characters from start of line) */
18
+ readonly column: number;
19
+ }
20
+ /**
21
+ * Calculate position information from input and current offset.
22
+ *
23
+ * @param input - The original input string
24
+ * @param offset - Character offset into the input
25
+ * @returns Position with line and column numbers
26
+ */
27
+ export declare function calculatePosition(input: string, offset: number): SourcePosition;
28
+ /** Error kinds for categorization */
29
+ export type ParseErrorKind = 'no_match' | 'type_mismatch' | 'unterminated_string' | 'unclosed_paren' | 'unexpected_token' | 'empty_input';
30
+ /**
31
+ * Rich parse error with position and context information.
32
+ */
33
+ export interface RichParseError {
34
+ /** Error marker for type discrimination */
35
+ readonly __error: true;
36
+ /** Error kind for categorization */
37
+ readonly kind: ParseErrorKind;
38
+ /** Human-readable error message */
39
+ readonly message: string;
40
+ /** Position where the error occurred */
41
+ readonly position: SourcePosition;
42
+ /** The problematic input snippet */
43
+ readonly snippet: string;
44
+ /** The full original input */
45
+ readonly input: string;
46
+ /** Additional context (e.g., expected type, actual type) */
47
+ readonly context?: ErrorContext;
48
+ }
49
+ /**
50
+ * Additional error context for specific error types.
51
+ */
52
+ export interface ErrorContext {
53
+ /** Expected type for type mismatch errors */
54
+ expected?: string;
55
+ /** Actual type for type mismatch errors */
56
+ actual?: string;
57
+ /** What was being parsed when error occurred */
58
+ parsing?: string;
59
+ }
60
+ /**
61
+ * Create a snippet of the input around the error position.
62
+ *
63
+ * @param input - The full input string
64
+ * @param offset - Character offset where error occurred
65
+ * @param contextChars - Number of characters to show around the error
66
+ * @returns A snippet with error position marker
67
+ */
68
+ export declare function createSnippet(input: string, offset: number, contextChars?: number): string;
69
+ /**
70
+ * Create a RichParseError.
71
+ */
72
+ export declare function createParseError(kind: ParseErrorKind, message: string, input: string, offset: number, context?: ErrorContext): RichParseError;
73
+ /**
74
+ * Create a "no match" error.
75
+ */
76
+ export declare function noMatchError(input: string, offset: number): RichParseError;
77
+ /**
78
+ * Create a "type mismatch" error.
79
+ */
80
+ export declare function typeMismatchError(input: string, offset: number, expected: string, actual: string, parsing?: string): RichParseError;
81
+ /**
82
+ * Create an "unterminated string" error.
83
+ */
84
+ export declare function unterminatedStringError(input: string, offset: number, quote: string): RichParseError;
85
+ /**
86
+ * Create an "unclosed parenthesis" error.
87
+ */
88
+ export declare function unclosedParenError(input: string, offset: number): RichParseError;
89
+ /**
90
+ * Create an "unexpected token" error.
91
+ */
92
+ export declare function unexpectedTokenError(input: string, offset: number, found: string, expected?: string): RichParseError;
93
+ /**
94
+ * Create an "empty input" error.
95
+ */
96
+ export declare function emptyInputError(input: string): RichParseError;
97
+ import type { ASTNode } from './primitive/index.js';
98
+ /**
99
+ * Extended parse result that can include error information.
100
+ * - Empty array: no match (backward compatible)
101
+ * - [node, remaining]: successful parse
102
+ * - [node, remaining, errors]: successful parse with collected errors
103
+ */
104
+ export type ParseResultWithErrors<T extends ASTNode<string, unknown> = ASTNode<string, unknown>> = [] | [T & {}, string] | [T & {}, string, RichParseError[]];
105
+ /**
106
+ * Check if a result includes errors.
107
+ */
108
+ export declare function hasErrors(result: ParseResultWithErrors): boolean;
109
+ /**
110
+ * Get errors from a result, or empty array if none.
111
+ */
112
+ export declare function getErrors(result: ParseResultWithErrors): RichParseError[];
113
+ /**
114
+ * Format an error for display with source context.
115
+ */
116
+ export declare function formatError(error: RichParseError): string;
117
+ /**
118
+ * Format multiple errors for display.
119
+ */
120
+ export declare function formatErrors(errors: RichParseError[]): string;
121
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,sDAAsD;IACtD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,4BAA4B;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,8DAA8D;IAC9D,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,cAAc,CAO/E;AAMD,qCAAqC;AACrC,MAAM,MAAM,cAAc,GACtB,UAAU,GACV,eAAe,GACf,qBAAqB,GACrB,gBAAgB,GAChB,kBAAkB,GAClB,aAAa,CAAC;AAElB;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,2CAA2C;IAC3C,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC;IACvB,oCAAoC;IACpC,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC;IAC9B,mCAAmC;IACnC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,wCAAwC;IACxC,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAC;IAClC,oCAAoC;IACpC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,8BAA8B;IAC9B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,4DAA4D;IAC5D,QAAQ,CAAC,OAAO,CAAC,EAAE,YAAY,CAAC;CACjC;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,6CAA6C;IAC7C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,2CAA2C;IAC3C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gDAAgD;IAChD,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAMD;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,GAAE,MAAW,GAAG,MAAM,CAuB9F;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,cAAc,EACpB,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,YAAY,GACrB,cAAc,CAUhB;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,cAAc,CAe1E;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,MAAM,GACf,cAAc,CAYhB;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,GACZ,cAAc,CAOhB;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,cAAc,CAOhF;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,EACb,QAAQ,CAAC,EAAE,MAAM,GAChB,cAAc,CAWhB;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,cAAc,CAE7D;AAMD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAEpD;;;;;GAKG;AACH,MAAM,MAAM,qBAAqB,CAAC,CAAC,SAAS,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,IAC3F,EAAE,GACF,CAAC,CAAC,GAAG,EAAE,EAAE,MAAM,CAAC,GAChB,CAAC,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,cAAc,EAAE,CAAC,CAAC;AAEvC;;GAEG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAEhE;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,qBAAqB,GAAG,cAAc,EAAE,CAKzE;AAMD;;GAEG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,cAAc,GAAG,MAAM,CAmBzD;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,cAAc,EAAE,GAAG,MAAM,CAE7D"}