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