Skip to content

Commit

Permalink
Permit using string macro with "qualified" name (#18690)
Browse files Browse the repository at this point in the history
* Permit using string macro with "qualified" name

Close #6970.
Close #14208.

* Add NEWS, doc for string and command macros
  • Loading branch information
TotalVerb authored and simonbyrne committed Dec 7, 2016
1 parent 07cb7c1 commit ed7dfe4
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 7 deletions.
12 changes: 12 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,16 @@ New language features
Language changes
----------------

* Multiline and singleline nonstandard command literals have been added. A
nonstandard command literal is like a nonstandard string literal, but the
syntax uses backquotes (``` ` ```) instead of double quotes, and the
resulting macro called is suffixed with `_cmd`. For instance, the syntax
``` q`xyz` ``` is equivalent to `@q_cmd "xyz"`. ([#18644])

* Nonstandard string and command literals can now be qualified with their
module. For instance, `Base.r"x"` is now parsed as `Base.@r_str "x"`.
Previously, this syntax parsed as an implicit multiplication. ([#18690])

Breaking changes
----------------

Expand Down Expand Up @@ -707,6 +717,8 @@ Language tooling improvements
[#18346]: https://github.com/JuliaLang/julia/issues/18346
[#18442]: https://github.com/JuliaLang/julia/issues/18442
[#18473]: https://github.com/JuliaLang/julia/issues/18473
[#18644]: https://github.com/JuliaLang/julia/issues/18644
[#18690]: https://github.com/JuliaLang/julia/issues/18690
[#18839]: https://github.com/JuliaLang/julia/issues/18839
[#18931]: https://github.com/JuliaLang/julia/issues/18931
[#18977]: https://github.com/JuliaLang/julia/issues/18977
Expand Down
16 changes: 15 additions & 1 deletion doc/manual/metaprogramming.rst
Original file line number Diff line number Diff line change
Expand Up @@ -864,13 +864,27 @@ majority of use cases, however, regular expressions are not constructed
based on run-time data. In this majority of cases, the ability to write
regular expressions as compile-time values is invaluable.

Like non-standard string literals, non-standard command literals exist using a
prefixed variant of the command literal syntax. The command literal
``custom`literal``` is parsed as ``@custom_cmd "literal"``. Julia itself does
not contain any non-standard command literals, but packages can make use of
this syntax. Aside from the different syntax and the ``_cmd`` suffix instead of
the ``_str`` suffix, non-standard command literals behave exactly like
non-standard string literals.

In the event that two modules provide non-standard string or command literals
with the same name, it is possible to qualify the string or command literal
with a module name. For instance, if both ``Foo`` and ``Bar`` provide
non-standard string literal ``@x_str``, then one can write ``Foo.x"literal"``
or ``Bar.x"literal"`` to disambiguate between the two.

The mechanism for user-defined string literals is deeply, profoundly
powerful. Not only are Julia's non-standard literals implemented using
it, but also the command literal syntax (```echo "Hello, $person"```)
is implemented with the following innocuous-looking macro::

macro cmd(str)
:(cmd_gen($shell_parse(str)))
:(cmd_gen($(shell_parse(str)[1])))
end

Of course, a large amount of complexity is hidden in the functions used
Expand Down
14 changes: 8 additions & 6 deletions src/julia-parser.scm
Original file line number Diff line number Diff line change
Expand Up @@ -1067,13 +1067,14 @@
(take-token s)
(loop (list* 'curly ex (parse-arglist s #\} ))))
((#\" #\`)
(if (and (symbol? ex) (not (operator? ex))
(if (and (or (symbol? ex) (valid-modref? ex))
(not (operator? ex))
(not (ts:space? s)))
;; custom string and command literals; x"s" => @x_str "s"
(let* ((macstr (begin (take-token s)
(parse-raw-literal s t)))
(nxt (peek-token s))
(macname (symbol (string #\@ ex (macsuffix t)))))
(macname (macroify-name ex (macsuffix t))))
(if (and (symbol? nxt) (not (operator? nxt))
(not (ts:space? s)))
;; string literal suffix, "s"x
Expand Down Expand Up @@ -2058,10 +2059,11 @@
(or (symbol? (cadr e))
(valid-modref? (cadr e)))))

(define (macroify-name e)
(cond ((symbol? e) (symbol (string #\@ e)))
((valid-modref? e) `(|.| ,(cadr e)
(quote ,(macroify-name (cadr (caddr e))))))
(define (macroify-name e . suffixes)
(cond ((symbol? e) (symbol (apply string #\@ e suffixes)))
((valid-modref? e)
`(|.| ,(cadr e)
(quote ,(apply macroify-name (cadr (caddr e)) suffixes))))
(else (error (string "invalid macro use \"@(" (deparse e) ")\"" )))))

(define (simple-string-literal? e) (string? e))
Expand Down
17 changes: 17 additions & 0 deletions test/parse.jl
Original file line number Diff line number Diff line change
Expand Up @@ -845,3 +845,20 @@ begin
end"""))
@test !any(x->(x == Expr(:meta, :push_loc, :none)), ex.args)
end

# Check qualified string macros
Base.r"regex" == r"regex"

module QualifiedStringMacro
module SubModule
macro x_str(x)
1
end
macro y_cmd(x)
2
end
end
end

@test QualifiedStringMacro.SubModule.x"" === 1
@test QualifiedStringMacro.SubModule.y`` === 2

0 comments on commit ed7dfe4

Please sign in to comment.