rt_identifier.go 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. /*
  2. * ECAL
  3. *
  4. * Copyright 2020 Matthias Ladkau. All rights reserved.
  5. *
  6. * This Source Code Form is subject to the terms of the MIT
  7. * License, If a copy of the MIT License was not distributed with this
  8. * file, You can obtain one at https://opensource.org/licenses/MIT.
  9. */
  10. package interpreter
  11. import (
  12. "fmt"
  13. "strings"
  14. "devt.de/krotik/common/stringutil"
  15. "devt.de/krotik/ecal/parser"
  16. "devt.de/krotik/ecal/scope"
  17. "devt.de/krotik/ecal/stdlib"
  18. "devt.de/krotik/ecal/util"
  19. )
  20. /*
  21. identifierRuntime is the runtime component for identifiers.
  22. */
  23. type identifierRuntime struct {
  24. *baseRuntime
  25. }
  26. /*
  27. identifierRuntimeInst returns a new runtime component instance.
  28. */
  29. func identifierRuntimeInst(erp *ECALRuntimeProvider, node *parser.ASTNode) parser.Runtime {
  30. return &identifierRuntime{newBaseRuntime(erp, node)}
  31. }
  32. /*
  33. Eval evaluate this runtime component.
  34. */
  35. func (rt *identifierRuntime) Eval(vs parser.Scope, is map[string]interface{}) (interface{}, error) {
  36. return rt.resolveValue(vs, is, rt.node)
  37. }
  38. /*
  39. resolveValue resolves the value of this identifier.
  40. */
  41. func (rt *identifierRuntime) resolveValue(vs parser.Scope, is map[string]interface{}, node *parser.ASTNode) (interface{}, error) {
  42. var anode *parser.ASTNode
  43. var astring string
  44. var result interface{}
  45. var err error
  46. functionResolved := func(astring string, rnode *parser.ASTNode) *parser.ASTNode {
  47. res := &parser.ASTNode{ // Create a dummy identifier which models the value evaluation so far
  48. Name: parser.NodeIDENTIFIER,
  49. Token: &parser.LexToken{
  50. ID: node.Token.ID,
  51. Identifier: node.Token.Identifier,
  52. Lline: node.Token.Lline,
  53. Lpos: node.Token.Lpos,
  54. Pos: node.Token.Pos,
  55. Val: strings.Replace(astring, ".", ">", -1),
  56. },
  57. Children: nil,
  58. }
  59. for i, c := range rnode.Children {
  60. if c.Name == parser.NodeFUNCCALL {
  61. res.Children = rnode.Children[i+1:]
  62. }
  63. }
  64. return res
  65. }
  66. anode, astring, err = buildAccessString(rt.erp, vs, is, node, node.Token.Val)
  67. if len(node.Children) == 0 {
  68. // Simple case we just have a variable
  69. result, _, err = vs.GetValue(node.Token.Val)
  70. } else if cval, ok := stdlib.GetStdlibConst(astring); ok {
  71. result = cval
  72. } else {
  73. if rerr, ok := err.(*util.RuntimeError); err == nil || ok && rerr.Type == util.ErrInvalidConstruct {
  74. funcCallInAccessStringExecuted := ok && rerr.Type == util.ErrInvalidConstruct
  75. if result, _, err = vs.GetValue(astring); err == nil {
  76. if funcCallInAccessStringExecuted {
  77. result, err = rt.resolveFunction(astring, vs, is, rerr.Node, result, err)
  78. node = functionResolved(astring, anode)
  79. if len(node.Children) > 0 {
  80. // We have more identifiers after the func call - there is more to do ...
  81. vs = scope.NewScope("funcresult")
  82. vs.SetValue(node.Token.Val, result)
  83. result, err = rt.resolveValue(vs, is, node)
  84. }
  85. } else {
  86. result, err = rt.resolveFunction(astring, vs, is, node, result, err)
  87. }
  88. }
  89. }
  90. }
  91. return result, err
  92. }
  93. /*
  94. resolveFunction execute function calls and return the result.
  95. */
  96. func (rt *identifierRuntime) resolveFunction(astring string, vs parser.Scope, is map[string]interface{},
  97. node *parser.ASTNode, result interface{}, err error) (interface{}, error) {
  98. is["erp"] = rt.erp // All functions have access to the ECAL Runtime Provider
  99. is["astnode"] = rt.node // ... and the AST node
  100. for _, funccall := range node.Children {
  101. if funccall.Name == parser.NodeFUNCCALL {
  102. var funcObj util.ECALFunction
  103. ok := astring == "log" || astring == "error" || astring == "debug"
  104. if !ok {
  105. funcObj, ok = result.(util.ECALFunction)
  106. if !ok {
  107. // Check for stdlib function
  108. funcObj, ok = stdlib.GetStdlibFunc(astring)
  109. if !ok {
  110. // Check for inbuild function
  111. funcObj, ok = inbuildFuncMap[astring]
  112. }
  113. }
  114. }
  115. if ok {
  116. var args []interface{}
  117. // Collect the parameter values
  118. for _, c := range funccall.Children {
  119. var val interface{}
  120. if err == nil {
  121. val, err = c.Runtime.Eval(vs, is)
  122. args = append(args, val)
  123. }
  124. }
  125. if err == nil {
  126. if astring == "log" || astring == "error" || astring == "debug" {
  127. // Convert non-string structures
  128. for i, a := range args {
  129. if _, ok := args[i].(string); !ok {
  130. args[i] = stringutil.ConvertToPrettyString(a)
  131. }
  132. }
  133. if astring == "log" {
  134. rt.erp.Logger.LogInfo(args...)
  135. } else if astring == "error" {
  136. rt.erp.Logger.LogError(args...)
  137. } else if astring == "debug" {
  138. rt.erp.Logger.LogDebug(args...)
  139. }
  140. } else {
  141. // Execute the function and
  142. result, err = funcObj.Run(rt.instanceID, vs, is, args)
  143. _, ok1 := err.(*util.RuntimeError)
  144. _, ok2 := err.(*SinkRuntimeError)
  145. if err != nil && !ok1 && !ok2 {
  146. // Convert into a proper runtime error if necessary
  147. rerr := rt.erp.NewRuntimeError(util.ErrRuntimeError,
  148. err.Error(), node).(*util.RuntimeError)
  149. if err == util.ErrIsIterator || err == util.ErrEndOfIteration || err == util.ErrContinueIteration {
  150. rerr.Type = err
  151. }
  152. err = rerr
  153. }
  154. }
  155. }
  156. } else {
  157. err = rt.erp.NewRuntimeError(util.ErrUnknownConstruct,
  158. fmt.Sprintf("Unknown function: %v", node.Token.Val), node)
  159. }
  160. break
  161. }
  162. }
  163. return result, err
  164. }
  165. /*
  166. Set sets a value to this identifier.
  167. */
  168. func (rt *identifierRuntime) Set(vs parser.Scope, is map[string]interface{}, value interface{}) error {
  169. var err error
  170. if len(rt.node.Children) == 0 {
  171. // Simple case we just have a variable
  172. err = vs.SetValue(rt.node.Token.Val, value)
  173. } else {
  174. var as string
  175. _, as, err = buildAccessString(rt.erp, vs, is, rt.node, rt.node.Token.Val)
  176. if err == nil {
  177. // Collect all the children and find the right spot
  178. err = vs.SetValue(as, value)
  179. }
  180. }
  181. return err
  182. }
  183. /*
  184. buildAccessString builds an access string using a given node and a prefix.
  185. */
  186. func buildAccessString(erp *ECALRuntimeProvider, vs parser.Scope, is map[string]interface{},
  187. node *parser.ASTNode, prefix string) (*parser.ASTNode, string, error) {
  188. var err error
  189. res := prefix
  190. for i, c := range node.Children {
  191. if err == nil {
  192. // The unexpected construct error is used in two ways:
  193. // 1. Error message when a function call is used on the left hand of
  194. // an assignment.
  195. // 2. Signalling there is a function call involved on the right hand
  196. // of an assignment.
  197. if c.Name == parser.NodeCOMPACCESS {
  198. var val interface{}
  199. val, err = c.Children[0].Runtime.Eval(vs, is)
  200. res = fmt.Sprintf("%v.%v", res, val)
  201. if len(node.Children) > i+1 && node.Children[i+1].Name == parser.NodeFUNCCALL {
  202. err = erp.NewRuntimeError(util.ErrInvalidConstruct,
  203. "Unexpected construct", node)
  204. break
  205. }
  206. } else if c.Name == parser.NodeIDENTIFIER {
  207. res = fmt.Sprintf("%v.%v", res, c.Token.Val)
  208. if len(c.Children) > 0 && c.Children[0].Name == parser.NodeFUNCCALL {
  209. node = c
  210. err = erp.NewRuntimeError(util.ErrInvalidConstruct,
  211. "Unexpected construct", node)
  212. break
  213. }
  214. node, res, err = buildAccessString(erp, vs, is, c, res)
  215. }
  216. }
  217. }
  218. return node, res, err
  219. }