( input: string, iterable: ParseGenerator<Result> )
| 33 | | Iterable<ParseItem>; |
| 34 | |
| 35 | export function parse<Result = void>( |
| 36 | input: string, |
| 37 | iterable: ParseGenerator<Result> |
| 38 | ): ParseResult<Result> { |
| 39 | let lastResult: ParseYieldedValue<ParseItem> | undefined; |
| 40 | |
| 41 | let iterationCount = -1; |
| 42 | const iterator = iterable[Symbol.iterator](); |
| 43 | |
| 44 | main: while (true) { |
| 45 | const nestedErrors: Array<ParseError> = []; |
| 46 | |
| 47 | iterationCount += 1; |
| 48 | const next = iterator.next(lastResult as any); |
| 49 | if (next.done) { |
| 50 | if (next.value instanceof Error) { |
| 51 | return { |
| 52 | success: false, |
| 53 | remaining: input, |
| 54 | failedOn: { |
| 55 | iterationCount, |
| 56 | yielded: next.value, |
| 57 | }, |
| 58 | }; |
| 59 | } |
| 60 | |
| 61 | return { |
| 62 | success: true, |
| 63 | remaining: input, |
| 64 | result: next.value, |
| 65 | }; |
| 66 | } |
| 67 | |
| 68 | const yielded = next.value as ParseItem; |
| 69 | const choices = |
| 70 | typeof yielded !== 'string' && (yielded as any)[Symbol.iterator] |
| 71 | ? (yielded as Iterable<ParseItem>) |
| 72 | : [yielded]; |
| 73 | |
| 74 | for (const choice of choices) { |
| 75 | if (typeof choice === 'string') { |
| 76 | let found = false; |
| 77 | const newInput = input.replace(choice, (_1, offset: number) => { |
| 78 | found = offset === 0; |
| 79 | return ''; |
| 80 | }); |
| 81 | if (found) { |
| 82 | input = newInput; |
| 83 | lastResult = choice; |
| 84 | continue main; |
| 85 | } |
| 86 | } else if (choice instanceof RegExp) { |
| 87 | if (['^', '$'].includes(choice.source[0]) === false) { |
| 88 | throw new Error(`Regex must be from start: ${choice}`); |
| 89 | } |
| 90 | const match = input.match(choice); |
| 91 | if (match) { |
| 92 | lastResult = match; |
no outgoing calls