Fix five bugs surfaced by commonmark-app (jolt-3xur, dl4s, ik3a, w2wf, xtss) (#158)
* Fix four bugs surfaced by the commonmark-app example
- regex: bounded quantifiers {n,m} no longer expand exponentially. The
desugaring inlined the continuation per optional level, doubling it each
time, so {0,61} built a PEG the compiler expanded to ~2^61 nodes and hung.
Each level is now its own grammar rule referenced by name (jolt-3xur).
- strings are a seqable of chars for vec/set/into, matching seq. They went
through realize-for-iteration, which had no string case, so into/set got
raw bytes (code points) and set threw; vec used string/from-bytes (1-char
strings). realize-for-iteration now maps make-char over the bytes, and
core-vec matches (jolt-dl4s).
- clojure.string/split takes Java Pattern.split limit semantics: negative
keeps trailing empties, 0/omitted drops them (a no-match result stays
[input]), positive caps the part count with the remainder unsplit. It used
to delegate the limit to take, so a negative limit returned [] and the
2-arg form never trimmed trailing empties (jolt-ik3a).
- System/exit is registered (maps to os/exit), so (System/exit n) works
(jolt-w2wf).
* regex: single-digit backreferences \1..\9 (jolt-xtss)
\1..\9 now match the text captured by the corresponding group, so
patterns like ([-*_])\1\1 or (\w+) \1 work. The parser records which
groups are referenced; a referenced group additionally captures its text
under a tag and the backref compiles to a PEG (backmatch). Only referenced
groups change — they match possessively (the CPS-over-possessive-PEG engine
can't backtrack into a tagged capture), so backtracking back into a
backreferenced group isn't supported (rare). Unreferenced groups keep full
backtracking and position-based result capture unchanged.
---------
Co-authored-by: Yogthos <yogthos@gmail.com>
This commit is contained in:
parent
8192fc9541
commit
951a3000e6
9 changed files with 111 additions and 25 deletions
|
|
@ -159,6 +159,8 @@
|
|||
"[(Boolean/parseBoolean \"true\") (Boolean/parseBoolean \"false\") (Boolean/parseBoolean \"yes\")]"]
|
||||
["System/getenv is a map" "true"
|
||||
"(string? (get (System/getenv) \"HOME\"))"]
|
||||
# registered, so (System/exit n) resolves; actual exit verified via subprocess
|
||||
["System/exit resolves" "true" "(fn? System/exit)"]
|
||||
# NOT every? alone — it held vacuously while seq over a raw host table
|
||||
# yielded nothing, hiding that read-system-env came back empty
|
||||
["getenv entries destructure (non-empty)" "true"
|
||||
|
|
|
|||
|
|
@ -62,3 +62,22 @@
|
|||
[".matches partial -> false" "false" "(.matches \"abcd\" \"a.c\")"]
|
||||
[".replaceAll regex" "\"a-b-c\"" "(.replaceAll \"a_b_c\" \"_\" \"-\")"]
|
||||
[".replaceFirst regex" "\"a-b_c\"" "(.replaceFirst \"a_b_c\" \"_\" \"-\")"])
|
||||
|
||||
(defspec "regex / bounded quantifiers (jolt-3xur)"
|
||||
["exact {n}" "\"aaa\"" "(re-matches #\"a{3}\" \"aaa\")"]
|
||||
["range {n,m} max" "\"aaaa\"" "(re-find #\"a{2,4}\" \"aaaaa\")"]
|
||||
["zero lower {0,n}" "\"aa\"" "(re-find #\"a{0,3}\" \"aa\")"]
|
||||
["{n,} unbounded" "\"aaaa\"" "(re-find #\"a{2,}\" \"aaaa\")"]
|
||||
# nested bounded quantifiers must not blow up the PEG compiler — this used to
|
||||
# expand to ~2^61 nodes and hang at compile time.
|
||||
["nested bounds compile" "true"
|
||||
"(boolean (re-matches #\"[a-z](?:[a-z]{0,61}[a-z])?(?:-[a-z]{0,61}[a-z])*\" \"abc-def\"))"])
|
||||
|
||||
(defspec "regex / backreferences (jolt-xtss)"
|
||||
["repeated char" "true" "(boolean (re-find #\"(.)\\1\" \"abba\"))"]
|
||||
["no repeat = nil" "nil" "(re-find #\"(.)\\1\" \"abc\")"]
|
||||
["thematic-break run" "true" "(boolean (re-matches #\"([-*_])\\1\\1\" \"---\"))"]
|
||||
["mismatched run" "nil" "(re-matches #\"([-*_])\\1\\1\" \"-*_\")"]
|
||||
["repeated word" "[\"the the\" \"the\"]" "(re-find #\"(\\w+) \\1\" \"say the the word\")"]
|
||||
["group still captures" "[\"x=x\" \"x\"]" "(re-matches #\"(\\w+)=\\1\" \"x=x\")"]
|
||||
["html tag pair" "true" "(boolean (re-matches #\"<(\\w+)>.*</\\1>\" \"<b>hi</b>\"))"])
|
||||
|
|
|
|||
|
|
@ -20,6 +20,22 @@
|
|||
["pr-str of a defn" "\"#'user/gg\"" "(pr-str (defn gg [x] x))"]
|
||||
["seq of string" "[\\a \\b]" "(seq \"ab\")"])
|
||||
|
||||
(defspec "string as a seqable of chars (jolt-dl4s)"
|
||||
["vec of string" "[\\a \\b]" "(vec \"ab\")"]
|
||||
["into [] of string" "[\\a \\b]" "(into [] \"ab\")"]
|
||||
["set of string" "true" "(= #{\\a \\b} (set \"ab\"))"]
|
||||
["into #{} of string" "true" "(= #{\\a \\b} (into #{} \"ab\"))"]
|
||||
["set dedups chars" "2" "(count (set \"aab\"))"]
|
||||
["mapv over string" "[\\a \\b]" "(mapv identity \"ab\")"])
|
||||
|
||||
(defspec "clojure.string / split limit (jolt-ik3a)"
|
||||
["neg keeps trailing" "[\"a\" \"\" \"\"]" "(do (require (quote [clojure.string :as s])) (s/split \"a,,\" #\",\" -1))"]
|
||||
["zero trims trailing" "[\"a\"]" "(do (require (quote [clojure.string :as s])) (s/split \"a,,\" #\",\" 0))"]
|
||||
["omitted trims" "[\"a\"]" "(do (require (quote [clojure.string :as s])) (s/split \"a,,\" #\",\"))"]
|
||||
["positive caps parts" "[\"a\" \"b,c\"]" "(do (require (quote [clojure.string :as s])) (s/split \"a,b,c\" #\",\" 2))"]
|
||||
["empty string" "[\"\"]" "(do (require (quote [clojure.string :as s])) (s/split \"\" #\",\"))"]
|
||||
["interior empties kept" "[\"a\" \"\" \"b\"]" "(do (require (quote [clojure.string :as s])) (s/split \"a,,b\" #\",\"))"])
|
||||
|
||||
(defspec "clojure.string"
|
||||
["join" "\"a,b,c\"" "(do (require (quote [clojure.string :as s])) (s/join \",\" [\"a\" \"b\" \"c\"]))"]
|
||||
["join no sep" "\"abc\"" "(do (require (quote [clojure.string :as s])) (s/join [\"a\" \"b\" \"c\"]))"]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue