rt_identifier.go 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  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{}, tid uint64) (interface{}, error) {
  36. var res interface{}
  37. _, err := rt.baseRuntime.Eval(vs, is, tid)
  38. if err == nil {
  39. res, err = rt.resolveValue(vs, is, tid, rt.node)
  40. }
  41. return res, err
  42. }
  43. /*
  44. resolveValue resolves the value of this identifier.
  45. */
  46. func (rt *identifierRuntime) resolveValue(vs parser.Scope, is map[string]interface{}, tid uint64, node *parser.ASTNode) (interface{}, error) {
  47. var anode *parser.ASTNode
  48. var astring string
  49. var result interface{}
  50. var err error
  51. functionResolved := func(astring string, rnode *parser.ASTNode) *parser.ASTNode {
  52. res := &parser.ASTNode{ // Create a dummy identifier which models the value evaluation so far
  53. Name: parser.NodeIDENTIFIER,
  54. Token: &parser.LexToken{
  55. ID: node.Token.ID,
  56. Identifier: node.Token.Identifier,
  57. Lline: node.Token.Lline,
  58. Lpos: node.Token.Lpos,
  59. Pos: node.Token.Pos,
  60. Val: strings.Replace(astring, ".", ">", -1),
  61. },
  62. Children: nil,
  63. }
  64. for i, c := range rnode.Children {
  65. if c.Name == parser.NodeFUNCCALL {
  66. res.Children = rnode.Children[i+1:]
  67. }
  68. }
  69. return res
  70. }
  71. anode, astring, err = buildAccessString(rt.erp, vs, is, tid, node, node.Token.Val)
  72. if len(node.Children) == 0 {
  73. // Simple case we just have a variable
  74. result, _, err = vs.GetValue(node.Token.Val)
  75. } else if cval, ok := stdlib.GetStdlibConst(astring); ok {
  76. result = cval
  77. } else {
  78. if rerr, ok := err.(*util.RuntimeError); err == nil || ok && rerr.Type == util.ErrInvalidConstruct {
  79. funcCallInAccessStringExecuted := ok && rerr.Type == util.ErrInvalidConstruct
  80. if result, _, err = vs.GetValue(astring); err == nil {
  81. if funcCallInAccessStringExecuted {
  82. result, err = rt.resolveFunction(astring, vs, is, tid, rerr.Node, result, err)
  83. node = functionResolved(astring, anode)
  84. if len(node.Children) > 0 {
  85. // We have more identifiers after the func call - there is more to do ...
  86. vs = scope.NewScope("funcresult")
  87. vs.SetValue(node.Token.Val, result)
  88. result, err = rt.resolveValue(vs, is, tid, node)
  89. }
  90. } else {
  91. result, err = rt.resolveFunction(astring, vs, is, tid, node, result, err)
  92. }
  93. }
  94. }
  95. }
  96. return result, err
  97. }
  98. /*
  99. resolveFunction execute function calls and return the result.
  100. */
  101. func (rt *identifierRuntime) resolveFunction(astring string, vs parser.Scope, is map[string]interface{},
  102. tid uint64, node *parser.ASTNode, result interface{}, err error) (interface{}, error) {
  103. is["erp"] = rt.erp // All functions have access to the ECAL Runtime Provider
  104. is["astnode"] = rt.node // ... and the AST node
  105. for _, funccall := range node.Children {
  106. if funccall.Name == parser.NodeFUNCCALL {
  107. var funcObj util.ECALFunction
  108. ok := astring == "log" || astring == "error" || astring == "debug"
  109. if !ok {
  110. funcObj, ok = result.(util.ECALFunction)
  111. if !ok {
  112. // Check for stdlib function
  113. funcObj, ok = stdlib.GetStdlibFunc(astring)
  114. if !ok {
  115. // Check for inbuild function
  116. funcObj, ok = InbuildFuncMap[astring]
  117. }
  118. }
  119. }
  120. if ok {
  121. var args []interface{}
  122. // Collect the parameter values
  123. for _, c := range funccall.Children {
  124. var val interface{}
  125. if err == nil {
  126. val, err = c.Runtime.Eval(vs, make(map[string]interface{}), tid)
  127. args = append(args, val)
  128. }
  129. }
  130. if err == nil {
  131. if astring == "log" || astring == "error" || astring == "debug" {
  132. // Convert non-string structures
  133. for i, a := range args {
  134. if _, ok := a.(string); !ok {
  135. args[i] = stringutil.ConvertToPrettyString(a)
  136. }
  137. }
  138. if astring == "log" {
  139. rt.erp.Logger.LogInfo(args...)
  140. } else if astring == "error" {
  141. rt.erp.Logger.LogError(args...)
  142. } else if astring == "debug" {
  143. rt.erp.Logger.LogDebug(args...)
  144. }
  145. } else {
  146. if rt.erp.Debugger != nil {
  147. rt.erp.Debugger.VisitStepInState(node, vs, tid)
  148. }
  149. // Execute the function
  150. result, err = funcObj.Run(rt.instanceID, vs, is, tid, args)
  151. if rt.erp.Debugger != nil {
  152. rt.erp.Debugger.VisitStepOutState(node, vs, tid)
  153. }
  154. _, ok1 := err.(*util.RuntimeError)
  155. _, ok2 := err.(*util.RuntimeErrorWithDetail)
  156. if err != nil && !ok1 && !ok2 {
  157. // Convert into a proper runtime error if necessary
  158. rerr := rt.erp.NewRuntimeError(util.ErrRuntimeError,
  159. err.Error(), node).(*util.RuntimeError)
  160. if err == util.ErrIsIterator || err == util.ErrEndOfIteration || err == util.ErrContinueIteration {
  161. rerr.Type = err
  162. }
  163. err = rerr
  164. }
  165. if tr, ok := err.(util.TraceableRuntimeError); ok {
  166. // Add tracing information to the error
  167. tr.AddTrace(rt.node)
  168. }
  169. }
  170. }
  171. } else {
  172. err = rt.erp.NewRuntimeError(util.ErrUnknownConstruct,
  173. fmt.Sprintf("Unknown function: %v", node.Token.Val), node)
  174. }
  175. break
  176. }
  177. }
  178. return result, err
  179. }
  180. /*
  181. Set sets a value to this identifier.
  182. */
  183. func (rt *identifierRuntime) Set(vs parser.Scope, is map[string]interface{}, tid uint64, value interface{}) error {
  184. var err error
  185. if len(rt.node.Children) == 0 {
  186. // Simple case we just have a variable
  187. err = vs.SetValue(rt.node.Token.Val, value)
  188. } else {
  189. var as string
  190. _, as, err = buildAccessString(rt.erp, vs, is, tid, rt.node, rt.node.Token.Val)
  191. if err == nil {
  192. // Collect all the children and find the right spot
  193. err = vs.SetValue(as, value)
  194. }
  195. }
  196. return err
  197. }
  198. /*
  199. buildAccessString builds an access string using a given node and a prefix.
  200. */
  201. func buildAccessString(erp *ECALRuntimeProvider, vs parser.Scope, is map[string]interface{},
  202. tid uint64, node *parser.ASTNode, prefix string) (*parser.ASTNode, string, error) {
  203. var err error
  204. res := prefix
  205. for i, c := range node.Children {
  206. if err == nil {
  207. // The unexpected construct error is used in two ways:
  208. // 1. Error message when a function call is used on the left hand of
  209. // an assignment.
  210. // 2. Signalling there is a function call involved on the right hand
  211. // of an assignment.
  212. if c.Name == parser.NodeCOMPACCESS {
  213. var val interface{}
  214. val, err = c.Children[0].Runtime.Eval(vs, is, tid)
  215. res = fmt.Sprintf("%v.%v", res, val)
  216. if len(node.Children) > i+1 && node.Children[i+1].Name == parser.NodeFUNCCALL {
  217. err = erp.NewRuntimeError(util.ErrInvalidConstruct,
  218. "Unexpected construct", node)
  219. break
  220. }
  221. } else if c.Name == parser.NodeIDENTIFIER {
  222. res = fmt.Sprintf("%v.%v", res, c.Token.Val)
  223. if len(c.Children) > 0 && c.Children[0].Name == parser.NodeFUNCCALL {
  224. node = c
  225. err = erp.NewRuntimeError(util.ErrInvalidConstruct,
  226. "Unexpected construct", node)
  227. break
  228. }
  229. node, res, err = buildAccessString(erp, vs, is, tid, c, res)
  230. }
  231. }
  232. }
  233. return node, res, err
  234. }