A simple embeddable scripting language which supports concurrent event processing.

ECAL

ECAL is an ECA (Event Condition Action) language for concurrent event processing. ECAL can define event-based systems using rules which are triggered by events. ECAL is intended to be embedded into other software to provide an easy to use scripting language which can react to external events.

Code coverage Go Report Card Go Reference Mentioned in Awesome Go

Features

  • Simple intuitive syntax.
  • Minimalistic base language (by default only writing to a log is supported).
  • Language can be easily extended either by auto generating bridge adapters to Go functions or by adding custom function into the standard library (stdlib).
  • External events can be easily pushed into the interpreter and scripts written in ECAL can react to these events.
  • Simple but powerful concurrent event-based processing supporting priorities and scoping for control flow.
  • Handling event rules can match on event state and rules can suppress each other.

Getting started

You can either download a pre-compiled package for Windows (win64) or Linux (amd64) here or clone the repository and build the ECAL executable with a simple make command. You need Go 1.14 or higher.

Run ./ecal to start an interactive session. You can now write simple one line statements and evaluate them:

>>>a:=2;b:=a*4;a+b
10
>>>"Result is {{a+b}}"
Result is 10

Close the interpreter by pressing +d and change into the directory examples/fib. There are 2 ECAL files in here:

lib.ecal

# Library for fib

/*
fib calculates the fibonacci series using recursion.
*/
func fib(n) {
    if (n <= 1) {
        return n
    }
    return fib(n-1) + fib(n-2)
}

fib.ecal

import "lib.ecal" as lib

for a in range(2, 20, 2) {
  log("fib({{a}}) = ", lib.fib(a))
}

Run the ECAL program with: sh run.sh. The output should be like:

$ sh run.sh
2000/01/01 12:12:01 fib(2) = 1
2000/01/01 12:12:01 fib(4) = 3
2000/01/01 12:12:01 fib(6) = 8
2000/01/01 12:12:01 fib(8) = 21
2000/01/01 12:12:01 fib(10) = 55
2000/01/01 12:12:01 fib(12) = 144
2000/01/01 12:12:02 fib(14) = 377
2000/01/01 12:12:02 fib(16) = 987
2000/01/01 12:12:02 fib(18) = 2584
2000/01/01 12:12:02 fib(20) = 6765

The interpreter can be run in debug mode which adds debug commands to the console. Run the ECAL program in debug mode with: sh debug.sh - this will also start a debug server which external development environments can connect to. There is a VSCode integration available which allows debugging via a graphical interface.

It is possible to package your ECAL project into an executable that can be run without a separate ECAL interpreter. Run the sh pack.sh and see the script for details.

Embedding ECAL and using event processing

The primary purpose of ECAL is to be a simple multi-purpose language which can be embedded into other software:

  • It has a minimal (quite generic) syntax.
  • By default the language can only reach outside the interpreter via return values, injecting events or logging.
  • External systems can interact with the code via events which maybe be handled in sink systems with varying complexity.
  • A standard library of function can easily be created by either generating proxy code to standard Go functions or by adding simple straight-forward function objects.

The core of the ECAL interpreter is the runtime provider object which is constructed with a given logger and import locator. The import locator is used by the import statement to load other ECAL code at runtime. The logger is used to process log statements from the interpreter.

logger := util.NewStdOutLogger()
importLocator := &util.FileImportLocator{Root: "/somedir"}
rtp := interpreter.NewECALRuntimeProvider("Some Program Title", importLocator, logger)

The ECALRuntimeProvider provides additionally to the logger and import locator also the following: A cron object to schedule recurring events. An ECA processor which triggers sinks and can be used to inject events into the interpreter. A debugger object which can be used to debug ECAL code supporting thread suspension, thread inspection, value injection and extraction and stepping through statements.

The actual ECAL code has to be first parsed into an Abstract Syntax Tree. The tree is annotated during its construction with runtime components created by the runtime provider.

ast, err := parser.ParseWithRuntime("sourcefilename", code, rtp)

The code is executed by calling the Validate() and Eval() function.

err = ast.Runtime.Validate()
vs := scope.NewScope(scope.GlobalScope)
res, err := ast.Runtime.Eval(vs, make(map[string]interface{}), threadId)

Eval is given a variable scope which stores the values of variables, an instance state for internal use and a thread ID identifying the executing thread.

If events are to be used then the processor of the runtime provider needs to be started first.

rtp.Processor.Start()

The processor must be started after all sinks have been declared and before events are thrown.

Events can then be injected into the interpreter.

monitor, err := rtp.Processor.AddEventAndWait(engine.NewEvent("MyEvent", []string{"foo", "bar", "myevent"}, map[interface{}]interface{}{
  "data1": 123,
  "data2": "123",
}), nil)

All errors are collected in the returned monitor.

monitor.RootMonitor().AllErrors()

The above event could be handled in ECAL with the following sinks:

sink mysink
  kindmatch [ "foo.bar.myevent" ],
{
  log("Got event: ", event)
}

sink mysink2
  kindmatch [ "foo.*.*" ],
{
  log("Got event: ", event)
}

Using Go plugins in ECAL

ECAL supports to extend the standard library (stdlib) functions via Go plugins. The intention of this feature is to allow easy expansion of the standard library even with platform dependent code.

Go plugins come with quite a few extra requirements and drawbacks and should be considered carefully. One major requirement is that CGO_ENABLED must be enabled because plugins use the libc dynamic linker. Using CGO means that cross-platform compilation is difficult as the compilation requires platform specific system libraries.

ECAL stdlib functions defined in plugins must conform to the following interface:

/*
ECALPluginFunction models a callable function in ECAL which can be imported via a plugin.
*/
type ECALPluginFunction interface {

	/*
		Run executes this function with a given list of arguments.
	*/
	Run(args []interface{}) (interface{}, error)

	/*
	   DocString returns a descriptive text about this function.
	*/
	DocString() string
}

There is a plugin example in the directory examples/plugin. The example assumes that the interpreter binary has been compiled with CGO_ENABLED which is the default when building the interpreter via the Makefile but not when using the pre-compiled binaries except the Linux binary. The plugin .so file can be compiled with buildplugin.sh (the Go compiler must have the same version as the one which compiled the interpreter binary). Running the example with run.sh will make the ECAL interpreter load the compiled plugin before executing the ECAL code. The example demonstrates normal and error output. The plugins to load can be defined in a .ecal.json file in the interpreter's root directory.

Further Reading:

License

ECAL source code is available under the MIT License.

Owner
Similar Resources

concurrent map implementation using bucket list like a skip list.

Skip List Map in Golang Skip List Map is an ordered and concurrent map. this Map is goroutine safety for reading/updating/deleting, no-require locking

Oct 8, 2022

Fast, Docker-ready image processing server written in Go and libvips, with Thumbor URL syntax

Imagor Imagor is a fast, Docker-ready image processing server written in Go. Imagor uses one of the most efficient image processing library libvips (w

Dec 30, 2022

Parallel processing through go routines, copy and delete thousands of key within some minutes

redis-dumper CLI Parallel processing through go routines, copy and delete thousands of key within some minutes copy data by key pattern from one redis

Dec 26, 2021

Telegraf - An agent for collecting, processing, aggregating, and writing metrics

Telegraf Telegraf is an agent for collecting, processing, aggregating, and writi

Feb 11, 2022

AWS Cloudtrail event alerting lambda function. Send alerts to Slack, Email, or SNS.

AWS Cloudtrail event alerting lambda function. Send alerts to Slack, Email, or SNS.

Cloudtrail-Tattletail is a Lambda based Cloudtrail alerting tool. It allows you to write simple rules for interesting Cloudtrail events and forward those events to a number of different systems.

Jan 6, 2023

TriggerMesh open source event-driven integration platform powered by Kubernetes and Knative.

TriggerMesh open source event-driven integration platform powered by Kubernetes and Knative. TriggerMesh allows you to declaratively define event flows between sources and targets as well as add even filter, splitting and processing using functions.

Dec 30, 2022

Drain-my-spot - Service draining the k8s worker node in case of spot instances related event occurrence

drain-my-spot Service draining the k8s worker node in case of spot instances rel

Feb 5, 2022

crud is a cobra based CLI utility which helps in scaffolding a simple go based micro-service along with build scripts, api documentation, micro-service documentation and k8s deployment manifests

crud crud is a CLI utility which helps in scaffolding a simple go based micro-service along with build scripts, api documentation, micro-service docum

Nov 29, 2021

A kubernetes plugin which enables dynamically add or remove GPU resources for a running Pod

A kubernetes plugin which enables dynamically add or remove GPU resources for a running Pod

GPU Mounter GPU Mounter is a kubernetes plugin which enables add or remove GPU resources for running Pods. This Introduction(In Chinese) is recommende

Jan 5, 2023
Comments
  • darwin support ? make mac-build has an error ...

    darwin support ? make mac-build has an error ...

    makefile seems to indicate that mac support is wanted.

    But when i tried to run it i hit some errors:

    
    make build-mac
    rm -f ecal
    go mod init || true
    go: /Users/apple/workspace/go/src/github.com/gerardwebb/research/blockchain/graphdb/krotik__ecal/ecal/go.mod already exists
    go mod tidy
    go generate devt.de/krotik/ecal/stdlib/generate
    Generating ECAL stdlib from Go functions ...
    fork/exec /var/folders/wp/ff6sz9qs6g71jnm12nj2kbyw0000gp/T/go-build757582189/b001/exe/generate: exec format error
    stdlib/generate/generate.go:35: running "go": exit status 1
    make: *** [generate] Error 1 
    
    

    I have not investigated what's going on yet, but wanted to first make this issue to get feedback..

do-nothing scripting framework

donothing donothing is a Go framework for do-nothing scripting. Do-nothing scripting is an approach to writing procedures. It allows you to start with

Dec 19, 2022
Gola is a Golang tool for automated scripting purpose

Gola Gola is a Golang tool for automated scripting purpose. How To Install You can find the install script here. Example Configuration commands: - n

Aug 12, 2022
A simple project (which is visitor counter) on kubernetesA simple project (which is visitor counter) on kubernetes

k8s playground This project aims to deploy a simple project (which is visitor counter) on kubernetes. Deploy steps kubectl apply -f secret.yaml kubect

Dec 16, 2022
A very simple, silly little kubectl plugin / utility that guesses which language an application running in a kubernetes pod was written in.

A very simple, silly little kubectl plugin / utility that guesses which language an application running in a kubernetes pod was written in.

Mar 9, 2022
(WIP) Extremely simple unixway GitHub webhook listener for push event

(WIP) puffy Puffy is an extremely simple unixway GitHub webhook listener and handler for push events Todo Add payload signature validation (WIP) Depen

Oct 15, 2022
Tool which gathers basic info from apk, which can be used for Android penetration testing.
Tool which gathers basic info from apk, which can be used for Android penetration testing.

APKSEC Tool which gathers basic info from apk, which can be used for Android penetration testing. REQUIREMENTS AND INSTALLATION Build APKSEC: git clon

Sep 2, 2022
An operator which complements grafana-operator for custom features which are not feasible to be merged into core operator

Grafana Complementary Operator A grafana which complements grafana-operator for custom features which are not feasible to be merged into core operator

Aug 16, 2022
Becca - A simple dynamic language for exploring language design

Becca A simple dynamic language for exploring language design What is Becca Becc

Aug 15, 2022
Fast, concurrent, streaming access to Amazon S3, including gof3r, a CLI. http://godoc.org/github.com/rlmcpherson/s3gof3r

s3gof3r s3gof3r provides fast, parallelized, pipelined streaming access to Amazon S3. It includes a command-line interface: gof3r. It is optimized for

Dec 26, 2022
concurrent, cache-efficient, and Dockerfile-agnostic builder toolkit
concurrent, cache-efficient, and Dockerfile-agnostic builder toolkit

BuildKit BuildKit is a toolkit for converting source code to build artifacts in an efficient, expressive and repeatable manner. Key features: Automati

Dec 31, 2022