Parser in parser-ts not consuming the whole input

28 Views Asked by At

I'm using parser-ts to write a simple parser for a template language. The problems with my current parser are:

  1. f I provide it with an incomplete template like Hello {{name} it enters in an infinite loop and never ends parsing
  2. If I provide a string that is longer than a text and a variable like Hello {{name}}! it only parses the first two elements (text and variable) but it does not continue until consuming the whole string (so it includes the !)
  3. When it finds an incomplete identifier, it tries to parse with the text parser and it succeeds, so I get two text tokens rather than a parsing error.
 import * as P from 'parser-ts/Parser'
 import { run } from 'parser-ts/code-frame'
 import * as C from 'parser-ts/char'
 import * as S from 'parser-ts/string'
 import { Either, pipe } from '@std';
 type TemplateText = { _tag: 'text', value: string }
 type TemplateVariable = { _tag: 'variable', value: string }
 type Token = TemplateText | TemplateVariable
 type ParsedTemplate = Token[];
 
 function TemplateText(value: string): TemplateText {
     return { _tag: 'text', value }
 }
 function TemplateVariable(value: string): TemplateVariable {
     return { _tag: 'variable', value }
 }
 
 
 // === Parsers ===
 type TokenParser = P.Parser<string, Token>
 // A parser that returns an empty string when it reaches the end of the input.
 // required to keep the same type as the other parsers.
 const EofStr = pipe(
     P.eof<string>(),
     P.map(() => ''))
 const open = S.fold([S.string('{{'), S.spaces])
 const close = P.expected(S.fold([S.spaces, S.string('}}')]), 'closing variable tag: "}}"')
 const identifier = S.many1(C.alphanum)
 const templateIdentifier: TokenParser = pipe(
     identifier,
     P.between(open, close),
     P.map(TemplateVariable),
 )
 
 
 export const OpenOrEof = P.either(open, () => EofStr)
 export const anythingUntilOpenOrEOF = P.many1Till(P.item<string>(), P.lookAhead(OpenOrEof))
 
 const text: TokenParser = pipe(
     anythingUntilOpenOrEOF,
     P.map((value) => TemplateText(value.join(''))))
 // function parseTemplate(template: string): E.Either<ParseError, ParsedTemplate> {
 const TextOrVariable: TokenParser = pipe(
     templateIdentifier,
     P.alt(() => text),
 )
 
 const Template = pipe(
     P.many(TextOrVariable),
     P.apFirst(P.eof()),
 )
 /**
  * Given a template string, parse it into an array of tokens.
  * Templates look like this:
  * "Hello {{name}}! You are {{age}} years old."
  * @param template a template string to convert into an array of tokens or an error
  */
 export function parseTemplate(template: string): Either<string, ParsedTemplate> {
     return run((Template), template)
     // return S.run(template)(P.many(Template))
 }
0

There are 0 best solutions below