rt_identifier.go 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  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. funcObj, ok := rt.resolveFunctionObject(astring, result)
  108. if ok {
  109. var args []interface{}
  110. // Collect the parameter values
  111. for _, c := range funccall.Children {
  112. var val interface{}
  113. if err == nil {
  114. val, err = c.Runtime.Eval(vs, make(map[string]interface{}), tid)
  115. args = append(args, val)
  116. }
  117. }
  118. if err == nil {
  119. result, err = rt.executeFunction(astring, funcObj, args, vs, is, tid, node)
  120. }
  121. } else {
  122. err = rt.erp.NewRuntimeError(util.ErrUnknownConstruct,
  123. fmt.Sprintf("Unknown function: %v", node.Token.Val), node)
  124. }
  125. break
  126. }
  127. }
  128. return result, err
  129. }
  130. /*
  131. resolveFunctionObject will resolve a given string or object into a concrete ECAL function.
  132. */
  133. func (rt *identifierRuntime) resolveFunctionObject(astring string, result interface{}) (util.ECALFunction, bool) {
  134. var funcObj util.ECALFunction
  135. ok := astring == "log" || astring == "error" || astring == "debug"
  136. if !ok {
  137. funcObj, ok = result.(util.ECALFunction)
  138. if !ok {
  139. // Check for stdlib function
  140. funcObj, ok = stdlib.GetStdlibFunc(astring)
  141. if !ok {
  142. // Check for inbuild function
  143. funcObj, ok = InbuildFuncMap[astring]
  144. }
  145. }
  146. }
  147. return funcObj, ok
  148. }
  149. /*
  150. executeFunction executes a function call with a given list of arguments and return the result.
  151. */
  152. func (rt *identifierRuntime) executeFunction(astring string, funcObj util.ECALFunction, args []interface{},
  153. vs parser.Scope, is map[string]interface{}, tid uint64, node *parser.ASTNode) (interface{}, error) {
  154. var result interface{}
  155. var err error
  156. if stringutil.IndexOf(astring, []string{"log", "error", "debug"}) != -1 {
  157. // Convert non-string structures
  158. for i, a := range args {
  159. if _, ok := a.(string); !ok {
  160. args[i] = stringutil.ConvertToPrettyString(a)
  161. }
  162. }
  163. if astring == "log" {
  164. rt.erp.Logger.LogInfo(args...)
  165. } else if astring == "error" {
  166. rt.erp.Logger.LogError(args...)
  167. } else if astring == "debug" {
  168. rt.erp.Logger.LogDebug(args...)
  169. }
  170. } else {
  171. if rt.erp.Debugger != nil {
  172. rt.erp.Debugger.VisitStepInState(node, vs, tid)
  173. }
  174. // Execute the function
  175. result, err = funcObj.Run(rt.instanceID, vs, is, tid, args)
  176. if rt.erp.Debugger != nil {
  177. rt.erp.Debugger.VisitStepOutState(node, vs, tid, err)
  178. }
  179. _, ok1 := err.(*util.RuntimeError)
  180. _, ok2 := err.(*util.RuntimeErrorWithDetail)
  181. if err != nil && !ok1 && !ok2 {
  182. // Convert into a proper runtime error if necessary
  183. rerr := rt.erp.NewRuntimeError(util.ErrRuntimeError,
  184. err.Error(), node).(*util.RuntimeError)
  185. if stringutil.IndexOf(err.Error(), []string{util.ErrIsIterator.Error(),
  186. util.ErrEndOfIteration.Error(), util.ErrContinueIteration.Error()}) != -1 {
  187. rerr.Type = err
  188. }
  189. err = rerr
  190. }
  191. if tr, ok := err.(util.TraceableRuntimeError); ok {
  192. // Add tracing information to the error
  193. tr.AddTrace(rt.node)
  194. }
  195. }
  196. return result, err
  197. }
  198. /*
  199. Set sets a value to this identifier.
  200. */
  201. func (rt *identifierRuntime) Set(vs parser.Scope, is map[string]interface{}, tid uint64, value interface{}) error {
  202. var err error
  203. if len(rt.node.Children) == 0 {
  204. // Simple case we just have a variable
  205. err = vs.SetValue(rt.node.Token.Val, value)
  206. } else {
  207. var as string
  208. _, as, err = buildAccessString(rt.erp, vs, is, tid, rt.node, rt.node.Token.Val)
  209. if err == nil {
  210. // Collect all the children and find the right spot
  211. err = vs.SetValue(as, value)
  212. }
  213. }
  214. return err
  215. }
  216. /*
  217. buildAccessString builds an access string using a given node and a prefix.
  218. */
  219. func buildAccessString(erp *ECALRuntimeProvider, vs parser.Scope, is map[string]interface{},
  220. tid uint64, node *parser.ASTNode, prefix string) (*parser.ASTNode, string, error) {
  221. var err error
  222. res := prefix
  223. for i, c := range node.Children {
  224. if err == nil {
  225. // The unexpected construct error is used in two ways:
  226. // 1. Error message when a function call is used on the left hand of
  227. // an assignment.
  228. // 2. Signalling there is a function call involved on the right hand
  229. // of an assignment.
  230. if c.Name == parser.NodeCOMPACCESS {
  231. var val interface{}
  232. val, err = c.Children[0].Runtime.Eval(vs, is, tid)
  233. res = fmt.Sprintf("%v.%v", res, val)
  234. if len(node.Children) > i+1 && node.Children[i+1].Name == parser.NodeFUNCCALL {
  235. err = erp.NewRuntimeError(util.ErrInvalidConstruct,
  236. "Unexpected construct", node)
  237. break
  238. }
  239. } else if c.Name == parser.NodeIDENTIFIER {
  240. res = fmt.Sprintf("%v.%v", res, c.Token.Val)
  241. if len(c.Children) > 0 && c.Children[0].Name == parser.NodeFUNCCALL {
  242. node = c
  243. err = erp.NewRuntimeError(util.ErrInvalidConstruct,
  244. "Unexpected construct", node)
  245. break
  246. }
  247. node, res, err = buildAccessString(erp, vs, is, tid, c, res)
  248. }
  249. }
  250. }
  251. return node, res, err
  252. }