0.1.2 • Published 4 months ago

arboret v0.1.2

Weekly downloads
-
License
UNLICENSED
Repository
github
Last release
4 months ago

Arboret

A descriptive, no-code, plain-text, data-driven query language.

Why use Arboret

Whilst building an application in a highly-restricted Node.js environment, I came into a need to query an evolving dataset. I wanted to be able to specify new queries on-the-fly, and needed the flexibility of performing complex computation when necessary. I couldn't access a database instance, which meant no SQL, and to eval is to give up the keys to your kingdom. Additionally, I needed the freedom to update the language with new constructs over time, without needing to update anything other than the dependencies. Lastly, I wanted the languaged used to be expressive and friendly to my non-technical peers.

Arboret is a decisions "language" that is written in Human JSON. Since it is data, it can be changed without the need to rebuild or redeploy. It has access to a limited context provided to the interpreter, and can perform manipulations on that data to enable advanced compositions for complex decisions. The design of Arboret satisfies four key goals:

  • Descriptive
    Everything is expressed in simple English, no symbols
  • Consistent
    With few exceptions, every statement takes the form:-
{
  expr: string
  params: [ ... ]
}
  • Powerful
    The language is capable of expressing complex decision making logic
  • Secure
    There's no eval, and no shenanigans — Arboret doesn't do anything you wouldn't

Getting Started

Installation is a doddle:

# or, your package manager of choice
npm install arboret

Once installed, using Arboret is straightforward. You have two options.

If your rules are written in Human JSON, the preferred syntax, you should call parseAndEvaluate on your unparsed HJson string.

import { Arboret } from 'arboret'

const arboret = new Arboret()
const hjson = `{
  expr: number
  value: 3
}`
arboret.parseAndEvaluate(hjson) // returns 3

Alternatively, you can write your rules as JSON and parse them separately. If you prefer to do things that way, you should call evaluate instead.

import { Arboret } from 'arboret'

const arboret = new Arboret()
const json = JSON.parse(`{ "expr": "number", "value": 5 }`)
arboret.evaluate(json) // returns 3

Should your application need to load and execute your ruleset often, pre-caching a parsed copy of your ruleset will improve performance.

Examples

Arithmetic

# (10 + 2) * 5
{
  expr: multiply
  params:
  [
    {
      expr: add
      params:
      [
        {
          expr: number
          value: 10
        }
        {
          expr: number
          value: 2
        }
      ]
    }
    {
      expr: number
      value: 5
    }
  ]
}

A random round of FizzBuzz

# context = { digit: Math.round(Math.random() * 100) }
{
  expr: if
  params:
  [
    {
      expr: equals
      params:
      [
        {
          expr: modulo
          params:
          [
            {
              expr: variable
              name: digit
            }
            {
              expr: number
              value: 15
            }
          ]
        }
        {
          expr: number
          value: 15
        }
      ]
    }
    {
      expr: string
      value: fizzbuzz
    }
    {
      expr: if
      params:
      [
        {
          expr: equals
          params:
          [
            {
              expr: modulo
              params:
              [
                {
                  expr: variable
                  name: digit
                }
                {
                  expr: number
                  value: 3
                }
              ]
            }
            {
              expr: number
              value: 3
            }
          ]
        }
        {
          expr: string
          value: fizz
        }
        {
          expr: if
          params:
          [
            {
              expr: equals
              params:
              [
                {
                  expr: modulo
                  params:
                  [
                    {
                      expr: variable
                      name: digit
                    }
                    {
                      expr: number
                      value: 5
                    }
                  ]
                }
                {
                  expr: number
                  value: 5
                }
              ]
            }
            {
              expr: string
              value: buzz
            }
            {
              expr: variable
              name: digit
            }
          ]
        }
      ]
    }
  ]
}

Further Reading

See SPECIFICATION for an exhaustive outline of the language.

0.1.2

4 months ago

0.1.1

4 months ago

0.1.0

4 months ago