Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Additional features for choosing how to parse input #53

Merged
merged 15 commits into from
Dec 13, 2020

Conversation

impl
Copy link
Contributor

@impl impl commented Dec 8, 2020

Hi!

We implemented a kubectl-like JSONPath template language on top of Gval, which we have been using in our production environment for about a year now with great success. To implement it we needed to add additional features to the base library.

For the languages:

  • Language.ChainEvaluable(ctx, parser): Immediately run this language when the next token is consumed by the given parser.
  • PrefixLanguage(prefix, lang, next): When the parser encounters the given prefix, it switches to another language. When that language cannot consume any more tokens, control is returned to the calling language and the next callback is invoked. Higher level interface for Language.ChainEvaluable(...).
  • Init(next): Rather than immediately calling parser.ParseExpression(), you can perform some setup work (like configuring the parser to handle whitespace or tokens a specific way).
  • Alternate(next): Called if no other parsing construct matches the given input.
  • Late(func(outer Language)) Language: Allow late-binding of languages. This way you can embed a language in itself recursively. This is useful for some template languages, or, say, Bash string/command interpolation.

For the token parser:

  • A language stack (see above).
  • SetMode, SetWhitespace, SetIsIdentRuneFunc: Modify the underlying Go text scanner with the corresponding configuration.

I also fixed a small bug in the infix operator merging strategy.

You can see our complete implementation of the JSONPath template language I mentioned here as an example:

Let me know if you have any questions or suggestions for improvements. I hope you find these additions as useful as we have!

@generikvault
Copy link
Collaborator

Hi Noah,

thank you for contributing such a great feature.

I saw just a few small issues. I will mark them directly in the code.

@generikvault
Copy link
Collaborator

And thanks for fixing some additional bugs.

language.go Outdated Show resolved Hide resolved
parser.go Show resolved Hide resolved
language.go Outdated
// Late allows late-binding of a language so that languages can be
// self-referencing. The return value of the passed function will be bound to
// the language.
func Late(fn func(late Language) Language) (binding Language) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm pretty sure you don't need this and could use somethig like

var a = PrefixExtension('x', parseX)
var b = PrefixExtension('y', parseY)

func parseX func(context.Context, *Parser) (Evaluable, error){
  p.ParseSublanguage(b)
}

func parseY func(context.Context, *Parser) (Evaluable, error){
  p.ParseSublanguage(a)
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, indeed, both this function and PrefixLanguage are trivially built using the other additions, and with entirely public interfaces.

Removing PrefixLanguage seems harmless, but my only concern about removing Late is that users may not discover such a mechanism for implementing recursive invocation by themselves. In many parser combinator libraries, in addition to the base set of parser constructs, they also expose some "patterns" that are built on top of the primitives, like Sequence, Many, Optional, etc. Do you think it would it make sense to have a patterns.go that has these type of utility language constructors in it?

For now, I've removed it altogether, but let me know if you'd like me to add it back in a different spot.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes that could be a problem. I think example tests may help.

language.go Outdated Show resolved Hide resolved
language.go Outdated Show resolved Hide resolved
…Extension, and place ParseSublanguage under the parser
@generikvault generikvault changed the base branch from master to sublanguage December 13, 2020 09:27
@generikvault generikvault merged commit fa27d20 into PaesslerAG:sublanguage Dec 13, 2020
@impl impl deleted the combinator-features branch December 13, 2020 17:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants