Best way to create your own DSL

Hi, i would like to get some ideas how to solve the problem for create a bit more then a simple configuration file.

Currently i have a working solution but its slow as hell and kind of expensive to maintain. I went the road to create a regex parsing tree. As the tree grows, the root regex gets slower and slower. Even with the currently limited features i am not happy with it.

This is a one example of my configuration:

Requires  {
     Default/Model.bp
}

Type House extends Object{
    Components{
        Component Door{
            Model : Models/default.j3o
            Value : M123123.j3o
        }
        Component Room{
            Model: Models/defaultRoom.j3o
        }
    }
    RemoveComponents{
        StaticObject
        PowerState
    }
}

The regex tree build the pattern:

((Type (([a-zA-Z]|_)+))(\s+extends\s+)(([a-zA-Z]|_)+)(\s*\{\s*))(((((Components\s*\{\s*))(((((Component\s*)(([a-zA-Z]|_)+)(\s*\{\s*))(((([a-zA-Z]+)(\s*:\s*)(([a-zA-Z0-9]|\/|\.)+\s*)))+)((\s*\}\s*))))+)((\s*\}\s*)))|(((RemoveComponents\s*\{\s*))((((([a-zA-Z]|_)+\s*)))+)((\s*\}\s*))))+)((\s*\}\s*))

As you can guess, in uncorrect config files it takes ages.

I am open to alternatives. ANY alternatives! The only requirement is that i can be loaded at runtime and does not have to be in classpath

Dont know if http://www.antlr.org/ is best way yet?

That looks like JSON. Why not just use JSON?

3 Likes

Some jvm lang that can be used to create DSL :

But IMO, I prefer YAML, TOML or https://github.com/typesafehub/config (more reusable than hard link to classes)

Yeah, regex is pretty horrible for parsing this way though as a tokenizer it’s not bad.

My vote would be for Groovy. You can either create a parser directly for your existing format pretty easily or even better make some tweaks to your format to support a Groovy-based DSL.

Quick possible example that is really Groovy with some built-ins you’d write:

Requires [
    "Default/Model.hp"
]

Type( name:"House", extends:"Object" ) {
    Components {
        Component("Door") {
            Model : "Models/default.j3o"
            Value : "M123123.j3o"
        }
        Component("Room") {
            Model: "Models/defaultRoom.j3o"
        }
    }
    RemoveComponents [
        StaticObject,
        PowerState
    ]
}

That is basically just groovy if you had a Requires method and a Type method and the Type method applies a closure to a created Type object, etc… It could also be done with builders but I’m less familiar with them.

Groovy also has parsing support for truly custom formats. The advantage of using groovy directly is that you have access to all of Java and your scripts can technically be compiled to .class files if you ever want to to that.

1 Like

Or use javascript, comes bunden with the jvm.

The down side of Javascript is that it’s (kind of) interpreted… versus groovy which only requires a jar on the classpath and is compiled (at runtime). Groovy classes are Java classes in every way.

I am currently playing a bit around with groovy. In theory it offers already i need. And with the closure functions it allows to define quite a DSL quite flexible

How would the signature look like to allow parameters like this:

Type( name:"House", extends:"Object" )

There is no “code”, i.e. no user defined functionality in your example, its just a hierarchical markup. So how about using XML for this?
Just my two cents.

Ugh… XML is so ugly and so VERBOSE for even the simplest things. It’s so 1990s.

As a method:

public void Type( Map args, Closure config ) {
    // And for any object with the properly named fields or setters then creating
    // the object is as simple as:
    Type type = new Type(args);  // <- groovy magic constructor on all objects
    type.with(config); // <- more groovy magic to run a closure on an object
}

…though now that I think about it you might have to put the parameters in brackets. I can’t remember if Groovy is smart enough to separate the map args from the closure and don’t have time to look at the moment.

Groovy is using ANTLR , (no s**t, sherlock). And of course if you can use groovy, especially with some more dependencies like ASM and such… you should go with Groovy with no hesitate.

Going to find out soon :wink:

Allright, got it working with groovy. Initially i had a few issues, and i still don’t like goovy’s way on how to deal with type checking.

    public static <T extends Component> T Component(Class<T> componentClass, @DelegatesTo(T) Closure config) {
        def component = componentClass.newInstance()
        component.with(config)
        return component
    }

While this is valid and working code, the @DelegatesTo seems not to work with generics, and so i have no autocomplete or whatever. Also in the closure, typos in variable names are simply not assigned, but no error gets generated.

Type("Test1") {
    Extends("Object1", "Object2")
    Components(
            Component(TestComponent.class) {
                value = "asdf"
                idddd = 123
            },
            Component(TestComponent.class) {
                factor=true
            }
    )
}

But its quite easy to create new types on the fly now :smile:

You can make this happen. I think you will find that it’s delegating up to the root and these things end up in the bindings. I believe if you force how the closure resolves its references then you can make it throw errors for misses rather than delegating up. If you look at the javadoc for closure the method should be obvious. I’m not in a position to pull up the link at the moment.

1 Like

Nice got it working.

 public static <T> T Component(@DelegatesTo.Target Class<T> componentClass, @DelegatesTo(strategy = Closure.DELEGATE_ONLY,genericTypeIndex = 0) Closure config) {

The trick was the @DelegatesTo(genericTypeIndex) in combination with @DelegatesTo.Target

Plays out better then expected…