123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464 |
- /*
- * 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/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{}) (interface{}, error) {
- _, err := rt.baseRuntime.Eval(vs, is)
- if err == nil {
- for _, child := range rt.node.Children {
- if _, err := child.Runtime.Eval(vs, is); err != nil {
- return nil, err
- }
- }
- }
- return nil, 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{}) (interface{}, error) {
- _, err := rt.baseRuntime.Eval(vs, is)
- 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)
- if err == nil && guardres.(bool) {
- // The guard holds true so we execture its statements
- return rt.node.Children[offset+1].Runtime.Eval(vs, is)
- }
- }
- }
- }
- 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{}) (interface{}, error) {
- var res interface{}
- _, err := rt.baseRuntime.Eval(vs, is)
- if err == nil {
- var ret interface{}
- // Evaluate the condition
- ret, err = rt.node.Children[0].Runtime.Eval(vs, is)
- // 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{}) (interface{}, error) {
- _, err := rt.baseRuntime.Eval(vs, is)
- 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)
- for err == nil && guardres.(bool) {
- // Execute block
- _, err = rt.node.Children[1].Runtime.Eval(vs, is)
- // 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)
- }
- }
- } 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)
- // 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)
- }
- 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 {
- if err = vs.SetValue(vars[0], res); err != nil {
- err = rt.erp.NewRuntimeError(util.ErrVarAccess,
- err.Error(), rt.node)
- }
- } else if resList, ok := res.([]interface{}); ok {
- if len(vars) != len(resList) {
- return nil, rt.erp.NewRuntimeError(util.ErrInvalidState,
- fmt.Sprintf("Assigned number of variables is different to "+
- "number of values (%v variables vs %v values)",
- len(vars), len(resList)), rt.node)
- }
- for i, v := range vars {
- if err == nil {
- if err = vs.SetValue(v, resList[i]); err != nil {
- err = rt.erp.NewRuntimeError(util.ErrVarAccess,
- err.Error(), rt.node)
- }
- }
- }
- } else {
- return nil, rt.erp.NewRuntimeError(util.ErrInvalidState,
- fmt.Sprintf("Result for loop variable is not a list (value is %v)", res),
- rt.node)
- }
- // Execute block
- if err == nil {
- _, err = rt.node.Children[1].Runtime.Eval(vs, is)
- }
- }
- // 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{}) (interface{}, error) {
- _, err := rt.baseRuntime.Eval(vs, is)
- 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{}) (interface{}, error) {
- _, err := rt.baseRuntime.Eval(vs, is)
- if err == nil {
- err = rt.erp.NewRuntimeError(util.ErrContinueIteration, "", rt.node)
- }
- return nil, err
- }
|