error.go 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  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. /*
  11. Package util contains utility definitions and functions for the event condition language ECAL.
  12. */
  13. package util
  14. import (
  15. "encoding/json"
  16. "errors"
  17. "fmt"
  18. "devt.de/krotik/ecal/parser"
  19. )
  20. /*
  21. TraceableRuntimeError can record and show a stack trace.
  22. */
  23. type TraceableRuntimeError interface {
  24. error
  25. /*
  26. AddTrace adds a trace step.
  27. */
  28. AddTrace(*parser.ASTNode)
  29. /*
  30. GetTrace returns the current stacktrace.
  31. */
  32. GetTrace() []*parser.ASTNode
  33. /*
  34. GetTrace returns the current stacktrace as a string.
  35. */
  36. GetTraceString() []string
  37. }
  38. /*
  39. RuntimeError is a runtime related error.
  40. */
  41. type RuntimeError struct {
  42. Source string // Name of the source which was given to the parser
  43. Type error // Error type (to be used for equal checks)
  44. Detail string // Details of this error
  45. Node *parser.ASTNode // AST Node where the error occurred
  46. Line int // Line of the error
  47. Pos int // Position of the error
  48. Trace []*parser.ASTNode // Stacktrace
  49. }
  50. /*
  51. Runtime related error types.
  52. */
  53. var (
  54. ErrRuntimeError = errors.New("Runtime error")
  55. ErrUnknownConstruct = errors.New("Unknown construct")
  56. ErrInvalidConstruct = errors.New("Invalid construct")
  57. ErrInvalidState = errors.New("Invalid state")
  58. ErrVarAccess = errors.New("Cannot access variable")
  59. ErrNotANumber = errors.New("Operand is not a number")
  60. ErrNotABoolean = errors.New("Operand is not a boolean")
  61. ErrNotAList = errors.New("Operand is not a list")
  62. ErrNotAMap = errors.New("Operand is not a map")
  63. ErrNotAListOrMap = errors.New("Operand is not a list nor a map")
  64. ErrSink = errors.New("Error in sink")
  65. // ErrReturn is not an error. It is used to return when executing a function
  66. ErrReturn = errors.New("*** return ***")
  67. // Error codes for loop operations
  68. ErrIsIterator = errors.New("Function is an iterator")
  69. ErrEndOfIteration = errors.New("End of iteration was reached")
  70. ErrContinueIteration = errors.New("End of iteration step - Continue iteration")
  71. )
  72. /*
  73. NewRuntimeError creates a new RuntimeError object.
  74. */
  75. func NewRuntimeError(source string, t error, d string, node *parser.ASTNode) error {
  76. if node.Token != nil {
  77. return &RuntimeError{source, t, d, node, node.Token.Lline, node.Token.Lpos, nil}
  78. }
  79. return &RuntimeError{source, t, d, node, 0, 0, nil}
  80. }
  81. /*
  82. Error returns a human-readable string representation of this error.
  83. */
  84. func (re *RuntimeError) Error() string {
  85. ret := fmt.Sprintf("ECAL error in %s: %v (%v)", re.Source, re.Type, re.Detail)
  86. if re.Line != 0 {
  87. // Add line if available
  88. ret = fmt.Sprintf("%s (Line:%d Pos:%d)", ret, re.Line, re.Pos)
  89. }
  90. return ret
  91. }
  92. /*
  93. AddTrace adds a trace step.
  94. */
  95. func (re *RuntimeError) AddTrace(n *parser.ASTNode) {
  96. re.Trace = append(re.Trace, n)
  97. }
  98. /*
  99. GetTrace returns the current stacktrace.
  100. */
  101. func (re *RuntimeError) GetTrace() []*parser.ASTNode {
  102. return re.Trace
  103. }
  104. /*
  105. GetTraceString returns the current stacktrace as a string.
  106. */
  107. func (re *RuntimeError) GetTraceString() []string {
  108. res := []string{}
  109. for _, t := range re.GetTrace() {
  110. pp, _ := parser.PrettyPrint(t)
  111. res = append(res, fmt.Sprintf("%v (%v:%v)", pp, t.Token.Lsource, t.Token.Lline))
  112. }
  113. return res
  114. }
  115. /*
  116. ToJSONObject returns this RuntimeError and all its children as a JSON object.
  117. */
  118. func (re *RuntimeError) ToJSONObject() map[string]interface{} {
  119. t := ""
  120. if re.Type != nil {
  121. t = re.Type.Error()
  122. }
  123. return map[string]interface{}{
  124. "Source": re.Source,
  125. "Type": t,
  126. "Detail": re.Detail,
  127. "Node": re.Node,
  128. "Trace": re.Trace,
  129. }
  130. }
  131. /*
  132. MarshalJSON serializes this RuntimeError into a JSON string.
  133. */
  134. func (re *RuntimeError) MarshalJSON() ([]byte, error) {
  135. return json.Marshal(re.ToJSONObject())
  136. }
  137. /*
  138. RuntimeErrorWithDetail is a runtime error with additional environment information.
  139. */
  140. type RuntimeErrorWithDetail struct {
  141. *RuntimeError
  142. Environment parser.Scope
  143. Data interface{}
  144. }
  145. /*
  146. ToJSONObject returns this RuntimeErrorWithDetail and all its children as a JSON object.
  147. */
  148. func (re *RuntimeErrorWithDetail) ToJSONObject() map[string]interface{} {
  149. res := re.RuntimeError.ToJSONObject()
  150. e := map[string]interface{}{}
  151. if re.Environment != nil {
  152. e = re.Environment.ToJSONObject()
  153. }
  154. res["Environment"] = e
  155. res["Data"] = re.Data
  156. return res
  157. }
  158. /*
  159. MarshalJSON serializes this RuntimeErrorWithDetail into a JSON string.
  160. */
  161. func (re *RuntimeErrorWithDetail) MarshalJSON() ([]byte, error) {
  162. return json.Marshal(re.ToJSONObject())
  163. }