/tutorial/backtracking/expand_expression/tree/expand_expression.e

http://github.com/tybor/Liberty · Specman e · 280 lines · 190 code · 24 blank · 66 comment · 15 complexity · cb36d321ac6545bc2d630d71e325eee5 MD5 · raw file

  1. -- See the Copyright notice at the end of this file.
  2. --
  3. class EXPAND_EXPRESSION
  4. --
  5. -- That program shows the use of ABSTRACT_BACKTRACKING.
  6. --
  7. -- That program read expressions from the standard input and print the
  8. -- expansion of the read expression on the standard output.
  9. -- The expressions are composed of sequence and alternative of terms.
  10. -- The expansion is the list of all the sequences allowed when
  11. -- alternatives are removed.
  12. --
  13. -- For example the input expression "(0+1)(0+1)(0+1)" will give the
  14. -- following output:
  15. --
  16. -- (1) 0 0 0
  17. -- (2) 0 0 1
  18. -- (3) 0 1 0
  19. -- (4) 0 1 1
  20. -- (5) 1 0 0
  21. -- (6) 1 0 1
  22. -- (7) 1 1 0
  23. -- (8) 1 1 1
  24. --
  25. -- The grammar of the input expressions is in ABNF like:
  26. --
  27. -- expression ::= alternative
  28. -- alternative ::= sequence [ '+' sequence ]...
  29. -- sequence ::= term [ ['.'] term ]...
  30. -- term ::= | '(' alternative ')' | "[^().+]*"
  31. --
  32. inherit
  33. BACKTRACKING
  34. undefine default_create
  35. end
  36. MINI_PARSER_BUFFER
  37. -- a nicer name when inherited!
  38. rename next as next_character
  39. end
  40. BACKTRACKING_NODE_GLOBALS
  41. undefine default_create
  42. end
  43. insert
  44. EXCEPTIONS
  45. undefine default_create
  46. end
  47. create {ANY}
  48. make
  49. feature {ANY} -- make
  50. make
  51. -- read one line and treat it until end of input
  52. do
  53. from
  54. initialise
  55. io.read_line
  56. until
  57. io.end_of_input
  58. loop
  59. parse
  60. expand_all
  61. io.read_line
  62. end
  63. end
  64. initialise
  65. -- initialisation
  66. do
  67. create buffer.make(10)
  68. create stack.with_capacity(100)
  69. create context.with_capacity(100)
  70. end
  71. feature {ANY} -- enumeration of expansions
  72. root: BACKTRACKING_NODE
  73. stack: FAST_ARRAY[STRING]
  74. context: FAST_ARRAY[INTEGER]
  75. top: INTEGER
  76. expand_all
  77. -- print all the expansions of the root
  78. local
  79. i, n: INTEGER
  80. do
  81. from
  82. -- go to the first solution
  83. stack.clear_count
  84. set_current_node(root)
  85. search_first
  86. until
  87. is_off
  88. loop
  89. -- print the solution using the path iterator
  90. if True then
  91. from
  92. n := n + 1
  93. io.put_string(once "%T(")
  94. io.put_integer(n)
  95. io.put_string(once ")%T")
  96. i := 0
  97. until
  98. i > stack.upper
  99. loop
  100. if i > 0 then
  101. io.put_character(' ')
  102. end
  103. io.put_string(stack.item(i))
  104. i := i + 1
  105. end
  106. io.put_new_line
  107. end
  108. -- go to the next solution
  109. search_next
  110. end
  111. io.flush
  112. end
  113. context_clear
  114. do
  115. context.clear_count
  116. stack.clear_count
  117. top := 0
  118. end
  119. context_push
  120. do
  121. context.add_last(top)
  122. top := stack.count
  123. end
  124. context_restore
  125. do
  126. stack.resize(top)
  127. end
  128. context_restore_and_pop
  129. do
  130. stack.resize(top)
  131. top := context.last
  132. context.remove_last
  133. end
  134. context_cut
  135. -- no cut allowed
  136. do
  137. check
  138. False
  139. end
  140. end
  141. feature {ANY} -- parsing
  142. parse
  143. -- initialise the mini_parser_buffer behavior
  144. -- then call parse and treat syntax errors with
  145. -- exceptions
  146. local
  147. cancelled: BOOLEAN
  148. do
  149. if not cancelled then
  150. initialize_with(io.last_string)
  151. skip_separators
  152. if end_reached then
  153. root := the_false_node
  154. else
  155. root := parse_alternative
  156. if not end_reached then
  157. if current_character = ')' then
  158. raise(once "unbounded ')'")
  159. else
  160. raise(once "end not reached")
  161. end
  162. end
  163. end
  164. end
  165. ensure
  166. root /= Void
  167. rescue
  168. root := the_false_node
  169. io.put_string("Syntax error: ")
  170. io.put_string(developer_exception_name)
  171. io.put_character('%N')
  172. io.last_string.replace_all('%T', ' ')
  173. io.put_string(io.last_string)
  174. io.put_character('%N')
  175. io.put_spaces(current_index - 1)
  176. io.put_character('^')
  177. io.put_character('%N')
  178. cancelled := True
  179. retry
  180. end
  181. parse_alternative: BACKTRACKING_NODE
  182. -- parse an alternative recursively to construct tree
  183. -- balanced to the right because it is more efficient
  184. do
  185. Result := parse_sequence
  186. if not end_reached and then current_character = '+' then
  187. next_character
  188. skip_separators
  189. create {BACKTRACKING_NODE_OR_PAIR} Result.make(Result, parse_alternative)
  190. end
  191. end
  192. parse_sequence: BACKTRACKING_NODE
  193. -- parse a sequence recursively to construct tree
  194. -- balanced to the right because it is more efficient
  195. do
  196. Result := parse_term
  197. if not end_reached and then not (once "+)").has(current_character) then
  198. create {BACKTRACKING_NODE_AND_PAIR} Result.make(Result, parse_sequence)
  199. end
  200. end
  201. parse_term: BACKTRACKING_NODE
  202. -- parse a term
  203. do
  204. -- skip any '.' that are noise
  205. from
  206. until
  207. end_reached or else current_character /= '.'
  208. loop
  209. next_character
  210. skip_separators
  211. end
  212. if end_reached or else (once "+)").has(current_character) then
  213. -- if already a termination, return the empty item
  214. Result := the_true_node
  215. elseif current_character = '(' then
  216. -- parse a sub expression in parenthesis
  217. next_character
  218. skip_separators
  219. Result := parse_alternative
  220. if end_reached or else current_character /= ')' then
  221. raise(once "unbounded '('")
  222. end
  223. next_character
  224. skip_separators
  225. else
  226. -- parse a term
  227. from
  228. buffer.clear_count
  229. until
  230. end_reached or else current_character.is_separator or else (once "()+.").has(current_character)
  231. loop
  232. buffer.add_last(current_character)
  233. next_character
  234. end
  235. create {STRING_NODE} Result.make(buffer.twin)
  236. skip_separators
  237. end
  238. end
  239. buffer: STRING
  240. end -- class EXPAND_EXPRESSION
  241. --
  242. -- ------------------------------------------------------------------------------------------------------------------------------
  243. -- Copyright notice below. Please read.
  244. --
  245. -- This file is free software, which comes along with SmartEiffel. This software is distributed in the hope that it will be
  246. -- useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  247. -- You can modify it as you want, provided this footer is kept unaltered, and a notification of the changes is added.
  248. -- You are allowed to redistribute it and sell it, alone or as a part of another product.
  249. --
  250. -- Copyright(C) 1994-2002: INRIA - LORIA (INRIA Lorraine) - ESIAL U.H.P. - University of Nancy 1 - FRANCE
  251. -- Copyright(C) 2003-2005: INRIA - LORIA (INRIA Lorraine) - I.U.T. Charlemagne - University of Nancy 2 - FRANCE
  252. --
  253. -- Authors: Dominique COLNET, Philippe RIBET, Cyril ADRIAN, Vincent CROIZIER, Frederic MERIZEN
  254. --
  255. -- http://SmartEiffel.loria.fr - SmartEiffel@loria.fr
  256. -- ------------------------------------------------------------------------------------------------------------------------------