optimizer.odin 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530
  1. package regex_optimizer
  2. /*
  3. (c) Copyright 2024 Feoramund <[email protected]>.
  4. Made available under Odin's BSD-3 license.
  5. List of contributors:
  6. Feoramund: Initial implementation.
  7. */
  8. import "base:intrinsics"
  9. @require import "core:io"
  10. import "core:slice"
  11. import "core:text/regex/common"
  12. import "core:text/regex/parser"
  13. Rune_Class_Range :: parser.Rune_Class_Range
  14. Node :: parser.Node
  15. Node_Rune :: parser.Node_Rune
  16. Node_Rune_Class :: parser.Node_Rune_Class
  17. Node_Wildcard :: parser.Node_Wildcard
  18. Node_Concatenation :: parser.Node_Concatenation
  19. Node_Alternation :: parser.Node_Alternation
  20. Node_Repeat_Zero :: parser.Node_Repeat_Zero
  21. Node_Repeat_Zero_Non_Greedy :: parser.Node_Repeat_Zero_Non_Greedy
  22. Node_Repeat_One :: parser.Node_Repeat_One
  23. Node_Repeat_One_Non_Greedy :: parser.Node_Repeat_One_Non_Greedy
  24. Node_Repeat_N :: parser.Node_Repeat_N
  25. Node_Optional :: parser.Node_Optional
  26. Node_Optional_Non_Greedy :: parser.Node_Optional_Non_Greedy
  27. Node_Group :: parser.Node_Group
  28. Node_Anchor :: parser.Node_Anchor
  29. Node_Word_Boundary :: parser.Node_Word_Boundary
  30. Node_Match_All_And_Escape :: parser.Node_Match_All_And_Escape
  31. class_range_sorter :: proc(i, j: Rune_Class_Range) -> bool {
  32. return i.lower < j.lower
  33. }
  34. optimize_subtree :: proc(tree: Node, flags: common.Flags) -> (result: Node, changes: int) {
  35. if tree == nil {
  36. return nil, 0
  37. }
  38. result = tree
  39. switch specific in tree {
  40. // No direct optimization possible on these nodes:
  41. case ^Node_Rune: break
  42. case ^Node_Wildcard: break
  43. case ^Node_Anchor: break
  44. case ^Node_Word_Boundary: break
  45. case ^Node_Match_All_And_Escape: break
  46. case ^Node_Concatenation:
  47. // * Composition: Consume All to Anchored End
  48. //
  49. // DO: `.*$` => <special opcode>
  50. // DO: `.+$` => `.` <special opcode>
  51. if .Multiline not_in flags && len(specific.nodes) >= 2 {
  52. i := len(specific.nodes) - 2
  53. wrza: {
  54. subnode := specific.nodes[i].(^Node_Repeat_Zero) or_break wrza
  55. _ = subnode.inner.(^Node_Wildcard) or_break wrza
  56. next_node := specific.nodes[i+1].(^Node_Anchor) or_break wrza
  57. if next_node.start == false {
  58. specific.nodes[i] = new(Node_Match_All_And_Escape)
  59. ordered_remove(&specific.nodes, i + 1)
  60. changes += 1
  61. break
  62. }
  63. }
  64. wroa: {
  65. subnode := specific.nodes[i].(^Node_Repeat_One) or_break wroa
  66. subsubnode := subnode.inner.(^Node_Wildcard) or_break wroa
  67. next_node := specific.nodes[i+1].(^Node_Anchor) or_break wroa
  68. if next_node.start == false {
  69. specific.nodes[i] = subsubnode
  70. specific.nodes[i+1] = new(Node_Match_All_And_Escape)
  71. changes += 1
  72. break
  73. }
  74. }
  75. }
  76. // Only recursive optimizations:
  77. #no_bounds_check for i := 0; i < len(specific.nodes); i += 1 {
  78. subnode, subnode_changes := optimize_subtree(specific.nodes[i], flags)
  79. changes += subnode_changes
  80. if subnode == nil {
  81. ordered_remove(&specific.nodes, i)
  82. i -= 1
  83. changes += 1
  84. } else {
  85. specific.nodes[i] = subnode
  86. }
  87. }
  88. if len(specific.nodes) == 1 {
  89. result = specific.nodes[0]
  90. changes += 1
  91. } else if len(specific.nodes) == 0 {
  92. return nil, changes + 1
  93. }
  94. case ^Node_Repeat_Zero:
  95. specific.inner, changes = optimize_subtree(specific.inner, flags)
  96. if specific.inner == nil {
  97. return nil, changes + 1
  98. }
  99. case ^Node_Repeat_Zero_Non_Greedy:
  100. specific.inner, changes = optimize_subtree(specific.inner, flags)
  101. if specific.inner == nil {
  102. return nil, changes + 1
  103. }
  104. case ^Node_Repeat_One:
  105. specific.inner, changes = optimize_subtree(specific.inner, flags)
  106. if specific.inner == nil {
  107. return nil, changes + 1
  108. }
  109. case ^Node_Repeat_One_Non_Greedy:
  110. specific.inner, changes = optimize_subtree(specific.inner, flags)
  111. if specific.inner == nil {
  112. return nil, changes + 1
  113. }
  114. case ^Node_Repeat_N:
  115. specific.inner, changes = optimize_subtree(specific.inner, flags)
  116. if specific.inner == nil {
  117. return nil, changes + 1
  118. }
  119. case ^Node_Optional:
  120. specific.inner, changes = optimize_subtree(specific.inner, flags)
  121. if specific.inner == nil {
  122. return nil, changes + 1
  123. }
  124. case ^Node_Optional_Non_Greedy:
  125. specific.inner, changes = optimize_subtree(specific.inner, flags)
  126. if specific.inner == nil {
  127. return nil, changes + 1
  128. }
  129. case ^Node_Group:
  130. specific.inner, changes = optimize_subtree(specific.inner, flags)
  131. if specific.inner == nil {
  132. return nil, changes + 1
  133. }
  134. if !specific.capture {
  135. result = specific.inner
  136. changes += 1
  137. }
  138. // Full optimization:
  139. case ^Node_Rune_Class:
  140. // * Class Simplification
  141. //
  142. // DO: `[aab]` => `[ab]`
  143. // DO: `[aa]` => `[a]`
  144. runes_seen: map[rune]bool
  145. for r in specific.runes {
  146. runes_seen[r] = true
  147. }
  148. if len(runes_seen) != len(specific.runes) {
  149. clear(&specific.runes)
  150. for key in runes_seen {
  151. append(&specific.runes, key)
  152. }
  153. changes += 1
  154. }
  155. // * Class Reduction
  156. //
  157. // DO: `[a]` => `a`
  158. if !specific.negating && len(specific.runes) == 1 && len(specific.ranges) == 0 {
  159. only_rune := specific.runes[0]
  160. node := new(Node_Rune)
  161. node.data = only_rune
  162. return node, changes + 1
  163. }
  164. // * Range Construction
  165. //
  166. // DO: `[abc]` => `[a-c]`
  167. slice.sort(specific.runes[:])
  168. if len(specific.runes) > 1 {
  169. new_range: Rune_Class_Range
  170. new_range.lower = specific.runes[0]
  171. new_range.upper = specific.runes[0]
  172. #no_bounds_check for i := 1; i < len(specific.runes); i += 1 {
  173. r := specific.runes[i]
  174. if new_range.lower == -1 {
  175. new_range = { r, r }
  176. continue
  177. }
  178. if r == new_range.lower - 1 {
  179. new_range.lower -= 1
  180. ordered_remove(&specific.runes, i)
  181. i -= 1
  182. changes += 1
  183. } else if r == new_range.upper + 1 {
  184. new_range.upper += 1
  185. ordered_remove(&specific.runes, i)
  186. i -= 1
  187. changes += 1
  188. } else if new_range.lower != new_range.upper {
  189. append(&specific.ranges, new_range)
  190. new_range = { -1, -1 }
  191. changes += 1
  192. }
  193. }
  194. if new_range.lower != new_range.upper {
  195. append(&specific.ranges, new_range)
  196. changes += 1
  197. }
  198. }
  199. // * Rune Merging into Range
  200. //
  201. // DO: `[aa-c]` => `[a-c]`
  202. for range in specific.ranges {
  203. #no_bounds_check for i := 0; i < len(specific.runes); i += 1 {
  204. r := specific.runes[i]
  205. if range.lower <= r && r <= range.upper {
  206. ordered_remove(&specific.runes, i)
  207. i -= 1
  208. changes += 1
  209. }
  210. }
  211. }
  212. // * Range Merging
  213. //
  214. // DO: `[a-cc-e]` => `[a-e]`
  215. // DO: `[a-cd-e]` => `[a-e]`
  216. // DO: `[a-cb-e]` => `[a-e]`
  217. slice.sort_by(specific.ranges[:], class_range_sorter)
  218. #no_bounds_check for i := 0; i < len(specific.ranges) - 1; i += 1 {
  219. for j := i + 1; j < len(specific.ranges); j += 1 {
  220. left_range := &specific.ranges[i]
  221. right_range := specific.ranges[j]
  222. if left_range.upper == right_range.lower ||
  223. left_range.upper == right_range.lower - 1 ||
  224. left_range.lower <= right_range.lower && right_range.lower <= left_range.upper {
  225. left_range.upper = max(left_range.upper, right_range.upper)
  226. ordered_remove(&specific.ranges, j)
  227. j -= 1
  228. changes += 1
  229. } else {
  230. break
  231. }
  232. }
  233. }
  234. if len(specific.ranges) == 0 {
  235. specific.ranges = {}
  236. }
  237. if len(specific.runes) == 0 {
  238. specific.runes = {}
  239. }
  240. // * NOP
  241. //
  242. // DO: `[]` => <nil>
  243. if len(specific.ranges) + len(specific.runes) == 0 {
  244. return nil, 1
  245. }
  246. slice.sort(specific.runes[:])
  247. slice.sort_by(specific.ranges[:], class_range_sorter)
  248. case ^Node_Alternation:
  249. // Perform recursive optimization first.
  250. left_changes, right_changes: int
  251. specific.left, left_changes = optimize_subtree(specific.left, flags)
  252. specific.right, right_changes = optimize_subtree(specific.right, flags)
  253. changes += left_changes + right_changes
  254. // * Alternation to Optional
  255. //
  256. // DO: `a|` => `a?`
  257. if specific.left != nil && specific.right == nil {
  258. node := new(Node_Optional)
  259. node.inner = specific.left
  260. return node, 1
  261. }
  262. // * Alternation to Optional Non-Greedy
  263. //
  264. // DO: `|a` => `a??`
  265. if specific.right != nil && specific.left == nil {
  266. node := new(Node_Optional_Non_Greedy)
  267. node.inner = specific.right
  268. return node, 1
  269. }
  270. // * NOP
  271. //
  272. // DO: `|` => <nil>
  273. if specific.left == nil && specific.right == nil {
  274. return nil, 1
  275. }
  276. left_rune, left_is_rune := specific.left.(^Node_Rune)
  277. right_rune, right_is_rune := specific.right.(^Node_Rune)
  278. if left_is_rune && right_is_rune {
  279. if left_rune.data == right_rune.data {
  280. // * Alternation Reduction
  281. //
  282. // DO: `a|a` => `a`
  283. return left_rune, 1
  284. } else {
  285. // * Alternation to Class
  286. //
  287. // DO: `a|b` => `[ab]`
  288. node := new(Node_Rune_Class)
  289. append(&node.runes, left_rune.data)
  290. append(&node.runes, right_rune.data)
  291. return node, 1
  292. }
  293. }
  294. left_wildcard, left_is_wildcard := specific.left.(^Node_Wildcard)
  295. right_wildcard, right_is_wildcard := specific.right.(^Node_Wildcard)
  296. // * Class Union
  297. //
  298. // DO: `[a0]|[b1]` => `[a0b1]`
  299. left_class, left_is_class := specific.left.(^Node_Rune_Class)
  300. right_class, right_is_class := specific.right.(^Node_Rune_Class)
  301. if left_is_class && right_is_class {
  302. for r in right_class.runes {
  303. append(&left_class.runes, r)
  304. }
  305. for range in right_class.ranges {
  306. append(&left_class.ranges, range)
  307. }
  308. return left_class, 1
  309. }
  310. // * Class Union
  311. //
  312. // DO: `[a-b]|c` => `[a-bc]`
  313. if left_is_class && right_is_rune {
  314. append(&left_class.runes, right_rune.data)
  315. return left_class, 1
  316. }
  317. // * Class Union
  318. //
  319. // DO: `a|[b-c]` => `[b-ca]`
  320. if left_is_rune && right_is_class {
  321. append(&right_class.runes, left_rune.data)
  322. return right_class, 1
  323. }
  324. // * Wildcard Reduction
  325. //
  326. // DO: `a|.` => `.`
  327. if left_is_rune && right_is_wildcard {
  328. return right_wildcard, 1
  329. }
  330. // * Wildcard Reduction
  331. //
  332. // DO: `.|a` => `.`
  333. if left_is_wildcard && right_is_rune {
  334. return left_wildcard, 1
  335. }
  336. // * Wildcard Reduction
  337. //
  338. // DO: `[ab]|.` => `.`
  339. if left_is_class && right_is_wildcard {
  340. return right_wildcard, 1
  341. }
  342. // * Wildcard Reduction
  343. //
  344. // DO: `.|[ab]` => `.`
  345. if left_is_wildcard && right_is_class {
  346. return left_wildcard, 1
  347. }
  348. left_concatenation, left_is_concatenation := specific.left.(^Node_Concatenation)
  349. right_concatenation, right_is_concatenation := specific.right.(^Node_Concatenation)
  350. // * Common Suffix Elimination
  351. //
  352. // DO: `blueberry|strawberry` => `(?:blue|straw)berry`
  353. if left_is_concatenation && right_is_concatenation {
  354. // Remember that a concatenation could contain any node, not just runes.
  355. left_len := len(left_concatenation.nodes)
  356. right_len := len(right_concatenation.nodes)
  357. least_len := min(left_len, right_len)
  358. same_len := 0
  359. for i := 1; i <= least_len; i += 1 {
  360. left_subrune, left_is_subrune := left_concatenation.nodes[left_len - i].(^Node_Rune)
  361. right_subrune, right_is_subrune := right_concatenation.nodes[right_len - i].(^Node_Rune)
  362. if !left_is_subrune || !right_is_subrune {
  363. // One of the nodes isn't a rune; there's nothing more we can do.
  364. break
  365. }
  366. if left_subrune.data == right_subrune.data {
  367. same_len += 1
  368. } else {
  369. // No more similarities.
  370. break
  371. }
  372. }
  373. if same_len > 0 {
  374. // Dissolve this alternation into a concatenation.
  375. cat_node := new(Node_Concatenation)
  376. group_node := new(Node_Group)
  377. append(&cat_node.nodes, group_node)
  378. // Turn the concatenation into the common suffix.
  379. for i := left_len - same_len; i < left_len; i += 1 {
  380. append(&cat_node.nodes, left_concatenation.nodes[i])
  381. }
  382. // Construct the group of alternating prefixes.
  383. for i := same_len; i > 0; i -= 1 {
  384. pop(&left_concatenation.nodes)
  385. pop(&right_concatenation.nodes)
  386. }
  387. // (Re-using this alternation node.)
  388. alter_node := specific
  389. alter_node.left = left_concatenation
  390. alter_node.right = right_concatenation
  391. group_node.inner = alter_node
  392. return cat_node, 1
  393. }
  394. }
  395. // * Common Prefix Elimination
  396. //
  397. // DO: `abi|abe` => `ab(?:i|e)`
  398. if left_is_concatenation && right_is_concatenation {
  399. // Try to identify a common prefix.
  400. // Remember that a concatenation could contain any node, not just runes.
  401. least_len := min(len(left_concatenation.nodes), len(right_concatenation.nodes))
  402. same_len := 0
  403. for i := 0; i < least_len; i += 1 {
  404. left_subrune, left_is_subrune := left_concatenation.nodes[i].(^Node_Rune)
  405. right_subrune, right_is_subrune := right_concatenation.nodes[i].(^Node_Rune)
  406. if !left_is_subrune || !right_is_subrune {
  407. // One of the nodes isn't a rune; there's nothing more we can do.
  408. break
  409. }
  410. if left_subrune.data == right_subrune.data {
  411. same_len = i + 1
  412. } else {
  413. // No more similarities.
  414. break
  415. }
  416. }
  417. if same_len > 0 {
  418. cat_node := new(Node_Concatenation)
  419. for i := 0; i < same_len; i += 1 {
  420. append(&cat_node.nodes, left_concatenation.nodes[i])
  421. }
  422. for i := same_len; i > 0; i -= 1 {
  423. ordered_remove(&left_concatenation.nodes, 0)
  424. ordered_remove(&right_concatenation.nodes, 0)
  425. }
  426. group_node := new(Node_Group)
  427. // (Re-using this alternation node.)
  428. alter_node := specific
  429. alter_node.left = left_concatenation
  430. alter_node.right = right_concatenation
  431. group_node.inner = alter_node
  432. append(&cat_node.nodes, group_node)
  433. return cat_node, 1
  434. }
  435. }
  436. }
  437. return
  438. }
  439. optimize :: proc(tree: Node, flags: common.Flags) -> (result: Node, changes: int) {
  440. result = tree
  441. new_changes := 0
  442. when common.ODIN_DEBUG_REGEX {
  443. io.write_string(common.debug_stream, "AST before Optimizer: ")
  444. parser.write_node(common.debug_stream, tree)
  445. io.write_byte(common.debug_stream, '\n')
  446. }
  447. // Keep optimizing until no more changes are seen.
  448. for {
  449. result, new_changes = optimize_subtree(result, flags)
  450. changes += new_changes
  451. if new_changes == 0 {
  452. break
  453. }
  454. }
  455. when common.ODIN_DEBUG_REGEX {
  456. io.write_string(common.debug_stream, "AST after Optimizer: ")
  457. parser.write_node(common.debug_stream, result)
  458. io.write_byte(common.debug_stream, '\n')
  459. }
  460. return
  461. }