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)”

 

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!

Share
Published by

Recent Posts

Digital Twin (my playground)

I am embarking on a side project that involves memory and multimodal understanding for an…

9 months ago

“This is exactly what LLMs are made for”

I was in Toronto, Canada. I'm on the flight back home now. The trip was…

1 year ago

AI is enhancing me

AI as an enhancer of human abilities.

1 year ago

“How we fell out of love with voice assistants”

The BBC article "How we fell out of love with voice assistants" by Katherine Latham…

2 years ago

Ontology-based reasoning with ChatGPT’s help

Like so many others out there, I played a bit with ChatGPT. I noticed examples…

2 years ago

Break from work

Hi all… It’s been a while since I posted on this blog. It’s been an…

2 years ago