123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473 |
- /*
- * 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"
- "strconv"
- "devt.de/krotik/ecal/parser"
- "devt.de/krotik/ecal/scope"
- "devt.de/krotik/ecal/util"
- )
- /*
- inbuildFuncMap contains the mapping of inbuild functions.
- */
- var inbuildFuncMap = map[string]util.ECALFunction{
- "range": &rangeFunc{&inbuildBaseFunc{}},
- "new": &newFunc{&inbuildBaseFunc{}},
- "len": &lenFunc{&inbuildBaseFunc{}},
- "del": &delFunc{&inbuildBaseFunc{}},
- "add": &addFunc{&inbuildBaseFunc{}},
- "concat": &concatFunc{&inbuildBaseFunc{}},
- "dumpenv": &dumpenvFunc{&inbuildBaseFunc{}},
- }
- /*
- inbuildBaseFunc is the base structure for inbuild functions providing some
- utility functions.
- */
- type inbuildBaseFunc struct {
- }
- /*
- AssertNumParam converts a general interface{} parameter into a number.
- */
- func (ibf *inbuildBaseFunc) AssertNumParam(index int, val interface{}) (float64, error) {
- var err error
- resNum, ok := val.(float64)
- if !ok {
- resNum, err = strconv.ParseFloat(fmt.Sprint(val), 64)
- if err != nil {
- err = fmt.Errorf("Parameter %v should be a number", index)
- }
- }
- return resNum, err
- }
- /*
- AssertMapParam converts a general interface{} parameter into a map.
- */
- func (ibf *inbuildBaseFunc) AssertMapParam(index int, val interface{}) (map[interface{}]interface{}, error) {
- valMap, ok := val.(map[interface{}]interface{})
- if ok {
- return valMap, nil
- }
- return nil, fmt.Errorf("Parameter %v should be a map", index)
- }
- /*
- AssertListParam converts a general interface{} parameter into a list.
- */
- func (ibf *inbuildBaseFunc) AssertListParam(index int, val interface{}) ([]interface{}, error) {
- valList, ok := val.([]interface{})
- if ok {
- return valList, nil
- }
- return nil, fmt.Errorf("Parameter %v should be a list", index)
- }
- // Range
- // =====
- /*
- rangeFunc is an interator function which returns a range of numbers.
- */
- type rangeFunc struct {
- *inbuildBaseFunc
- }
- /*
- Run executes this function.
- */
- func (rf *rangeFunc) Run(instanceID string, vs parser.Scope, is map[string]interface{}, args []interface{}) (interface{}, error) {
- var currVal, to float64
- var err error
- lenargs := len(args)
- from := 0.
- step := 1.
- if lenargs == 0 {
- err = fmt.Errorf("Need at least an end range as first parameter")
- }
- if err == nil {
- if stepVal, ok := is[instanceID+"step"]; ok {
- step = stepVal.(float64)
- from = is[instanceID+"from"].(float64)
- to = is[instanceID+"to"].(float64)
- currVal = is[instanceID+"currVal"].(float64)
- is[instanceID+"currVal"] = currVal + step
- // Check for end of iteration
- if (from < to && currVal > to) || (from > to && currVal < to) || from == to {
- err = util.ErrEndOfIteration
- }
- } else {
- if lenargs == 1 {
- to, err = rf.AssertNumParam(1, args[0])
- } else {
- from, err = rf.AssertNumParam(1, args[0])
- if err == nil {
- to, err = rf.AssertNumParam(2, args[1])
- }
- if err == nil && lenargs > 2 {
- step, err = rf.AssertNumParam(3, args[2])
- }
- }
- if err == nil {
- is[instanceID+"from"] = from
- is[instanceID+"to"] = to
- is[instanceID+"step"] = step
- is[instanceID+"currVal"] = from
- currVal = from
- }
- }
- }
- if err == nil {
- err = util.ErrIsIterator // Identify as iterator
- }
- return currVal, err
- }
- /*
- DocString returns a descriptive string.
- */
- func (rf *rangeFunc) DocString() (string, error) {
- return "Range function which can be used to iterate over number ranges. Parameters are start, end and step.", nil
- }
- // New
- // ===
- /*
- newFunc instantiates a new object.
- */
- type newFunc struct {
- *inbuildBaseFunc
- }
- /*
- Run executes this function.
- */
- func (rf *newFunc) Run(instanceID string, vs parser.Scope, is map[string]interface{}, args []interface{}) (interface{}, error) {
- var res interface{}
- err := fmt.Errorf("Need a map as first parameter")
- if len(args) > 0 {
- var argMap map[interface{}]interface{}
- if argMap, err = rf.AssertMapParam(1, args[0]); err == nil {
- obj := make(map[interface{}]interface{})
- res = obj
- _, err = rf.addSuperClasses(vs, is, obj, argMap)
- if initObj, ok := obj["init"]; ok {
- if initFunc, ok := initObj.(*function); ok {
- initvs := scope.NewScope(fmt.Sprintf("newfunc: %v", instanceID))
- initis := make(map[string]interface{})
- _, err = initFunc.Run(instanceID, initvs, initis, args[1:])
- }
- }
- }
- }
- return res, err
- }
- /*
- addSuperClasses adds super class functions to a given object.
- */
- func (rf *newFunc) addSuperClasses(vs parser.Scope, is map[string]interface{},
- obj map[interface{}]interface{}, template map[interface{}]interface{}) (interface{}, error) {
- var err error
- var initFunc interface{}
- var initSuperList []interface{}
- // First loop into the base classes (i.e. top-most classes)
- if super, ok := template["super"]; ok {
- if superList, ok := super.([]interface{}); ok {
- for _, superObj := range superList {
- var superInit interface{}
- if superTemplate, ok := superObj.(map[interface{}]interface{}); ok {
- superInit, err = rf.addSuperClasses(vs, is, obj, superTemplate)
- initSuperList = append(initSuperList, superInit) // Build up the list of super functions
- }
- }
- } else {
- err = fmt.Errorf("Property _super must be a list of super classes")
- }
- }
- // Copy all properties from template to obj
- for k, v := range template {
- // Save previous init function
- if funcVal, ok := v.(*function); ok {
- newFunction := &function{funcVal.name, nil, obj, funcVal.declaration}
- if k == "init" {
- newFunction.super = initSuperList
- initFunc = newFunction
- }
- obj[k] = newFunction
- } else {
- obj[k] = v
- }
- }
- return initFunc, err
- }
- /*
- DocString returns a descriptive string.
- */
- func (rf *newFunc) DocString() (string, error) {
- return "New creates a new object instance.", nil
- }
- // Len
- // ===
- /*
- lenFunc returns the size of a list or map.
- */
- type lenFunc struct {
- *inbuildBaseFunc
- }
- /*
- Run executes this function.
- */
- func (rf *lenFunc) Run(instanceID string, vs parser.Scope, is map[string]interface{}, args []interface{}) (interface{}, error) {
- var res float64
- err := fmt.Errorf("Need a list or a map as first parameter")
- if len(args) > 0 {
- argList, ok1 := args[0].([]interface{})
- argMap, ok2 := args[0].(map[interface{}]interface{})
- if ok1 {
- res = float64(len(argList))
- err = nil
- } else if ok2 {
- res = float64(len(argMap))
- err = nil
- }
- }
- return res, err
- }
- /*
- DocString returns a descriptive string.
- */
- func (rf *lenFunc) DocString() (string, error) {
- return "Len returns the size of a list or map.", nil
- }
- // Del
- // ===
- /*
- delFunc removes an element from a list or map.
- */
- type delFunc struct {
- *inbuildBaseFunc
- }
- /*
- Run executes this function.
- */
- func (rf *delFunc) Run(instanceID string, vs parser.Scope, is map[string]interface{}, args []interface{}) (interface{}, error) {
- var res interface{}
- err := fmt.Errorf("Need a list or a map as first parameter and an index or key as second parameter")
- if len(args) == 2 {
- if argList, ok1 := args[0].([]interface{}); ok1 {
- var index float64
- index, err = rf.AssertNumParam(2, args[1])
- if err == nil {
- res = append(argList[:int(index)], argList[int(index+1):]...)
- }
- }
- if argMap, ok2 := args[0].(map[interface{}]interface{}); ok2 {
- key := fmt.Sprint(args[1])
- delete(argMap, key)
- res = argMap
- err = nil
- }
- }
- return res, err
- }
- /*
- DocString returns a descriptive string.
- */
- func (rf *delFunc) DocString() (string, error) {
- return "Del removes an item from a list or map.", nil
- }
- // Add
- // ===
- /*
- addFunc adds an element to a list.
- */
- type addFunc struct {
- *inbuildBaseFunc
- }
- /*
- Run executes this function.
- */
- func (rf *addFunc) Run(instanceID string, vs parser.Scope, is map[string]interface{}, args []interface{}) (interface{}, error) {
- var res interface{}
- err := fmt.Errorf("Need a list as first parameter and a value as second parameter")
- if len(args) > 1 {
- var argList []interface{}
- if argList, err = rf.AssertListParam(1, args[0]); err == nil {
- if len(args) == 3 {
- var index float64
- if index, err = rf.AssertNumParam(3, args[2]); err == nil {
- argList = append(argList, 0)
- copy(argList[int(index+1):], argList[int(index):])
- argList[int(index)] = args[1]
- res = argList
- }
- } else {
- res = append(argList, args[1])
- }
- }
- }
- return res, err
- }
- /*
- DocString returns a descriptive string.
- */
- func (rf *addFunc) DocString() (string, error) {
- return "Add adds an item to a list. The item is added at the optionally given index or at the end if no index is specified.", nil
- }
- // Concat
- // ======
- /*
- concatFunc joins one or more lists together.
- */
- type concatFunc struct {
- *inbuildBaseFunc
- }
- /*
- Run executes this function.
- */
- func (rf *concatFunc) Run(instanceID string, vs parser.Scope, is map[string]interface{}, args []interface{}) (interface{}, error) {
- var res interface{}
- err := fmt.Errorf("Need at least two lists as parameters")
- if len(args) > 1 {
- var argList []interface{}
- resList := make([]interface{}, 0)
- err = nil
- for _, a := range args {
- if err == nil {
- if argList, err = rf.AssertListParam(1, a); err == nil {
- resList = append(resList, argList...)
- }
- }
- }
- if err == nil {
- res = resList
- }
- }
- return res, err
- }
- /*
- DocString returns a descriptive string.
- */
- func (rf *concatFunc) DocString() (string, error) {
- return "Concat joins one or more lists together. The result is a new list.", nil
- }
- // dumpenv
- // =======
- /*
- dumpenvFunc returns the current variable environment as a string.
- */
- type dumpenvFunc struct {
- *inbuildBaseFunc
- }
- /*
- Run executes this function.
- */
- func (rf *dumpenvFunc) Run(instanceID string, vs parser.Scope, is map[string]interface{}, args []interface{}) (interface{}, error) {
- return vs.String(), nil
- }
- /*
- DocString returns a descriptive string.
- */
- func (rf *dumpenvFunc) DocString() (string, error) {
- return "Dumpenv returns the current variable environment as a string.", nil
- }
|