ast.go 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  1. package mar
  2. import (
  3. "math/rand"
  4. )
  5. // Node represents a node within the AST.
  6. type Node interface {
  7. node()
  8. }
  9. func (*Document) node() {}
  10. func (*Transition) node() {}
  11. func (*ActionBlock) node() {}
  12. func (*Action) node() {}
  13. func (*Arg) node() {}
  14. func (*Pos) node() {}
  15. type Document struct {
  16. UUID int
  17. Format string
  18. Connection Pos
  19. Lparen Pos
  20. Transport string
  21. TransportPos Pos
  22. Comma Pos
  23. Port string
  24. PortPos Pos
  25. Rparen Pos
  26. Colon Pos
  27. Transitions []*Transition
  28. ActionBlocks []*ActionBlock
  29. }
  30. // FirstSender returns the party that initiates the protocol.
  31. func (doc *Document) FirstSender() string {
  32. if doc.Format == "ftp_pasv_transfer" {
  33. return "server"
  34. }
  35. return "client"
  36. }
  37. // ActionBlock returns an action block by name.
  38. func (doc *Document) ActionBlock(name string) *ActionBlock {
  39. for _, blk := range doc.ActionBlocks {
  40. if blk.Name == name {
  41. return blk
  42. }
  43. }
  44. return nil
  45. }
  46. // HasTransition returns true if there is a transition between src and dst.
  47. func (doc *Document) HasTransition(src, dst string) bool {
  48. for _, transition := range doc.Transitions {
  49. if transition.Source == src && transition.Destination == dst {
  50. return true
  51. }
  52. }
  53. return false
  54. }
  55. // Normalize ensures document conforms to expected state.
  56. func (doc *Document) Normalize() error {
  57. // Add dead state transitions.
  58. if !doc.HasTransition("end", "dead") {
  59. doc.Transitions = append(doc.Transitions, &Transition{Source: "end", Destination: "dead", ActionBlock: "NULL", Probability: 1})
  60. }
  61. if !doc.HasTransition("dead", "dead") {
  62. doc.Transitions = append(doc.Transitions, &Transition{Source: "dead", Destination: "dead", ActionBlock: "NULL", Probability: 1})
  63. }
  64. return nil
  65. }
  66. type Transition struct {
  67. Source string
  68. SourcePos Pos
  69. Destination string
  70. DestinationPos Pos
  71. ActionBlock string
  72. ActionBlockPos Pos
  73. Probability float64
  74. ProbabilityPos Pos
  75. IsErrorTransition bool
  76. }
  77. func FilterTransitionsBySource(a []*Transition, name string) []*Transition {
  78. other := make([]*Transition, 0, len(a))
  79. for _, t := range a {
  80. if t.Source == name {
  81. other = append(other, t)
  82. }
  83. }
  84. return other
  85. }
  86. func FilterTransitionsByDestination(a []*Transition, name string) []*Transition {
  87. other := make([]*Transition, 0, len(a))
  88. for _, t := range a {
  89. if t.Destination == name {
  90. other = append(other, t)
  91. }
  92. }
  93. return other
  94. }
  95. func FilterProbableTransitions(a []*Transition) []*Transition {
  96. other := make([]*Transition, 0, len(a))
  97. for _, t := range a {
  98. if t.Probability > 0 {
  99. other = append(other, t)
  100. }
  101. }
  102. return other
  103. }
  104. func FilterErrorTransitions(a []*Transition) []*Transition {
  105. var other []*Transition
  106. for _, t := range a {
  107. if t.IsErrorTransition {
  108. other = append(other, t)
  109. }
  110. }
  111. return other
  112. }
  113. func FilterNonErrorTransitions(a []*Transition) []*Transition {
  114. other := make([]*Transition, 0, len(a))
  115. for _, t := range a {
  116. if !t.IsErrorTransition {
  117. other = append(other, t)
  118. }
  119. }
  120. return other
  121. }
  122. // TransitionsDestinations returns the destination state names from the transitions.
  123. func TransitionsDestinations(a []*Transition) []string {
  124. other := make([]string, 0, len(a))
  125. for _, t := range a {
  126. other = append(other, t.Destination)
  127. }
  128. return other
  129. }
  130. // TransitionsErrorState returns the first error state in a list of transitions.
  131. func TransitionsErrorState(a []*Transition) string {
  132. for _, t := range a {
  133. if t.IsErrorTransition {
  134. return t.Destination
  135. }
  136. }
  137. return ""
  138. }
  139. func ChooseTransitions(a []*Transition, rand *rand.Rand) []*Transition {
  140. // If PRNG not available then return all transitions with a non-zero probability.
  141. if rand == nil {
  142. return FilterProbableTransitions(a)
  143. }
  144. // If there is only one transition then return it.
  145. if len(a) == 1 {
  146. return a
  147. }
  148. // Otherwise randomly choose a transition based on probability.
  149. sum, coin := float64(0), rand.Float64()
  150. for _, t := range a {
  151. if t.Probability <= 0 {
  152. continue
  153. }
  154. sum += t.Probability
  155. if sum >= coin {
  156. return []*Transition{t}
  157. }
  158. }
  159. return []*Transition{a[len(a)-1]}
  160. }
  161. type ActionBlock struct {
  162. Action Pos
  163. Name string
  164. NamePos Pos
  165. Colon Pos
  166. Actions []*Action
  167. }
  168. type Action struct {
  169. Party string
  170. PartyPos Pos
  171. Module string
  172. ModulePos Pos
  173. Dot Pos
  174. Method string
  175. MethodPos Pos
  176. Lparen Pos
  177. Args []*Arg
  178. Rparen Pos
  179. If Pos
  180. RegexMatchIncoming Pos
  181. RegexMatchIncomingLparen Pos
  182. Regex string
  183. RegexPos Pos
  184. RegexMatchIncomingRparen Pos
  185. }
  186. // Name returns the concatenation of the module & method.
  187. func (a *Action) Name() string {
  188. return a.Module + "." + a.Method
  189. }
  190. func (a *Action) ArgValues() []interface{} {
  191. other := make([]interface{}, len(a.Args))
  192. for i, arg := range a.Args {
  193. other[i] = arg.Value
  194. }
  195. return other
  196. }
  197. // Transform converts the action to its complement depending on the party.
  198. func (a *Action) Transform(party string) {
  199. switch party {
  200. case "client":
  201. a.transform("server", "client")
  202. case "server":
  203. a.transform("client", "server")
  204. }
  205. }
  206. func (a *Action) transform(from, to string) {
  207. if a.Party == from {
  208. switch a.Module {
  209. case "fte", "tg":
  210. if a.Method == "send" {
  211. a.Method = "recv"
  212. } else if a.Method == "send_async" {
  213. a.Method = "recv_async"
  214. }
  215. a.Party = to
  216. case "io":
  217. if a.Method == "gets" {
  218. a.Method = "puts"
  219. } else if a.Method == "puts" {
  220. a.Method = "gets"
  221. }
  222. a.Party = to
  223. }
  224. }
  225. }
  226. // FilterActionsByParty returns a slice of actions matching party.
  227. func FilterActionsByParty(actions []*Action, party string) []*Action {
  228. other := make([]*Action, 0, len(actions))
  229. for _, action := range actions {
  230. if action.Party == party {
  231. other = append(other, action)
  232. }
  233. }
  234. return other
  235. }
  236. type Arg struct {
  237. Value interface{}
  238. Pos Pos
  239. EndPos Pos
  240. }
  241. // Pos specifies the line and character position of a token.
  242. // The Char and Line are both zero-based indexes.
  243. type Pos struct {
  244. Char int
  245. Line int
  246. }
  247. // Walk traverses an AST in depth-first order.
  248. func Walk(v Visitor, node Node) {
  249. if v = v.Visit(node); v == nil {
  250. return
  251. }
  252. // Walk children.
  253. switch node := node.(type) {
  254. case *Document:
  255. for _, transition := range node.Transitions {
  256. Walk(v, transition)
  257. }
  258. for _, blk := range node.ActionBlocks {
  259. Walk(v, blk)
  260. }
  261. case *ActionBlock:
  262. for _, action := range node.Actions {
  263. Walk(v, action)
  264. }
  265. case *Action:
  266. for _, arg := range node.Args {
  267. Walk(v, arg)
  268. }
  269. }
  270. v.Visit(nil)
  271. }
  272. // Visitor represents an object for iterating over nodes using Walk().
  273. type Visitor interface {
  274. Visit(node Node) (w Visitor)
  275. }
  276. // VisitorFunc implements a type to use a function as a Visitor.
  277. type VisitorFunc func(node Node)
  278. func (fn VisitorFunc) Visit(node Node) Visitor {
  279. fn(node)
  280. return fn
  281. }