A DSL that changes itself?

Remember those experiments with MGrammar and Famulus?

As part of some more coding effort on knowledge graphs and inferencing, I thought that it'd be cool to evolve my Domain-Specific Language so that it modifies itself.

As part of my investigations, I needed to check whether the entered predicates (I am working with First Order Logic these days) have been previously defined. When using the "knowledge console", you define a predicate by just entering it on its own. I also have an XML-based representation but for the moment I am concentrating on the interactive console for playing around with the knowledge graph.

Well, instead of having to query the knowledge graph every time to check whether the entered predicate is available, I thought of just dynamically changing the grammar and have the MGrammar compiler do this for me. This was just out of curiosity. Of course I am not suggesting that this is used seriously when interacting with large amounts of data but the approach could definitely be used when writing a DSL that can change itself (e.g. introduce new keywords). It turned out to be remarkably easy.

Here's an example...

  • The predicates "savas" and "knows" are entered in the knowledge base
  • The predicate "knows(savas jim)" is entered
  • The grammar does not accept it as valid input
  • The predicate "jim" is entered in the knowledge base
  • The grammar now accepts "knows(savas jim)"

 image

This behavior is trivial to implement with MGrammar. All you need is the DynamicParser, which i was using anyway. You rewrite your grammar every time a new predicate is introduced. I have an MGrammar token that maintains a list of all the predicates that have been entered. Using that list, I recreate and then re-compile the following grammar.

         1: module Aristotle.PredicateLanguage
         2: {
         3:     language Predicate
         4:     {
         5:         syntax Main = e:Expr => e;
         6:
    
         7:         syntax Expr = precedence 1: p:Pred => p
         8:                     | precedence 2: p:PredicateExpression => p;
         9:  
        10:         syntax PredicateExpression = i:DefinedPredicate => i
        11:                                    | i:DefinedPredicate "(" e:InternalExpression+ ")" => id(i) [valuesof(e)];
        12:         syntax InternalExpression = p:PredicateExpression => p
        13:                                   | l:Literal => l;
        14:  
        15:         token DefinedPredicate = p:("_") => p;
        16:  
        17:         token Literal = l:Language.Grammar.TextLiteral => l;
        18:         token Pred = (Language.Grammar.Identifier ".")? Language.Grammar.Identifier;
        19:         interleave Whitespace = Language.Base.Whitespace;
        20:     }
        21: }

The trick is done with line 15, which evolves at runtime to look like this...

p:("savas"|"knows"|"jim") => p

Enjoy!