123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325 |
- /*
- * ECAL
- *
- * Copyright 2020 Matthias Ladkau. All rights reserved.
- *
- * This Source Code Form is subject to the terms of the MIT
- * License, If a copy of the MIT License was not distributed with this
- * file, You can obtain one at https://opensource.org/licenses/MIT.
- */
- package interpreter
- import (
- "fmt"
- "math"
- "strings"
- "devt.de/krotik/ecal/engine"
- "devt.de/krotik/ecal/parser"
- "devt.de/krotik/ecal/scope"
- "devt.de/krotik/ecal/util"
- )
- /*
- sinkRuntime is the runtime for sink declarations.
- */
- type sinkRuntime struct {
- *baseRuntime
- }
- /*
- sinkRuntimeInst returns a new runtime component instance.
- */
- func sinkRuntimeInst(erp *ECALRuntimeProvider, node *parser.ASTNode) parser.Runtime {
- return &sinkRuntime{newBaseRuntime(erp, node)}
- }
- /*
- Validate this node and all its child nodes.
- */
- func (rt *sinkRuntime) Validate() error {
- err := rt.baseRuntime.Validate()
- if err == nil {
- nameNode := rt.node.Children[0]
- // Make sure there is a constant as a name
- if _, ok := nameNode.Runtime.(*identifierRuntime); !ok {
- return rt.erp.NewRuntimeError(util.ErrInvalidConstruct,
- "Must have an identifier as sink name", rt.node)
- }
- // Check that all children are valid
- for _, child := range rt.node.Children[1:] {
- switch child.Name {
- case parser.NodeKINDMATCH:
- case parser.NodeSCOPEMATCH:
- case parser.NodeSTATEMATCH:
- case parser.NodePRIORITY:
- case parser.NodeSUPPRESSES:
- case parser.NodeSTATEMENTS:
- continue
- default:
- err = rt.erp.NewRuntimeError(util.ErrInvalidConstruct,
- fmt.Sprintf("Unknown expression in sink declaration %v", child.Token.Val),
- child)
- }
- if err != nil {
- break
- }
- }
- }
- return err
- }
- /*
- Eval evaluate this runtime component.
- */
- func (rt *sinkRuntime) Eval(vs parser.Scope, is map[string]interface{}) (interface{}, error) {
- var kindMatch, scopeMatch, suppresses []string
- var stateMatch map[string]interface{}
- var priority int
- var statements *parser.ASTNode
- var err error
- // Create default scope
- scopeMatch = []string{}
- // Get the name of the sink
- name := rt.node.Children[0].Token.Val
- // Create helper function
- makeStringList := func(child *parser.ASTNode) ([]string, error) {
- var ret []string
- val, err := child.Runtime.Eval(vs, is)
- if err == nil {
- for _, v := range val.([]interface{}) {
- ret = append(ret, fmt.Sprint(v))
- }
- }
- return ret, err
- }
- // Collect values from children
- for _, child := range rt.node.Children[1:] {
- switch child.Name {
- case parser.NodeKINDMATCH:
- kindMatch, err = makeStringList(child)
- break
- case parser.NodeSCOPEMATCH:
- scopeMatch, err = makeStringList(child)
- break
- case parser.NodeSTATEMATCH:
- var val interface{}
- stateMatch = make(map[string]interface{})
- if val, err = child.Runtime.Eval(vs, is); err == nil {
- for k, v := range val.(map[interface{}]interface{}) {
- stateMatch[fmt.Sprint(k)] = v
- }
- }
- break
- case parser.NodePRIORITY:
- var val interface{}
- if val, err = child.Runtime.Eval(vs, is); err == nil {
- priority = int(math.Floor(val.(float64)))
- }
- break
- case parser.NodeSUPPRESSES:
- suppresses, err = makeStringList(child)
- break
- case parser.NodeSTATEMENTS:
- statements = child
- break
- }
- if err != nil {
- break
- }
- }
- if err == nil && statements != nil {
- var desc string
- sinkName := fmt.Sprint(name)
- if len(rt.node.Meta) > 0 &&
- (rt.node.Meta[0].Type() == parser.MetaDataPreComment ||
- rt.node.Meta[0].Type() == parser.MetaDataPostComment) {
- desc = strings.TrimSpace(rt.node.Meta[0].Value())
- }
- rule := &engine.Rule{
- Name: sinkName, // Name
- Desc: desc, // Description
- KindMatch: kindMatch, // Kind match
- ScopeMatch: scopeMatch, // Match on event cascade scope
- StateMatch: stateMatch, // No state match
- Priority: priority, // Priority of the rule
- SuppressionList: suppresses, // List of suppressed rules by this rule
- Action: func(p engine.Processor, m engine.Monitor, e *engine.Event) error { // Action of the rule
- // Create a new root variable scope
- sinkVS := scope.NewScope(fmt.Sprintf("sink: %v", sinkName))
- // Create a new instance state with the monitor - everything called
- // by the rule will have access to the current monitor.
- sinkIs := map[string]interface{}{
- "monitor": m,
- }
- err = sinkVS.SetValue("event", map[interface{}]interface{}{
- "name": e.Name(),
- "kind": strings.Join(e.Kind(), engine.RuleKindSeparator),
- "state": e.State(),
- })
- if err == nil {
- scope.SetParentOfScope(sinkVS, vs)
- if _, err = statements.Runtime.Eval(sinkVS, sinkIs); err != nil {
- if sre, ok := err.(*SinkRuntimeError); ok {
- sre.environment = sinkVS
- } else {
- // Provide additional information for unexpected errors
- err = &SinkRuntimeError{
- err.(*util.RuntimeError),
- sinkVS,
- nil,
- }
- }
- }
- }
- return err
- },
- }
- if err = rt.erp.Processor.AddRule(rule); err != nil {
- err = rt.erp.NewRuntimeError(util.ErrInvalidState, err.Error(), rt.node)
- }
- }
- return nil, err
- }
- /*
- SinkRuntimeError is a sink specific error with additional environment information.
- */
- type SinkRuntimeError struct {
- *util.RuntimeError
- environment parser.Scope
- detail interface{}
- }
- // Sink child nodes
- // ================
- /*
- sinkDetailRuntime is the runtime for sink detail declarations.
- */
- type sinkDetailRuntime struct {
- *baseRuntime
- valType string
- }
- /*
- Eval evaluate this runtime component.
- */
- func (rt *sinkDetailRuntime) Eval(vs parser.Scope, is map[string]interface{}) (interface{}, error) {
- ret, err := rt.node.Children[0].Runtime.Eval(vs, is)
- if err == nil {
- // Check value is of expected type
- if rt.valType == "list" {
- if _, ok := ret.([]interface{}); !ok {
- return nil, rt.erp.NewRuntimeError(util.ErrInvalidConstruct,
- fmt.Sprintf("Expected a list as value"),
- rt.node)
- }
- } else if rt.valType == "map" {
- if _, ok := ret.(map[interface{}]interface{}); !ok {
- return nil, rt.erp.NewRuntimeError(util.ErrInvalidConstruct,
- fmt.Sprintf("Expected a map as value"),
- rt.node)
- }
- } else if rt.valType == "int" {
- if _, ok := ret.(float64); !ok {
- return nil, rt.erp.NewRuntimeError(util.ErrInvalidConstruct,
- fmt.Sprintf("Expected a number as value"),
- rt.node)
- }
- }
- }
- return ret, err
- }
- /*
- kindMatchRuntimeInst returns a new runtime component instance.
- */
- func kindMatchRuntimeInst(erp *ECALRuntimeProvider, node *parser.ASTNode) parser.Runtime {
- return &sinkDetailRuntime{newBaseRuntime(erp, node), "list"}
- }
- /*
- scopeMatchRuntimeInst returns a new runtime component instance.
- */
- func scopeMatchRuntimeInst(erp *ECALRuntimeProvider, node *parser.ASTNode) parser.Runtime {
- return &sinkDetailRuntime{newBaseRuntime(erp, node), "list"}
- }
- /*
- stateMatchRuntimeInst returns a new runtime component instance.
- */
- func stateMatchRuntimeInst(erp *ECALRuntimeProvider, node *parser.ASTNode) parser.Runtime {
- return &sinkDetailRuntime{newBaseRuntime(erp, node), "map"}
- }
- /*
- priorityRuntimeInst returns a new runtime component instance.
- */
- func priorityRuntimeInst(erp *ECALRuntimeProvider, node *parser.ASTNode) parser.Runtime {
- return &sinkDetailRuntime{newBaseRuntime(erp, node), "int"}
- }
- /*
- suppressesRuntimeInst returns a new runtime component instance.
- */
- func suppressesRuntimeInst(erp *ECALRuntimeProvider, node *parser.ASTNode) parser.Runtime {
- return &sinkDetailRuntime{newBaseRuntime(erp, node), "list"}
- }
|