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!

Recent Posts

BrainExpanded – Login State Caching Issue in iOS Share Extension

As part of the BrainExpanded project, I’m building an iOS app that lets users easily…

1 week ago

Is AI Good or Bad?

Artificial Intelligence (AI) has rapidly evolved over the past few decades, becoming an integral part…

2 weeks ago

BrainExpanded – Copilot

Happy New Year everyone! I was planning for my next BrainExpanded post to be a…

2 months ago

BrainExpanded – The Timeline

See "BrainExpanded - Introduction" for context on this post. Notes and links Over the years,…

2 months ago

BrainExpanded – Introduction

This is the first post, in what I think is going to be a series,…

2 months ago

Digital twin follow up

Back in February, I shared the results of some initial experimentation with a digital twin.…

2 months ago