From unknown Sat Aug 09 15:55:10 2025 X-Loop: help-debbugs@gnu.org Subject: bug#37849: composable character alternatives in rx Resent-From: Mattias =?UTF-8?Q?Engdeg=C3=A5rd?= Original-Sender: "Debbugs-submit" Resent-CC: bug-gnu-emacs@gnu.org Resent-Date: Mon, 21 Oct 2019 10:25:01 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: report 37849 X-GNU-PR-Package: emacs X-GNU-PR-Keywords: To: 37849@debbugs.gnu.org X-Debbugs-Original-To: bug-gnu-emacs@gnu.org Received: via spool by submit@debbugs.gnu.org id=B.157165347521099 (code B ref -1); Mon, 21 Oct 2019 10:25:01 +0000 Received: (at submit) by debbugs.gnu.org; 21 Oct 2019 10:24:35 +0000 Received: from localhost ([127.0.0.1]:55773 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1iMUra-0005UF-PG for submit@debbugs.gnu.org; Mon, 21 Oct 2019 06:24:35 -0400 Received: from lists.gnu.org ([209.51.188.17]:35453) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1iMUrY-0005U8-D0 for submit@debbugs.gnu.org; Mon, 21 Oct 2019 06:24:32 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:42263) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1iMUrW-0001zR-Ny for bug-gnu-emacs@gnu.org; Mon, 21 Oct 2019 06:24:32 -0400 X-Spam-Checker-Version: SpamAssassin 3.3.2 (2011-06-06) on eggs.gnu.org X-Spam-Level: X-Spam-Status: No, score=0.1 required=5.0 tests=BAYES_50,RCVD_IN_DNSWL_LOW, URIBL_BLOCKED autolearn=disabled version=3.3.2 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1iMUrV-000896-73 for bug-gnu-emacs@gnu.org; Mon, 21 Oct 2019 06:24:30 -0400 Received: from mail80c50.megamailservers.eu ([91.136.10.90]:35876 helo=mail70c50.megamailservers.eu) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1iMUrU-00088U-Hi for bug-gnu-emacs@gnu.org; Mon, 21 Oct 2019 06:24:29 -0400 X-Authenticated-User: mattiase@bredband.net DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=megamailservers.eu; s=maildub; t=1571653463; bh=MS/WJe3Oxd/r0y2NU7gp+9E+RjbxurZ8DttwaqM7+dU=; h=From:Subject:Date:To:From; b=GHa5iJLH4b/AvesfV1nJ7Kyg76RgGD9Qw335C9cOQVub3PF89cbM9CY4O+Ycogx4r AZ2B0BH1U9I6cdyjlTGY194VmPlAFMqJxweSSmcOQlrVFSXWS3REDQo46jljDc+9FJ b0ATyFWfV2oLlR8neOkLoFjKZWLpeZIJmUCtoJp4= Feedback-ID: mattiase@acm.or Received: from [192.168.1.64] (c-e636e253.032-75-73746f71.bbcust.telenor.se [83.226.54.230]) (authenticated bits=0) by mail70c50.megamailservers.eu (8.14.9/8.13.1) with ESMTP id x9LAOLiZ026603 for ; Mon, 21 Oct 2019 10:24:23 +0000 From: Mattias =?UTF-8?Q?Engdeg=C3=A5rd?= Content-Type: multipart/mixed; boundary="Apple-Mail=_A5D7A71E-F7D3-4086-A2E5-8E608F8FFC42" Mime-Version: 1.0 (Mac OS X Mail 12.4 \(3445.104.11\)) Message-Id: <0F77D35E-52A7-4332-913E-DDD286461F00@acm.org> Date: Mon, 21 Oct 2019 12:24:21 +0200 X-Mailer: Apple Mail (2.3445.104.11) X-CTCH-RefID: str=0001.0A0B0204.5DAD8757.0041, ss=1, re=0.000, recu=0.000, reip=0.000, cl=1, cld=1, fgs=0 X-CTCH-VOD: Unknown X-CTCH-Spam: Unknown X-CTCH-Score: 0.000 X-CTCH-Rules: X-CTCH-Flags: 0 X-CTCH-ScoreCust: 0.000 X-CSC: 0 X-CHA: v=2.3 cv=c6bVvi1l c=1 sm=1 tr=0 a=M+GU/qJco4WXjv8D6jB2IA==:117 a=M+GU/qJco4WXjv8D6jB2IA==:17 a=M51BFTxLslgA:10 a=OqN6m0tyAAAA:8 a=UhdY8sGSxoVGZ3PE4iEA:9 a=CjuIK1q_8ugA:10 a=zQ3WUrxWzkcA:10 a=NP-Ykj6oU4nmw8ZbQoEA:9 a=B2y7HmGcmWMA:10 a=MNNCb3fd_EpUFEaGHBWG:22 X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x (no timestamps) [generic] [fuzzy] X-Received-From: 91.136.10.90 X-Spam-Score: -1.3 (-) X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -2.3 (--) --Apple-Mail=_A5D7A71E-F7D3-4086-A2E5-8E608F8FFC42 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset=us-ascii Now that rx is user-extendible, some holes are showing. Example (from = python.el): (simple-operator . ,(rx (any ?+ ?- ?/ ?& ?^ ?~ ?| ?* ?< ?> ?=3D= ?%))) ;; FIXME: rx should support (not simple-operator). (not-simple-operator . ,(rx (not (any ?+ ?- ?/ ?& ?^ ?~ ?| ?* ?< ?> ?=3D = ?%)))) (This code uses the old rx-constituents mechanism, but the point applies = equally to new-style definitions.) More generally, there is currently no way to: (1) Get the complement of a defined (any ...) form (2) Get the union of two defined (any ...) forms (3) Get the intersection of two defined (not (any ...)) forms (1), which the example above was about, could be solved by expanding = definitions inside 'not'. This is a step away from the principle that = user-defined things are only allowed where general rx forms are, but = perhaps tolerable. Proposed patch attached. (2) can be solved by expanding definitions inside 'any', and allowing = 'any' inside 'any' (flattening). Not sure I like this. An alternative is to ensure that (or (any X) (any Y)) -> (any X Y), but = then we either need to allow 'or' inside 'not', or add an intersection = operator: (intersect (not (any X)) (not (any Y)) -> (not (any X Y)) We could also make 'not' variadic, turning it into complement-of-union: (not (any A) (any B)) -> (not (any A B)) Olin Shivers's SRE has a complete and closed set of operations on = character sets (https://scsh.net/docu/post/sre.html). That would be = principled and perhaps useful, but difficult to do fully in rx because = not all such expressions can be rendered into Emacs regexps. Nothing = prevents us from making a partial implementation, however. --Apple-Mail=_A5D7A71E-F7D3-4086-A2E5-8E608F8FFC42 Content-Disposition: attachment; filename=0001-Expand-rx-definitions-inside-not.patch Content-Type: application/octet-stream; x-unix-mode=0644; name="0001-Expand-rx-definitions-inside-not.patch" Content-Transfer-Encoding: quoted-printable =46rom=20a2f7d4fbe0b1d37c233e0beffc4b2b8fd4df3013=20Mon=20Sep=2017=20= 00:00:00=202001=0AFrom:=20=3D?UTF-8?q?Mattias=3D20Engdeg=3DC3=3DA5rd?=3D=20= =0ADate:=20Fri,=2018=20Oct=202019=2016:03:20=20+0200=0A= Subject:=20[PATCH]=20Expand=20rx=20definitions=20inside=20(not=20...)=0A=0A= *=20lisp/emacs-lisp/rx.el=20(rx--lookup-def,=20rx--expand-def)=0A= (rx--translate-symbol,=20rx--translate-any,=20rx--translate-form):=0A*=20= test/lisp/emacs-lisp/rx-tests.el=20(rx-not,=20rx-def-in-not):=0A*=20= doc/lispref/searching.texi=20(Rx=20Constructs,=20Extending=20Rx):=0A= Allow=20user-defined=20rx=20constructs=20to=20be=20expanded=20inside=20= (not=20...)=0Aforms,=20for=20better=20composability.=0A---=0A=20= doc/lispref/searching.texi=20=20=20=20=20=20=20|=20=20=204=20+-=0A=20= lisp/emacs-lisp/rx.el=20=20=20=20=20=20=20=20=20=20=20=20|=20100=20= ++++++++++++++++++-------------=0A=20test/lisp/emacs-lisp/rx-tests.el=20= |=20=2017=20+++++-=0A=203=20files=20changed,=2077=20insertions(+),=2044=20= deletions(-)=0A=0Adiff=20--git=20a/doc/lispref/searching.texi=20= b/doc/lispref/searching.texi=0Aindex=205178575a3b..74b15cfc7f=20100644=0A= ---=20a/doc/lispref/searching.texi=0A+++=20b/doc/lispref/searching.texi=0A= @@=20-1214,7=20+1214,7=20@@=20Rx=20Constructs=0A=20@item=20@code{(not=20= @var{charspec})}=0A=20@cindex=20@code{not}=20in=20rx=0A=20Match=20a=20= character=20not=20included=20in=20@var{charspec}.=20=20@var{charspec}=20= can=0A-be=20an=20@code{any},=20@code{syntax}=20or=20@code{category}=20= form,=20or=20a=0A+be=20an=20@code{any},=20@code{not},=20@code{syntax}=20= or=20@code{category}=20form,=20or=20a=0A=20character=20class.@*=0A=20= Corresponding=20string=20regexp:=20@samp{[^@dots{}]},=20= @samp{\S@var{code}},=0A=20@samp{\C@var{code}}=0A@@=20-1581,7=20+1581,7=20= @@=20Extending=20Rx=0A=20User-defined=20forms=20are=20allowed=20wherever=20= arbitrary=20@code{rx}=0A=20expressions=20are=20expected;=20for=20= example,=20in=20the=20body=20of=20a=0A=20@code{zero-or-one}=20form,=20= but=20not=20inside=20@code{any}=20or=20@code{category}=0A-forms.=0A= +forms.=20=20They=20are=20also=20allowed=20inside=20@code{not}=20forms.=0A= =20@end=20itemize=0A=20=0A=20@defmac=20rx-define=20name=20[arglist]=20= rx-form=0Adiff=20--git=20a/lisp/emacs-lisp/rx.el=20= b/lisp/emacs-lisp/rx.el=0Aindex=20006a393921..8d8db5f3c4=20100644=0A---=20= a/lisp/emacs-lisp/rx.el=0A+++=20b/lisp/emacs-lisp/rx.el=0A@@=20-122,9=20= +122,27=20@@=20rx--local-definitions=0A=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20as=20the=20rx=20form=20DEF=20(which=20can=20= contain=20members=20of=20ARGS).")=0A=20=0A=20(defsubst=20rx--lookup-def=20= (name)=0A+=20=20"Current=20definition=20of=20NAME:=20(DEF)=20or=20(ARGS=20= DEF),=20or=20nil=20if=20none."=0A=20=20=20(or=20(cdr=20(assq=20name=20= rx--local-definitions))=0A=20=20=20=20=20=20=20(get=20name=20= 'rx-definition)))=0A=20=0A+(defun=20rx--expand-def=20(form)=0A+=20=20= "FORM=20expanded=20(once)=20if=20a=20user-defined=20construct;=20= otherwise=20nil."=0A+=20=20(cond=20((symbolp=20form)=0A+=20=20=20=20=20=20= =20=20=20(let=20((def=20(rx--lookup-def=20form)))=0A+=20=20=20=20=20=20=20= =20=20=20=20(and=20def=0A+=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= (if=20(cdr=20def)=0A+=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20(error=20"Not=20an=20`rx'=20symbol=20definition:=20%s"=20form)=0A+=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20(car=20def)))))=0A+=20= =20=20=20=20=20=20=20((consp=20form)=0A+=20=20=20=20=20=20=20=20=20(let*=20= ((op=20(car=20form))=0A+=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= (def=20(rx--lookup-def=20op)))=0A+=20=20=20=20=20=20=20=20=20=20=20(and=20= def=0A+=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20(if=20(cdr=20def)=0A= +=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= (rx--expand-template=0A+=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20op=20(cdr=20form)=20(nth=200=20def)=20(nth=201=20def))=0A+=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20(error=20"Not=20an=20= `rx'=20form=20definition:=20%s"=20op)))))))=0A+=0A=20;;=20TODO:=20= Additions=20to=20consider:=0A=20;;=20-=20A=20construct=20like=20`or'=20= but=20without=20the=20match=20order=20guarantee,=0A=20;;=20=20=20maybe=20= `unordered-or'.=20=20Useful=20for=20composition=20or=20generation=20of=0A= @@=20-155,11=20+173,8=20@@=20rx--translate-symbol=0A=20=20=20=20=20=20=20= ((let=20((class=20(cdr=20(assq=20sym=20rx--char-classes))))=0A=20=20=20=20= =20=20=20=20=20=20(and=20class=20(cons=20(list=20(concat=20"[[:"=20= (symbol-name=20class)=20":]]"))=20t))))=0A=20=0A-=20=20=20=20=20=20((let=20= ((definition=20(rx--lookup-def=20sym)))=0A-=20=20=20=20=20=20=20=20=20= (and=20definition=0A-=20=20=20=20=20=20=20=20=20=20=20=20=20=20(if=20= (cdr=20definition)=0A-=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20(error=20"Not=20an=20`rx'=20symbol=20definition:=20%s"=20sym)=0A-=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20(rx--translate=20(nth=200=20= definition))))))=0A+=20=20=20=20=20=20((let=20((expanded=20= (rx--expand-def=20sym)))=0A+=20=20=20=20=20=20=20=20=20(and=20expanded=20= (rx--translate=20expanded))))=0A=20=0A=20=20=20=20=20=20=20;;=20For=20= compatibility=20with=20old=20rx.=0A=20=20=20=20=20=20=20((let=20((entry=20= (assq=20sym=20rx-constituents)))=0A@@=20-445,21=20+460,26=20@@=20= rx--translate-not=0A=20=20=20=20=20(error=20"rx=20`not'=20form=20takes=20= exactly=20one=20argument"))=0A=20=20=20(let=20((arg=20(car=20body)))=0A=20= =20=20=20=20(cond=0A-=20=20=20=20=20((consp=20arg)=0A-=20=20=20=20=20=20= (pcase=20(car=20arg)=0A-=20=20=20=20=20=20=20=20((or=20'any=20'in=20= 'char)=20(rx--translate-any=20=20=20=20=20=20(not=20negated)=20(cdr=20= arg)))=0A-=20=20=20=20=20=20=20=20('syntax=20=20=20=20=20=20=20=20=20=20=20= =20=20(rx--translate-syntax=20=20=20(not=20negated)=20(cdr=20arg)))=0A-=20= =20=20=20=20=20=20=20('category=20=20=20=20=20=20=20=20=20=20=20= (rx--translate-category=20(not=20negated)=20(cdr=20arg)))=0A-=20=20=20=20= =20=20=20=20('not=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= (rx--translate-not=20=20=20=20=20=20(not=20negated)=20(cdr=20arg)))=0A-=20= =20=20=20=20=20=20=20(_=20(error=20"Illegal=20argument=20to=20rx=20= `not':=20%S"=20arg))))=0A+=20=20=20=20=20((and=20(consp=20arg)=0A+=20=20=20= =20=20=20=20=20=20=20=20(pcase=20(car=20arg)=0A+=20=20=20=20=20=20=20=20=20= =20=20=20=20((or=20'any=20'in=20'char)=0A+=20=20=20=20=20=20=20=20=20=20=20= =20=20=20(rx--translate-any=20=20=20=20=20=20(not=20negated)=20(cdr=20= arg)))=0A+=20=20=20=20=20=20=20=20=20=20=20=20=20('syntax=0A+=20=20=20=20= =20=20=20=20=20=20=20=20=20=20(rx--translate-syntax=20=20=20(not=20= negated)=20(cdr=20arg)))=0A+=20=20=20=20=20=20=20=20=20=20=20=20=20= ('category=0A+=20=20=20=20=20=20=20=20=20=20=20=20=20=20= (rx--translate-category=20(not=20negated)=20(cdr=20arg)))=0A+=20=20=20=20= =20=20=20=20=20=20=20=20=20('not=0A+=20=20=20=20=20=20=20=20=20=20=20=20=20= =20(rx--translate-not=20=20=20=20=20=20(not=20negated)=20(cdr=20= arg))))))=0A+=20=20=20=20=20((let=20((class=20(cdr=20(assq=20arg=20= rx--char-classes))))=0A+=20=20=20=20=20=20=20=20(and=20class=0A+=20=20=20= =20=20=20=20=20=20=20=20=20=20(rx--translate-any=20(not=20negated)=20= (list=20class)))))=0A=20=20=20=20=20=20((eq=20arg=20'word-boundary)=0A=20= =20=20=20=20=20=20(rx--translate-symbol=0A=20=20=20=20=20=20=20=20(if=20= negated=20'word-boundary=20'not-word-boundary)))=0A-=20=20=20=20=20(t=0A= -=20=20=20=20=20=20(let=20((class=20(cdr=20(assq=20arg=20= rx--char-classes))))=0A-=20=20=20=20=20=20=20=20(if=20class=0A-=20=20=20=20= =20=20=20=20=20=20=20=20(rx--translate-any=20(not=20negated)=20(list=20= class))=0A-=20=20=20=20=20=20=20=20=20=20(error=20"Illegal=20argument=20= to=20rx=20`not':=20%s"=20arg)))))))=0A+=20=20=20=20=20((let=20((expanded=20= (rx--expand-def=20arg)))=0A+=20=20=20=20=20=20=20=20(and=20expanded=0A+=20= =20=20=20=20=20=20=20=20=20=20=20=20(rx--translate-not=20negated=20(list=20= expanded)))))=0A+=20=20=20=20=20(t=20(error=20"Illegal=20argument=20to=20= rx=20`not':=20%S"=20arg)))))=0A=20=0A=20(defun=20rx--atomic-regexp=20= (item)=0A=20=20=20"ITEM=20is=20(REGEXP=20.=20PRECEDENCE);=20return=20a=20= regexp=20of=20precedence=20t."=0A@@=20-873,30=20+893,28=20@@=20= rx--translate-form=0A=20=20=20=20=20=20=20((or=20'regexp=20'regex)=20=20=20= =20=20=20(rx--translate-regexp=20body))=0A=20=0A=20=20=20=20=20=20=20(op=0A= -=20=20=20=20=20=20=20(unless=20(symbolp=20op)=0A-=20=20=20=20=20=20=20=20= =20(error=20"Bad=20rx=20operator=20`%S'"=20op))=0A-=20=20=20=20=20=20=20= (let=20((definition=20(rx--lookup-def=20op)))=0A-=20=20=20=20=20=20=20=20= =20(if=20definition=0A-=20=20=20=20=20=20=20=20=20=20=20=20=20(if=20(cdr=20= definition)=0A-=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= (rx--translate=0A-=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= (rx--expand-template=0A-=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20op=20body=20(nth=200=20definition)=20(nth=201=20definition)))=0A-=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20(error=20"Not=20an=20`rx'=20= form=20definition:=20%s"=20op))=0A-=0A-=20=20=20=20=20=20=20=20=20=20=20= ;;=20For=20compatibility=20with=20old=20rx.=0A-=20=20=20=20=20=20=20=20=20= =20=20(let=20((entry=20(assq=20op=20rx-constituents)))=0A-=20=20=20=20=20= =20=20=20=20=20=20=20=20(if=20(progn=0A-=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20(while=20(and=20entry=20(not=20(consp=20(cdr=20= entry))))=0A-=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20(setq=20entry=0A-=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20(if=20(symbolp=20(cdr=20entry))=0A-=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20;;=20Alias=20for=20another=20entry.=0A-=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20(assq=20= (cdr=20entry)=20rx-constituents)=0A-=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20;;=20Wrong=20type,=20try=20= further=20down=20the=20list.=0A-=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20(assq=20(car=20entry)=0A-=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20(cdr=20(memq=20entry=20rx-constituents))))))=0A= -=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20entry)=0A-=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20(rx--translate-compat-form=20= (cdr=20entry)=20form)=0A-=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= (error=20"Unknown=20rx=20form=20`%s'"=20op)))))))))=0A+=20=20=20=20=20=20= =20(cond=0A+=20=20=20=20=20=20=20=20((not=20(symbolp=20op))=20(error=20= "Bad=20rx=20operator=20`%S'"=20op))=0A+=0A+=20=20=20=20=20=20=20=20((let=20= ((expanded=20(rx--expand-def=20form)))=0A+=20=20=20=20=20=20=20=20=20=20=20= (and=20expanded=0A+=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= (rx--translate=20expanded))))=0A+=0A+=20=20=20=20=20=20=20=20;;=20For=20= compatibility=20with=20old=20rx.=0A+=20=20=20=20=20=20=20=20((let=20= ((entry=20(assq=20op=20rx-constituents)))=0A+=20=20=20=20=20=20=20=20=20=20= =20(and=20(progn=0A+=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= (while=20(and=20entry=20(not=20(consp=20(cdr=20entry))))=0A+=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20(setq=20entry=0A+=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20(if=20= (symbolp=20(cdr=20entry))=0A+=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20;;=20Alias=20for=20another=20= entry.=0A+=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20(assq=20(cdr=20entry)=20rx-constituents)=0A+=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20;;=20Wrong=20type,=20try=20further=20down=20the=20list.=0A+=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= (assq=20(car=20entry)=0A+=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20(cdr=20(memq=20= entry=20rx-constituents))))))=0A+=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20entry)=0A+=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= (rx--translate-compat-form=20(cdr=20entry)=20form))))=0A+=0A+=20=20=20=20= =20=20=20=20(t=20(error=20"Unknown=20rx=20form=20`%s'"=20op)))))))=0A=20=0A= =20(defconst=20rx--builtin-forms=0A=20=20=20'(seq=20sequence=20:=20and=20= or=20|=20any=20in=20char=20not-char=20not=0Adiff=20--git=20= a/test/lisp/emacs-lisp/rx-tests.el=20b/test/lisp/emacs-lisp/rx-tests.el=0A= index=20ef2541d83a..4ecc805aea=20100644=0A---=20= a/test/lisp/emacs-lisp/rx-tests.el=0A+++=20= b/test/lisp/emacs-lisp/rx-tests.el=0A@@=20-268,7=20+268,9=20@@=20rx-not=0A= =20=20=20(should=20(equal=20(rx=20(not=20(syntax=20punctuation))=20(not=20= (syntax=20escape)))=0A=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20"\\S.\\S\\"))=0A=20=20=20(should=20(equal=20(rx=20(not=20(category=20= tone-mark))=20(not=20(category=20lao)))=0A-=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20"\\C4\\Co")))=0A+=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20"\\C4\\Co"))=0A+=20=20(should=20(equal=20(rx=20(not=20= (not=20ascii))=20(not=20(not=20(not=20(any=20"a-z")))))=0A+=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20"[[:ascii:]][^a-z]")))=0A=20=0A=20= (ert-deftest=20rx-group=20()=0A=20=20=20(should=20(equal=20(rx=20(group=20= nonl)=20(submatch=20"x")=0A@@=20-404,6=20+406,19=20@@=20= rx-redefine-builtin=0A=20=20=20(should-error=20(rx-let-eval=20= '((not-char=20()=20"x"))=20nil))=0A=20=20=20(should-error=20(rx-let-eval=20= '((not-char=20"x"))=20nil)))=0A=20=0A+(ert-deftest=20rx-def-in-not=20()=0A= +=20=20"Test=20definition=20expansion=20inside=20(not=20...)."=0A+=20=20= (rx-let=20((a=20alpha)=0A+=20=20=20=20=20=20=20=20=20=20=20(b=20(not=20= hex))=0A+=20=20=20=20=20=20=20=20=20=20=20(c=20(not=20(category=20= base)))=0A+=20=20=20=20=20=20=20=20=20=20=20(d=20(x)=20(any=20?a=20x=20= ?z))=0A+=20=20=20=20=20=20=20=20=20=20=20(e=20(x)=20(syntax=20x))=0A+=20=20= =20=20=20=20=20=20=20=20=20(f=20(not=20b)))=0A+=20=20=20=20(should=20= (equal=20(rx=20(not=20a)=20(not=20b)=20(not=20c)=20(not=20f))=0A+=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= "[^[:alpha:]][[:xdigit:]]\\c.[^[:xdigit:]]"))=0A+=20=20=20=20(should=20= (equal=20(rx=20(not=20(d=20?m))=20(not=20(e=20symbol)))=0A+=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20"[^amz]\\S_"))))=0A+=0A=20= (ert-deftest=20rx-constituents=20()=0A=20=20=20(let=20((rx-constituents=0A= =20=20=20=20=20=20=20=20=20=20(append=20'((beta=20.=20gamma)=0A--=20=0A= 2.21.0=20(Apple=20Git-122)=0A=0A= --Apple-Mail=_A5D7A71E-F7D3-4086-A2E5-8E608F8FFC42-- From unknown Sat Aug 09 15:55:10 2025 X-Loop: help-debbugs@gnu.org Subject: bug#37849: composable character alternatives in rx References: <0F77D35E-52A7-4332-913E-DDD286461F00@acm.org> In-Reply-To: <0F77D35E-52A7-4332-913E-DDD286461F00@acm.org> Resent-From: Mattias =?UTF-8?Q?Engdeg=C3=A5rd?= Original-Sender: "Debbugs-submit" Resent-CC: bug-gnu-emacs@gnu.org Resent-Date: Sun, 27 Oct 2019 09:18:02 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 37849 X-GNU-PR-Package: emacs X-GNU-PR-Keywords: To: 37849@debbugs.gnu.org Received: via spool by 37849-submit@debbugs.gnu.org id=B37849.157216786928947 (code B ref 37849); Sun, 27 Oct 2019 09:18:02 +0000 Received: (at 37849) by debbugs.gnu.org; 27 Oct 2019 09:17:49 +0000 Received: from localhost ([127.0.0.1]:41675 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1iOegG-0007Wp-Pb for submit@debbugs.gnu.org; Sun, 27 Oct 2019 05:17:48 -0400 Received: from mail233c50.megamailservers.eu ([91.136.10.243]:49840 helo=mail37c50.megamailservers.eu) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1iOegD-0007Wb-Ey for 37849@debbugs.gnu.org; Sun, 27 Oct 2019 05:17:47 -0400 X-Authenticated-User: mattiase@bredband.net DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=megamailservers.eu; s=maildub; t=1572167863; bh=bm+zIcn1nZaWGLDTA9I6Zgm0f/l92ZmbzAYEixuTgnI=; h=From:Subject:Date:To:From; b=Q5EJPUKroq5pZyPdQlQ8LKkDqBAxSWVc3CP7dwhO92B4W68SrtfGGSn51jWFafH+T +zehOCkKyqMXI0wXgMJxUSWuc188cNQQNSlHoDKQi2Sm+Jtbl5pONkYbdboVKRExs5 TSrRcJfUjZjSoP8ZNdQwNBzTADVTVcAORSr9UohY= Feedback-ID: mattiase@acm.or Received: from [192.168.0.4] (c188-150-171-71.bredband.comhem.se [188.150.171.71]) (authenticated bits=0) by mail37c50.megamailservers.eu (8.14.9/8.13.1) with ESMTP id x9R9Hfna013430 for <37849@debbugs.gnu.org>; Sun, 27 Oct 2019 09:17:42 +0000 From: Mattias =?UTF-8?Q?Engdeg=C3=A5rd?= Content-Type: text/plain; charset=us-ascii Content-Transfer-Encoding: quoted-printable Mime-Version: 1.0 (Mac OS X Mail 12.4 \(3445.104.11\)) Message-Id: Date: Sun, 27 Oct 2019 10:17:40 +0100 X-Mailer: Apple Mail (2.3445.104.11) X-CTCH-RefID: str=0001.0A0B020C.5DB560B7.0003, ss=1, re=0.000, recu=0.000, reip=0.000, cl=1, cld=1, fgs=0 X-CTCH-VOD: Unknown X-CTCH-Spam: Unknown X-CTCH-Score: 0.000 X-CTCH-Rules: X-CTCH-Flags: 0 X-CTCH-ScoreCust: 0.000 X-CSC: 0 X-CHA: v=2.3 cv=eN1tc0h1 c=1 sm=1 tr=0 a=SF+I6pRkHZhrawxbOkkvaA==:117 a=SF+I6pRkHZhrawxbOkkvaA==:17 a=kj9zAlcOel0A:10 a=M51BFTxLslgA:10 a=1ARHx0tzj0sTjJdY5qkA:9 a=CjuIK1q_8ugA:10 a=pHzHmUro8NiASowvMSCR:22 a=6VlIyEUom7LUIeUMNQJH:22 X-Spam-Score: 0.3 (/) X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -0.7 (/) Expansion inside (not ...) should be uncontroversial; now pushed = (cbd439e785). Character set operators (union, intersection, difference) would be = useful. Consider: (rx-define ident-chars (any "a-zA-Z0-9")) (rx-define operator-chars (any ?+ ?- ?* ?/ ?< ?> ?=3D)) There is then currently no way to form the set of characters that = excludes both the above sets. From unknown Sat Aug 09 15:55:10 2025 X-Loop: help-debbugs@gnu.org Subject: bug#37849: composable character alternatives in rx References: <0F77D35E-52A7-4332-913E-DDD286461F00@acm.org> In-Reply-To: <0F77D35E-52A7-4332-913E-DDD286461F00@acm.org> Resent-From: Mattias =?UTF-8?Q?Engdeg=C3=A5rd?= Original-Sender: "Debbugs-submit" Resent-CC: bug-gnu-emacs@gnu.org Resent-Date: Fri, 06 Dec 2019 21:59:02 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 37849 X-GNU-PR-Package: emacs X-GNU-PR-Keywords: To: 37849@debbugs.gnu.org Received: via spool by 37849-submit@debbugs.gnu.org id=B37849.157566953920497 (code B ref 37849); Fri, 06 Dec 2019 21:59:02 +0000 Received: (at 37849) by debbugs.gnu.org; 6 Dec 2019 21:58:59 +0000 Received: from localhost ([127.0.0.1]:47124 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1idLco-0005KW-JE for submit@debbugs.gnu.org; Fri, 06 Dec 2019 16:58:58 -0500 Received: from mail1475c50.megamailservers.eu ([91.136.14.75]:35302 helo=mail118c50.megamailservers.eu) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1idLcm-0005KH-7L for 37849@debbugs.gnu.org; Fri, 06 Dec 2019 16:58:57 -0500 X-Authenticated-User: mattiase@bredband.net DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=megamailservers.eu; s=maildub; t=1575669529; bh=MWkN3K5RwyG/bVLEc/UwuUEmkHsReYSJI8dlyA5qgF0=; h=From:Subject:Date:To:From; b=e9w5JlIfMBLPLVmwyVVx6tDSHt/x0+vdNR/NEROVqOGsISEyaWLXIz7coi9N/+omX FcwkmsO55ORv2c5WkQyTPg7RLjEsU5G3c3id+DwJyxh0wmPYQECLF3JUd/S9kfwO5t anwK936kcaCzAJgb42noETwpASBQtm1qP00Q4OvQ= Feedback-ID: mattiase@acm.or Received: from stanniol.lan (c-6f4fe655.032-75-73746f71.bbcust.telenor.se [85.230.79.111]) (authenticated bits=0) by mail118c50.megamailservers.eu (8.14.9/8.13.1) with ESMTP id xB6Lwl7f023924 for <37849@debbugs.gnu.org>; Fri, 6 Dec 2019 21:58:48 +0000 From: Mattias =?UTF-8?Q?Engdeg=C3=A5rd?= Content-Type: multipart/mixed; boundary="Apple-Mail=_C688033D-9F8B-4891-B400-E7544B217FCA" Mime-Version: 1.0 (Mac OS X Mail 12.4 \(3445.104.11\)) Message-Id: Date: Fri, 6 Dec 2019 22:58:46 +0100 X-Mailer: Apple Mail (2.3445.104.11) X-CTCH-RefID: str=0001.0A0B020F.5DEACF19.000A, ss=1, re=0.000, recu=0.000, reip=0.000, cl=1, cld=1, fgs=0 X-CTCH-VOD: Unknown X-CTCH-Spam: Unknown X-CTCH-Score: 0.000 X-CTCH-Rules: X-CTCH-Flags: 0 X-CTCH-ScoreCust: 0.000 X-CSC: 0 X-CHA: v=2.3 cv=dLXYZ9Rb c=1 sm=1 tr=0 a=fHaj9vQUQVKQ4sUldAaXuQ==:117 a=fHaj9vQUQVKQ4sUldAaXuQ==:17 a=M51BFTxLslgA:10 a=Z9LDBL19FG3jgPoqjTwA:9 a=CjuIK1q_8ugA:10 a=3tTRy8xHFvmLKJ7eeIsA:9 a=B2y7HmGcmWMA:10 X-Spam-Score: 2.9 (++) X-Spam-Report: Spam detection software, running on the system "debbugs.gnu.org", has NOT identified this incoming email as spam. The original message has been attached to this so you can view it or label similar future email. If you have any questions, see the administrator of that system for details. Content preview: This patch adds `union' and `intersection' to rx. They both take zero or more charsets as arguments. A charset is either an `any' form that does not contain character classes, a `union' or `intersecti [...] Content analysis details: (2.9 points, 10.0 required) pts rule name description ---- ---------------------- -------------------------------------------------- 0.0 URIBL_BLOCKED ADMINISTRATOR NOTICE: The query to URIBL was blocked. See http://wiki.apache.org/spamassassin/DnsBlocklists#dnsbl-block for more information. [URIs: megamailservers.eu] 0.0 SPF_HELO_NONE SPF: HELO does not publish an SPF Record 1.0 SPF_SOFTFAIL SPF: sender does not match SPF record (softfail) 1.5 FAKE_REPLY_B No description available. 0.4 KHOP_HELO_FCRDNS Relay HELO differs from its IP's reverse DNS X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: 1.5 (+) X-Spam-Report: Spam detection software, running on the system "debbugs.gnu.org", has NOT identified this incoming email as spam. The original message has been attached to this so you can view it or label similar future email. If you have any questions, see the administrator of that system for details. Content preview: This patch adds `union' and `intersection' to rx. They both take zero or more charsets as arguments. A charset is either an `any' form that does not contain character classes, a `union' or `intersecti [...] Content analysis details: (1.5 points, 10.0 required) pts rule name description ---- ---------------------- -------------------------------------------------- 0.0 URIBL_BLOCKED ADMINISTRATOR NOTICE: The query to URIBL was blocked. See http://wiki.apache.org/spamassassin/DnsBlocklists#dnsbl-block for more information. [URIs: megamailservers.eu] 0.0 SPF_HELO_NONE SPF: HELO does not publish an SPF Record 1.0 SPF_SOFTFAIL SPF: sender does not match SPF record (softfail) -1.0 MAILING_LIST_MULTI Multiple indicators imply a widely-seen list manager 1.5 FAKE_REPLY_B No description available. --Apple-Mail=_C688033D-9F8B-4891-B400-E7544B217FCA Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset=us-ascii This patch adds `union' and `intersection' to rx. They both take zero or = more charsets as arguments. A charset is either an `any' form that does = not contain character classes, a `union' or `intersection' form, or a = `not' form with charset argument. Example: (rx (union (any "a-f") (any "b-m"))) =3D> "[a-m]" (rx (intersection (any "a-f") (any "b-m"))) =3D> "[b-f]" The character class limitation stems from the inability to complement or = intersect classes in general. It would be possible to partially lift = this restriction for `union'; it is clear that (rx (union (any "ab" space) (any "bc" space digit))) =3D> "[abc[:space:][:digit:]]" but it makes the facility harder to explain to the user in a way that = makes sense. Still, it could be a future extension. A `difference' operator was not included but could be added; it is = trivially defined in rx as (rx-define difference (a b) (intersection a (not b))) The names `union' and `intersection' are verbose, but should be rare = enough that it's better with something descriptive. SRE, from where the concept was taken, uses `|' and `&' respectively, = and `~' for complement, `-' for difference. --Apple-Mail=_C688033D-9F8B-4891-B400-E7544B217FCA Content-Disposition: attachment; filename=0001-Add-union-and-intersection-to-rx-bug-37849.patch Content-Type: application/octet-stream; x-unix-mode=0644; name="0001-Add-union-and-intersection-to-rx-bug-37849.patch" Content-Transfer-Encoding: quoted-printable =46rom=203d3bc1529ae90d3cfd5605055060f14696d815c2=20Mon=20Sep=2017=20= 00:00:00=202001=0AFrom:=20=3D?UTF-8?q?Mattias=3D20Engdeg=3DC3=3DA5rd?=3D=20= =0ADate:=20Fri,=206=20Dec=202019=2022:23:57=20+0100=0A= Subject:=20[PATCH]=20Add=20`union'=20and=20`intersection'=20to=20rx=20= (bug#37849)=0A=0AThese=20character=20set=20operations,=20together=20with=20= `not'=20for=20set=0Acomplement,=20improve=20the=20compositionality=20of=20= rx,=20and=20reduce=20duplication=0Ain=20complicated=20cases.=20=20Named=20= character=20classes=20are=20not=20permitted=20in=0Aset=20operations.=0A=0A= *=20lisp/emacs-lisp/rx.el=20(rx--translate-any):=20Split=20into=20= multiple=0Afunctions.=0A(rx--foldl,=20rx--parse-any,=20rx--generate-alt,=20= rx--intervals-to-alt)=0A(rx--complement-intervals,=20= rx--intersect-intervals)=0A(rx--union-intervals,=20= rx--charset-intervals,=20rx--charset-union)=0A(rx--charset-all,=20= rx--charset-intersection,=20rx--translate-union)=0A= (rx--translate-intersection):=20New.=0A(rx--translate-not,=20= rx--translate-form,=20rx--builtin-forms):=0AAdd=20`union'=20and=20= `intersection'.=0A*=20test/lisp/emacs-lisp/rx-tests.el=20(rx-union=20= ,rx-def-in-union)=0A(rx-intersection,=20rx-def-in-intersection):=20New=20= tests.=0A*=20doc/lispref/searching.texi=20(Rx=20Constructs):=0A*=20= etc/NEWS:=0ADocument=20`union'=20and=20`intersection'.=0A---=0A=20= doc/lispref/searching.texi=20=20=20=20=20=20=20|=20=2014=20+-=0A=20= etc/NEWS=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20|=20=20=207=20+-=0A=20lisp/emacs-lisp/rx.el=20=20=20=20=20=20=20= =20=20=20=20=20|=20302=20+++++++++++++++++++++----------=0A=20= test/lisp/emacs-lisp/rx-tests.el=20|=20=2057=20++++++=0A=204=20files=20= changed,=20284=20insertions(+),=2096=20deletions(-)=0A=0Adiff=20--git=20= a/doc/lispref/searching.texi=20b/doc/lispref/searching.texi=0Aindex=20= 74b15cfc7f..19888e7cfa=20100644=0A---=20a/doc/lispref/searching.texi=0A= +++=20b/doc/lispref/searching.texi=0A@@=20-1214,11=20+1214,21=20@@=20Rx=20= Constructs=0A=20@item=20@code{(not=20@var{charspec})}=0A=20@cindex=20= @code{not}=20in=20rx=0A=20Match=20a=20character=20not=20included=20in=20= @var{charspec}.=20=20@var{charspec}=20can=0A-be=20an=20@code{any},=20= @code{not},=20@code{syntax}=20or=20@code{category}=20form,=20or=20a=0A= -character=20class.@*=0A+be=20an=20@code{any},=20@code{not},=20= @code{union},=20@code{intersection},=0A+@code{syntax}=20or=20= @code{category}=20form,=20or=20a=20character=20class.@*=0A=20= Corresponding=20string=20regexp:=20@samp{[^@dots{}]},=20= @samp{\S@var{code}},=0A=20@samp{\C@var{code}}=0A=20=0A+@item=20= @code{(union=20@var{charset}@dots{})}=0A+@itemx=20@code{(intersection=20= @var{charset}@dots{})}=0A+@cindex=20@code{union}=20in=20rx=0A+@cindex=20= @code{intersection}=20in=20rx=0A+Match=20a=20character=20that=20matches=20= the=20union=20or=20intersection,=0A+respectively,=20of=20the=20= @var{charset}s.=20=20Each=20@var{charset}=20can=20be=20an=0A+@code{any}=20= form=20without=20character=20classes,=20or=20a=20@code{union},=0A= +@code{intersection}=20or=20@code{not}=20form=20whose=20arguments=20are=20= also=0A+@var{charset}s.=0A+=0A=20@item=20@code{not-newline},=20= @code{nonl}=0A=20@cindex=20@code{not-newline}=20in=20rx=0A=20@cindex=20= @code{nonl}=20in=20rx=0Adiff=20--git=20a/etc/NEWS=20b/etc/NEWS=0Aindex=20= 28bcb720cd..fb95a6e704=20100644=0A---=20a/etc/NEWS=0A+++=20b/etc/NEWS=0A= @@=20-2100,9=20+2100,14=20@@=20at=20run=20time,=20instead=20of=20a=20= constant=20string.=0A=20These=20macros=20add=20new=20forms=20to=20the=20= rx=20notation.=0A=20=0A=20+++=0A-***=20'anychar'=20is=20now=20an=20alias=20= for=20'anything'=0A+***=20'anychar'=20is=20now=20an=20alias=20for=20= 'anything'.=0A=20Both=20match=20any=20single=20character;=20'anychar'=20= is=20more=20descriptive.=0A=20=0A++++=0A+***=20New=20'union'=20and=20= 'intersection'=20forms=20for=20character=20sets.=0A+These=20permit=20= composing=20character-matching=20expressions=20from=20simpler=0A+parts.=0A= +=0A=20**=20Frames=0A=20=0A=20+++=0Adiff=20--git=20= a/lisp/emacs-lisp/rx.el=20b/lisp/emacs-lisp/rx.el=0Aindex=20= 0dc6e19866..b17f44f1a7=20100644=0A---=20a/lisp/emacs-lisp/rx.el=0A+++=20= b/lisp/emacs-lisp/rx.el=0A@@=20-246,6=20+246,14=20@@=20rx--every=0A=20=20= =20=20=20(setq=20list=20(cdr=20list)))=0A=20=20=20(null=20list))=0A=20=0A= +(defun=20rx--foldl=20(f=20x=20l)=0A+=20=20"(F=20(F=20(F=20X=20L0)=20L1)=20= L2)=20...=0A+Left-fold=20the=20list=20L,=20starting=20with=20X,=20by=20= the=20binary=20function=20F."=0A+=20=20(while=20l=0A+=20=20=20=20(setq=20= x=20(funcall=20f=20x=20(car=20l)))=0A+=20=20=20=20(setq=20l=20(cdr=20= l)))=0A+=20=20x)=0A+=0A=20(defun=20rx--translate-or=20(body)=0A=20=20=20= "Translate=20an=20or-pattern=20of=20zero=20or=20more=20rx=20items.=0A=20= Return=20(REGEXP=20.=20PRECEDENCE)."=0A@@=20-343,22=20+351,11=20@@=20= rx--condense-intervals=0A=20=20=20=20=20=20=20=20=20(setq=20tail=20d)))=0A= =20=20=20=20=20intervals))=0A=20=0A-;;=20FIXME:=20Consider=20expanding=20= definitions=20inside=20(any=20...)=20and=20(not=20...),=0A-;;=20and=20= perhaps=20allow=20(any=20...)=20inside=20(any=20...).=0A-;;=20It=20would=20= be=20benefit=20composability=20(build=20a=20character=20alternative=20by=20= pieces)=0A-;;=20and=20be=20handy=20for=20obtaining=20the=20complement=20= of=20a=20defined=20set=20of=0A-;;=20characters.=20=20(See,=20for=20= example,=20python.el:421,=20`not-simple-operator'.)=0A-;;=20(Expansion=20= in=20other=20non-rx=20positions=20is=20probably=20not=20a=20good=20idea:=0A= -;;=20syntax,=20category,=20backref,=20and=20the=20integer=20parameters=20= of=20group-n,=0A-;;=20=3D,=20>=3D,=20**,=20repeat)=0A-;;=20Similar=20= effect=20could=20be=20attained=20by=20ensuring=20that=0A-;;=20(or=20(any=20= X)=20(any=20Y))=20->=20(any=20X=20Y),=20and=20find=20a=20way=20to=20= compose=20negative=0A-;;=20sets.=20=20`and'=20is=20taken,=20but=20we=20= could=20add=0A-;;=20(intersection=20(not=20(any=20X))=20(not=20(any=20= Y)))=20->=20(not=20(any=20X=20Y)).=0A-=0A-(defun=20rx--translate-any=20= (negated=20body)=0A-=20=20"Translate=20an=20(any=20...)=20construct.=20=20= Return=20(REGEXP=20.=20PRECEDENCE).=0A-If=20NEGATED,=20negate=20the=20= sense."=0A+(defun=20rx--parse-any=20(body)=0A+=20=20"Parse=20arguments=20= of=20an=20(any=20...)=20construct.=0A+Return=20(INTERVALS=20.=20= CLASSES),=20where=20INTERVALS=20is=20a=20sorted=20list=20of=0A+disjoint=20= intervals=20(each=20a=20cons=20of=20chars),=20and=20CLASSES=0A+a=20list=20= of=20named=20character=20classes=20in=20the=20order=20they=20occur=20in=20= BODY."=0A=20=20=20(let=20((classes=20nil)=0A=20=20=20=20=20=20=20=20=20= (strings=20nil)=0A=20=20=20=20=20=20=20=20=20(conses=20nil))=0A@@=20= -380,81=20+377,109=20@@=20rx--translate-any=0A=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20(or=20(memq=20class=20= classes)=0A=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20(progn=20(push=20class=20classes)=20t))))))=0A= =20=20=20=20=20=20=20=20=20=20=20=20=20(t=20(error=20"Invalid=20rx=20= `any'=20argument:=20%s"=20arg))))=0A-=20=20=20=20(let=20((items=0A-=20=20= =20=20=20=20=20=20=20=20=20;;=20Translate=20strings=20and=20conses=20= into=20nonoverlapping=20intervals,=0A-=20=20=20=20=20=20=20=20=20=20=20= ;;=20and=20add=20classes=20as=20symbols=20at=20the=20end.=0A-=20=20=20=20= =20=20=20=20=20=20=20(append=0A-=20=20=20=20=20=20=20=20=20=20=20=20= (rx--condense-intervals=0A-=20=20=20=20=20=20=20=20=20=20=20=20=20(sort=20= (append=20conses=0A-=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20(mapcan=20#'rx--string-to-intervals=20= strings))=0A-=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= #'car-less-than-car))=0A-=20=20=20=20=20=20=20=20=20=20=20=20(reverse=20= classes))))=0A-=0A-=20=20=20=20=20=20;;=20Move=20lone=20]=20and=20range=20= ]-x=20to=20the=20start.=0A-=20=20=20=20=20=20(let=20((rbrac-l=20(assq=20= ?\]=20items)))=0A-=20=20=20=20=20=20=20=20(when=20rbrac-l=0A-=20=20=20=20= =20=20=20=20=20=20(setq=20items=20(cons=20rbrac-l=20(delq=20rbrac-l=20= items)))))=0A-=0A-=20=20=20=20=20=20;;=20Split=20x-]=20and=20move=20the=20= lone=20]=20to=20the=20start.=0A-=20=20=20=20=20=20(let=20((rbrac-r=20= (rassq=20?\]=20items)))=0A-=20=20=20=20=20=20=20=20(when=20(and=20= rbrac-r=20(not=20(eq=20(car=20rbrac-r)=20?\])))=0A-=20=20=20=20=20=20=20=20= =20=20(setcdr=20rbrac-r=20?\\)=0A-=20=20=20=20=20=20=20=20=20=20(setq=20= items=20(cons=20'(?\]=20.=20?\])=20items))))=0A-=0A-=20=20=20=20=20=20;;=20= Split=20,--=20(which=20would=20end=20up=20as=20,-=20otherwise).=0A-=20=20= =20=20=20=20(let=20((dash-r=20(rassq=20?-=20items)))=0A-=20=20=20=20=20=20= =20=20(when=20(eq=20(car=20dash-r)=20?,)=0A-=20=20=20=20=20=20=20=20=20=20= (setcdr=20dash-r=20?,)=0A-=20=20=20=20=20=20=20=20=20=20(setq=20items=20= (nconc=20items=20'((?-=20.=20?-))))))=0A-=0A-=20=20=20=20=20=20;;=20= Remove=20-=20(lone=20or=20at=20start=20of=20interval)=0A-=20=20=20=20=20=20= (let=20((dash-l=20(assq=20?-=20items)))=0A-=20=20=20=20=20=20=20=20(when=20= dash-l=0A-=20=20=20=20=20=20=20=20=20=20(if=20(eq=20(cdr=20dash-l)=20?-)=0A= -=20=20=20=20=20=20=20=20=20=20=20=20=20=20(setq=20items=20(delq=20= dash-l=20items))=20=20=20;=20Remove=20lone=20-=0A-=20=20=20=20=20=20=20=20= =20=20=20=20(setcar=20dash-l=20?.))=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20;=20Reduce=20--x=20to=20.-x=0A-=20=20=20=20=20=20=20=20=20= =20(setq=20items=20(nconc=20items=20'((?-=20.=20?-))))))=0A-=0A-=20=20=20= =20=20=20;;=20Deal=20with=20leading=20^=20and=20range=20^-x.=0A-=20=20=20= =20=20=20(when=20(and=20(consp=20(car=20items))=0A-=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20(eq=20(caar=20items)=20?^)=0A-=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20(cdr=20items))=0A-=20=20=20=20=20=20=20=20= ;;=20Move=20^=20and=20^-x=20to=20second=20place.=0A-=20=20=20=20=20=20=20= =20(setq=20items=20(cons=20(cadr=20items)=0A-=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20(cons=20(car=20items)=20= (cddr=20items)))))=0A+=20=20=20=20(cons=20(rx--condense-intervals=0A+=20=20= =20=20=20=20=20=20=20=20=20(sort=20(append=20conses=0A+=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20(mapcan=20= #'rx--string-to-intervals=20strings))=0A+=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20#'car-less-than-car))=0A+=20=20=20=20=20=20=20=20=20=20= (reverse=20classes))))=0A+=0A+(defun=20rx--generate-alt=20(negated=20= intervals=20classes)=0A+=20=20"Generate=20a=20character=20alternative.=20= =20Return=20(REGEXP=20.=20PRECEDENCE).=0A+If=20NEGATED=20is=20non-nil,=20= negate=20the=20result;=20INTERVALS=20is=20a=20sorted=0A+list=20of=20= disjoint=20intervals=20and=20CLASSES=20a=20list=20of=20named=20character=0A= +classes."=0A+=20=20(let=20((items=20(append=20intervals=20classes)))=0A= +=20=20=20=20;;=20Move=20lone=20]=20and=20range=20]-x=20to=20the=20= start.=0A+=20=20=20=20(let=20((rbrac-l=20(assq=20?\]=20items)))=0A+=20=20= =20=20=20=20(when=20rbrac-l=0A+=20=20=20=20=20=20=20=20(setq=20items=20= (cons=20rbrac-l=20(delq=20rbrac-l=20items)))))=0A+=0A+=20=20=20=20;;=20= Split=20x-]=20and=20move=20the=20lone=20]=20to=20the=20start.=0A+=20=20=20= =20(let=20((rbrac-r=20(rassq=20?\]=20items)))=0A+=20=20=20=20=20=20(when=20= (and=20rbrac-r=20(not=20(eq=20(car=20rbrac-r)=20?\])))=0A+=20=20=20=20=20= =20=20=20(setcdr=20rbrac-r=20?\\)=0A+=20=20=20=20=20=20=20=20(setq=20= items=20(cons=20'(?\]=20.=20?\])=20items))))=0A+=0A+=20=20=20=20;;=20= Split=20,--=20(which=20would=20end=20up=20as=20,-=20otherwise).=0A+=20=20= =20=20(let=20((dash-r=20(rassq=20?-=20items)))=0A+=20=20=20=20=20=20= (when=20(eq=20(car=20dash-r)=20?,)=0A+=20=20=20=20=20=20=20=20(setcdr=20= dash-r=20?,)=0A+=20=20=20=20=20=20=20=20(setq=20items=20(nconc=20items=20= '((?-=20.=20?-))))))=0A+=0A+=20=20=20=20;;=20Remove=20-=20(lone=20or=20= at=20start=20of=20interval)=0A+=20=20=20=20(let=20((dash-l=20(assq=20?-=20= items)))=0A+=20=20=20=20=20=20(when=20dash-l=0A+=20=20=20=20=20=20=20=20= (if=20(eq=20(cdr=20dash-l)=20?-)=0A+=20=20=20=20=20=20=20=20=20=20=20=20= (setq=20items=20(delq=20dash-l=20items))=20=20=20;=20Remove=20lone=20-=0A= +=20=20=20=20=20=20=20=20=20=20(setcar=20dash-l=20?.))=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20;=20Reduce=20--x=20to=20.-x=0A+=20=20=20= =20=20=20=20=20(setq=20items=20(nconc=20items=20'((?-=20.=20?-))))))=0A+=0A= +=20=20=20=20;;=20Deal=20with=20leading=20^=20and=20range=20^-x.=0A+=20=20= =20=20(when=20(and=20(consp=20(car=20items))=0A+=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20(eq=20(caar=20items)=20?^)=0A+=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20(cdr=20items))=0A+=20=20=20=20=20=20;;=20Move=20^=20= and=20^-x=20to=20second=20place.=0A+=20=20=20=20=20=20(setq=20items=20= (cons=20(cadr=20items)=0A+=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20(cons=20(car=20items)=20(cddr=20items)))))=0A=20=0A= -=20=20=20=20=20=20(cond=0A-=20=20=20=20=20=20=20;;=20Empty=20set:=20if=20= negated,=20any=20char,=20otherwise=20match-nothing.=0A-=20=20=20=20=20=20= =20((null=20items)=0A-=20=20=20=20=20=20=20=20(if=20negated=0A-=20=20=20=20= =20=20=20=20=20=20=20=20(rx--translate-symbol=20'anything)=0A-=20=20=20=20= =20=20=20=20=20=20(rx--empty)))=0A-=20=20=20=20=20=20=20;;=20Single=20= non-negated=20character.=0A-=20=20=20=20=20=20=20((and=20(null=20(cdr=20= items))=0A-=20=20=20=20=20=20=20=20=20=20=20=20=20(consp=20(car=20= items))=0A-=20=20=20=20=20=20=20=20=20=20=20=20=20(eq=20(caar=20items)=20= (cdar=20items))=0A-=20=20=20=20=20=20=20=20=20=20=20=20=20(not=20= negated))=0A-=20=20=20=20=20=20=20=20(cons=20(list=20(regexp-quote=20= (char-to-string=20(caar=20items))))=0A-=20=20=20=20=20=20=20=20=20=20=20=20= =20=20t))=0A-=20=20=20=20=20=20=20;;=20At=20least=20one=20character=20or=20= class,=20possibly=20negated.=0A-=20=20=20=20=20=20=20(t=0A-=20=20=20=20=20= =20=20=20(cons=0A-=20=20=20=20=20=20=20=20=20(list=0A-=20=20=20=20=20=20=20= =20=20=20(concat=0A-=20=20=20=20=20=20=20=20=20=20=20"["=0A-=20=20=20=20=20= =20=20=20=20=20=20(and=20negated=20"^")=0A-=20=20=20=20=20=20=20=20=20=20= =20(mapconcat=20(lambda=20(item)=0A-=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20(cond=20((symbolp=20item)=0A-=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20(format=20"[:%s:]"=20item))=0A-=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20((eq=20(car=20= item)=20(cdr=20item))=0A-=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20(char-to-string=20(car=20= item)))=0A-=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20((eq=20(1+=20(car=20item))=20(cdr=20item))=0A= -=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20(string=20(car=20item)=20(cdr=20item)))=0A-=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20(t=0A-=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20(string=20(car=20item)=20?-=20(cdr=20= item)))))=0A-=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20items=20nil)=0A-=20=20=20=20=20=20=20=20=20=20=20"]"))=0A-=20=20=20= =20=20=20=20=20=20t))))))=0A+=20=20=20=20(cond=0A+=20=20=20=20=20;;=20= Empty=20set:=20if=20negated,=20any=20char,=20otherwise=20match-nothing.=0A= +=20=20=20=20=20((null=20items)=0A+=20=20=20=20=20=20(if=20negated=0A+=20= =20=20=20=20=20=20=20=20=20(rx--translate-symbol=20'anything)=0A+=20=20=20= =20=20=20=20=20(rx--empty)))=0A+=20=20=20=20=20;;=20Single=20non-negated=20= character.=0A+=20=20=20=20=20((and=20(null=20(cdr=20items))=0A+=20=20=20=20= =20=20=20=20=20=20=20(consp=20(car=20items))=0A+=20=20=20=20=20=20=20=20=20= =20=20(eq=20(caar=20items)=20(cdar=20items))=0A+=20=20=20=20=20=20=20=20=20= =20=20(not=20negated))=0A+=20=20=20=20=20=20(cons=20(list=20= (regexp-quote=20(char-to-string=20(caar=20items))))=0A+=20=20=20=20=20=20= =20=20=20=20=20=20t))=0A+=20=20=20=20=20;;=20At=20least=20one=20= character=20or=20class,=20possibly=20negated.=0A+=20=20=20=20=20(t=0A+=20= =20=20=20=20=20(cons=0A+=20=20=20=20=20=20=20(list=0A+=20=20=20=20=20=20=20= =20(concat=0A+=20=20=20=20=20=20=20=20=20"["=0A+=20=20=20=20=20=20=20=20=20= (and=20negated=20"^")=0A+=20=20=20=20=20=20=20=20=20(mapconcat=20(lambda=20= (item)=0A+=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20(cond=20((symbolp=20item)=0A+=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20(format=20"[:%s:]"=20item))=0A= +=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20((eq=20(car=20item)=20(cdr=20item))=0A+=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= (char-to-string=20(car=20item)))=0A+=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20((eq=20(1+=20(car=20item))=20= (cdr=20item))=0A+=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20(string=20(car=20item)=20(cdr=20item)))=0A= +=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20(t=0A+=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20(string=20(car=20item)=20?-=20(cdr=20= item)))))=0A+=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= items=20nil)=0A+=20=20=20=20=20=20=20=20=20"]"))=0A+=20=20=20=20=20=20=20= t)))))=0A+=0A+(defun=20rx--translate-any=20(negated=20body)=0A+=20=20= "Translate=20an=20(any=20...)=20construct.=20=20Return=20(REGEXP=20.=20= PRECEDENCE).=0A+If=20NEGATED,=20negate=20the=20sense."=0A+=20=20(let=20= ((parsed=20(rx--parse-any=20body)))=0A+=20=20=20=20(rx--generate-alt=20= negated=20(car=20parsed)=20(cdr=20parsed))))=0A+=0A+(defun=20= rx--intervals-to-alt=20(negated=20intervals)=0A+=20=20"Generate=20a=20= character=20alternative=20from=20an=20interval=20set.=0A+Return=20= (REGEXP=20.=20PRECEDENCE).=0A+INTERVALS=20is=20a=20sorted=20list=20of=20= disjoint=20intervals.=0A+If=20NEGATED,=20negate=20the=20sense."=0A+=20=20= ;;=20Detect=20whether=20the=20interval=20set=20is=20better=20described=20= in=0A+=20=20;;=20complemented=20form.=20=20This=20is=20not=20just=20a=20= matter=20of=20aesthetics:=20any=0A+=20=20;;=20range=20from=20ASCII=20to=20= raw=20bytes=20will=20automatically=20exclude=20the=0A+=20=20;;=20entire=20= non-ASCII=20Unicode=20range=20by=20the=20regexp=20engine.=0A+=20=20(if=20= (rx--every=20(lambda=20(iv)=20(not=20(<=3D=20(car=20iv)=20#x3ffeff=20= (cdr=20iv))))=0A+=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= intervals)=0A+=20=20=20=20=20=20(rx--generate-alt=20negated=20intervals=20= nil)=0A+=20=20=20=20(rx--generate-alt=0A+=20=20=20=20=20(not=20negated)=20= (rx--complement-intervals=20intervals)=20nil)))=0A+=0A+;;=20FIXME:=20= Consider=20turning=20`not'=20into=20a=20variadic=20operator,=20following=20= SRE:=0A+;;=20(not=20A=20B)=20=3D=20(not=20(union=20A=20B))=20=3D=20= (intersection=20(not=20A)=20(not=20B)),=20and=0A+;;=20(not)=20=3D=20= anychar.=0A+;;=20Maybe=20allow=20singleton=20characters=20as=20= arguments.=0A=20=0A=20(defun=20rx--translate-not=20(negated=20body)=0A=20= =20=20"Translate=20a=20(not=20...)=20construct.=20=20Return=20(REGEXP=20= .=20PRECEDENCE).=0A@@=20-472,10=20+497,14=20@@=20rx--translate-not=0A=20=20= =20=20=20=20=20=20=20=20=20=20=20=20('category=0A=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20(rx--translate-category=20(not=20negated)=20(cdr=20= arg)))=0A=20=20=20=20=20=20=20=20=20=20=20=20=20=20('not=0A-=20=20=20=20=20= =20=20=20=20=20=20=20=20=20(rx--translate-not=20=20=20=20=20=20(not=20= negated)=20(cdr=20arg))))))=0A+=20=20=20=20=20=20=20=20=20=20=20=20=20=20= (rx--translate-not=20=20=20=20=20=20(not=20negated)=20(cdr=20arg)))=0A+=20= =20=20=20=20=20=20=20=20=20=20=20=20('union=0A+=20=20=20=20=20=20=20=20=20= =20=20=20=20=20(rx--translate-union=20=20=20=20(not=20negated)=20(cdr=20= arg)))=0A+=20=20=20=20=20=20=20=20=20=20=20=20=20('intersection=0A+=20=20= =20=20=20=20=20=20=20=20=20=20=20=20(rx--translate-intersection=20(not=20= negated)=20(cdr=20arg))))))=0A=20=20=20=20=20=20((let=20((class=20(cdr=20= (assq=20arg=20rx--char-classes))))=0A=20=20=20=20=20=20=20=20=20(and=20= class=0A-=20=20=20=20=20=20=20=20=20=20=20=20=20(rx--translate-any=20= (not=20negated)=20(list=20class)))))=0A+=20=20=20=20=20=20=20=20=20=20=20= =20=20(rx--generate-alt=20(not=20negated)=20nil=20(list=20class)))))=0A=20= =20=20=20=20=20((eq=20arg=20'word-boundary)=0A=20=20=20=20=20=20=20= (rx--translate-symbol=0A=20=20=20=20=20=20=20=20(if=20negated=20= 'word-boundary=20'not-word-boundary)))=0A@@=20-484,6=20+513,91=20@@=20= rx--translate-not=0A=20=20=20=20=20=20=20=20=20=20=20=20=20=20= (rx--translate-not=20negated=20(list=20expanded)))))=0A=20=20=20=20=20=20= (t=20(error=20"Illegal=20argument=20to=20rx=20`not':=20%S"=20arg)))))=0A=20= =0A+(defun=20rx--complement-intervals=20(intervals)=0A+=20=20"Complement=20= of=20the=20interval=20list=20INTERVALS."=0A+=20=20(let=20((compl=20nil)=0A= +=20=20=20=20=20=20=20=20(c=200))=0A+=20=20=20=20(dolist=20(iv=20= intervals)=0A+=20=20=20=20=20=20(when=20(<=20c=20(car=20iv))=0A+=20=20=20= =20=20=20=20=20(push=20(cons=20c=20(1-=20(car=20iv)))=20compl))=0A+=20=20= =20=20=20=20(setq=20c=20(1+=20(cdr=20iv))))=0A+=20=20=20=20(when=20(<=20= c=20(max-char))=0A+=20=20=20=20=20=20(push=20(cons=20c=20(max-char))=20= compl))=0A+=20=20=20=20(nreverse=20compl)))=0A+=0A+(defun=20= rx--intersect-intervals=20(ivs-a=20ivs-b)=0A+=20=20"Intersection=20of=20= the=20interval=20lists=20IVS-A=20and=20IVS-B."=0A+=20=20(let=20((isect=20= nil))=0A+=20=20=20=20(while=20(and=20ivs-a=20ivs-b)=0A+=20=20=20=20=20=20= (let=20((a=20(car=20ivs-a))=0A+=20=20=20=20=20=20=20=20=20=20=20=20(b=20= (car=20ivs-b)))=0A+=20=20=20=20=20=20=20=20(cond=0A+=20=20=20=20=20=20=20= =20=20((<=20(cdr=20a)=20(car=20b))=20(setq=20ivs-a=20(cdr=20ivs-a)))=0A+=20= =20=20=20=20=20=20=20=20((>=20(car=20a)=20(cdr=20b))=20(setq=20ivs-b=20= (cdr=20ivs-b)))=0A+=20=20=20=20=20=20=20=20=20(t=0A+=20=20=20=20=20=20=20= =20=20=20(push=20(cons=20(max=20(car=20a)=20(car=20b))=0A+=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20(min=20(cdr=20a)=20= (cdr=20b)))=0A+=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20isect)=0A= +=20=20=20=20=20=20=20=20=20=20(setq=20ivs-a=20(cdr=20ivs-a))=0A+=20=20=20= =20=20=20=20=20=20=20(setq=20ivs-b=20(cdr=20ivs-b))=0A+=20=20=20=20=20=20= =20=20=20=20(cond=20((<=20(cdr=20a)=20(cdr=20b))=0A+=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20(push=20(cons=20(1+=20(cdr=20a))=20(cdr=20= b))=0A+=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20ivs-b))=0A+=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20((>=20(cdr=20= a)=20(cdr=20b))=0A+=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= (push=20(cons=20(1+=20(cdr=20b))=20(cdr=20a))=0A+=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20ivs-a)))))))=0A+=20=20=20=20= (nreverse=20isect)))=0A+=0A+(defun=20rx--union-intervals=20(ivs-a=20= ivs-b)=0A+=20=20"Union=20of=20the=20interval=20lists=20IVS-A=20and=20= IVS-B."=0A+=20=20(rx--complement-intervals=0A+=20=20=20= (rx--intersect-intervals=0A+=20=20=20=20(rx--complement-intervals=20= ivs-a)=0A+=20=20=20=20(rx--complement-intervals=20ivs-b))))=0A+=0A= +(defun=20rx--charset-intervals=20(charset)=0A+=20=20"Return=20a=20= sorted=20list=20of=20non-adjacent=20disjoint=20intervals=20from=20= CHARSET.=0A+CHARSET=20is=20any=20expression=20allowed=20in=20a=20= character=20set=20expression:=0A+either=20`any'=20(no=20classes=20= permitted),=20or=20`not',=20`union'=20or=20`intersection'=0A+forms=20= whose=20arguments=20are=20charsets."=0A+=20=20(pcase=20charset=0A+=20=20=20= =20(`(,(or=20'any=20'in=20'char)=20.=20,body)=0A+=20=20=20=20=20(let=20= ((parsed=20(rx--parse-any=20body)))=0A+=20=20=20=20=20=20=20(when=20(cdr=20= parsed)=0A+=20=20=20=20=20=20=20=20=20(error=0A+=20=20=20=20=20=20=20=20=20= =20"Character=20class=20not=20permitted=20in=20set=20operations:=20%S"=0A= +=20=20=20=20=20=20=20=20=20=20(cadr=20parsed)))=0A+=20=20=20=20=20=20=20= (car=20parsed)))=0A+=20=20=20=20(`(not=20,x)=20(rx--complement-intervals=20= (rx--charset-intervals=20x)))=0A+=20=20=20=20(`(union=20.=20,xs)=20= (rx--charset-union=20xs))=0A+=20=20=20=20(`(intersection=20.=20,xs)=20= (rx--charset-intersection=20xs))=0A+=20=20=20=20(_=20(let=20((expanded=20= (rx--expand-def=20charset)))=0A+=20=20=20=20=20=20=20=20=20(if=20= expanded=0A+=20=20=20=20=20=20=20=20=20=20=20=20=20= (rx--charset-intervals=20expanded)=0A+=20=20=20=20=20=20=20=20=20=20=20= (error=20"Bad=20character=20set:=20%S"=20charset))))))=0A+=0A+(defun=20= rx--charset-union=20(charsets)=0A+=20=20"Union=20of=20CHARSETS,=20as=20a=20= set=20of=20intervals."=0A+=20=20(rx--foldl=20#'rx--union-intervals=20nil=0A= +=20=20=20=20=20=20=20=20=20=20=20=20=20(mapcar=20= #'rx--charset-intervals=20charsets)))=0A+=0A+(defconst=20rx--charset-all=20= (list=20(cons=200=20(max-char))))=0A+=0A+(defun=20= rx--charset-intersection=20(charsets)=0A+=20=20"Intersection=20of=20= CHARSETS,=20as=20a=20set=20of=20intervals."=0A+=20=20(rx--foldl=20= #'rx--intersect-intervals=20rx--charset-all=0A+=20=20=20=20=20=20=20=20=20= =20=20=20=20(mapcar=20#'rx--charset-intervals=20charsets)))=0A+=0A= +(defun=20rx--translate-union=20(negated=20body)=0A+=20=20"Translate=20a=20= (union=20...)=20construct.=20=20Return=20(REGEXP=20.=20PRECEDENCE).=0A= +If=20NEGATED,=20negate=20the=20sense."=0A+=20=20(rx--intervals-to-alt=20= negated=20(rx--charset-union=20body)))=0A+=0A+(defun=20= rx--translate-intersection=20(negated=20body)=0A+=20=20"Translate=20an=20= (intersection=20...)=20construct.=20=20Return=20(REGEXP=20.=20= PRECEDENCE).=0A+If=20NEGATED,=20negate=20the=20sense."=0A+=20=20= (rx--intervals-to-alt=20negated=20(rx--charset-intersection=20body)))=0A= +=0A=20(defun=20rx--atomic-regexp=20(item)=0A=20=20=20"ITEM=20is=20= (REGEXP=20.=20PRECEDENCE);=20return=20a=20regexp=20of=20precedence=20t."=0A= =20=20=20(if=20(eq=20(cdr=20item)=20t)=0A@@=20-862,6=20+976,8=20@@=20= rx--translate-form=0A=20=20=20=20=20=20=20((or=20'any=20'in=20'char)=20=20= =20=20=20=20(rx--translate-any=20nil=20body))=0A=20=20=20=20=20=20=20= ('not-char=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= (rx--translate-any=20t=20body))=0A=20=20=20=20=20=20=20('not=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20(rx--translate-not=20nil=20= body))=0A+=20=20=20=20=20=20('union=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20(rx--translate-union=20nil=20body))=0A+=20=20=20=20=20=20= ('intersection=20=20=20=20=20=20=20=20=20=20=20=20= (rx--translate-intersection=20nil=20body))=0A=20=0A=20=20=20=20=20=20=20= ('repeat=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= (rx--translate-repeat=20body))=0A=20=20=20=20=20=20=20('=3D=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20(rx--translate-=3D=20= body))=0A@@=20-920,7=20+1036,7=20@@=20rx--translate-form=0A=20=20=20=20=20= =20=20=20=20(t=20(error=20"Unknown=20rx=20form=20`%s'"=20op)))))))=0A=20=0A= =20(defconst=20rx--builtin-forms=0A-=20=20'(seq=20sequence=20:=20and=20= or=20|=20any=20in=20char=20not-char=20not=0A+=20=20'(seq=20sequence=20:=20= and=20or=20|=20any=20in=20char=20not-char=20not=20union=20intersection=0A= =20=20=20=20=20repeat=20=3D=20>=3D=20**=0A=20=20=20=20=20zero-or-more=20= 0+=20*=0A=20=20=20=20=20one-or-more=201+=20+=0Adiff=20--git=20= a/test/lisp/emacs-lisp/rx-tests.el=20b/test/lisp/emacs-lisp/rx-tests.el=0A= index=2026e39f8c8e..fdf8db61df=20100644=0A---=20= a/test/lisp/emacs-lisp/rx-tests.el=0A+++=20= b/test/lisp/emacs-lisp/rx-tests.el=0A@@=20-274,6=20+274,63=20@@=20rx-not=0A= =20=20=20(should=20(equal=20(rx=20(not=20(not=20ascii))=20(not=20(not=20= (not=20(any=20"a-z")))))=0A=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20"[[:ascii:]][^a-z]")))=0A=20=0A+(ert-deftest=20rx-union=20()=0A+=20= =20(should=20(equal=20(rx=20(union))=0A+=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20"\\`a\\`"))=0A+=20=20(should=20(equal=20(rx=20(union=20= (any=20"ba")))=0A+=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= "[ab]"))=0A+=20=20(should=20(equal=20(rx=20(union=20(any=20"a-f")=20(any=20= "c-k"=20?y)=20(any=20?r=20"x-z")))=0A+=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20"[a-krx-z]"))=0A+=20=20(should=20(equal=20(rx=20(union=20= (not=20(any=20"a-m"))=20(not=20(any=20"f-p"))))=0A+=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20"[^f-m]"))=0A+=20=20(should=20(equal=20(rx=20= (union=20(any=20"e-m")=20(not=20(any=20"a-z"))))=0A+=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20"[^a-dn-z]"))=0A+=20=20(should=20(equal=20= (rx=20(union=20(not=20(any=20"g-r"))=20(not=20(any=20"t"))))=0A+=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20"[^z-a]"))=0A+=20=20(should=20= (equal=20(rx=20(not=20(union=20(not=20(any=20"g-r"))=20(not=20(any=20= "t")))))=0A+=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= "\\`a\\`"))=0A+=20=20(should=20(equal=20(rx=20(union=20(union=20(any=20= "a-f")=20(any=20"u-z"))=0A+=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20(any=20"g-r")))=0A+=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20"[a-ru-z]"))=0A+=20=20(should=20(equal=20= (rx=20(union=20(intersection=20(any=20"c-z")=20(any=20"a-g"))=0A+=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= (not=20(any=20"a-k"))))=0A+=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20"[^abh-k]")))=0A+=0A+(ert-deftest=20rx-def-in-union=20()=0A+=20=20= (rx-let=20((a=20(any=20"badc"))=0A+=20=20=20=20=20=20=20=20=20=20=20(b=20= (union=20a=20(any=20"def"))))=0A+=20=20=20=20(should=20(equal(rx=20= (union=20b=20(any=20"q")))=0A+=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20"[a-fq]"))))=0A+=0A+(ert-deftest=20rx-intersection=20()=0A+=20=20= (should=20(equal=20(rx=20(intersection))=0A+=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20"[^z-a]"))=0A+=20=20(should=20(equal=20(rx=20= (intersection=20(any=20"ba")))=0A+=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20"[ab]"))=0A+=20=20(should=20(equal=20(rx=20(intersection=20= (any=20"a-j"=20"u-z")=20(any=20"c-k"=20?y)=0A+=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20(any=20"a-i"=20"x-z")))=0A+=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20"[c-iy]"))=0A+=20=20(should=20(equal=20(rx=20(intersection=20(not=20= (any=20"a-m"))=20(not=20(any=20"f-p"))))=0A+=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20"[^a-p]"))=0A+=20=20(should=20(equal=20(rx=20= (intersection=20(any=20"a-z")=20(not=20(any=20"g-q"))))=0A+=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20"[a-fr-z]"))=0A+=20=20(should=20= (equal=20(rx=20(intersection=20(any=20"a-d")=20(any=20"e")))=0A+=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20"\\`a\\`"))=0A+=20=20(should=20= (equal=20(rx=20(not=20(intersection=20(any=20"a-d")=20(any=20"e"))))=0A+=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20"[^z-a]"))=0A+=20=20= (should=20(equal=20(rx=20(intersection=20(any=20"d-u")=0A+=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20(intersection=20(any=20"e-z")=20(any=20"a-m"))))=0A+=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20"[e-m]"))=0A+=20=20(should=20= (equal=20(rx=20(intersection=20(union=20(any=20"a-f")=20(any=20"f-t"))=0A= +=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20(any=20"e-w")))=0A+=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20"[e-t]")))=0A+=0A+(ert-deftest=20= rx-def-in-intersection=20()=0A+=20=20(rx-let=20((a=20(any=20"a-g"))=0A+=20= =20=20=20=20=20=20=20=20=20=20(b=20(intersection=20a=20(any=20"d-j"))))=0A= +=20=20=20=20(should=20(equal(rx=20(intersection=20b=20(any=20"e-k")))=0A= +=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20"[e-g]"))))=0A+=0A= =20(ert-deftest=20rx-group=20()=0A=20=20=20(should=20(equal=20(rx=20= (group=20nonl)=20(submatch=20"x")=0A=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20(group-n=203=20"y")=20(submatch-n=2013=20"z")=20= (backref=201))=0A--=20=0A2.21.0=20(Apple=20Git-122.2)=0A=0A= --Apple-Mail=_C688033D-9F8B-4891-B400-E7544B217FCA-- From unknown Sat Aug 09 15:55:10 2025 X-Loop: help-debbugs@gnu.org Subject: bug#37849: composable character alternatives in rx Resent-From: Mattias =?UTF-8?Q?Engdeg=C3=A5rd?= Original-Sender: "Debbugs-submit" Resent-CC: bug-gnu-emacs@gnu.org Resent-Date: Mon, 09 Dec 2019 11:05:02 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 37849 X-GNU-PR-Package: emacs X-GNU-PR-Keywords: To: 37849@debbugs.gnu.org Cc: Eli Zaretskii Received: via spool by 37849-submit@debbugs.gnu.org id=B37849.157588948717713 (code B ref 37849); Mon, 09 Dec 2019 11:05:02 +0000 Received: (at 37849) by debbugs.gnu.org; 9 Dec 2019 11:04:47 +0000 Received: from localhost ([127.0.0.1]:52684 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1ieGqN-0004bc-Ce for submit@debbugs.gnu.org; Mon, 09 Dec 2019 06:04:47 -0500 Received: from mail73c50.megamailservers.eu ([91.136.10.83]:43432 helo=mail92c50.megamailservers.eu) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1ieGqL-0004bT-7e for 37849@debbugs.gnu.org; Mon, 09 Dec 2019 06:04:46 -0500 X-Authenticated-User: mattiase@bredband.net DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=megamailservers.eu; s=maildub; t=1575889483; bh=+KgMNTIxhb316eTOXXClTL9HkyILJB+woUw/PAEGxhs=; h=Subject:From:In-Reply-To:Date:Cc:References:To:From; b=hPg7TLWJ9uAGuEAdBXJs7mFNpVYT2BWMZsV2jrIccqRYDFD/MB9ydyyHj8g1LEZ2e BUZQN1zv+0wlmbN0M2eRpxPTEsdziqVbN02qcoUEgrcBJ4255wldy8MI5Ud4ZyRbU2 GmY4nOdxPLo1xRZVIaa9Qm1PdROcQBpuTllQ2Fnw= Feedback-ID: mattiase@acm.or Received: from stanniol.lan (c-6f4fe655.032-75-73746f71.bbcust.telenor.se [85.230.79.111]) (authenticated bits=0) by mail92c50.megamailservers.eu (8.14.9/8.13.1) with ESMTP id xB9B4e0b021480; Mon, 9 Dec 2019 11:04:42 +0000 Content-Type: text/plain; charset=us-ascii Mime-Version: 1.0 (Mac OS X Mail 12.4 \(3445.104.11\)) From: Mattias =?UTF-8?Q?Engdeg=C3=A5rd?= In-Reply-To: Date: Mon, 9 Dec 2019 12:04:40 +0100 Content-Transfer-Encoding: quoted-printable Message-Id: References: X-Mailer: Apple Mail (2.3445.104.11) X-CTCH-RefID: str=0001.0A0B0212.5DEE2A4B.000D, ss=1, re=0.000, recu=0.000, reip=0.000, cl=1, cld=1, fgs=0 X-CTCH-VOD: Unknown X-CTCH-Spam: Unknown X-CTCH-Score: 0.000 X-CTCH-Rules: X-CTCH-Flags: 0 X-CTCH-ScoreCust: 0.000 X-CSC: 0 X-CHA: v=2.3 cv=RJbN4Lq+ c=1 sm=1 tr=0 a=fHaj9vQUQVKQ4sUldAaXuQ==:117 a=fHaj9vQUQVKQ4sUldAaXuQ==:17 a=jpOVt7BSZ2e4Z31A5e1TngXxSK0=:19 a=kj9zAlcOel0A:10 a=M51BFTxLslgA:10 a=ElsIJCL--iEsu_lAR9QA:9 a=CjuIK1q_8ugA:10 X-Spam-Score: 0.3 (/) X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -0.7 (/) Eli, as a matter of protocol: assuming the union/intersection patch = meets no opposition, can it be pushed to master? It is self-contained = and should not affect anything outside rx. From unknown Sat Aug 09 15:55:10 2025 X-Loop: help-debbugs@gnu.org Subject: bug#37849: composable character alternatives in rx Resent-From: Eli Zaretskii Original-Sender: "Debbugs-submit" Resent-CC: bug-gnu-emacs@gnu.org Resent-Date: Mon, 09 Dec 2019 13:37:02 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 37849 X-GNU-PR-Package: emacs X-GNU-PR-Keywords: To: Mattias =?UTF-8?Q?Engdeg=C3=A5rd?= Cc: 37849@debbugs.gnu.org Received: via spool by 37849-submit@debbugs.gnu.org id=B37849.15758985998876 (code B ref 37849); Mon, 09 Dec 2019 13:37:02 +0000 Received: (at 37849) by debbugs.gnu.org; 9 Dec 2019 13:36:39 +0000 Received: from localhost ([127.0.0.1]:52776 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1ieJDL-0002J6-5H for submit@debbugs.gnu.org; Mon, 09 Dec 2019 08:36:39 -0500 Received: from eggs.gnu.org ([209.51.188.92]:43236) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1ieJDK-0002Is-1i for 37849@debbugs.gnu.org; Mon, 09 Dec 2019 08:36:38 -0500 Received: from fencepost.gnu.org ([2001:470:142:3::e]:33019) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ieJDD-0004Av-Of; Mon, 09 Dec 2019 08:36:31 -0500 Received: from [176.228.60.248] (port=2656 helo=home-c4e4a596f7) by fencepost.gnu.org with esmtpsa (TLS1.2:RSA_AES_256_CBC_SHA1:256) (Exim 4.82) (envelope-from ) id 1ieJDC-0003Jf-Gy; Mon, 09 Dec 2019 08:36:31 -0500 Date: Mon, 09 Dec 2019 15:36:15 +0200 Message-Id: <83immpd3jk.fsf@gnu.org> From: Eli Zaretskii In-reply-to: (message from Mattias =?UTF-8?Q?Engdeg=C3=A5rd?= on Mon, 9 Dec 2019 12:04:40 +0100) References: MIME-version: 1.0 Content-type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Spam-Score: -2.3 (--) X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -3.3 (---) > From: Mattias EngdegÄrd > Date: Mon, 9 Dec 2019 12:04:40 +0100 > Cc: Eli Zaretskii > > Eli, as a matter of protocol: assuming the union/intersection patch meets no opposition, can it be pushed to master? It is self-contained and should not affect anything outside rx. It's a new feature, so yes, assuming that you've verified it passes all the tests and cannot possibly interfere with any existing code. Thanks. From unknown Sat Aug 09 15:55:10 2025 X-Loop: help-debbugs@gnu.org Subject: bug#37849: composable character alternatives in rx Resent-From: Mattias =?UTF-8?Q?Engdeg=C3=A5rd?= Original-Sender: "Debbugs-submit" Resent-CC: bug-gnu-emacs@gnu.org Resent-Date: Tue, 10 Dec 2019 21:40:02 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 37849 X-GNU-PR-Package: emacs X-GNU-PR-Keywords: To: Eli Zaretskii Cc: 37849@debbugs.gnu.org Received: via spool by 37849-submit@debbugs.gnu.org id=B37849.15760139833100 (code B ref 37849); Tue, 10 Dec 2019 21:40:02 +0000 Received: (at 37849) by debbugs.gnu.org; 10 Dec 2019 21:39:43 +0000 Received: from localhost ([127.0.0.1]:56707 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1ienEN-0000nv-15 for submit@debbugs.gnu.org; Tue, 10 Dec 2019 16:39:43 -0500 Received: from mail75c50.megamailservers.eu ([91.136.10.85]:45930 helo=mail92c50.megamailservers.eu) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1ienEK-0000nk-H0 for 37849@debbugs.gnu.org; Tue, 10 Dec 2019 16:39:41 -0500 X-Authenticated-User: mattiase@bredband.net DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=megamailservers.eu; s=maildub; t=1576013978; bh=ZgVtmWaw0N7kjI3sAZaO/XQvqjM7MDnX/tvxgpwGIAA=; h=Subject:From:In-Reply-To:Date:Cc:References:To:From; b=p89BIU48UR8VB0Br1m4/4aQRKbVWWwVBFmXq1uYjfB2rTTQWmj/3bJXYS45CYTdrQ 1VhcqbzThjjZAUPQt8GBvO7GOaovzaDKj5ml29NttL8tTYNbEl4Ub4eKiXBsSoI/24 SPpDdE6DhSDgdU72hVgK6OVpwhq73o4EBwMttRQE= Feedback-ID: mattiase@acm.or Received: from stanniol.lan (c-6f4fe655.032-75-73746f71.bbcust.telenor.se [85.230.79.111]) (authenticated bits=0) by mail92c50.megamailservers.eu (8.14.9/8.13.1) with ESMTP id xBALdaqu011766; Tue, 10 Dec 2019 21:39:38 +0000 Content-Type: text/plain; charset=us-ascii Mime-Version: 1.0 (Mac OS X Mail 12.4 \(3445.104.11\)) From: Mattias =?UTF-8?Q?Engdeg=C3=A5rd?= In-Reply-To: <83immpd3jk.fsf@gnu.org> Date: Tue, 10 Dec 2019 22:39:35 +0100 Content-Transfer-Encoding: 7bit Message-Id: <61EF2B41-9B48-4694-BDDB-D1ED66309DC1@acm.org> References: <83immpd3jk.fsf@gnu.org> X-Mailer: Apple Mail (2.3445.104.11) X-CTCH-RefID: str=0001.0A0B020A.5DF0109A.003C, ss=1, re=0.000, recu=0.000, reip=0.000, cl=1, cld=1, fgs=0 X-CTCH-VOD: Unknown X-CTCH-Spam: Unknown X-CTCH-Score: 0.000 X-CTCH-Rules: X-CTCH-Flags: 0 X-CTCH-ScoreCust: 0.000 X-CSC: 0 X-CHA: v=2.3 cv=RJbN4Lq+ c=1 sm=1 tr=0 a=fHaj9vQUQVKQ4sUldAaXuQ==:117 a=fHaj9vQUQVKQ4sUldAaXuQ==:17 a=jpOVt7BSZ2e4Z31A5e1TngXxSK0=:19 a=kj9zAlcOel0A:10 a=M51BFTxLslgA:10 a=141-EEKQ1Ove4T2_unYA:9 a=CjuIK1q_8ugA:10 X-Spam-Score: 0.3 (/) X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -0.7 (/) Thank you, now pushed. From unknown Sat Aug 09 15:55:10 2025 MIME-Version: 1.0 X-Mailer: MIME-tools 5.505 (Entity 5.505) X-Loop: help-debbugs@gnu.org From: help-debbugs@gnu.org (GNU bug Tracking System) To: Mattias =?UTF-8?Q?Engdeg=C3=A5rd?= Subject: bug#37849: closed (Re: bug#37849: composable character alternatives in rx ) Message-ID: References: <0F77D35E-52A7-4332-913E-DDD286461F00@acm.org> X-Gnu-PR-Message: they-closed 37849 X-Gnu-PR-Package: emacs Reply-To: 37849@debbugs.gnu.org Date: Fri, 13 Dec 2019 12:37:02 +0000 Content-Type: multipart/mixed; boundary="----------=_1576240622-25138-1" This is a multi-part message in MIME format... ------------=_1576240622-25138-1 Content-Disposition: inline Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Your bug report #37849: composable character alternatives in rx which was filed against the emacs package, has been closed. The explanation is attached below, along with your original report. If you require more details, please reply to 37849@debbugs.gnu.org. --=20 37849: http://debbugs.gnu.org/cgi/bugreport.cgi?bug=3D37849 GNU Bug Tracking System Contact help-debbugs@gnu.org with problems ------------=_1576240622-25138-1 Content-Type: message/rfc822 Content-Disposition: inline Content-Transfer-Encoding: 7bit Received: (at 37849-done) by debbugs.gnu.org; 13 Dec 2019 12:36:22 +0000 Received: from localhost ([127.0.0.1]:33147 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1ifkBC-0006Wc-HG for submit@debbugs.gnu.org; Fri, 13 Dec 2019 07:36:22 -0500 Received: from mail1452c50.megamailservers.eu ([91.136.14.52]:37172 helo=mail266c50.megamailservers.eu) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1ifkB8-0006WK-AQ for 37849-done@debbugs.gnu.org; Fri, 13 Dec 2019 07:36:21 -0500 X-Authenticated-User: mattiase@bredband.net DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=megamailservers.eu; s=maildub; t=1576240565; bh=zvLqZhnOvzENhKnfsaw1rg+VBXGQKHVZdNkvU0Faa1g=; h=Subject:From:In-Reply-To:Date:Cc:References:To:From; b=U1tkOmCpFrlQmDA0r4SgH77qbcUe2tZFYSnQGIwFAe0FiHxbv5++5C2pl9ZBJSQAV 9UWuYQv3R8W5Tgio+Z+A0SvEdlbGGDO36VAQcFhZ+A0Gth/SO+yrLhpBxCDBIR29t9 XxVCvEfmHQQqQS5Gtx0vm6o4Nnag3i+IGPqtJhVs= Feedback-ID: mattiase@acm.or Received: from stanniol.lan (c-6f4fe655.032-75-73746f71.bbcust.telenor.se [85.230.79.111]) (authenticated bits=0) by mail266c50.megamailservers.eu (8.14.9/8.13.1) with ESMTP id xBDCZhva019512; Fri, 13 Dec 2019 12:36:05 +0000 Content-Type: text/plain; charset=us-ascii Mime-Version: 1.0 (Mac OS X Mail 12.4 \(3445.104.11\)) Subject: Re: bug#37849: composable character alternatives in rx From: =?utf-8?Q?Mattias_Engdeg=C3=A5rd?= In-Reply-To: <61EF2B41-9B48-4694-BDDB-D1ED66309DC1@acm.org> Date: Fri, 13 Dec 2019 13:35:42 +0100 Content-Transfer-Encoding: quoted-printable Message-Id: References: <83immpd3jk.fsf@gnu.org> <61EF2B41-9B48-4694-BDDB-D1ED66309DC1@acm.org> To: Stefan Monnier X-Mailer: Apple Mail (2.3445.104.11) X-CTCH-RefID: str=0001.0A0B020F.5DF385B5.003A, ss=1, re=0.000, recu=0.000, reip=0.000, cl=1, cld=1, fgs=0 X-CTCH-VOD: Unknown X-CTCH-Spam: Unknown X-CTCH-Score: 0.000 X-CTCH-Rules: X-CTCH-Flags: 0 X-CTCH-ScoreCust: 0.000 X-CSC: 0 X-CHA: v=2.3 cv=PNJxBsiC c=1 sm=1 tr=0 a=fHaj9vQUQVKQ4sUldAaXuQ==:117 a=fHaj9vQUQVKQ4sUldAaXuQ==:17 a=jpOVt7BSZ2e4Z31A5e1TngXxSK0=:19 a=kj9zAlcOel0A:10 a=M51BFTxLslgA:10 a=hPPfrGLNHUItjIo_gXwA:9 a=CjuIK1q_8ugA:10 X-Spam-Score: 1.4 (+) X-Spam-Report: Spam detection software, running on the system "debbugs.gnu.org", has NOT identified this incoming email as spam. The original message has been attached to this so you can view it or label similar future email. If you have any questions, see the administrator of that system for details. Content preview: As suggested by Stefan Monnier, 'union' was replaced with plain 'or' for character sets as well. A minor usability improvement has been pushed to master as well: characters and single-char strings no longer have to be wrapped in (any...), so (not (any ?a)) can now be written (not ?a). Content analysis details: (1.4 points, 10.0 required) pts rule name description ---- ---------------------- -------------------------------------------------- 0.0 URIBL_BLOCKED ADMINISTRATOR NOTICE: The query to URIBL was blocked. See http://wiki.apache.org/spamassassin/DnsBlocklists#dnsbl-block for more information. [URIs: megamailservers.eu] 1.0 SPF_SOFTFAIL SPF: sender does not match SPF record (softfail) 0.0 SPF_HELO_NONE SPF: HELO does not publish an SPF Record 0.4 KHOP_HELO_FCRDNS Relay HELO differs from its IP's reverse DNS X-Debbugs-Envelope-To: 37849-done Cc: 37849-done@debbugs.gnu.org X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -0.0 (/) As suggested by Stefan Monnier, 'union' was replaced with plain 'or' for = character sets as well. A minor usability improvement has been pushed to master as well: = characters and single-char strings no longer have to be wrapped in = (any...), so (not (any ?a)) can now be written (not ?a). ------------=_1576240622-25138-1 Content-Type: message/rfc822 Content-Disposition: inline Content-Transfer-Encoding: 7bit Received: (at submit) by debbugs.gnu.org; 21 Oct 2019 10:24:35 +0000 Received: from localhost ([127.0.0.1]:55773 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1iMUra-0005UF-PG for submit@debbugs.gnu.org; Mon, 21 Oct 2019 06:24:35 -0400 Received: from lists.gnu.org ([209.51.188.17]:35453) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1iMUrY-0005U8-D0 for submit@debbugs.gnu.org; Mon, 21 Oct 2019 06:24:32 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:42263) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1iMUrW-0001zR-Ny for bug-gnu-emacs@gnu.org; Mon, 21 Oct 2019 06:24:32 -0400 X-Spam-Checker-Version: SpamAssassin 3.3.2 (2011-06-06) on eggs.gnu.org X-Spam-Level: X-Spam-Status: No, score=0.1 required=5.0 tests=BAYES_50,RCVD_IN_DNSWL_LOW, URIBL_BLOCKED autolearn=disabled version=3.3.2 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1iMUrV-000896-73 for bug-gnu-emacs@gnu.org; Mon, 21 Oct 2019 06:24:30 -0400 Received: from mail80c50.megamailservers.eu ([91.136.10.90]:35876 helo=mail70c50.megamailservers.eu) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1iMUrU-00088U-Hi for bug-gnu-emacs@gnu.org; Mon, 21 Oct 2019 06:24:29 -0400 X-Authenticated-User: mattiase@bredband.net DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=megamailservers.eu; s=maildub; t=1571653463; bh=MS/WJe3Oxd/r0y2NU7gp+9E+RjbxurZ8DttwaqM7+dU=; h=From:Subject:Date:To:From; b=GHa5iJLH4b/AvesfV1nJ7Kyg76RgGD9Qw335C9cOQVub3PF89cbM9CY4O+Ycogx4r AZ2B0BH1U9I6cdyjlTGY194VmPlAFMqJxweSSmcOQlrVFSXWS3REDQo46jljDc+9FJ b0ATyFWfV2oLlR8neOkLoFjKZWLpeZIJmUCtoJp4= Feedback-ID: mattiase@acm.or Received: from [192.168.1.64] (c-e636e253.032-75-73746f71.bbcust.telenor.se [83.226.54.230]) (authenticated bits=0) by mail70c50.megamailservers.eu (8.14.9/8.13.1) with ESMTP id x9LAOLiZ026603 for ; Mon, 21 Oct 2019 10:24:23 +0000 From: =?utf-8?Q?Mattias_Engdeg=C3=A5rd?= Content-Type: multipart/mixed; boundary="Apple-Mail=_A5D7A71E-F7D3-4086-A2E5-8E608F8FFC42" Mime-Version: 1.0 (Mac OS X Mail 12.4 \(3445.104.11\)) Subject: composable character alternatives in rx Message-Id: <0F77D35E-52A7-4332-913E-DDD286461F00@acm.org> Date: Mon, 21 Oct 2019 12:24:21 +0200 To: bug-gnu-emacs@gnu.org X-Mailer: Apple Mail (2.3445.104.11) X-CTCH-RefID: str=0001.0A0B0204.5DAD8757.0041, ss=1, re=0.000, recu=0.000, reip=0.000, cl=1, cld=1, fgs=0 X-CTCH-VOD: Unknown X-CTCH-Spam: Unknown X-CTCH-Score: 0.000 X-CTCH-Rules: X-CTCH-Flags: 0 X-CTCH-ScoreCust: 0.000 X-CSC: 0 X-CHA: v=2.3 cv=c6bVvi1l c=1 sm=1 tr=0 a=M+GU/qJco4WXjv8D6jB2IA==:117 a=M+GU/qJco4WXjv8D6jB2IA==:17 a=M51BFTxLslgA:10 a=OqN6m0tyAAAA:8 a=UhdY8sGSxoVGZ3PE4iEA:9 a=CjuIK1q_8ugA:10 a=zQ3WUrxWzkcA:10 a=NP-Ykj6oU4nmw8ZbQoEA:9 a=B2y7HmGcmWMA:10 a=MNNCb3fd_EpUFEaGHBWG:22 X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x (no timestamps) [generic] [fuzzy] X-Received-From: 91.136.10.90 X-Spam-Score: -1.3 (-) X-Debbugs-Envelope-To: submit X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -2.3 (--) --Apple-Mail=_A5D7A71E-F7D3-4086-A2E5-8E608F8FFC42 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset=us-ascii Now that rx is user-extendible, some holes are showing. Example (from = python.el): (simple-operator . ,(rx (any ?+ ?- ?/ ?& ?^ ?~ ?| ?* ?< ?> ?=3D= ?%))) ;; FIXME: rx should support (not simple-operator). (not-simple-operator . ,(rx (not (any ?+ ?- ?/ ?& ?^ ?~ ?| ?* ?< ?> ?=3D = ?%)))) (This code uses the old rx-constituents mechanism, but the point applies = equally to new-style definitions.) More generally, there is currently no way to: (1) Get the complement of a defined (any ...) form (2) Get the union of two defined (any ...) forms (3) Get the intersection of two defined (not (any ...)) forms (1), which the example above was about, could be solved by expanding = definitions inside 'not'. This is a step away from the principle that = user-defined things are only allowed where general rx forms are, but = perhaps tolerable. Proposed patch attached. (2) can be solved by expanding definitions inside 'any', and allowing = 'any' inside 'any' (flattening). Not sure I like this. An alternative is to ensure that (or (any X) (any Y)) -> (any X Y), but = then we either need to allow 'or' inside 'not', or add an intersection = operator: (intersect (not (any X)) (not (any Y)) -> (not (any X Y)) We could also make 'not' variadic, turning it into complement-of-union: (not (any A) (any B)) -> (not (any A B)) Olin Shivers's SRE has a complete and closed set of operations on = character sets (https://scsh.net/docu/post/sre.html). That would be = principled and perhaps useful, but difficult to do fully in rx because = not all such expressions can be rendered into Emacs regexps. Nothing = prevents us from making a partial implementation, however. --Apple-Mail=_A5D7A71E-F7D3-4086-A2E5-8E608F8FFC42 Content-Disposition: attachment; filename=0001-Expand-rx-definitions-inside-not.patch Content-Type: application/octet-stream; x-unix-mode=0644; name="0001-Expand-rx-definitions-inside-not.patch" Content-Transfer-Encoding: quoted-printable =46rom=20a2f7d4fbe0b1d37c233e0beffc4b2b8fd4df3013=20Mon=20Sep=2017=20= 00:00:00=202001=0AFrom:=20=3D?UTF-8?q?Mattias=3D20Engdeg=3DC3=3DA5rd?=3D=20= =0ADate:=20Fri,=2018=20Oct=202019=2016:03:20=20+0200=0A= Subject:=20[PATCH]=20Expand=20rx=20definitions=20inside=20(not=20...)=0A=0A= *=20lisp/emacs-lisp/rx.el=20(rx--lookup-def,=20rx--expand-def)=0A= (rx--translate-symbol,=20rx--translate-any,=20rx--translate-form):=0A*=20= test/lisp/emacs-lisp/rx-tests.el=20(rx-not,=20rx-def-in-not):=0A*=20= doc/lispref/searching.texi=20(Rx=20Constructs,=20Extending=20Rx):=0A= Allow=20user-defined=20rx=20constructs=20to=20be=20expanded=20inside=20= (not=20...)=0Aforms,=20for=20better=20composability.=0A---=0A=20= doc/lispref/searching.texi=20=20=20=20=20=20=20|=20=20=204=20+-=0A=20= lisp/emacs-lisp/rx.el=20=20=20=20=20=20=20=20=20=20=20=20|=20100=20= ++++++++++++++++++-------------=0A=20test/lisp/emacs-lisp/rx-tests.el=20= |=20=2017=20+++++-=0A=203=20files=20changed,=2077=20insertions(+),=2044=20= deletions(-)=0A=0Adiff=20--git=20a/doc/lispref/searching.texi=20= b/doc/lispref/searching.texi=0Aindex=205178575a3b..74b15cfc7f=20100644=0A= ---=20a/doc/lispref/searching.texi=0A+++=20b/doc/lispref/searching.texi=0A= @@=20-1214,7=20+1214,7=20@@=20Rx=20Constructs=0A=20@item=20@code{(not=20= @var{charspec})}=0A=20@cindex=20@code{not}=20in=20rx=0A=20Match=20a=20= character=20not=20included=20in=20@var{charspec}.=20=20@var{charspec}=20= can=0A-be=20an=20@code{any},=20@code{syntax}=20or=20@code{category}=20= form,=20or=20a=0A+be=20an=20@code{any},=20@code{not},=20@code{syntax}=20= or=20@code{category}=20form,=20or=20a=0A=20character=20class.@*=0A=20= Corresponding=20string=20regexp:=20@samp{[^@dots{}]},=20= @samp{\S@var{code}},=0A=20@samp{\C@var{code}}=0A@@=20-1581,7=20+1581,7=20= @@=20Extending=20Rx=0A=20User-defined=20forms=20are=20allowed=20wherever=20= arbitrary=20@code{rx}=0A=20expressions=20are=20expected;=20for=20= example,=20in=20the=20body=20of=20a=0A=20@code{zero-or-one}=20form,=20= but=20not=20inside=20@code{any}=20or=20@code{category}=0A-forms.=0A= +forms.=20=20They=20are=20also=20allowed=20inside=20@code{not}=20forms.=0A= =20@end=20itemize=0A=20=0A=20@defmac=20rx-define=20name=20[arglist]=20= rx-form=0Adiff=20--git=20a/lisp/emacs-lisp/rx.el=20= b/lisp/emacs-lisp/rx.el=0Aindex=20006a393921..8d8db5f3c4=20100644=0A---=20= a/lisp/emacs-lisp/rx.el=0A+++=20b/lisp/emacs-lisp/rx.el=0A@@=20-122,9=20= +122,27=20@@=20rx--local-definitions=0A=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20as=20the=20rx=20form=20DEF=20(which=20can=20= contain=20members=20of=20ARGS).")=0A=20=0A=20(defsubst=20rx--lookup-def=20= (name)=0A+=20=20"Current=20definition=20of=20NAME:=20(DEF)=20or=20(ARGS=20= DEF),=20or=20nil=20if=20none."=0A=20=20=20(or=20(cdr=20(assq=20name=20= rx--local-definitions))=0A=20=20=20=20=20=20=20(get=20name=20= 'rx-definition)))=0A=20=0A+(defun=20rx--expand-def=20(form)=0A+=20=20= "FORM=20expanded=20(once)=20if=20a=20user-defined=20construct;=20= otherwise=20nil."=0A+=20=20(cond=20((symbolp=20form)=0A+=20=20=20=20=20=20= =20=20=20(let=20((def=20(rx--lookup-def=20form)))=0A+=20=20=20=20=20=20=20= =20=20=20=20(and=20def=0A+=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= (if=20(cdr=20def)=0A+=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20(error=20"Not=20an=20`rx'=20symbol=20definition:=20%s"=20form)=0A+=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20(car=20def)))))=0A+=20= =20=20=20=20=20=20=20((consp=20form)=0A+=20=20=20=20=20=20=20=20=20(let*=20= ((op=20(car=20form))=0A+=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= (def=20(rx--lookup-def=20op)))=0A+=20=20=20=20=20=20=20=20=20=20=20(and=20= def=0A+=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20(if=20(cdr=20def)=0A= +=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= (rx--expand-template=0A+=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20op=20(cdr=20form)=20(nth=200=20def)=20(nth=201=20def))=0A+=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20(error=20"Not=20an=20= `rx'=20form=20definition:=20%s"=20op)))))))=0A+=0A=20;;=20TODO:=20= Additions=20to=20consider:=0A=20;;=20-=20A=20construct=20like=20`or'=20= but=20without=20the=20match=20order=20guarantee,=0A=20;;=20=20=20maybe=20= `unordered-or'.=20=20Useful=20for=20composition=20or=20generation=20of=0A= @@=20-155,11=20+173,8=20@@=20rx--translate-symbol=0A=20=20=20=20=20=20=20= ((let=20((class=20(cdr=20(assq=20sym=20rx--char-classes))))=0A=20=20=20=20= =20=20=20=20=20=20(and=20class=20(cons=20(list=20(concat=20"[[:"=20= (symbol-name=20class)=20":]]"))=20t))))=0A=20=0A-=20=20=20=20=20=20((let=20= ((definition=20(rx--lookup-def=20sym)))=0A-=20=20=20=20=20=20=20=20=20= (and=20definition=0A-=20=20=20=20=20=20=20=20=20=20=20=20=20=20(if=20= (cdr=20definition)=0A-=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20(error=20"Not=20an=20`rx'=20symbol=20definition:=20%s"=20sym)=0A-=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20(rx--translate=20(nth=200=20= definition))))))=0A+=20=20=20=20=20=20((let=20((expanded=20= (rx--expand-def=20sym)))=0A+=20=20=20=20=20=20=20=20=20(and=20expanded=20= (rx--translate=20expanded))))=0A=20=0A=20=20=20=20=20=20=20;;=20For=20= compatibility=20with=20old=20rx.=0A=20=20=20=20=20=20=20((let=20((entry=20= (assq=20sym=20rx-constituents)))=0A@@=20-445,21=20+460,26=20@@=20= rx--translate-not=0A=20=20=20=20=20(error=20"rx=20`not'=20form=20takes=20= exactly=20one=20argument"))=0A=20=20=20(let=20((arg=20(car=20body)))=0A=20= =20=20=20=20(cond=0A-=20=20=20=20=20((consp=20arg)=0A-=20=20=20=20=20=20= (pcase=20(car=20arg)=0A-=20=20=20=20=20=20=20=20((or=20'any=20'in=20= 'char)=20(rx--translate-any=20=20=20=20=20=20(not=20negated)=20(cdr=20= arg)))=0A-=20=20=20=20=20=20=20=20('syntax=20=20=20=20=20=20=20=20=20=20=20= =20=20(rx--translate-syntax=20=20=20(not=20negated)=20(cdr=20arg)))=0A-=20= =20=20=20=20=20=20=20('category=20=20=20=20=20=20=20=20=20=20=20= (rx--translate-category=20(not=20negated)=20(cdr=20arg)))=0A-=20=20=20=20= =20=20=20=20('not=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= (rx--translate-not=20=20=20=20=20=20(not=20negated)=20(cdr=20arg)))=0A-=20= =20=20=20=20=20=20=20(_=20(error=20"Illegal=20argument=20to=20rx=20= `not':=20%S"=20arg))))=0A+=20=20=20=20=20((and=20(consp=20arg)=0A+=20=20=20= =20=20=20=20=20=20=20=20(pcase=20(car=20arg)=0A+=20=20=20=20=20=20=20=20=20= =20=20=20=20((or=20'any=20'in=20'char)=0A+=20=20=20=20=20=20=20=20=20=20=20= =20=20=20(rx--translate-any=20=20=20=20=20=20(not=20negated)=20(cdr=20= arg)))=0A+=20=20=20=20=20=20=20=20=20=20=20=20=20('syntax=0A+=20=20=20=20= =20=20=20=20=20=20=20=20=20=20(rx--translate-syntax=20=20=20(not=20= negated)=20(cdr=20arg)))=0A+=20=20=20=20=20=20=20=20=20=20=20=20=20= ('category=0A+=20=20=20=20=20=20=20=20=20=20=20=20=20=20= (rx--translate-category=20(not=20negated)=20(cdr=20arg)))=0A+=20=20=20=20= =20=20=20=20=20=20=20=20=20('not=0A+=20=20=20=20=20=20=20=20=20=20=20=20=20= =20(rx--translate-not=20=20=20=20=20=20(not=20negated)=20(cdr=20= arg))))))=0A+=20=20=20=20=20((let=20((class=20(cdr=20(assq=20arg=20= rx--char-classes))))=0A+=20=20=20=20=20=20=20=20(and=20class=0A+=20=20=20= =20=20=20=20=20=20=20=20=20=20(rx--translate-any=20(not=20negated)=20= (list=20class)))))=0A=20=20=20=20=20=20((eq=20arg=20'word-boundary)=0A=20= =20=20=20=20=20=20(rx--translate-symbol=0A=20=20=20=20=20=20=20=20(if=20= negated=20'word-boundary=20'not-word-boundary)))=0A-=20=20=20=20=20(t=0A= -=20=20=20=20=20=20(let=20((class=20(cdr=20(assq=20arg=20= rx--char-classes))))=0A-=20=20=20=20=20=20=20=20(if=20class=0A-=20=20=20=20= =20=20=20=20=20=20=20=20(rx--translate-any=20(not=20negated)=20(list=20= class))=0A-=20=20=20=20=20=20=20=20=20=20(error=20"Illegal=20argument=20= to=20rx=20`not':=20%s"=20arg)))))))=0A+=20=20=20=20=20((let=20((expanded=20= (rx--expand-def=20arg)))=0A+=20=20=20=20=20=20=20=20(and=20expanded=0A+=20= =20=20=20=20=20=20=20=20=20=20=20=20(rx--translate-not=20negated=20(list=20= expanded)))))=0A+=20=20=20=20=20(t=20(error=20"Illegal=20argument=20to=20= rx=20`not':=20%S"=20arg)))))=0A=20=0A=20(defun=20rx--atomic-regexp=20= (item)=0A=20=20=20"ITEM=20is=20(REGEXP=20.=20PRECEDENCE);=20return=20a=20= regexp=20of=20precedence=20t."=0A@@=20-873,30=20+893,28=20@@=20= rx--translate-form=0A=20=20=20=20=20=20=20((or=20'regexp=20'regex)=20=20=20= =20=20=20(rx--translate-regexp=20body))=0A=20=0A=20=20=20=20=20=20=20(op=0A= -=20=20=20=20=20=20=20(unless=20(symbolp=20op)=0A-=20=20=20=20=20=20=20=20= =20(error=20"Bad=20rx=20operator=20`%S'"=20op))=0A-=20=20=20=20=20=20=20= (let=20((definition=20(rx--lookup-def=20op)))=0A-=20=20=20=20=20=20=20=20= =20(if=20definition=0A-=20=20=20=20=20=20=20=20=20=20=20=20=20(if=20(cdr=20= definition)=0A-=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= (rx--translate=0A-=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= (rx--expand-template=0A-=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20op=20body=20(nth=200=20definition)=20(nth=201=20definition)))=0A-=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20(error=20"Not=20an=20`rx'=20= form=20definition:=20%s"=20op))=0A-=0A-=20=20=20=20=20=20=20=20=20=20=20= ;;=20For=20compatibility=20with=20old=20rx.=0A-=20=20=20=20=20=20=20=20=20= =20=20(let=20((entry=20(assq=20op=20rx-constituents)))=0A-=20=20=20=20=20= =20=20=20=20=20=20=20=20(if=20(progn=0A-=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20(while=20(and=20entry=20(not=20(consp=20(cdr=20= entry))))=0A-=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20(setq=20entry=0A-=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20(if=20(symbolp=20(cdr=20entry))=0A-=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20;;=20Alias=20for=20another=20entry.=0A-=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20(assq=20= (cdr=20entry)=20rx-constituents)=0A-=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20;;=20Wrong=20type,=20try=20= further=20down=20the=20list.=0A-=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20(assq=20(car=20entry)=0A-=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20(cdr=20(memq=20entry=20rx-constituents))))))=0A= -=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20entry)=0A-=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20(rx--translate-compat-form=20= (cdr=20entry)=20form)=0A-=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= (error=20"Unknown=20rx=20form=20`%s'"=20op)))))))))=0A+=20=20=20=20=20=20= =20(cond=0A+=20=20=20=20=20=20=20=20((not=20(symbolp=20op))=20(error=20= "Bad=20rx=20operator=20`%S'"=20op))=0A+=0A+=20=20=20=20=20=20=20=20((let=20= ((expanded=20(rx--expand-def=20form)))=0A+=20=20=20=20=20=20=20=20=20=20=20= (and=20expanded=0A+=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= (rx--translate=20expanded))))=0A+=0A+=20=20=20=20=20=20=20=20;;=20For=20= compatibility=20with=20old=20rx.=0A+=20=20=20=20=20=20=20=20((let=20= ((entry=20(assq=20op=20rx-constituents)))=0A+=20=20=20=20=20=20=20=20=20=20= =20(and=20(progn=0A+=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= (while=20(and=20entry=20(not=20(consp=20(cdr=20entry))))=0A+=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20(setq=20entry=0A+=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20(if=20= (symbolp=20(cdr=20entry))=0A+=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20;;=20Alias=20for=20another=20= entry.=0A+=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20(assq=20(cdr=20entry)=20rx-constituents)=0A+=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20;;=20Wrong=20type,=20try=20further=20down=20the=20list.=0A+=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= (assq=20(car=20entry)=0A+=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20(cdr=20(memq=20= entry=20rx-constituents))))))=0A+=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20entry)=0A+=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= (rx--translate-compat-form=20(cdr=20entry)=20form))))=0A+=0A+=20=20=20=20= =20=20=20=20(t=20(error=20"Unknown=20rx=20form=20`%s'"=20op)))))))=0A=20=0A= =20(defconst=20rx--builtin-forms=0A=20=20=20'(seq=20sequence=20:=20and=20= or=20|=20any=20in=20char=20not-char=20not=0Adiff=20--git=20= a/test/lisp/emacs-lisp/rx-tests.el=20b/test/lisp/emacs-lisp/rx-tests.el=0A= index=20ef2541d83a..4ecc805aea=20100644=0A---=20= a/test/lisp/emacs-lisp/rx-tests.el=0A+++=20= b/test/lisp/emacs-lisp/rx-tests.el=0A@@=20-268,7=20+268,9=20@@=20rx-not=0A= =20=20=20(should=20(equal=20(rx=20(not=20(syntax=20punctuation))=20(not=20= (syntax=20escape)))=0A=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20"\\S.\\S\\"))=0A=20=20=20(should=20(equal=20(rx=20(not=20(category=20= tone-mark))=20(not=20(category=20lao)))=0A-=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20"\\C4\\Co")))=0A+=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20"\\C4\\Co"))=0A+=20=20(should=20(equal=20(rx=20(not=20= (not=20ascii))=20(not=20(not=20(not=20(any=20"a-z")))))=0A+=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20"[[:ascii:]][^a-z]")))=0A=20=0A=20= (ert-deftest=20rx-group=20()=0A=20=20=20(should=20(equal=20(rx=20(group=20= nonl)=20(submatch=20"x")=0A@@=20-404,6=20+406,19=20@@=20= rx-redefine-builtin=0A=20=20=20(should-error=20(rx-let-eval=20= '((not-char=20()=20"x"))=20nil))=0A=20=20=20(should-error=20(rx-let-eval=20= '((not-char=20"x"))=20nil)))=0A=20=0A+(ert-deftest=20rx-def-in-not=20()=0A= +=20=20"Test=20definition=20expansion=20inside=20(not=20...)."=0A+=20=20= (rx-let=20((a=20alpha)=0A+=20=20=20=20=20=20=20=20=20=20=20(b=20(not=20= hex))=0A+=20=20=20=20=20=20=20=20=20=20=20(c=20(not=20(category=20= base)))=0A+=20=20=20=20=20=20=20=20=20=20=20(d=20(x)=20(any=20?a=20x=20= ?z))=0A+=20=20=20=20=20=20=20=20=20=20=20(e=20(x)=20(syntax=20x))=0A+=20=20= =20=20=20=20=20=20=20=20=20(f=20(not=20b)))=0A+=20=20=20=20(should=20= (equal=20(rx=20(not=20a)=20(not=20b)=20(not=20c)=20(not=20f))=0A+=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= "[^[:alpha:]][[:xdigit:]]\\c.[^[:xdigit:]]"))=0A+=20=20=20=20(should=20= (equal=20(rx=20(not=20(d=20?m))=20(not=20(e=20symbol)))=0A+=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20"[^amz]\\S_"))))=0A+=0A=20= (ert-deftest=20rx-constituents=20()=0A=20=20=20(let=20((rx-constituents=0A= =20=20=20=20=20=20=20=20=20=20(append=20'((beta=20.=20gamma)=0A--=20=0A= 2.21.0=20(Apple=20Git-122)=0A=0A= --Apple-Mail=_A5D7A71E-F7D3-4086-A2E5-8E608F8FFC42-- ------------=_1576240622-25138-1--