ohm icon indicating copy to clipboard operation
ohm copied to clipboard

Foreign rule invocation (reference namespaced grammars without extending them)

Open nickclaw opened this issue 7 years ago • 3 comments

For example, something like:

import ohm from 'ohm';
import { grammar as json } from 'ohm-grammar-json';

const namespace = ohm.namespace({ JSON: json });
const definition = ```
Example {
  Start
    = "foo" JSON.Value
}
```;

export default ns.grammar(definition, namespace);

where JSON.Value represents the Value syntactic rule of the JSON namespace.

nickclaw avatar Feb 25 '17 19:02 nickclaw

Hi Nicholas,

OMeta has a mechanism for doing just what you've described. It's called "foreign rule invocation", and it was very good for modularity.

It would be nice to support foreign rule invocation in Ohm, too. I've wanted to do this since the beginning, but there were always higher-priority items for us to work on. I don't think it would be very tricky to do, but if we're really going to do it, we should first come up with a good way for the programmer to specify semantic actions for rules that come from the other grammar(s). Let's start the discussion on this thread, and see where it goes.

I'll start with the following proposal: in an action dictionary, semantic actions that correspond to rules from other grammars should be specified like this:

{ ...

// Semantic action for the Value rule of the JSON grammar

"JSON.Value": function(/* num. args here depends on JSON's Value rule

*/) { ... },

// Semantic action for the Start rule of the Example grammar

// (it doesn't need a "GrammarName." prefix b/c the Example grammar is

the

// grammar that this Semantics belongs to.

Start(foo, value) {
  ...
},

...

}

This kind of thing is necessary because different grammars may have rules with the same name, and we must be able to specify which rule a semantic action is for.

A couple of thoughts about this initial proposal:

  • We may want to let programmers to specify semantic actions just using rule names, and only require "fully-qualified names" if / when there is an ambiguity.

  • If we use a "$" instead of a "." to separate the name of the grammar and the name of the rule, we can take advantage the ES6 sugar for methods in object literals: JSON$Value(x, y, z) { ... }. The downside is that JSON$Value doesn't look like the application of that rule in the grammar (JSON.Value). Also, there's an argument to be made against optimizing Ohm's design for interop with JS.

What do you people think? Does this seem reasonable? Can you think of a better alternative to what I've proposed?

And more importantly, would you use foreign rule invocation if Ohm supported it?

Cheers, Alex

On Sat, Feb 25, 2017 at 11:03 AM, Nicholas Clawson <[email protected]

wrote:

For example, something like:

import ohm from 'ohm';import { grammar as json } from 'ohm-grammar-json'; const namespace = ohm.namespace({ JSON: json });const definition = Example { Start = "foo" JSON.Value}; export default ns.grammar(definition, namespace);

where JSON.Value represents the Value syntactic rule of the JSON namespace.

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/harc/ohm/issues/191, or mute the thread https://github.com/notifications/unsubscribe-auth/AAPHi8VqPCmFTkO5qZIXg1-8abrpuBZ4ks5rgHsVgaJpZM4MMGoF .

alexwarth avatar Feb 25 '17 20:02 alexwarth

Thanks for responding to this so quickly, I'm glad you're interested in something like this.

What about a format like this for actions and attributes?


{
   Start(...args) { /* code */ },
    
   // this way you could potentially get the logic here from the module
   // that implements the grammar. The `<Symbol>` (tbd) would be
   // there to distinguish the namespace from rules of the same name
   <Symbol>CustomNamespaceNameForJson: {
      Start(...args) {  /* code */ },
   },
}

As for your other points I think:

  1. I personally prefer always having fully qualified names, I think it's important for "self documenting" so other people reading the grammar on have no question about ownership of a rule.

  2. No strong opinion about the syntax. Another form could be Rule@Grammar.

nickclaw avatar Feb 25 '17 22:02 nickclaw

Hello Alex,

What do you people think? Does this seem reasonable? Can you think of a better alternative to what I've proposed?

I think your suggestion sounds very reasonable. Regarding the fully qualified names, I would make them mandatory unless the rule belongs to the "own" grammar. As to the syntax, I would slightly prefer Rule$Grammar over "Rule.Grammar", because having a valid JS identifier can be quite useful.

Since I could really use foreign rule invocation for my master's thesis, I would be willing to invest a few days (if this is realistic) to implement it for Ohm. Could you give me some pointers on how to approach this, @alexwarth? This would be super helpful, since I do not really have a good understanding of the inner parts of Ohm.

My first idea would be to change pexprs.Apply.prototype.reallyEval so that it detects foreign rule invocations and then swaps the current grammar in the MatchState with the foreign one. After the grammar was swapped, it could do the usual rule lookup and then swap the grammar back. However, I'm not sure if this is the best way to approach this, is it?

philippotto avatar Jun 21 '17 15:06 philippotto