diff options
-rw-r--r-- | share/txr/stdlib/match.tl | 23 | ||||
-rw-r--r-- | txr.1 | 135 |
2 files changed, 148 insertions, 10 deletions
diff --git a/share/txr/stdlib/match.tl b/share/txr/stdlib/match.tl index fa0ccb80..df143dac 100644 --- a/share/txr/stdlib/match.tl +++ b/share/txr/stdlib/match.tl @@ -616,6 +616,8 @@ (sys:var (compile-var-match (cadr pat) obj-var var-list)) (sys:quasi (compile-match (expand-quasi-match (cdr pat) var-list) obj-var var-list)) + (sys:qquote (compile-match (transform-qquote (cadr pat)) + obj-var var-list)) (t (if (non-triv-pat-p pat) (compile-cons-structure pat obj-var var-list) (compile-atom-match pat obj-var var-list))))) @@ -844,6 +846,7 @@ ((@(eq 'sys:expr) (@(bindable) . @nil)) t) ((@(eq 'sys:var) @(or @(bindable) nil) . @nil) t) ((@(eq 'sys:quasi) . @(some @(consp))) t) + ((@(eq 'sys:qquote) @nil) t) ((@pat . @rest) (or (non-triv-pat-p pat) (non-triv-pat-p rest))) (#R(@from @to) (or (non-triv-pat-p from) @@ -984,6 +987,26 @@ @(with ,pos 0) ,*(quasi-match var-list (normalize args) nil str pos))))) +(defun transform-qquote (syn) + (match-case syn + ((sys:hash-lit @props . @(coll (@key @val))) + (if props + (error "~s: only equal hash tables supported" syn) + ^@(hash ,*(zip [mapcar transform-qquote key] + [mapcar transform-qquote val])))) + ((sys:struct-lit @type . @args) + ^@(struct ,(transform-qquote type) + ,*[mapcar transform-qquote args])) + ((sys:vector-lit @elems) + ^#(,*[mapcar transform-qquote elems])) + ((json quote @arg) (transform-qquote arg)) + ((sys:unquote @pat) (if (symbolp pat) + ^(sys:var ,pat) + ^(sys:expr ,pat))) + ((@ca . @cd) (cons (transform-qquote ca) + (transform-qquote cd))) + (@else else))) + (defun each-match-expander (f pat-seq-list body fun) (unless (and (proper-list-p pat-seq-list) (evenp (len pat-seq-list))) @@ -40724,23 +40724,45 @@ as well as useful pattern matching operators like which matches a list or sublist whose elements all match .metn pattern . +The quasiquote syntax is specially supported for expressing matching, +in an alternative style. For instance the quasiquote +.code "^(1 2 ,a)" +is a pattern equivalent to the +.codn "(1 2 @a)" . + Structure objects are matched using a dedicated .code "@(struct name ...)" -operator rather than using +operator, or else in the quasiquote style using +.code "^#S(name ...)" +syntax. The non-quasiquoted literal syntax .code "#S(name ...)" -notation. The reason is that a struct literal produces an object -which loses information about how it was specified in the literal syntax, -but those details are critically important in pattern matching. +cannot be used for matching. Similarly, hash objects are matched using a .code "@(hash ...)" -operator rather than +operator, or else +.code "^#H(...)" +syntax in the quasiquote style. .code "#H(...)" -syntax. The reason is that the keys are not ordered in a hash -table, whereas the order of subpatterns in a pattern operator -can be significant. One subpattern may be expected to produce -a match for a variable, which is then back-referenced in another -subpattern. +cannot be used. + +Note: the non-quasiquoted +.code #S +and +.code #H +literals are not and cannot be used for matching because they produce structure +and hash objects which lose important information about how they were specified +in the syntax, and carry restrictions which are unacceptable for pattern +matching. The order of sub-patterns is important in pattern syntax, but struct +and hash objects do not preserve the order in which their elements were +specified. A struct literal is required to specify the name of an existing +struct type, and slot names which are valid for that type, otherwise it is +erroneous. This is not acceptable for pattern matching, because patterns may +appear in place of those elements. The pattern match for a hash may specify the +same key pattern more than once, which means that the key pattern cannot be an +actual key in an actual hash, which requires every key to be unique. Structure +and hash quasiquotes do not have these issues; they are not actually literal +structure and hash objects, but list-based syntax. .NP* Variables in Patterns @@ -41255,6 +41277,99 @@ supported in quasiliteral patterns. .brev +.NP* Quasiliteral matching notation +.synb +.mets >> ^ qq-syntax +.syne +.desc +Quasiquoting provides an alternative pattern matching syntax. It uses a subset +of the quasiquoting notation. Only specific kinds of quasiquoted objects listed +in this description are supported. Within a quasiquote used for +pattern-matching, unquotes indicate operators and variables instead of the +.code @ +prefix. Splicing unquote syntax plays no role; its presence produces +unspecified behavior. + +The quasiquote matching notation is described, understood and implementing +in terms of a translation to the standard pattern-matching syntax, according +to the following rules. The +.code [X] +notation used here indicates that the element enclosed in brackets is +subject to a recursive translation according to the rules: +.RS +.meIP >> , expr +An unquoted expression occurring in the quasiquote is translated to the +.mono +.meti >> @ expr +.onom +pattern matching syntax. If +.meta expr +is an symbol, then this is a meta-variable: +.mono +.meti (sys:var << expr ) +.onom +otherwise it is translated to the +.mono +.meti (sys:expr << expr ) +.onom +syntax. +.meIP >> ~ expr +In JSON syntax, unquotes are given the same above treatment as +.code , +(comma) unquotes in ordinary syntax. +.meIP #H(() >> ( k0 << v0 ) >> ( k1 << v1 ) ...) +Hash quasiliteral syntax is translated according to the +.mono +.meti @(hash <> ([ k0 ] <> [ v0 ]) <> ([ k0 ] <> [ v0 ]) ...) +.onom +pattern, with each key and value recursively translated. +The syntax must specify +.code () +for the hash construction arguments part, otherwise an error is diagnosed. +That is to say, it must be of the form +.codn "#H(() ...)" . +where the first element is +.codn () . +.meIP >> #S( type < e0 < e1 ...) +Structure quasiliteral syntax is translated according to the +.mono +.meti @(struct <> [ type ] <> [ e0 ] <> [ e1 ] ...) +.onom +pattern. +.meIP >> #( e0 < e1 ...) +Vector quasiliteral syntax is translated according to the +.mono +.meti <> #([ e0 ] <> [ e1 ] ...) +.onom +pattern: it becomes a vector object containing embedded patterns. +.meIP <> #J[ e0 , << e1 , ...] +A JSON array quasiquote is translated into +.mono +.meti <> #([ e0 ] <> [ e1 ] ...) +.onom +exactly like a vector. Here, the +.code [X] +transformation recognizes JSON +.code ~ +(tilde) unquotes, and recursively recognizes and transform JSON syntax not +prefixed by +.codn #J . +.meIP >> #J{ k0 : << v0 , < k1 : << v1 , ...} +A JSON hash quasiquote is translated into +.mono +.meti @(hash <> ([ k0 ] <> [ v0 ]) <> ([ k0 ] <> [ v0 ]) ...) +.onom +exactly like a hash. +.meIP >> ( car . << cdr ) +Tree structure is translated according ot the +.mono +.meti <> ([ car ] . <> [ cdr ]) +.onom +pattern: it is recursively examined for translations. +.meIP < obj +Any other quasiquoted object is left untranslated. +.RE + .coNP Pattern operator @ struct .synb .mets @(struct < name >> { slot-name << pattern }*) |