Sometimes, letting a piece of code evolving by itself without much planning does not usually end well. However I was quite pleased with a by-product of it and I am currently formalizing it. So the by-product is some sort of DSL for a rule engine that I implemented to process records. It started as some lambda functions in Python but eventually becomes something else.
A simple rule in the record processing script may be, if record A has fieldA with value X, then change fieldB to Y. The condition check rule, where fieldA has value of X can be written as
lambda record: record['fieldA'] == 'X'
This worked fine for a while, but when I tried to make work run concurrently Python refused to pickle lambda functions. So I had to restructure all these lambda functions into real functions, and use partial function to allow certain function parameters to be left empty. However, as the ruleset grows, I decided to further simplify it to something that can be represented in JSON.
["_equals", "fieldA", "X"]
The syntax itself very much resembles lisp, as I personally find it simple enough. While the implementation was not perfect (there’s no formalization or whatsoever done to the petit DSL), it did work well. Also since it could be represented in JSON then developing a web UI to populate it is possible, somehow.
Recently I am building a game that is somehow similar to game of life. While the hobby project is probably going to take forever to complete, I have just re-implemented the parser in Javascript as the game needs one. The API is not really finalized, and I am still trying to formalize the schema to make it feels consistent.
The script itself requires underscore.js, and currently recognizes some of the functions already. For instance, the previous case is now written as
["condition.Equal", "fieldA", "X"]
In order to chain multiple rules together, one could write
["boolean.And", ["condition.Equal", "fieldA", "X"], ["condition.Equal", "fieldB", "Y"]]
In order to parse this and turn it into a proper function
func = ruler.parse(["boolean.And", ["condition.Equal", "fieldA", "X"], ["condition.Equal", "fieldB", "Y"]])
Then, to execute it
func({fieldA: "X", fieldB: "Y"})
It should yield true
.
The code is named Ruler, and the first draft of code can be found here, I will populate the README with more examples probably later today.