Hi, I have tried to port this TypeScript script: https://github.com/sigma-engineering/blog-combinators/blob/master/index.ts
to Vim9 script (please find the full code at the end of this message). It appears to work, but to make it work I had to add a "forward declaration" of the Call() function: var Call: func(dict<any>): dict<any> If I comment out the above line, I get a "E1001: Variable not found: Call" error, pointing at this line: const TrailingArg = Map(Sequence([Str(","), Expr]), (args) => args[1]) What baffles me is that Vim doesn't complain in the same way about NumberLiteral. Does anyone have an explanation for this behaviour? Using Vim 8.2.3350. Thanks, Life. ############################################################################## vim9script # See also: https://github.com/sigma-engineering/blog-combinators # type TSuccess dict<any> # type TFailure dict<any> # type TResult TSuccess | TFailure # type TContext dict<any> # type TParser func(TContext): TResult def Success(ctx: dict<any>, value: any): dict<any> return { success: true, value: value, ctx: ctx } enddef def Failure(ctx: dict<any>, expected: string): dict<any> return { success: false, expected: expected, ctx: ctx } enddef # Match a string exactly, or fail def Str(match: string): func(dict<any>): dict<any> return (ctx: dict<any>): dict<any> => { const endIndex = ctx.index + len(match) - 1 if (ctx.text[(ctx.index) : (endIndex)] ==# match) ctx.index = endIndex + 1 return Success(ctx, match) else return Failure(ctx, match) endif } enddef # Match a regexp or fail def Regex(re: string, expected: string): func(dict<any>): dict<any> return (ctx: dict<any>): dict<any> => { const res = matchstrpos(ctx.text, re, ctx.index) if res[1] == ctx.index ctx.index = res[2] return Success(ctx, res[0]) else return Failure(ctx, expected) endif } enddef # Try each matcher in order, starting from the same point in the input. return # the first one that succeeds. or return the failure that got furthest in the # input string. Which failure to return is a matter of taste; we prefer the # furthest failure because it tends be the most useful/complete error # message. def Either(Parsers: list<func(dict<any>): dict<any>>): func(dict<any>): dict<any> return (ctx: dict<any>): dict<any> => { var furthestRes: dict<any> = {ctx: {index: -1}} for Parser in Parsers const res = Parser(ctx) if res.success return res endif if furthestRes.ctx.index < res.ctx.index furthestRes = res endif endfor return furthestRes } enddef def Null(ctx: dict<any>): dict<any> return Success(ctx, null) enddef # Match a parser, or fail silently def Optional(Parser: func(dict<any>): dict<any>): func(dict<any>): dict<any> return Either([Parser, Null]) enddef def Many(Parser: func(dict<any>): dict<any>): func(dict<any>): dict<any> return (ctx): dict<any> => { var values = [] var nextCtx = ctx while (true) const res = Parser(nextCtx) if (!res.success) break endif values->add(res.value) nextCtx = res.ctx endwhile return Success(nextCtx, values) } enddef # Look for an exact sequence of parsers, or fail def Sequence(Parsers: list<func(dict<any>): dict<any>>): func(dict<any>): dict<any> return (ctx): dict<any> => { var values = [] var nextCtx = ctx for Parser in Parsers const res = Parser(nextCtx) if (!res.success) return res endif values->add(res.value) nextCtx = res.ctx endfor return Success(nextCtx, values) } enddef # A convenience method that will map a Success to callback, to let us do # common things like build AST nodes from input strings. def Map(Parser: func(dict<any>): dict<any>, Fn: func(any): any): func(dict<any>): dict<any> return (ctx: dict<any>): dict<any> => { const res = Parser(ctx) return res.success ? Success(res.ctx, Fn(res.value)) : res } enddef def Str2Nr(n: string): number return str2nr(n) enddef # Grammar-specific # Expr ::= Call | NumberLiteral # Call ::= Ident '(' [ArgList] ')' # ArgList ::= Expr (TrailingArg)* # TrailingArg ::= ',' Expr # Number ::= '[+-]\?[0-9]\+' # Ident ::= '[a-zA-Z][a-zA-Z0-9]*' ######################################################### # This seems necessary for Vim to digest what follows: # ######################################################### var Call: func(dict<any>): dict<any> ######################################################### # Why? # ######################################################### def Expr(ctx: dict<any>): dict<any> return Either([NumberLiteral, Call])(ctx) enddef const Ident = Regex('[a-zA-Z][a-zA-Z0-9]*', 'identifier') const NumberLiteral = Map(Regex('[+-]\?[0-9]\+', 'number'), Str2Nr) const TrailingArg = Map(Sequence([Str(","), Expr]), (args) => args[1]) const ArgList = Map(Sequence([Expr, Many(TrailingArg)]), (args) => flattennew(args)) Call = Sequence([Ident, Str('('), Optional(ArgList), Str(')')]) # Top level parsing function def Parse(text: string): any const res = Expr({ text: text, index: 0 }) if res.success return res.value endif return printf("Parse error, expected %s", res.expected) enddef def Example(code: string): void echo Parse(code) enddef Example("1") Example("Foo()") Example("Foo(Bar())") Example("Foo(Bar(1,2,3))") ############################################################################## -- -- You received this message from the "vim_use" maillist. Do not top-post! Type your reply below the text you are replying to. For more information, visit http://www.vim.org/maillist.php --- You received this message because you are subscribed to the Google Groups "vim_use" group. To unsubscribe from this group and stop receiving emails from it, send an email to vim_use+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/vim_use/sflhni%247rp%241%40ciao.gmane.io.