123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603 |
- /*
- * 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"
- "devt.de/krotik/common/errorutil"
- "devt.de/krotik/common/sortutil"
- "devt.de/krotik/ecal/parser"
- "devt.de/krotik/ecal/scope"
- "devt.de/krotik/ecal/util"
- )
- // Statements Runtime
- // ==================
- /*
- statementsRuntime is the runtime component for sequences of statements.
- */
- type statementsRuntime struct {
- *baseRuntime
- }
- /*
- statementsRuntimeInst returns a new runtime component instance.
- */
- func statementsRuntimeInst(erp *ECALRuntimeProvider, node *parser.ASTNode) parser.Runtime {
- return &statementsRuntime{newBaseRuntime(erp, node)}
- }
- /*
- Eval evaluate this runtime component.
- */
- func (rt *statementsRuntime) Eval(vs parser.Scope, is map[string]interface{}, tid uint64) (interface{}, error) {
- var res interface{}
- _, err := rt.baseRuntime.Eval(vs, is, tid)
- if err == nil {
- for _, child := range rt.node.Children {
- if res, err = child.Runtime.Eval(vs, is, tid); err != nil {
- return nil, err
- }
- }
- }
- return res, err
- }
- // Condition statement
- // ===================
- /*
- ifRuntime is the runtime for the if condition statement.
- */
- type ifRuntime struct {
- *baseRuntime
- }
- /*
- ifRuntimeInst returns a new runtime component instance.
- */
- func ifRuntimeInst(erp *ECALRuntimeProvider, node *parser.ASTNode) parser.Runtime {
- return &ifRuntime{newBaseRuntime(erp, node)}
- }
- /*
- Eval evaluate this runtime component.
- */
- func (rt *ifRuntime) Eval(vs parser.Scope, is map[string]interface{}, tid uint64) (interface{}, error) {
- _, err := rt.baseRuntime.Eval(vs, is, tid)
- if err == nil {
- // Create a new variable scope
- vs = vs.NewChild(scope.NameFromASTNode(rt.node))
- for offset := 0; offset < len(rt.node.Children); offset += 2 {
- var guardres interface{}
- // Evaluate guard
- if err == nil {
- guardres, err = rt.node.Children[offset].Runtime.Eval(vs, is, tid)
- if err == nil && guardres.(bool) {
- // The guard holds true so we execture its statements
- return rt.node.Children[offset+1].Runtime.Eval(vs, is, tid)
- }
- }
- }
- }
- return nil, err
- }
- // Guard Runtime
- // =============
- /*
- guardRuntime is the runtime for any guard condition (used in if, for, etc...).
- */
- type guardRuntime struct {
- *baseRuntime
- }
- /*
- guardRuntimeInst returns a new runtime component instance.
- */
- func guardRuntimeInst(erp *ECALRuntimeProvider, node *parser.ASTNode) parser.Runtime {
- return &guardRuntime{newBaseRuntime(erp, node)}
- }
- /*
- Eval evaluate this runtime component.
- */
- func (rt *guardRuntime) Eval(vs parser.Scope, is map[string]interface{}, tid uint64) (interface{}, error) {
- var res interface{}
- _, err := rt.baseRuntime.Eval(vs, is, tid)
- if err == nil {
- var ret interface{}
- // Evaluate the condition
- ret, err = rt.node.Children[0].Runtime.Eval(vs, is, tid)
- // Guard returns always a boolean
- res = ret != nil && ret != false && ret != 0
- }
- return res, err
- }
- // Loop statement
- // ==============
- /*
- loopRuntime is the runtime for the loop statement (for).
- */
- type loopRuntime struct {
- *baseRuntime
- leftInVarName []string
- }
- /*
- loopRuntimeInst returns a new runtime component instance.
- */
- func loopRuntimeInst(erp *ECALRuntimeProvider, node *parser.ASTNode) parser.Runtime {
- return &loopRuntime{newBaseRuntime(erp, node), nil}
- }
- /*
- Validate this node and all its child nodes.
- */
- func (rt *loopRuntime) Validate() error {
- err := rt.baseRuntime.Validate()
- if err == nil {
- if rt.node.Children[0].Name == parser.NodeIN {
- inVar := rt.node.Children[0].Children[0]
- if inVar.Name == parser.NodeIDENTIFIER {
- if len(inVar.Children) != 0 {
- return rt.erp.NewRuntimeError(util.ErrInvalidConstruct,
- "Must have a simple variable on the left side of the In expression", rt.node)
- }
- rt.leftInVarName = []string{inVar.Token.Val}
- } else if inVar.Name == parser.NodeLIST {
- rt.leftInVarName = make([]string, 0, len(inVar.Children))
- for _, child := range inVar.Children {
- if child.Name != parser.NodeIDENTIFIER || len(child.Children) != 0 {
- return rt.erp.NewRuntimeError(util.ErrInvalidConstruct,
- "Must have a list of simple variables on the left side of the In expression", rt.node)
- }
- rt.leftInVarName = append(rt.leftInVarName, child.Token.Val)
- }
- }
- }
- }
- return err
- }
- /*
- Eval evaluate this runtime component.
- */
- func (rt *loopRuntime) Eval(vs parser.Scope, is map[string]interface{}, tid uint64) (interface{}, error) {
- _, err := rt.baseRuntime.Eval(vs, is, tid)
- if err == nil {
- var guardres interface{}
- // Create a new variable scope
- vs = vs.NewChild(scope.NameFromASTNode(rt.node))
- // Create a new instance scope - elements in each loop iteration start from scratch
- is = make(map[string]interface{})
- if rt.node.Children[0].Name == parser.NodeGUARD {
- // Evaluate guard
- guardres, err = rt.node.Children[0].Runtime.Eval(vs, is, tid)
- for err == nil && guardres.(bool) {
- // Execute block
- _, err = rt.node.Children[1].Runtime.Eval(vs, is, tid)
- // Check for continue
- if err != nil {
- if eoi, ok := err.(*util.RuntimeError); ok {
- if eoi.Type == util.ErrContinueIteration {
- err = nil
- }
- }
- }
- if err == nil {
- // Evaluate guard
- guardres, err = rt.node.Children[0].Runtime.Eval(vs, is, tid)
- }
- }
- } else if rt.node.Children[0].Name == parser.NodeIN {
- var iterator func() (interface{}, error)
- var val interface{}
- it := rt.node.Children[0].Children[1]
- val, err = it.Runtime.Eval(vs, is, tid)
- // Create an iterator object
- if rterr, ok := err.(*util.RuntimeError); ok && rterr.Type == util.ErrIsIterator {
- // We got an iterator - all subsequent calls will return values
- iterator = func() (interface{}, error) {
- return it.Runtime.Eval(vs, is, tid)
- }
- err = nil
- } else {
- // We got a value over which we need to iterate
- if valList, isList := val.([]interface{}); isList {
- index := -1
- end := len(valList)
- iterator = func() (interface{}, error) {
- index++
- if index >= end {
- return nil, rt.erp.NewRuntimeError(util.ErrEndOfIteration, "", rt.node)
- }
- return valList[index], nil
- }
- } else if valMap, isMap := val.(map[interface{}]interface{}); isMap {
- var keys []interface{}
- index := -1
- for k := range valMap {
- keys = append(keys, k)
- }
- end := len(keys)
- // Try to sort according to string value
- sortutil.InterfaceStrings(keys)
- iterator = func() (interface{}, error) {
- index++
- if index >= end {
- return nil, rt.erp.NewRuntimeError(util.ErrEndOfIteration, "", rt.node)
- }
- key := keys[index]
- return []interface{}{key, valMap[key]}, nil
- }
- } else {
- // A single value will do exactly one iteration
- index := -1
- iterator = func() (interface{}, error) {
- index++
- if index > 0 {
- return nil, rt.erp.NewRuntimeError(util.ErrEndOfIteration, "", rt.node)
- }
- return val, nil
- }
- }
- }
- vars := rt.leftInVarName
- for err == nil {
- var res interface{}
- res, err = iterator()
- if err != nil {
- if eoi, ok := err.(*util.RuntimeError); ok {
- if eoi.Type == util.ErrIsIterator {
- err = nil
- }
- }
- }
- if err == nil {
- if len(vars) == 1 {
- err = vs.SetValue(vars[0], res)
- } else if resList, ok := res.([]interface{}); ok {
- if len(vars) != len(resList) {
- err = fmt.Errorf("Assigned number of variables is different to "+
- "number of values (%v variables vs %v values)",
- len(vars), len(resList))
- }
- if err == nil {
- for i, v := range vars {
- if err == nil {
- err = vs.SetValue(v, resList[i])
- }
- }
- }
- } else {
- err = fmt.Errorf("Result for loop variable is not a list (value is %v)", res)
- }
- if err != nil {
- return nil, rt.erp.NewRuntimeError(util.ErrRuntimeError,
- err.Error(), rt.node)
- }
- // Execute block
- _, err = rt.node.Children[1].Runtime.Eval(vs, is, tid)
- }
- // Check for continue
- if err != nil {
- if eoi, ok := err.(*util.RuntimeError); ok {
- if eoi.Type == util.ErrContinueIteration {
- err = nil
- }
- }
- }
- }
- // Check for end of iteration error
- if eoi, ok := err.(*util.RuntimeError); ok {
- if eoi.Type == util.ErrEndOfIteration {
- err = nil
- }
- }
- }
- }
- return nil, err
- }
- // Break statement
- // ===============
- /*
- breakRuntime is the runtime for the break statement.
- */
- type breakRuntime struct {
- *baseRuntime
- }
- /*
- breakRuntimeInst returns a new runtime component instance.
- */
- func breakRuntimeInst(erp *ECALRuntimeProvider, node *parser.ASTNode) parser.Runtime {
- return &breakRuntime{newBaseRuntime(erp, node)}
- }
- /*
- Eval evaluate this runtime component.
- */
- func (rt *breakRuntime) Eval(vs parser.Scope, is map[string]interface{}, tid uint64) (interface{}, error) {
- _, err := rt.baseRuntime.Eval(vs, is, tid)
- if err == nil {
- err = rt.erp.NewRuntimeError(util.ErrEndOfIteration, "", rt.node)
- }
- return nil, err
- }
- // Continue statement
- // ==================
- /*
- continueRuntime is the runtime for the continue statement.
- */
- type continueRuntime struct {
- *baseRuntime
- }
- /*
- continueRuntimeInst returns a new runtime component instance.
- */
- func continueRuntimeInst(erp *ECALRuntimeProvider, node *parser.ASTNode) parser.Runtime {
- return &continueRuntime{newBaseRuntime(erp, node)}
- }
- /*
- Eval evaluate this runtime component.
- */
- func (rt *continueRuntime) Eval(vs parser.Scope, is map[string]interface{}, tid uint64) (interface{}, error) {
- _, err := rt.baseRuntime.Eval(vs, is, tid)
- if err == nil {
- err = rt.erp.NewRuntimeError(util.ErrContinueIteration, "", rt.node)
- }
- return nil, err
- }
- // Try Runtime
- // ===========
- /*
- tryRuntime is the runtime for try blocks.
- */
- type tryRuntime struct {
- *baseRuntime
- }
- /*
- tryRuntimeInst returns a new runtime component instance.
- */
- func tryRuntimeInst(erp *ECALRuntimeProvider, node *parser.ASTNode) parser.Runtime {
- return &tryRuntime{newBaseRuntime(erp, node)}
- }
- /*
- Eval evaluate this runtime component.
- */
- func (rt *tryRuntime) Eval(vs parser.Scope, is map[string]interface{}, tid uint64) (interface{}, error) {
- var res interface{}
- evalExcept := func(errObj map[interface{}]interface{}, except *parser.ASTNode) bool {
- ret := false
- if len(except.Children) == 1 {
- // We only have statements - any exception is handled here
- evs := vs.NewChild(scope.NameFromASTNode(except))
- except.Children[0].Runtime.Eval(evs, is, tid)
- ret = true
- } else if len(except.Children) == 2 {
- // We have statements and the error object is available - any exception is handled here
- evs := vs.NewChild(scope.NameFromASTNode(except))
- evs.SetValue(except.Children[0].Token.Val, errObj)
- except.Children[1].Runtime.Eval(evs, is, tid)
- ret = true
- } else {
- errorVar := ""
- for i := 0; i < len(except.Children); i++ {
- child := except.Children[i]
- if !ret && child.Name == parser.NodeSTRING {
- exceptError, evalErr := child.Runtime.Eval(vs, is, tid)
- // If we fail evaluating the string we panic as otherwise
- // we would need to generate a new error while trying to handle another error
- errorutil.AssertOk(evalErr)
- ret = exceptError == fmt.Sprint(errObj["type"])
- } else if ret && child.Name == parser.NodeAS {
- errorVar = child.Children[0].Token.Val
- } else if ret && child.Name == parser.NodeSTATEMENTS {
- evs := vs.NewChild(scope.NameFromASTNode(except))
- if errorVar != "" {
- evs.SetValue(errorVar, errObj)
- }
- child.Runtime.Eval(evs, is, tid)
- }
- }
- }
- return ret
- }
- // Make sure the finally block is executed in any case
- if finally := rt.node.Children[len(rt.node.Children)-1]; finally.Name == parser.NodeFINALLY {
- fvs := vs.NewChild(scope.NameFromASTNode(finally))
- defer finally.Children[0].Runtime.Eval(fvs, is, tid)
- }
- _, err := rt.baseRuntime.Eval(vs, is, tid)
- if err == nil {
- tvs := vs.NewChild(scope.NameFromASTNode(rt.node))
- res, err = rt.node.Children[0].Runtime.Eval(tvs, is, tid)
- // Evaluate except clauses
- if err != nil {
- errObj := map[interface{}]interface{}{
- "type": "UnexpectedError",
- "error": err.Error(),
- }
- if rtError, ok := err.(*util.RuntimeError); ok {
- errObj["type"] = rtError.Type.Error()
- errObj["detail"] = rtError.Detail
- errObj["pos"] = rtError.Pos
- errObj["line"] = rtError.Line
- errObj["source"] = rtError.Source
- } else if rtError, ok := err.(*util.RuntimeErrorWithDetail); ok {
- errObj["type"] = rtError.Type.Error()
- errObj["detail"] = rtError.Detail
- errObj["pos"] = rtError.Pos
- errObj["line"] = rtError.Line
- errObj["source"] = rtError.Source
- errObj["data"] = rtError.Data
- }
- if te, ok := err.(util.TraceableRuntimeError); ok {
- if ts := te.GetTraceString(); ts != nil {
- errObj["trace"] = ts
- }
- }
- res = nil
- for i := 1; i < len(rt.node.Children); i++ {
- if child := rt.node.Children[i]; child.Name == parser.NodeEXCEPT {
- if evalExcept(errObj, child) {
- err = nil
- break
- }
- }
- }
- }
- }
- return res, err
- }
|