/src/lib/regular_expression/low_level/backtracking_regular_expression_builder.e

http://github.com/tybor/Liberty · Specman e · 784 lines · 578 code · 60 blank · 146 comment · 30 complexity · 3623d408503c0f2b7c70270debebac5a MD5 · raw file

  1. -- This file is part of a Liberty Eiffel library.
  2. -- See the full copyright at the end.
  3. --
  4. deferred class BACKTRACKING_REGULAR_EXPRESSION_BUILDER
  5. --
  6. -- The frame class of all the regular expression builders.
  7. --
  8. insert
  9. REGULAR_EXPRESSION_STRING_SCANNER
  10. rename scanned_string as expression,
  11. set_scanned_string as set_expression
  12. export {ANY} expression, set_expression, has_error, last_error, position;
  13. {BACKTRACKING_REGULAR_EXPRESSION_BUILDER} all
  14. redefine make
  15. end
  16. REGULAR_EXPRESSION_ITEM_GLOBALS
  17. export {BACKTRACKING_REGULAR_EXPRESSION_BUILDER} all
  18. end
  19. feature {ANY} -- make
  20. make
  21. -- Initialise the attributes.
  22. do
  23. Precursor
  24. create stack.with_capacity(10)
  25. create group_stack.with_capacity(10)
  26. end
  27. feature {ANY} -- behaviors
  28. is_case_insensitive: BOOLEAN
  29. -- Is the match case insensitive?
  30. -- Default is False
  31. is_case_sensitive: BOOLEAN
  32. -- Is the match case sensitive?
  33. -- Default is True
  34. do
  35. Result := not is_case_insensitive
  36. end
  37. set_case_sensitive
  38. -- Set the match as case sensitive.
  39. do
  40. is_case_insensitive := False
  41. ensure
  42. definition: is_case_insensitive = False and is_case_sensitive = True
  43. end
  44. set_case_insensitive
  45. -- Set the match as case insensitive.
  46. do
  47. is_case_insensitive := True
  48. ensure
  49. definition: is_case_insensitive = True and is_case_sensitive = False
  50. end
  51. does_any_match_newline: BOOLEAN
  52. -- Does the "any character" mark match a newline?
  53. -- Default is False
  54. set_any_match_newline
  55. -- The "any character" mark will match a newline.
  56. do
  57. does_any_match_newline := True
  58. ensure
  59. definition: does_any_match_newline = True
  60. end
  61. set_any_dont_match_newline
  62. -- The "any character" mark will not match a newline.
  63. do
  64. does_any_match_newline := False
  65. ensure
  66. definition: does_any_match_newline = False
  67. end
  68. does_match_line_boundary: BOOLEAN
  69. -- Does the begin/end marks match line boundary?
  70. -- Default is False
  71. does_match_text_boundary: BOOLEAN
  72. -- Does the begin/end marks match text boundary?
  73. -- Default is True
  74. do
  75. Result := not does_match_line_boundary
  76. ensure
  77. definition: Result = not does_match_line_boundary
  78. end
  79. set_match_line_boundary
  80. -- The begin/end marks will match line boundary.
  81. do
  82. does_match_line_boundary := True
  83. ensure
  84. definition: does_match_line_boundary = True and does_match_text_boundary = False
  85. end
  86. set_match_text_boundary
  87. -- The begin/end marks will match text boundary.
  88. do
  89. does_match_line_boundary := False
  90. ensure
  91. definition: does_match_line_boundary = False and does_match_text_boundary = True
  92. end
  93. set_default_options
  94. -- Set the default options
  95. do
  96. set_case_sensitive
  97. set_any_dont_match_newline
  98. set_match_text_boundary
  99. ensure
  100. is_case_sensitive
  101. not does_any_match_newline
  102. does_match_text_boundary
  103. end
  104. feature {} -- internal behavior
  105. is_greedy: BOOLEAN
  106. -- Does match a maximal repeat?
  107. -- Default is False
  108. set_greedy
  109. -- Will match a maximal repeat.
  110. do
  111. is_greedy := True
  112. ensure
  113. definition: is_greedy = True
  114. end
  115. set_not_greedy
  116. -- Will match a minimal repeat.
  117. do
  118. is_greedy := False
  119. ensure
  120. definition: is_greedy = False
  121. end
  122. is_looking_ahead: BOOLEAN
  123. -- Is building a look-ahead term?
  124. is_looking_behind: BOOLEAN
  125. -- Is building a look-behind term?
  126. is_looking_around: BOOLEAN
  127. -- Is building look-ahead or look-behind?
  128. do
  129. Result := is_looking_ahead or else is_looking_behind
  130. end
  131. is_looking_positive: BOOLEAN
  132. -- Is the current look-around positive or negative?
  133. feature {ANY} -- parsing
  134. parse_expression (expr: like expression)
  135. -- Set the expression to parse and parse it.
  136. -- When no error the result if put in feature
  137. -- 'last_regular_expression'.
  138. -- If there is an error, a human readable explanation
  139. -- is retrievable by the feature 'last_error'.
  140. require
  141. expression_not_void: expr /= Void
  142. do
  143. set_expression(expr)
  144. parse
  145. ensure
  146. error_or_result: has_error xor has_result
  147. end
  148. parse
  149. -- Parse the current expression.
  150. -- The result if any is got through 'last_regular_expression'
  151. require
  152. expression_not_void: expression /= Void
  153. internal_state_ok: stack.is_empty and group_stack.is_empty
  154. local
  155. pattern: BACKTRACKING_REGULAR_EXPRESSION_PATTERN
  156. do
  157. last_group_count := 0
  158. last_substrings_names.clear_count
  159. clear_error
  160. internal_parse
  161. if not has_error then
  162. pattern.make(stack.first, last_group_count, last_substrings_names)
  163. end
  164. stack.clear_count
  165. group_stack.clear_count
  166. last_pattern := pattern
  167. ensure
  168. internal_state_ok: stack.is_empty and group_stack.is_empty
  169. error_or_result: has_error xor has_result
  170. end
  171. feature {ANY} -- results
  172. has_result: BOOLEAN
  173. -- Did the last 'parse' or 'parse_expression' produced
  174. -- a result in 'last_regular_expression'?
  175. do
  176. Result := last_pattern.is_valid
  177. ensure
  178. definition: Result = last_pattern.is_valid
  179. end
  180. last_pattern: BACKTRACKING_REGULAR_EXPRESSION_PATTERN
  181. -- The last regular expression pattern built by
  182. -- 'parse' or 'parse_expression'
  183. feature {}
  184. internal_parse
  185. -- The parse function to be implemented by the
  186. -- effective builders.
  187. require
  188. at_first_position: position = expression.lower
  189. stack_is_empty: stack.is_empty
  190. no_groups: last_group_count = 0 and group_stack.is_empty
  191. deferred
  192. ensure
  193. error_or_result: has_error or else stack.count = 1 and group_stack.is_empty
  194. end
  195. feature {} -- build
  196. stack: FAST_ARRAY[BACKTRACKING_NODE]
  197. -- The stack of items.
  198. group_stack: FAST_ARRAY[INTEGER]
  199. -- The stack of groups
  200. last_group_count: INTEGER
  201. -- The count of groups currently found.
  202. last_substrings_names: HASHED_BIJECTIVE_DICTIONARY[INTEGER, FIXED_STRING]
  203. -- The names of the named subgroups
  204. once
  205. create Result.with_capacity(10)
  206. end
  207. Repeat_infiny: INTEGER -1
  208. -- Constant that means "infinite repetition".
  209. emit (item: BACKTRACKING_NODE)
  210. -- Pushes 'item' on the stack.
  211. -- [..] -> [.., item]
  212. require
  213. item_not_void: item /= Void
  214. do
  215. stack.add_last(item)
  216. ensure
  217. stack_count_increased_by_one: stack.count = old stack.count + 1
  218. stack_not_empty: stack.count > 0
  219. end
  220. unemit: BACKTRACKING_NODE
  221. -- Pops the Result from the stack.
  222. -- [... Result] -> [...]
  223. require
  224. stack_not_empty: stack.count > 0
  225. do
  226. Result := stack.last
  227. stack.remove_last
  228. ensure
  229. stack_count_decreased_by_one: stack.count = old stack.count - 1
  230. end
  231. emit_any_character
  232. -- Push the match to any characters
  233. do
  234. if does_any_match_newline then
  235. emit(the_any_character_item)
  236. else
  237. emit(the_not_end_of_line_item)
  238. end
  239. end
  240. emit_begin_of_line
  241. -- Push the match to begin of a line
  242. do
  243. if does_match_line_boundary then
  244. emit(the_begin_of_line_item)
  245. else
  246. emit(the_begin_of_text_item)
  247. end
  248. end
  249. emit_end_of_line
  250. -- Push the match to end of a line
  251. do
  252. if does_match_line_boundary then
  253. emit(the_end_of_line_item)
  254. else
  255. emit(the_end_of_text_item)
  256. end
  257. end
  258. prepare_group
  259. -- Declares that a new group begins.
  260. do
  261. last_group_count := last_group_count + 1
  262. group_stack.add_last(last_group_count)
  263. ensure
  264. last_group_count_increased_by_one: last_group_count = old last_group_count + 1
  265. group_greater_than_zero: last_group_count > 0
  266. group_stack_count_increased_by_one: group_stack.count = old group_stack.count + 1
  267. group_pushed: group_stack.last = last_group_count
  268. end
  269. emit_group
  270. -- Push the "end of group" item and update the
  271. -- group indicators
  272. -- [.. X] -> [.., end_group(i)]
  273. require
  274. group_greater_than_zero: last_group_count > 0
  275. group_stack_not_empty: not group_stack.is_empty
  276. enough_data: stack.count > 0
  277. local
  278. x: BACKTRACKING_NODE_AND_LIST; y: BACKTRACKING_NODE
  279. do
  280. y := create {REGULAR_EXPRESSION_ITEM_END_GROUP}.make(group_stack.last)
  281. x := create {BACKTRACKING_NODE_AND_LIST}.make(y, Void)
  282. x := create {BACKTRACKING_NODE_AND_LIST}.make(unemit, x)
  283. y := create {REGULAR_EXPRESSION_ITEM_BEGIN_GROUP}.make(group_stack.last)
  284. x := create {BACKTRACKING_NODE_AND_LIST}.make(y, x)
  285. emit(x)
  286. group_stack.remove_last
  287. ensure
  288. constant_stack_count: stack.count = old stack.count
  289. stack_not_empty: stack.count > 0
  290. last_group_count_unchanged: last_group_count = old last_group_count
  291. group_stack_count_decreased_by_one: group_stack.count = old group_stack.count - 1
  292. end
  293. emit_begin_group
  294. -- Push the "begin of group" item and update the
  295. -- group indicators
  296. -- [..] -> [.., begin_group(i)]
  297. obsolete "Use `declare_group'/`emit_group' instead (February 2006)."
  298. do
  299. last_group_count := last_group_count + 1
  300. group_stack.add_last(last_group_count)
  301. emit(create {REGULAR_EXPRESSION_ITEM_BEGIN_GROUP}.make(last_group_count))
  302. ensure
  303. stack_count_increased_by_one: stack.count = old stack.count + 1
  304. stack_not_empty: stack.count > 0
  305. last_group_count_increased_by_one: last_group_count = old last_group_count + 1
  306. group_greater_than_zero: last_group_count > 0
  307. group_stack_count_increased_by_one: group_stack.count = old group_stack.count + 1
  308. group_pushed: group_stack.last = last_group_count
  309. end
  310. emit_end_group
  311. -- Push the "end of group" item and update the
  312. -- group indicators
  313. -- [..] -> [.., end_group(i)]
  314. obsolete "Use `declare_group'/`emit_group' instead (February 2006)."
  315. require
  316. group_greater_than_zero: last_group_count > 0
  317. group_stack_not_empty: not group_stack.is_empty
  318. do
  319. emit(create {REGULAR_EXPRESSION_ITEM_END_GROUP}.make(group_stack.last))
  320. group_stack.remove_last
  321. ensure
  322. stack_count_increased_by_one: stack.count = old stack.count + 1
  323. stack_not_empty: stack.count > 0
  324. last_group_count_unchanged: last_group_count = old last_group_count
  325. group_stack_count_decreased_by_one: group_stack.count = old group_stack.count - 1
  326. end
  327. emit_match_previous_group (group: INTEGER)
  328. -- Push the item that matches the character 'char'
  329. -- [..] -> [.., previous_group(group)]
  330. require
  331. valid_group: 0 < group and group <= last_group_count
  332. closed_group: not group_stack.has(group)
  333. do
  334. if is_case_sensitive then
  335. emit(create {REGULAR_EXPRESSION_ITEM_PREVIOUS_GROUP}.make(group))
  336. else
  337. emit(create {REGULAR_EXPRESSION_ITEM_PREVIOUS_GROUP_NO_CASE}.make(group))
  338. end
  339. ensure
  340. stack_count_increased_by_one: stack.count = old stack.count + 1
  341. stack_not_empty: stack.count > 0
  342. end
  343. emit_match_single (char: CHARACTER)
  344. -- Push the item that matches the character 'char'
  345. -- [..] -> [.., char]
  346. do
  347. if is_case_sensitive then
  348. emit(create {REGULAR_EXPRESSION_ITEM_SINGLE}.make(char))
  349. else
  350. emit(create {REGULAR_EXPRESSION_ITEM_SINGLE_NO_CASE}.make(char))
  351. end
  352. ensure
  353. stack_count_increased_by_one: stack.count = old stack.count + 1
  354. stack_not_empty: stack.count > 0
  355. end
  356. emit_match_range (lower, upper: CHARACTER)
  357. -- Push the item that matches the character range 'lower'..'upper'.
  358. -- [..] -> [.., lower..upper]
  359. require
  360. valid_range: lower <= upper
  361. local
  362. binf, bsup, low, up: CHARACTER
  363. do
  364. if is_case_sensitive then
  365. emit(create {REGULAR_EXPRESSION_ITEM_RANGE}.make(lower, upper))
  366. else
  367. low := lower
  368. up := upper
  369. if up >= 'A' and then low <= 'Z' then
  370. binf := low.max('A').to_lower
  371. bsup := up.min('Z').to_lower
  372. if binf < low and then bsup >= low then
  373. low := binf
  374. end
  375. if binf <= up and then bsup > up then
  376. up := bsup
  377. end
  378. end
  379. if up >= 'a' and then low <= 'z' then
  380. binf := low.max('a').to_upper
  381. bsup := up.min('z').to_upper
  382. if binf < low and then bsup >= low then
  383. low := binf
  384. end
  385. if binf <= up and then bsup > up then
  386. up := bsup
  387. end
  388. end
  389. begin_collect
  390. emit(create {REGULAR_EXPRESSION_ITEM_RANGE}.make(low, up))
  391. if up >= 'A' and then low <= 'Z' then
  392. binf := low.max('A').to_lower
  393. bsup := up.min('Z').to_lower
  394. if bsup > up or else binf < low then
  395. emit(create {REGULAR_EXPRESSION_ITEM_RANGE}.make(binf, bsup))
  396. end
  397. end
  398. if up >= 'a' and then low <= 'z' then
  399. binf := low.max('a').to_upper
  400. bsup := up.min('z').to_upper
  401. if bsup > up or else binf < low then
  402. emit(create {REGULAR_EXPRESSION_ITEM_RANGE}.make(binf, bsup))
  403. end
  404. end
  405. end_collect_or
  406. end
  407. ensure
  408. stack_count_increased_by_one: stack.count = old stack.count + 1
  409. stack_not_empty: stack.count > 0
  410. end
  411. emit_match_text (text: STRING)
  412. -- Push the item that matches the 'text'
  413. -- [..] -> [.., text]
  414. do
  415. if is_looking_behind then
  416. text.reverse
  417. end
  418. if is_case_sensitive then
  419. emit(create {REGULAR_EXPRESSION_ITEM_TEXT}.make(text.twin))
  420. else
  421. emit(create {REGULAR_EXPRESSION_ITEM_TEXT_NO_CASE}.make(text))
  422. end
  423. ensure
  424. stack_count_increased_by_one: stack.count = old stack.count + 1
  425. stack_not_empty: stack.count > 0
  426. end
  427. begin_collect
  428. -- Begin to collect a collection of items by pushing Void on the stack.
  429. -- After calling 'begin_collect', one of the features
  430. -- 'end_collect_or' or 'end_collect_and' have to be called.
  431. -- That kind of group is intended to manage the collections
  432. -- of alternatives or sequences in an optimal way.
  433. -- [..] -> [.., Void]
  434. do
  435. stack.add_last(Void)
  436. ensure
  437. has_collect: stack.fast_occurrences(Void) > 0
  438. emit_group_empty: stack.last = Void
  439. emit_group_count_incremented: stack.fast_occurrences(Void) = old stack.fast_occurrences(Void) + 1
  440. end
  441. is_collect_empty: BOOLEAN
  442. -- True if currently begun collect is empty
  443. require
  444. is_collecting: stack.fast_occurrences(Void) > 0
  445. do
  446. Result := stack.last = Void
  447. ensure
  448. definition: Result = (stack.last = Void)
  449. end
  450. end_collect_true
  451. -- Replace an empty collection by TRUE
  452. -- [.., Void] -> [.., TRUE]
  453. require
  454. is_collecting: stack.fast_occurrences(Void) > 0
  455. collect_empty: is_collect_empty
  456. do
  457. stack.put(the_true_node, stack.upper)
  458. end
  459. end_collect_or
  460. -- Collects the item on the stack until the collect mark (a Void)
  461. -- and replace it by a single item that is a or of all of them.
  462. -- The collection must not be empty.
  463. -- The order of evaluation will remain.
  464. -- The binary or's tree is recursive on right for efficiency.
  465. -- [.., Void, X] -> [.., X]
  466. -- [.., Void, Y, X] -> [.., Y or X]
  467. -- [.., Void, Z, Y, X] -> [.., Z or (Y or X)]
  468. -- ...
  469. require
  470. is_collecting: stack.fast_occurrences(Void) > 0
  471. collect_not_empty: not is_collect_empty
  472. local
  473. x: BACKTRACKING_NODE_OR_LIST; y: BACKTRACKING_NODE
  474. do
  475. y := unemit
  476. if stack.last = Void then
  477. stack.remove_last
  478. emit(y)
  479. else
  480. from
  481. until
  482. y = Void
  483. loop
  484. x := create {BACKTRACKING_NODE_OR_LIST}.make(y, x)
  485. y := unemit
  486. end
  487. emit(x)
  488. end
  489. ensure
  490. stack_not_empty: stack.count > 0
  491. emit_group_count_decremented: stack.fast_occurrences(Void) = old stack.fast_occurrences(Void) - 1
  492. end
  493. revert_and_list(x: BACKTRACKING_NODE_AND_LIST): BACKTRACKING_NODE_AND_LIST
  494. local
  495. y, n: BACKTRACKING_NODE_AND_LIST
  496. do
  497. from
  498. y := x
  499. until
  500. y = Void
  501. loop
  502. n := y.next
  503. y.set_next(Result)
  504. Result := y
  505. y := n
  506. end
  507. end
  508. end_collect_and
  509. -- Collects the item on the stack until the collect mark (a Void)
  510. -- and replace it by a single item that is a and of all of them.
  511. -- The collection must not be empty.
  512. -- The order of evaluation will remain.
  513. -- The binary and's tree is recursive on right for efficiency.
  514. -- [.., Void, X] -> [.., X]
  515. -- [.., Void, Y, X] -> [.., Y and X]
  516. -- [.., Void, Z, Y, X] -> [.., Z and (Y and X)]
  517. -- ...
  518. require
  519. is_collecting: stack.fast_occurrences(Void) > 0
  520. collect_not_empty: not is_collect_empty
  521. local
  522. x: BACKTRACKING_NODE_AND_LIST; y: BACKTRACKING_NODE
  523. do
  524. y := unemit
  525. if stack.last = Void then
  526. stack.remove_last
  527. emit(y)
  528. else
  529. from
  530. until
  531. y = Void
  532. loop
  533. x := create {BACKTRACKING_NODE_AND_LIST}.make(y, x)
  534. y := unemit
  535. end
  536. if is_looking_behind then
  537. x := revert_and_list(x)
  538. end
  539. emit(x)
  540. end
  541. ensure
  542. stack_not_empty: stack.count > 0
  543. emit_group_count_decremented: stack.fast_occurrences(Void) = old stack.fast_occurrences(Void) - 1
  544. end
  545. emit_not
  546. -- Replaces the top of the stack by its negation.
  547. -- [.., X] -> [.., not(X)]
  548. -- (where not(X) is like (X and (CUT and FALSE)) or TRUE)
  549. require
  550. enough_data: stack.count > 0
  551. local
  552. node, x: BACKTRACKING_NODE
  553. do
  554. x := unemit
  555. node := create {BACKTRACKING_NODE_NOT}.make(x)
  556. emit(node)
  557. ensure
  558. constant_stack_count: stack.count = old stack.count
  559. stack_not_empty: stack.count > 0
  560. end
  561. emit_not_then_any
  562. -- Replaces the top of the stack by its negation followed by any.
  563. -- [.., X] -> [.., not(X)]
  564. -- (where not(X) is like (X and (CUT and FALSE)) or ANY)
  565. require
  566. enough_data: stack.count > 0
  567. local
  568. node, x: BACKTRACKING_NODE
  569. do
  570. x := unemit
  571. node := create {REGULAR_EXPRESSION_ITEM_NOT_THEN_ANY}.make(x)
  572. emit(node)
  573. ensure
  574. constant_stack_count: stack.count = old stack.count
  575. stack_not_empty: stack.count > 0
  576. end
  577. emit_true_or
  578. -- Replaces the top of the stack by true or it
  579. -- [.., X] -> [.., true or X]
  580. require
  581. enough_data: stack.count > 0
  582. local
  583. node, x: BACKTRACKING_NODE
  584. do
  585. x := unemit
  586. node := create {BACKTRACKING_NODE_TRUE_OR}.make(x)
  587. emit(node)
  588. ensure
  589. constant_stack_count: stack.count = old stack.count
  590. stack_not_empty: stack.count > 0
  591. end
  592. emit_or_true
  593. -- Replaces the top of the stack by it or true
  594. -- [.., X] -> [.., X or true]
  595. require
  596. enough_data: stack.count > 0
  597. local
  598. node, x: BACKTRACKING_NODE
  599. do
  600. x := unemit
  601. node := create {BACKTRACKING_NODE_OR_TRUE}.make(x)
  602. emit(node)
  603. ensure
  604. constant_stack_count: stack.count = old stack.count
  605. stack_not_empty: stack.count > 0
  606. end
  607. emit_controled_or_true
  608. -- Replaces the top of the stack by
  609. -- if is_greedy then [.., X] -> [.., X or true]
  610. -- else [.., X] -> [.., true or X]
  611. local
  612. x: BACKTRACKING_NODE
  613. do
  614. x := unemit
  615. emit(controled_or_true_item(x))
  616. end
  617. controled_or_true_item (x: BACKTRACKING_NODE): BACKTRACKING_NODE
  618. -- Returns an item for " 'x' or true ". The returned item depend on
  619. -- the flag 'is_greedy'.
  620. -- if is_greedy then Result = (X or true)
  621. -- else Result = (true or X)
  622. do
  623. if is_greedy then
  624. Result := create {BACKTRACKING_NODE_OR_TRUE}.make(x)
  625. else
  626. Result := create {BACKTRACKING_NODE_TRUE_OR}.make(x)
  627. end
  628. end
  629. emit_repeat (mini, maxi: INTEGER)
  630. -- Takes the top of the stack and replace it with
  631. -- a construction that will evaluate the repeating of
  632. -- it from 'mini' to 'maxi' times.
  633. -- The feature boolean 'is_greedy' controls if
  634. -- the matched repeat will be of minimal length
  635. -- or of maximal length. That feature
  636. -- is reset to its default (False) value.
  637. require
  638. enough_data: stack.count > 0
  639. mini_is_valid: mini >= 0 and then mini /= Repeat_infiny
  640. maxi_is_valid: maxi = Repeat_infiny or else maxi >= mini
  641. not_droping: mini = 0 implies maxi /= 0
  642. local
  643. expr, resu: BACKTRACKING_NODE; exp_and: BACKTRACKING_NODE_AND_PAIR; i: INTEGER
  644. do
  645. expr := unemit
  646. if maxi = Repeat_infiny then
  647. create exp_and.make(expr, the_false_node)
  648. resu := controled_or_true_item(exp_and)
  649. exp_and.set_second(resu)
  650. elseif mini < maxi then
  651. from
  652. resu := controled_or_true_item(expr)
  653. i := mini + 2
  654. until
  655. i > maxi
  656. loop
  657. create exp_and.make(expr, resu)
  658. resu := controled_or_true_item(exp_and)
  659. i := i + 1
  660. end
  661. end
  662. from
  663. if mini = maxi then
  664. check
  665. resu = Void
  666. end
  667. i := 2
  668. resu := expr
  669. else
  670. check
  671. resu /= Void
  672. end
  673. i := 1
  674. end
  675. until
  676. i > mini
  677. loop
  678. create exp_and.make(expr, resu)
  679. resu := exp_and
  680. i := i + 1
  681. end
  682. emit(resu)
  683. ensure
  684. constant_stack_count: stack.count = old stack.count
  685. stack_not_empty: stack.count > 0
  686. end
  687. emit_looking
  688. require
  689. enough_data: stack.count > 0
  690. is_looking: is_looking_around
  691. local
  692. node, x: BACKTRACKING_NODE
  693. do
  694. x := unemit
  695. node := create {REGULAR_EXPRESSION_ITEM_LOOK}.make(x, is_looking_ahead, is_looking_positive)
  696. emit(node)
  697. ensure
  698. constant_stack_count: stack.count = old stack.count
  699. stack_not_empty: stack.count > 0
  700. end
  701. invariant
  702. last_string_not_void: last_string /= Void
  703. stack_not_void: stack /= Void
  704. end -- class BACKTRACKING_REGULAR_EXPRESSION_BUILDER
  705. --
  706. -- Copyright (C) 2009-2017: by all the people cited in the AUTHORS file.
  707. --
  708. -- Permission is hereby granted, free of charge, to any person obtaining a copy
  709. -- of this software and associated documentation files (the "Software"), to deal
  710. -- in the Software without restriction, including without limitation the rights
  711. -- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  712. -- copies of the Software, and to permit persons to whom the Software is
  713. -- furnished to do so, subject to the following conditions:
  714. --
  715. -- The above copyright notice and this permission notice shall be included in
  716. -- all copies or substantial portions of the Software.
  717. --
  718. -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  719. -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  720. -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  721. -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  722. -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  723. -- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  724. -- THE SOFTWARE.