main.go 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. /*
  2. * ECAL Embedding Example
  3. */
  4. package main
  5. import (
  6. "fmt"
  7. "log"
  8. "devt.de/krotik/ecal/engine"
  9. "devt.de/krotik/ecal/interpreter"
  10. "devt.de/krotik/ecal/parser"
  11. "devt.de/krotik/ecal/scope"
  12. "devt.de/krotik/ecal/stdlib"
  13. "devt.de/krotik/ecal/util"
  14. )
  15. func main() {
  16. // The code to execute
  17. code := `
  18. sink mysink
  19. kindmatch [ "foo.*" ],
  20. {
  21. log("Handling: ", event)
  22. log("Result: ", event.state.op1 + event.state.op2)
  23. }
  24. sink mysink2
  25. kindmatch [ "foo.*" ],
  26. {
  27. raise("Some error")
  28. }
  29. func compute(x) {
  30. let result := x + 1
  31. return result
  32. }
  33. mystuff.add(compute(5), 1)
  34. `
  35. // Add a stdlib function
  36. stdlib.AddStdlibPkg("mystuff", "My special functions")
  37. // A single instance if the ECALFunction struct will be used for all function calls across all threads
  38. stdlib.AddStdlibFunc("mystuff", "add", &AddFunc{})
  39. // Logger for log() statements in the code
  40. logger := util.NewMemoryLogger(100)
  41. // Import locator when using import statements in the code
  42. importLocator := &util.MemoryImportLocator{Files: make(map[string]string)}
  43. // Runtime provider which contains all objects needed by the interpreter
  44. rtp := interpreter.NewECALRuntimeProvider("Embedded Example", importLocator, logger)
  45. // First we need to parse the code into an Abstract Syntax Tree
  46. ast, err := parser.ParseWithRuntime("code1", code, rtp)
  47. if err != nil {
  48. log.Fatal(err)
  49. }
  50. // Then we need to validate the code - this prepares certain runtime bits
  51. // of the AST for execution.
  52. if err = ast.Runtime.Validate(); err != nil {
  53. log.Fatal(err)
  54. }
  55. // We need a global variable scope which contains all declared variables - use
  56. // this object to inject initialization values into the ECAL program.
  57. vs := scope.NewScope(scope.GlobalScope)
  58. // Each thread which evaluates the Runtime of an AST should get a unique thread ID
  59. var threadId uint64 = 1
  60. // Evaluate the Runtime of an AST with a variable scope
  61. res, err := ast.Runtime.Eval(vs, make(map[string]interface{}), threadId)
  62. if err != nil {
  63. log.Fatal(err)
  64. }
  65. // The executed code returns the value of the last statement
  66. fmt.Println("Computation result:", res)
  67. // We can also react to events
  68. rtp.Processor.Start()
  69. monitor, err := rtp.Processor.AddEventAndWait(engine.NewEvent("MyEvent", []string{"foo", "bar"}, map[interface{}]interface{}{
  70. "op1": float64(5.2),
  71. "op2": float64(5.3),
  72. }), nil)
  73. if err != nil {
  74. log.Fatal(err)
  75. }
  76. // All errors can be found on the returned monitor object
  77. fmt.Println("Event result:", monitor.RootMonitor().AllErrors())
  78. // The log messages of a program can be collected
  79. fmt.Println("Log:", logger.String())
  80. }
  81. /*
  82. AddFunc is a simple add function which calculates the sum of two numbers.
  83. */
  84. type AddFunc struct {
  85. }
  86. func (f *AddFunc) Run(instanceID string, vs parser.Scope, is map[string]interface{}, tid uint64, args []interface{}) (interface{}, error) {
  87. // This should have some proper error checking
  88. // Arguments are either of type string, float64, map[interface{}]interface{}
  89. // or []interface{}
  90. return args[0].(float64) + args[1].(float64), nil
  91. }
  92. func (f *AddFunc) DocString() (string, error) {
  93. return "Sum up two numbers", nil
  94. }