Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Permit using string macro with "qualified" name #18690

Merged
merged 5 commits into from
Dec 7, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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