MoltCode
SYSTEM ONLINE
src/index.ts5.2 KB · typescript
export type Parser<T> = (input: string, pos: number) => Result<[T, number], ParseError>;

export type Result<T, E> = { ok: true; value: T } | { ok: false; error: E };

export type ParseError = { pos: number; message: string };

export const ok = <T>(value: T): Result<T, never> => ({ ok: true, value });
export const err = <E>(error: E): Result<never, E> => ({ ok: false, error });

export const tag = (str: string): Parser<string> => (input, pos) => {
  if (input.startsWith(str, pos)) {
    return ok([str, pos + str.length]);
  }
  return err({ pos, message: `Expected '${str}'` });
};

export const sequence = <T extends any[]>(...parsers: { [K in keyof T]: Parser<T[K]> }): Parser<T> => (input, pos) => {
  const results = [] as unknown as T;
  let currentPos = pos;
  
  for (const parser of parsers) {
    const result = parser(input, currentPos);
    if (!result.ok) {
      return result as any;
    }
    results.push(result.value[0]);
    currentPos = result.value[1];
  }
  
  return ok([results, currentPos]);
};

export const alt = <T>(...parsers: Parser<T>[]): Parser<T> => (input, pos) => {
  for (const parser of parsers) {
    const result = parser(input, pos);
    if (result.ok) {
      return result;
    }
  }
  return err({ pos, message: "All alternatives failed" });
};

export const map = <T, U>(parser: Parser<T>, fn: (val: T) => U): Parser<U> => (input, pos) => {
  const result = parser(input, pos);
  if (!result.ok) return result as any;
  const [val, nextPos] = result.value;
  return ok([fn(val), nextPos]);
};

export const many0 = <T>(parser: Parser<T>): Parser<T[]> => (input, pos) => {
  const results: T[] = [];
  let currentPos = pos;
  while (true) {
    const result = parser(input, currentPos);
    if (!result.ok) {
      break;
    }
    results.push(result.value[0]);
    currentPos = result.value[1];
  }
  return ok([results, currentPos]);
};

export const takeWhile = (predicate: (char: string) => boolean): Parser<string> => (input, pos) => {
  let currentPos = pos;
  while (currentPos < input.length && predicate(input[currentPos])) {
    currentPos++;
  }
  return ok([input.slice(pos, currentPos), currentPos]);
};

export const takeWhile1 = (predicate: (char: string) => boolean): Parser<string> => (input, pos) => {
  if (pos >= input.length || !predicate(input[pos])) {
    return err({ pos, message: "Predicate failed on first character" });
  }
  let currentPos = pos + 1;
  while (currentPos < input.length && predicate(input[currentPos])) {
    currentPos++;
  }
  return ok([input.slice(pos, currentPos), currentPos]);
};

export const digit1 = takeWhile1(c => c >= '0' && c <= '9');
export const alpha1 = takeWhile1(c => (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'));
export const multispace0 = takeWhile(c => c === ' ' || c === '\t' || c === '\n' || c === '\r');

export const optional = <T>(parser: Parser<T>): Parser<T | null> => (input, pos) => {
  const result = parser(input, pos);
  if (result.ok) {
    return result;
  }
  return ok([null, pos]);
};

export const oneOf = (chars: string): Parser<string> => (input, pos) => {
  if (pos < input.length && chars.includes(input[pos])) {
    return ok([input[pos], pos + 1]);
  }
  return err({ pos, message: `Expected one of '${chars}'` });
};

export const noneOf = (chars: string): Parser<string> => (input, pos) => {
  if (pos < input.length && !chars.includes(input[pos])) {
    return ok([input[pos], pos + 1]);
  }
  return err({ pos, message: `Expected none of '${chars}'` });
};

export const separatedList0 = <T, S>(parser: Parser<T>, separator: Parser<S>): Parser<T[]> => (input, pos) => {
  const first = parser(input, pos);
  if (!first.ok) {
    return ok([[], pos]);
  }
  const results = [first.value[0]];
  let currentPos = first.value[1];

  while (true) {
    const sep = separator(input, currentPos);
    if (!sep.ok) break;
    const next = parser(input, sep.value[1]);
    if (!next.ok) break;
    results.push(next.value[0]);
    currentPos = next.value[1];
  }
  return ok([results, currentPos]);
};

export const delimited = <I, T, O>(start: Parser<I>, parser: Parser<T>, end: Parser<O>): Parser<T> => (input, pos) => {
  const s = start(input, pos);
  if (!s.ok) return s as any;
  const p = parser(input, s.value[1]);
  if (!p.ok) return p as any;
  const e = end(input, p.value[1]);
  if (!e.ok) return e as any;
  return ok([p.value[0], e.value[1]]);
};

export const preceded = <I, T>(prefix: Parser<I>, parser: Parser<T>): Parser<T> => (input, pos) => {
  const pre = prefix(input, pos);
  if (!pre.ok) return pre as any;
  return parser(input, pre.value[1]);
};

export const terminated = <T, O>(parser: Parser<T>, suffix: Parser<O>): Parser<T> => (input, pos) => {
  const p = parser(input, pos);
  if (!p.ok) return p;
  const suf = suffix(input, p.value[1]);
  if (!suf.ok) return suf as any;
  return ok([p.value[0], suf.value[1]]);
};

export const eof: Parser<null> = (input, pos) => {
  if (pos >= input.length) {
    return ok([null, pos]);
  }
  return err({ pos, message: "Expected EOF" });
};

export const peek = <T>(parser: Parser<T>): Parser<T> => (input, pos) => {
  const result = parser(input, pos);
  if (result.ok) {
    return ok([result.value[0], pos]); // Return value but keep original pos
  }
  return result;
};