Generic rules engine in golang using antlr

Nikunj Yadav
3 min readMay 9, 2019

--

rules ? I dont need no stinking rules

Just the other day one of my friends asked me to write an architecture document for what an ideal dynamic configuration system would look like. If you don’t know what I mean by dynamic configuration, you can check out this paper (configuration at Facebook) or this twitter eng blog entry. I might write an entry about it myself at some point.

Anyway, I digress. So let’s say if you were building a dynamic config system, and you deployed your code that gets some value from a configuration key:

if config_client.GetInt64(`my_feature`, map[string]interface{}{
"country": r.URL.Query()["country"],
})

How do you design this ? Sure there are going to be a lot of moving parts but I need a key my_featureto spit out different values based on the map[string]interface{} that I am passing in. Okay !

I hope it is easy for me to make the case that for a use case like this I want to be able to write rules in a flexible grammar, and a library should be able to parse that and then return whether my input evaluated as true or false. And then I will be able to write different rules and their associated values for a configuration key.

my_feature:when country is india then value is ==> 2app version is greater than 2.0.0 ==> 3project_id is 1234 or request time is before monday ==> 4....

So codifying that, what if the library could do this:

rule := `x gt 1 and y.b eq 1`assert.True(t, eval(rule, map[string]interface{
"x": 2,
"y": map[string]interface{} {
"b": 1,
},
})
assert.False(t, eval(rule, map[string]interface{
"x": 2,
"y": 3,
})
// You can even do
rule = `x gt 1.0.2 or x le 0.04`
assert.True(t, eval(rule, map[string]interface{
"x": "1.1.4,
})

So I wrote something that does just that. I used antlr to generate code for this grammar, and I hope you find this open sourced library useful for your various future endeavors.

Note about Antlr

I found this tutorial useful for understanding what is antlr (reading just the first section about AST should be enough). I used the visitor pattern of evaluating AST, but there are some concerns with the golang bindings around that, I am definitely not a fan of the code that gets auto-generated. Here is a GitHub issue related to that.

Okay, still though, what is it good for?

What? Antlr? I am sure that is an easy case to make. You have a grammar in your mind and you don’t want to hand write the parser, use Antlr. It supports multiple languages, does lexer stuff for you and generates AST, you can write the eval code in terms of what happens when you traverse each node on the AST.

My library? Well, you got a JSON object, and you got a set of rules you want to enforce/evaluate on that said object, something that is way more powerful than JSONSchema. Then you might find my library useful or it might make a good case for how you can write your own or even do a PR on my repo (it’s lonely out there) ;)

Resources

  1. Rules Engine library
  2. Antlr project
  3. Antlr guide
  4. Toy calculator written in antlr by me

--

--