From b6c9d0fed298b8ebb7d562c91afdecd664178db6 Mon Sep 17 00:00:00 2001 From: Truong Hoang Dung Date: Mon, 23 Dec 2024 01:27:28 +0700 Subject: [PATCH] Update index.ts --- src/index.ts | 341 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 341 insertions(+) diff --git a/src/index.ts b/src/index.ts index e69de29..2e270de 100644 --- a/src/index.ts +++ b/src/index.ts @@ -0,0 +1,341 @@ +// src/parser-combinator/types.ts +export interface ParseResult { + success: boolean; + value?: T; + index: number; + error?: string; +} + +export interface ParseState { + input: string; + index: number; +} + +export type ParserFn = (state: ParseState) => ParseResult; + +export class Parser { + constructor(public parse: ParserFn) {} + + map(fn: (value: T) => U): Parser { + return new Parser(state => { + const result = this.parse(state); + if (!result.success) return result; + return { + success: true, + value: fn(result.value!), + index: result.index + }; + }); + } + + chain(fn: (value: T) => Parser): Parser { + return new Parser(state => { + const result = this.parse(state); + if (!result.success) return result; + return fn(result.value!).parse({ ...state, index: result.index }); + }); + } + + or(other: Parser): Parser { + return new Parser(state => { + const result = this.parse(state); + if (result.success) return result; + return other.parse(state); + }); + } + + then(other: Parser): Parser { + return this.chain(() => other); + } + + skip(other: Parser): Parser { + return this.chain(value => other.map(() => value)); + } + + many(): Parser { + return new Parser(state => { + const results: T[] = []; + let currentState = state; + + while (true) { + const result = this.parse(currentState); + if (!result.success) break; + + results.push(result.value!); + currentState = { ...currentState, index: result.index }; + } + + return { + success: true, + value: results, + index: currentState.index + }; + }); + } + + many1(): Parser { + return new Parser(state => { + const result = this.many().parse(state); + if (!result.success) return result; + if (result.value!.length === 0) { + return { + success: false, + index: state.index, + error: 'Expected at least one match' + }; + } + return result; + }); + } + + optional(): Parser { + return new Parser(state => { + const result = this.parse(state); + if (result.success) return result; + return { + success: true, + value: null, + index: state.index + }; + }); + } + + trim(): Parser { + return whitespace.then(this).skip(whitespace); + } +} + +// src/parser-combinator/primitives.ts +export const str = (s: string): Parser => + new Parser(state => { + const { input, index } = state; + if (input.slice(index).startsWith(s)) { + return { + success: true, + value: s, + index: index + s.length + }; + } + return { + success: false, + index, + error: `Expected "${s}"` + }; + }); + +export const regex = (pattern: RegExp): Parser => + new Parser(state => { + const { input, index } = state; + pattern.lastIndex = index; + const match = pattern.exec(input); + + if (match && match.index === index) { + return { + success: true, + value: match[0], + index: index + match[0].length + }; + } + + return { + success: false, + index, + error: `Expected pattern ${pattern}` + }; + }); + +export const succeed = (value: T): Parser => + new Parser(state => ({ + success: true, + value, + index: state.index + })); + +export const fail = (error: string): Parser => + new Parser(state => ({ + success: false, + index: state.index, + error + })); + +export const eof = new Parser(state => { + if (state.index >= state.input.length) { + return { + success: true, + value: null, + index: state.index + }; + } + return { + success: false, + index: state.index, + error: 'Expected end of input' + }; +}); + +// src/parser-combinator/combinators.ts +export const lazy = (parserThunk: () => Parser): Parser => + new Parser(state => { + const parser = parserThunk(); + return parser.parse(state); + }); + +export const sepBy = ( + parser: Parser, + separator: Parser +): Parser => + new Parser(state => { + const results: T[] = []; + let currentState = state; + + // Parse first element + const firstResult = parser.parse(currentState); + if (!firstResult.success) { + return { + success: true, + value: results, + index: currentState.index + }; + } + + results.push(firstResult.value!); + currentState = { ...currentState, index: firstResult.index }; + + // Parse subsequent elements + while (true) { + const sepResult = separator.parse(currentState); + if (!sepResult.success) break; + + const elemResult = parser.parse({ + ...currentState, + index: sepResult.index + }); + + if (!elemResult.success) break; + + results.push(elemResult.value!); + currentState = { ...currentState, index: elemResult.index }; + } + + return { + success: true, + value: results, + index: currentState.index + }; + }); + +export const between = ( + open: Parser, + close: Parser, + parser: Parser +): Parser => + open.then(parser).skip(close); + +export const choice = (parsers: Parser[]): Parser => + parsers.reduce((acc, parser) => acc.or(parser)); + +// Common parsers +export const whitespace = regex(/^\s*/); +export const digits = regex(/^[0-9]+/); +export const letters = regex(/^[a-zA-Z]+/); +export const alphanumeric = regex(/^[a-zA-Z0-9]+/); +export const identifier = regex(/^[a-zA-Z_][a-zA-Z0-9_]*/); + +// Helper functions +export const sequence = (parsers: Parser[]): Parser => + new Parser(state => { + const results: T[] = []; + let currentState = state; + + for (const parser of parsers) { + const result = parser.parse(currentState); + if (!result.success) return result; + + results.push(result.value!); + currentState = { ...currentState, index: result.index }; + } + + return { + success: true, + value: results, + index: currentState.index + }; + }); + +// Additional utility combinators +export const not = (parser: Parser): Parser => + new Parser(state => { + const result = parser.parse(state); + if (result.success) { + return { + success: false, + index: state.index, + error: 'Unexpected match' + }; + } + return { + success: true, + value: null, + index: state.index + }; + }); + +export const lookAhead = (parser: Parser): Parser => + new Parser(state => { + const result = parser.parse(state); + if (!result.success) return result; + return { + ...result, + index: state.index + }; + }); + +export const takeUntil = (parser: Parser): Parser => + new Parser(state => { + let currentIndex = state.index; + while (currentIndex < state.input.length) { + const result = parser.parse({ + input: state.input, + index: currentIndex + }); + + if (result.success) { + return { + success: true, + value: state.input.slice(state.index, currentIndex), + index: currentIndex + }; + } + + currentIndex++; + } + + return { + success: true, + value: state.input.slice(state.index), + index: state.input.length + }; + }); + +// Example usage: +/* +const numberParser = digits.map(Number); +const stringParser = between( + str('"'), + str('"'), + takeUntil(str('"')) +); + +const arrayParser = between( + str('['), + str(']'), + sepBy( + choice([numberParser, stringParser]), + str(',').trim() + ) +); + +const input = '[1, "hello", 42]'; +const result = arrayParser.parse({ input, index: 0 }); +console.log(result); +*/