Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions example/typebox/interpreted/runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,10 +139,7 @@ const Reference = Runtime.Ident((result, context) => ReferenceMapping(result, co
function TemplateTextMapping(input: string) {
return T.Literal(input)
}
const TemplateText = Runtime.Union([
Runtime.Until('${'),
Runtime.Until('`'),
], TemplateTextMapping)
const TemplateText = Runtime.Until(['`', '${'], TemplateTextMapping)
// ------------------------------------------------------------------
// TemplateInterpolate
// ------------------------------------------------------------------
Expand Down
8 changes: 4 additions & 4 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -189,20 +189,20 @@ const R3 = Runtime.Parse(T, 'Y Z') // const R3 = [[], 'Y Z']

### Until

The Until combinator parses all characters up to (but not including) the specified string. The specified string remains unconsumed in the input. If the string is not found, parsing fails.
The Until combinator will parse characters up to (but not including) one of the specified sentinel string values. If a sentinel value is not found, parsing fails.

**BNF**

```bnf
<T> ::= ? any character until 'Z' ?
<T> ::= ? any character until ['Z'] ?
```

**TypeScript**

```typescript
const T = Runtime.Until('Z') // const T = {
const T = Runtime.Until(['Z']) // const T = {
// type: 'Until',
// value: 'Z'
// values: ['Z']
// }

const R = Runtime.Parse(T, 'X Y Z') // const R = ['X Y ', 'Z']
Expand Down
7 changes: 4 additions & 3 deletions src/compile/func/func.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,9 @@ function FromConst(options: Options, name: string, value: string): string {
// ------------------------------------------------------------------
// Const
// ------------------------------------------------------------------
function FromUntil(options: Options, name: string, value: string): string {
return `Runtime.Token.Until('${Escape(value)}', input)`
function FromUntil(options: Options, name: string, values: string[]): string {
const escaped = values.map(value => `'${Escape(value)}'`)
return `Runtime.Token.Until(${escaped.join(', ')}, input)`
}
// ------------------------------------------------------------------
// Ident
Expand Down Expand Up @@ -125,7 +126,7 @@ function FromParser(options: Options, name: string, parser: Runtime.IParser): st
Runtime.IsOptional(parser) ? FromOptional(options, name, parser) :
Runtime.IsString(parser) ? FromString(options, name, parser.options) :
Runtime.IsConst(parser) ? FromConst(options, name, parser.value) :
Runtime.IsUntil(parser) ? FromUntil(options, name, parser.value) :
Runtime.IsUntil(parser) ? FromUntil(options, name, parser.values) :
Runtime.IsRef(parser) ? FromRef(options, name, parser.ref) :
Runtime.IsIdent(parser) ? FromIdent(options, name) :
Runtime.IsNumber(parser) ? FromNumber(options, name) :
Expand Down
7 changes: 4 additions & 3 deletions src/compile/type/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,9 @@ function FromConst(options: Options, name: string, value: string): string {
// ------------------------------------------------------------------
// Until
// ------------------------------------------------------------------
function FromUntil(options: Options, name: string, value: string): string {
return `Static.Token.Until<'${Escape(value)}', Input>`
function FromUntil(options: Options, name: string, values: string[]): string {
const escaped = values.map(value => `'${Escape(value)}'`)
return `Static.Token.Until<${escaped.join(', ')}, Input>`
}
// ------------------------------------------------------------------
// Ident
Expand Down Expand Up @@ -127,7 +128,7 @@ function FromParser(options: Options, name: string, parser: Runtime.IParser): st
Runtime.IsOptional(parser) ? FromOptional(options, name, parser) :
Runtime.IsString(parser) ? FromString(options, name, parser.options) :
Runtime.IsConst(parser) ? FromConst(options, name, parser.value) :
Runtime.IsUntil(parser) ? FromUntil(options, name, parser.value) :
Runtime.IsUntil(parser) ? FromUntil(options, name, parser.values) :
Runtime.IsRef(parser) ? FromRef(options, name, parser.ref) :
Runtime.IsIdent(parser) ? FromIdent(options, name) :
Runtime.IsNumber(parser) ? FromNumber(options, name) :
Expand Down
14 changes: 7 additions & 7 deletions src/runtime/parse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,12 +66,6 @@ function ParseConst<Value extends string>(value: Value, code: string, context: u
return Token.Const(value, code) as never
}
// ------------------------------------------------------------------
// Until
// ------------------------------------------------------------------
function ParseUntil<Value extends string>(value: Value, code: string, context: unknown): [] | [Value, string] {
return Token.Until(value, code) as never
}
// ------------------------------------------------------------------
// Ident
// ------------------------------------------------------------------
function ParseIdent(code: string, _context: unknown): [] | [string, string] {
Expand Down Expand Up @@ -129,6 +123,12 @@ function ParseUnion<ModuleProperties extends Types.IModuleProperties, Parsers ex
return []
}
// ------------------------------------------------------------------
// Until
// ------------------------------------------------------------------
function ParseUntil<Values extends string[]>(values: [...Values], code: string, context: unknown): [] | [string, string] {
return Token.Until(values, code) as never
}
// ------------------------------------------------------------------
// Parser
// ------------------------------------------------------------------
function ParseParser<Parser extends Types.IParser>(moduleProperties: Types.IModuleProperties, parser: Parser, code: string, context: unknown): [] | [Types.StaticParser<Parser>, string] {
Expand All @@ -143,7 +143,7 @@ function ParseParser<Parser extends Types.IParser>(moduleProperties: Types.IModu
Types.IsString(parser) ? ParseString(parser.options, code, context) :
Types.IsTuple(parser) ? ParseTuple(moduleProperties, parser.parsers, code, context) :
Types.IsUnion(parser) ? ParseUnion(moduleProperties, parser.parsers, code, context) :
Types.IsUntil(parser) ? ParseUntil(parser.value, code, context) :
Types.IsUntil(parser) ? ParseUntil(parser.values, code, context) :
[]
)
return (result.length === 2 ? [parser.mapping(result[0], context), result[1]] : result) as never
Expand Down
26 changes: 15 additions & 11 deletions src/runtime/token.ts
Original file line number Diff line number Diff line change
Expand Up @@ -233,18 +233,22 @@ export function String(options: string[], input: string) {
// ------------------------------------------------------------------
// Until
// ------------------------------------------------------------------
function UntilStartsWith(value: string, input: string) {
return input.startsWith(value)
}
export function Until(value: string, input: string, result: string = ''): [] | [string, string] {
function UntilStartsWith(value: string[], input: string): boolean {
const [left, ...right] = value
return (typeof left === 'string')
? input.startsWith(left)
? true
: UntilStartsWith(right, input)
: false
}
export function Until(value: string[], input: string, result: string = ''): [] | [string, string] {
return (
input === '' ? [] : (() => {
return UntilStartsWith(value, input)
? [result, input]
: (() => {
const [left, right] = [input.slice(0, 1), input.slice(1)]
return Until(value, right, `${result}${left}`)
})()
input === '' ? [] :
UntilStartsWith(value, input)
? [result, input]
: (() => {
const [left, right] = [input.slice(0, 1), input.slice(1)]
return Until(value, right, `${result}${left}`)
})()
)
}
14 changes: 7 additions & 7 deletions src/runtime/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -324,22 +324,22 @@ export function IsUnion(value: unknown): value is IUnion {
// ------------------------------------------------------------------
export interface IUntil<Output extends unknown = unknown> extends IParser<Output> {
type: 'Until'
value: string
values: string[]
}
/** `[TERM]` Creates a Until Parser */
export function Until<Mapping extends IMapping<string>>(value: string, mapping: Mapping): IUntil<string>
export function Until<Mapping extends IMapping<string>>(values: string[], mapping: Mapping): IUntil<string>
/** `[TERM]` Creates a Until Parser */
export function Until(value: string): IUntil<string>
export function Until(values: string[]): IUntil<string>
/** `[TERM]` Creates a Until Parser */
export function Until(...args: unknown[]): never {
const [value, mapping] = args.length === 2 ? [args[0], args[1]] : [args[0], Identity]
return { type: 'Until', value, mapping } as never
const [values, mapping] = args.length === 2 ? [args[0], args[1]] : [args[0], Identity]
return { type: 'Until', values, mapping } as never
}
/** Returns true if the value is a Until Parser */
export function IsUntil(value: unknown): value is IUntil {
return Guard.IsObject(value)
&& Guard.HasPropertyKey(value, 'type')
&& Guard.HasPropertyKey(value, 'value')
&& Guard.HasPropertyKey(value, 'values')
&& Guard.IsEqual(value.type, 'Until')
&& Guard.IsString(value.value)
&& Guard.IsArray(value.values)
}
18 changes: 9 additions & 9 deletions src/static/parse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,6 @@ type ConstParser<Value extends string, Input extends string, _Context extends un
: []
)
// ------------------------------------------------------------------
// Until
// ------------------------------------------------------------------
type UntilParser<Value extends string, Input extends string, _Context extends unknown> = (
Tokens.Until<Value, Input> extends [infer Match extends string, infer Rest extends string]
? [Match, Rest]
: []
)
// ------------------------------------------------------------------
// Ident
// ------------------------------------------------------------------
type IdentParser<Input extends string, _Context extends unknown> = (
Expand Down Expand Up @@ -116,6 +108,14 @@ type UnionParser<Parsers extends Types.IParser[], Input extends string, Context
: []
)
// ------------------------------------------------------------------
// Until
// ------------------------------------------------------------------
type UntilParser<Values extends string[], Input extends string, _Context extends unknown> = (
Tokens.Until<Values, Input> extends [infer Match extends string, infer Rest extends string]
? [Match, Rest]
: []
)
// ------------------------------------------------------------------
// Parse
// ------------------------------------------------------------------
type ParseCode<Parser extends Types.IParser, Input extends string, Context extends unknown = unknown> = (
Expand All @@ -128,7 +128,7 @@ type ParseCode<Parser extends Types.IParser, Input extends string, Context exten
Parser extends Types.String<infer Options extends string[]> ? StringParser<Options, Input, Context> :
Parser extends Types.Tuple<infer Parsers extends Types.IParser[]> ? TupleParser<Parsers, Input, Context> :
Parser extends Types.Union<infer Parsers extends Types.IParser[]> ? UnionParser<Parsers, Input, Context> :
Parser extends Types.Until<infer Value extends string> ? UntilParser<Value, Input, Context> :
Parser extends Types.Until<infer Values extends string[]> ? UntilParser<Values, Input, Context> :
[]
)
type ParseMapping<Parser extends Types.IParser, Result extends unknown, Context extends unknown = unknown> = (
Expand Down
14 changes: 9 additions & 5 deletions src/static/token.ts
Original file line number Diff line number Diff line change
Expand Up @@ -204,14 +204,18 @@ export type Ident<Code extends string> = NextIdent<Trim.TrimAll<Code>>
// ------------------------------------------------------------------
// Until
// ------------------------------------------------------------------
type UntilStartsWith<Value extends string, Input extends string> = (
Input extends `${Value}${string}` ? true : false
type UntilStartsWith<Values extends string[], Input extends string> = (
Values extends [infer Left extends string, ...infer Right extends string[]]
? Input extends `${Left}${string}`
? true
: UntilStartsWith<Right, Input>
: false
)
export type Until<Value extends string, Input extends string, Result extends string = ''> = (
export type Until<Values extends string[], Input extends string, Result extends string = ''> = (
Input extends `` ? [] :
UntilStartsWith<Value, Input> extends true
UntilStartsWith<Values, Input> extends true
? [Result, Input]
: Input extends `${infer Left extends string}${infer Right extends string}`
? Until<Value, Right, `${Result}${Left}`>
? Until<Values, Right, `${Result}${Left}`>
: never
)
9 changes: 4 additions & 5 deletions src/static/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,25 +88,24 @@ export interface Const<Value extends string = string, Mapping extends IMapping =
// Until
// ------------------------------------------------------------------
/** Creates a Until Parser */
export interface Until<Value extends string = string, Mapping extends IMapping = Identity> extends IParser<Mapping> {
export interface Until<Values extends string[] = string[], Mapping extends IMapping = Identity> extends IParser<Mapping> {
type: 'Until'
value: Value
values: Values
}
// ------------------------------------------------------------------
// Ident
// ------------------------------------------------------------------
/** Creates an Ident Parser. */
/** Creates an Ident Parser */
export interface Ident<Mapping extends IMapping = Identity> extends IParser<Mapping> {
type: 'Ident'
}
// ------------------------------------------------------------------
// Number
// ------------------------------------------------------------------
/** Creates a Number Parser. */
/** Creates a Number Parser */
export interface Number<Mapping extends IMapping = Identity> extends IParser<Mapping> {
type: 'Number'
}

// ------------------------------------------------------------------
// Optional
// ------------------------------------------------------------------
Expand Down
2 changes: 1 addition & 1 deletion tasks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ Task.run('build', () => Task.build('src', {
packageJson: {
name: '@sinclair/parsebox',
description: 'Parser Combinators in the TypeScript Type System',
version: '0.9.3',
version: '0.9.4',
keywords: ['typescript', 'parser', 'combinator'],
license: 'MIT',
author: 'sinclairzx81',
Expand Down
14 changes: 10 additions & 4 deletions test/__tests__/runtime/parse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,17 @@ Deno.test('Const', () => {
// Until
// ----------------------------------------------------------------
Deno.test('Until', () => {
Assert(Runtime.Parse(Runtime.Until('A'), ''), [])
Assert(Runtime.Parse(Runtime.Until('A'), 'A'), ['', 'A'])
Assert(Runtime.Parse(Runtime.Until('A'), ' A'), [' ', 'A'])
Assert(Runtime.Parse(Runtime.Until('A'), ' A '), [' ', 'A '])
Assert(Runtime.Parse(Runtime.Until(['A']), ''), [])
Assert(Runtime.Parse(Runtime.Until(['A']), 'A'), ['', 'A'])
Assert(Runtime.Parse(Runtime.Until(['A']), ' A'), [' ', 'A'])
Assert(Runtime.Parse(Runtime.Until(['A']), ' A '), [' ', 'A '])

Assert(Runtime.Parse(Runtime.Until(['A', 'B']), ''), [])
Assert(Runtime.Parse(Runtime.Until(['A', 'B']), 'BA'), ['', 'BA'])
Assert(Runtime.Parse(Runtime.Until(['A', 'B']), ' BA'), [' ', 'BA'])
Assert(Runtime.Parse(Runtime.Until(['A', 'B']), ' BA '), [' ', 'BA '])
})

// ----------------------------------------------------------------
// Ident
// ----------------------------------------------------------------
Expand Down
Loading