diff --git a/src/integration/expr.jl b/src/integration/expr.jl index eb1cefac..038bad9a 100644 --- a/src/integration/expr.jl +++ b/src/integration/expr.jl @@ -246,8 +246,6 @@ function node_to_expr(cursor, source, txtbuf::Vector{UInt8}, txtbuf_offset::UInt val isa UInt128 ? Symbol("@uint128_str") : Symbol("@big_str") return Expr(:macrocall, GlobalRef(Core, macname), nothing, str) - elseif k == K"MacroName" && val === Symbol("@.") - return Symbol("@__dot__") else return val end @@ -296,7 +294,31 @@ function node_to_expr(cursor, source, txtbuf::Vector{UInt8}, txtbuf_offset::UInt nodehead, source) end -# Split out from the above for codesize reasons, to avoid specialization on multiple +function adjust_macro_name!(retexpr::Union{Expr, Symbol}, k::Kind) + if !(retexpr isa Symbol) + retexpr::Expr + # can happen for incomplete or errors + (length(retexpr.args) < 2 || retexpr.head != :(.)) && return retexpr + arg2 = retexpr.args[2] + isa(arg2, QuoteNode) || return retexpr + retexpr.args[2] = QuoteNode(adjust_macro_name!(arg2.value, k)) + return retexpr + end + if k == K"macro_name" + if retexpr === Symbol(".") + return Symbol("@__dot__") + else + return Symbol("@$retexpr") + end + elseif k == K"macro_name_cmd" + return Symbol("@$(retexpr)_cmd") + else + @assert k == K"macro_name_str" + return Symbol("@$(retexpr)_str") + end +end + +# Split out from `node_to_expr` for codesize reasons, to avoid specialization on multiple # tree types. @noinline function _node_to_expr(retexpr::Expr, loc::LineNumberNode, srcrange::UnitRange{UInt32}, @@ -312,6 +334,8 @@ end # However, errors can add additional errors tokens which we represent # as e.g. `Expr(:var, ..., Expr(:error))`. return retexpr.args[1] + elseif k in KSet"macro_name macro_name_cmd macro_name_str" + return adjust_macro_name!(retexpr.args[1], k) elseif k == K"?" retexpr.head = :if elseif k == K"op=" && length(args) == 3 @@ -331,7 +355,7 @@ end elseif k == K"macrocall" if length(args) >= 2 a2 = args[2] - if @isexpr(a2, :macrocall) && kind(firstchildhead) == K"CmdMacroName" + if @isexpr(a2, :macrocall) && kind(firstchildhead) == K"macro_name_cmd" # Fix up for custom cmd macros like foo`x` args[2] = a2.args[3] end diff --git a/src/julia/kinds.jl b/src/julia/kinds.jl index 8a20a2b8..19a00eb2 100644 --- a/src/julia/kinds.jl +++ b/src/julia/kinds.jl @@ -194,15 +194,6 @@ register_kinds!(JuliaSyntax, 0, [ "BEGIN_IDENTIFIERS" "Identifier" "Placeholder" # Used for empty catch variables, and all-underscore identifiers in lowering - # Macro names are modelled as special kinds of identifiers because the full - # macro name may not appear as characters in the source: The `@` may be - # detached from the macro name as in `@A.x` (ugh!!), or have a _str or _cmd - # suffix appended. - "BEGIN_MACRO_NAMES" - "MacroName" - "StringMacroName" - "CmdMacroName" - "END_MACRO_NAMES" "END_IDENTIFIERS" "BEGIN_KEYWORDS" @@ -1048,6 +1039,10 @@ register_kinds!(JuliaSyntax, 0, [ "iteration" "comprehension" "typed_comprehension" + # Macro names + "macro_name" + "macro_name_cmd" + "macro_name_str" # Container for a single statement/atom plus any trivia and errors "wrapper" "END_SYNTAX_KINDS" @@ -1111,10 +1106,6 @@ const _nonunique_kind_names = Set([ K"String" K"Char" K"CmdString" - - K"MacroName" - K"StringMacroName" - K"CmdMacroName" ]) """ @@ -1201,7 +1192,6 @@ is_prec_unicode_ops(x) = K"BEGIN_UNICODE_OPS" <= kind(x) <= K"END_UNICODE_OPS" is_prec_pipe_lt(x) = kind(x) == K"<|" is_prec_pipe_gt(x) = kind(x) == K"|>" is_syntax_kind(x) = K"BEGIN_SYNTAX_KINDS"<= kind(x) <= K"END_SYNTAX_KINDS" -is_macro_name(x) = K"BEGIN_MACRO_NAMES" <= kind(x) <= K"END_MACRO_NAMES" is_syntactic_assignment(x) = K"BEGIN_SYNTACTIC_ASSIGNMENTS" <= kind(x) <= K"END_SYNTACTIC_ASSIGNMENTS" function is_string_delim(x) diff --git a/src/julia/literal_parsing.jl b/src/julia/literal_parsing.jl index f2b99b86..0d716e39 100644 --- a/src/julia/literal_parsing.jl +++ b/src/julia/literal_parsing.jl @@ -430,12 +430,6 @@ function parse_julia_literal(txtbuf::Vector{UInt8}, head::SyntaxHead, srcrange) Symbol(normalize_identifier(val_str)) elseif k == K"error" ErrorVal() - elseif k == K"MacroName" - Symbol("@$(normalize_identifier(val_str))") - elseif k == K"StringMacroName" - Symbol("@$(normalize_identifier(val_str))_str") - elseif k == K"CmdMacroName" - Symbol("@$(normalize_identifier(val_str))_cmd") elseif is_syntax_kind(head) nothing elseif is_keyword(k) diff --git a/src/julia/parser.jl b/src/julia/parser.jl index 49ba902c..2abed160 100644 --- a/src/julia/parser.jl +++ b/src/julia/parser.jl @@ -1488,6 +1488,13 @@ function parse_unary_prefix(ps::ParseState, has_unary_prefix=false) end end +function maybe_parsed_macro_name(ps, processing_macro_name, mark) + if processing_macro_name + emit(ps, mark, K"macro_name") + end + return false +end + # Parses a chain of suffixes at function call precedence, leftmost binding # tightest. This handles # * Bracketed calls like a() b[] c{} @@ -1505,13 +1512,14 @@ function parse_call_chain(ps::ParseState, mark, is_macrocall=false) # 2(x) ==> (* 2 x) return end + processing_macro_name = is_macrocall + saw_misplaced_atsym = false + misplaced_atsym_mark = nothing # source range of the @-prefixed part of a macro macro_atname_range = nothing - # $A.@x ==> (macrocall (. ($ A) @x)) + # $A.@x ==> (macrocall (. ($ A) (macro_name x))) maybe_strmac = true - # We record the last component of chains of dot-separated identifiers so we - # know which identifier was the macro name. - macro_name_position = position(ps) # points to same output span as peek_behind + last_identifier_orig_kind = peek_behind(ps).orig_kind while true maybe_strmac_1 = false t = peek_token(ps) @@ -1523,33 +1531,34 @@ function parse_call_chain(ps::ParseState, mark, is_macrocall=false) break elseif is_macrocall && (preceding_whitespace(t) || !(k in KSet"( [ { ' .")) # Macro calls with space-separated arguments - # @foo a b ==> (macrocall @foo a b) - # @foo (x) ==> (macrocall @foo (parens x)) - # @foo (x,y) ==> (macrocall @foo (tuple-p x y)) - # [@foo x] ==> (vect (macrocall @foo x)) - # [@foo] ==> (vect (macrocall @foo)) - # @var"#" a ==> (macrocall (var @#) a) - # A.@x y ==> (macrocall (. A @x) y) - # A.@var"#" a ==> (macrocall (. A (var @#)) a) - # @+x y ==> (macrocall @+ x y) - # A.@.x ==> (macrocall (. A @.) x) - fix_macro_name_kind!(ps, macro_name_position) + # @foo a b ==> (macrocall (macro_name foo) a b) + # @foo (x) ==> (macrocall (macro_name foo) (parens x)) + # @foo (x,y) ==> (macrocall (macro_name foo) (tuple-p x y)) + # [@foo x] ==> (vect (macrocall (macro_name foo) x)) + # [@foo] ==> (vect (macrocall (macro_name foo))) + # @var"#" a ==> (macrocall (macro_name (var #)) a) + # A.@x y ==> (macrocall (. A (macro_name x)) y) + # A.@var"#" a ==> (macrocall (. A (macro_name (var #))) a) + # @+x y ==> (macrocall (macro_name +) x y) + # A.@.x ==> (macrocall (. A (macro_name .)) x) + processing_macro_name = maybe_parsed_macro_name( + ps, processing_macro_name, mark) let ps = with_space_sensitive(ps) # Space separated macro arguments - # A.@foo a b ==> (macrocall (. A @foo) a b) - # @A.foo a b ==> (macrocall (. A @foo) a b) + # A.@foo a b ==> (macrocall (. A (macro_name foo)) a b) + # @A.foo a b ==> (macrocall (macro_name (. A foo)) a b) n_args = parse_space_separated_exprs(ps) - is_doc_macro = peek_behind(ps, macro_name_position).orig_kind == K"doc" + is_doc_macro = last_identifier_orig_kind == K"doc" if is_doc_macro && n_args == 1 # Parse extended @doc args on next line - # @doc x\ny ==> (macrocall @doc x y) - # A.@doc x\ny ==> (macrocall (. A @doc) doc x y) - # @A.doc x\ny ==> (macrocall (. A @doc) doc x y) - # @doc x y\nz ==> (macrocall @doc x y) + # @doc x\ny ==> (macrocall (macro_name doc) x y) + # A.@doc x\ny ==> (macrocall (. A (macro_name doc)) x y) + # @A.doc x\ny ==> (macrocall (macro_name (. A doc)) x y) + # @doc x y\nz ==> (macrocall (macro_name doc) x y) # # Excluded cases - # @doc x\n\ny ==> (macrocall @doc x) - # @doc x\nend ==> (macrocall @doc x) + # @doc x\n\ny ==> (macrocall (macro_name doc) x) + # @doc x\nend ==> (macrocall (macro_name doc) x) k2 = peek(ps, 2) if peek(ps) == K"NewlineWs" && !is_closing_token(ps, k2) && k2 != K"NewlineWs" @@ -1566,6 +1575,9 @@ function parse_call_chain(ps::ParseState, mark, is_macrocall=false) # f(a; b; c) ==> (call f a (parameters b) (parameters c)) # (a=1)() ==> (call (parens (= a 1))) # f (a) ==> (call f (error-t) a) + processing_macro_name = maybe_parsed_macro_name( + ps, processing_macro_name, mark) + processing_macro_name = false bump_disallowed_space(ps) bump(ps, TRIVIA_FLAG) opts = parse_call_arglist(ps, K")") @@ -1577,14 +1589,16 @@ function parse_call_chain(ps::ParseState, mark, is_macrocall=false) # TODO: Add PARENS_FLAG to all calls which use them? (is_macrocall ? PARENS_FLAG : EMPTY_FLAGS)|opts.delim_flags) if is_macrocall - # @x(a, b) ==> (macrocall-p @x a b) - # A.@x(y) ==> (macrocall-p (. A @x) y) - # A.@x(y).z ==> (. (macrocall-p (. A @x) y) z) - fix_macro_name_kind!(ps, macro_name_position) + # @x(a, b) ==> (macrocall-p (macro_name x) a b) + # A.@x(y) ==> (macrocall-p (. A (macro_name x)) y) + # A.@x(y).z ==> (. (macrocall-p (. A (macro_name x)) y) z) is_macrocall = false + # @f()() ==> (call (macrocall-p (macro_name f))) macro_atname_range = nothing end elseif k == K"[" + processing_macro_name = maybe_parsed_macro_name( + ps, processing_macro_name, mark) m = position(ps) # a [i] ==> (ref a (error-t) i) bump_disallowed_space(ps) @@ -1592,14 +1606,13 @@ function parse_call_chain(ps::ParseState, mark, is_macrocall=false) ckind, cflags, dim = parse_cat(ParseState(ps, end_symbol=true), K"]", ps.end_symbol) if is_macrocall - # @S[a,b] ==> (macrocall @S (vect a b)) - # @S[a b] ==> (macrocall @S (hcat a b)) - # @S[a; b] ==> (macrocall @S (vcat a b)) - # A.@S[a] ==> (macrocall (. A @S) (vect a)) - # @S[a].b ==> (. (macrocall @S (vect a)) b) - #v1.7: @S[a ;; b] ==> (macrocall @S (ncat-2 a b)) - #v1.6: @S[a ;; b] ==> (macrocall @S (error (ncat-2 a b))) - fix_macro_name_kind!(ps, macro_name_position) + # @S[a,b] ==> (macrocall (macro_name S) (vect a b)) + # @S[a b] ==> (macrocall (macro_name S) (hcat a b)) + # @S[a; b] ==> (macrocall (macro_name S) (vcat a b)) + # A.@S[a] ==> (macrocall (. A (macro_name S)) (vect a)) + # @S[a].b ==> (. (macrocall (macro_name S) (vect a)) b) + #v1.7: @S[a ;; b] ==> (macrocall (macro_name S) (ncat-2 a b)) + #v1.6: @S[a ;; b] ==> (macrocall (macro_name S) (error (ncat-2 a b))) emit(ps, m, ckind, cflags | set_numeric_flags(dim)) check_ncat_compat(ps, m, ckind) emit(ps, mark, K"macrocall") @@ -1637,19 +1650,24 @@ function parse_call_chain(ps::ParseState, mark, is_macrocall=false) emark = position(ps) if !isnothing(macro_atname_range) # Allow `@` in macrocall only in first and last position - # A.B.@x ==> (macrocall (. (. A B) @x)) - # @A.B.x ==> (macrocall (. (. A B) @x)) - # A.@B.x ==> (macrocall (. (. A B (error-t)) @x)) + # A.B.@x ==> (macrocall (. (. A B) (macro_name x))) + # @A.B.x ==> (macrocall (macro_name (. (. A B) x))) + # A.@B.x ==> (macrocall (. (. A (error-t) B) (macro_name (error-t) x))) emit_diagnostic(ps, macro_atname_range..., error="`@` must appear on first or last macro name component") - bump(ps, TRIVIA_FLAG, error="Unexpected `.` after macro name") - else - bump(ps, TRIVIA_FLAG) + # Recover by treating the `@` as if it had been on the last identifier + saw_misplaced_atsym = true + reset_node!(ps, macro_atname_range[2], kind=K"TOMBSTONE") + reset_node!(ps, macro_atname_range[1], kind=K"error") end + bump(ps, TRIVIA_FLAG) k = peek(ps) if k == K"(" if is_macrocall - # @M.(x) ==> (macrocall (dotcall @M (error-t) x)) + # Recover by pretending we do have the syntax + processing_macro_name = maybe_parsed_macro_name( + ps, processing_macro_name, mark) + # @M.(x) ==> (macrocall (dotcall (macro_name M) (error-t) x)) bump_invisible(ps, K"error", TRIVIA_FLAG) emit_diagnostic(ps, mark, error="dot call syntax not supported for macros") @@ -1672,29 +1690,34 @@ function parse_call_chain(ps::ParseState, mark, is_macrocall=false) elseif k == K"$" # f.$x ==> (. f ($ x)) # f.$(x+y) ==> (. f ($ (call + x y))) - # A.$B.@x ==> (macrocall (. (. A ($ B)) @x)) - # @A.$x a ==> (macrocall (. A (error x)) a) + # A.$B.@x ==> (macrocall (. (. A ($ B)) (macro_name x))) + # @A.$x a ==> (macrocall (macro_name (. A (error x))) a) m = position(ps) bump(ps, TRIVIA_FLAG) parse_atom(ps) - emit(ps, m, K"$") - macro_name_position = position(ps) + if is_macrocall + emit(ps, m, K"error", error="invalid macro name") + else + emit(ps, m, K"$") + end + last_identifier_orig_kind = K"$" emit(ps, mark, K".") elseif k == K"@" # A macro call after some prefix A has been consumed - # A.@x ==> (macrocall (. A @x)) - # A.@x a ==> (macrocall (. A @x) a) + # A.@x ==> (macrocall (. A (macro_name x))) + # A.@x a ==> (macrocall (. A (macro_name x)) a) m = position(ps) if is_macrocall - # @A.B.@x a ==> (macrocall (. (. A B) (error-t) @x) a) + # @A.B.@x a ==> (macrocall (. (. A B) (error-t) (macro_name x)) a) bump(ps, TRIVIA_FLAG, error="repeated `@` in macro module path") else bump(ps, TRIVIA_FLAG) - is_macrocall = true end parse_macro_name(ps) - macro_name_position = position(ps) + last_identifier_orig_kind = peek_behind(ps).orig_kind + !is_macrocall && emit(ps, m, K"macro_name") macro_atname_range = (m, position(ps)) + is_macrocall = true emit(ps, mark, K".") elseif k == K"'" # f.' => (dotcall-post f (error ')) @@ -1704,10 +1727,27 @@ function parse_call_chain(ps::ParseState, mark, is_macrocall=false) error="the .' operator for transpose is discontinued") emit(ps, mark, K"dotcall", POSTFIX_OP_FLAG) else + if saw_misplaced_atsym + # If we saw a misplaced `@` earlier, this might be the place + # where it should have been. Opportunistically bump the + # zero-width error token here. If that's not right, we'll + # reset it later. + if misplaced_atsym_mark !== nothing + reset_node!(ps, misplaced_atsym_mark[1], kind=K"TOMBSTONE") + reset_node!(ps, misplaced_atsym_mark[2], kind=K"TOMBSTONE") + end + macro_name_mark = position(ps) + bump_invisible(ps, K"error", TRIVIA_FLAG) + aterror_mark = position(ps) + end # Field/property syntax # f.x.y ==> (. (. f x) y) parse_atom(ps, false) - macro_name_position = position(ps) + if saw_misplaced_atsym + emit(ps, macro_name_mark, K"macro_name") + misplaced_atsym_mark = (aterror_mark, position(ps)) + end + last_identifier_orig_kind = peek_behind(ps).orig_kind maybe_strmac_1 = true emit(ps, mark, K".") end @@ -1717,6 +1757,8 @@ function parse_call_chain(ps::ParseState, mark, is_macrocall=false) bump(ps, remap_kind=K"Identifier") emit(ps, mark, K"call", POSTFIX_OP_FLAG) elseif k == K"{" + processing_macro_name = maybe_parsed_macro_name( + ps, processing_macro_name, mark) # Type parameter curlies and macro calls m = position(ps) # S {a} ==> (curly S (error-t) a) @@ -1724,10 +1766,9 @@ function parse_call_chain(ps::ParseState, mark, is_macrocall=false) bump(ps, TRIVIA_FLAG) opts = parse_call_arglist(ps, K"}") if is_macrocall - # @S{a,b} ==> (macrocall S (braces a b)) - # A.@S{a} ==> (macrocall (. A @S) (braces a)) - # @S{a}.b ==> (. (macrocall @S (braces a)) b) - fix_macro_name_kind!(ps, macro_name_position) + # @S{a,b} ==> (macrocall (macro_name S) (braces a b)) + # A.@S{a} ==> (macrocall (. A (macro_name S)) (braces a)) + # @S{a}.b ==> (. (macrocall (macro_name S) (braces a)) b) emit(ps, m, K"braces", opts.delim_flags) emit(ps, mark, K"macrocall") min_supported_version(v"1.6", ps, mark, "macro call without space before `{}`") @@ -1740,32 +1781,32 @@ function parse_call_chain(ps::ParseState, mark, is_macrocall=false) elseif k in KSet" \" \"\"\" ` ``` " && !preceding_whitespace(t) && maybe_strmac && (# Must mirror the logic in lex_quote() for consistency - origk = peek_behind(ps, macro_name_position).orig_kind; + origk = last_identifier_orig_kind; origk == K"Identifier" || is_contextual_keyword(origk) || is_word_operator(origk)) # Custom string and command literals - # x"str" ==> (macrocall @x_str (string-r "str")) - # x`str` ==> (macrocall @x_cmd (cmdstring-r "str")) - # x"" ==> (macrocall @x_str (string-r "")) - # x`` ==> (macrocall @x_cmd (cmdstring-r "")) + # x"str" ==> (macrocall (macro_name_str x) (string-r "str")) + # x`str` ==> (macrocall (macro_name_cmd x) (cmdstring-r "str")) + # x"" ==> (macrocall (macro_name_str x) (string-r "")) + # x`` ==> (macrocall (macro_name_cmd x) (cmdstring-r "")) # Triple quoted processing for custom strings - # r"""\nx""" ==> (macrocall @r_str (string-s-r "x")) - # r"""\n x\n y""" ==> (macrocall @r_str (string-s-r "x\n" "y")) - # r"""\n x\\n y""" ==> (macrocall @r_str (string-s-r "x\\\n" "y")) + # r"""\nx""" ==> (macrocall (macro_name_str r) (string-s-r "x")) + # r"""\n x\n y""" ==> (macrocall (macro_name_str r) (string-s-r "x\n" "y")) + # r"""\n x\\n y""" ==> (macrocall (macro_name_str r) (string-s-r "x\\\n" "y")) # # Use a special token kind for string and cmd macro names so the # names can be expanded later as necessary. - outk = is_string_delim(k) ? K"StringMacroName" : K"CmdMacroName" - fix_macro_name_kind!(ps, macro_name_position, outk) + outk = is_string_delim(k) ? K"macro_name_str" : K"macro_name_cmd" + emit(ps, mark, outk) parse_string(ps, true) t = peek_token(ps) k = kind(t) if !preceding_whitespace(t) && is_string_macro_suffix(k) # Macro suffixes can include keywords and numbers - # x"s"y ==> (macrocall @x_str (string-r "s") "y") - # x"s"end ==> (macrocall @x_str (string-r "s") "end") - # x"s"in ==> (macrocall @x_str (string-r "s") "in") - # x"s"2 ==> (macrocall @x_str (string-r "s") 2) - # x"s"10.0 ==> (macrocall @x_str (string-r "s") 10.0) + # x"s"y ==> (macrocall (macro_name_str x) (string-r "s") "y") + # x"s"end ==> (macrocall (macro_name_str x) (string-r "s") "end") + # x"s"in ==> (macrocall (macro_name_str x) (string-r "s") "in") + # x"s"2 ==> (macrocall (macro_name_str x) (string-r "s") 2) + # x"s"10.0 ==> (macrocall (macro_name_str x) (string-r "s") 10.0) suffix_kind = (k == K"Identifier" || is_keyword(k) || is_word_operator(k)) ? K"String" : k bump(ps, remap_kind=suffix_kind) @@ -2033,13 +2074,13 @@ function parse_resword(ps::ParseState) word == K"baremodule" ? BARE_MODULE_FLAG : EMPTY_FLAGS) elseif word in KSet"export public" # export a ==> (export a) - # export @a ==> (export @a) - # export a, \n @b ==> (export a @b) + # export @a ==> (export (macro_name a)) + # export a, \n @b ==> (export a (macro_name b)) # export +, == ==> (export + ==) # export \n a ==> (export a) # export \$a, \$(a*b) ==> (export (\$ a) (\$ (parens (call-i a * b)))) bump(ps, TRIVIA_FLAG) - parse_comma_separated(ps, x->parse_atsym(x, false)) + parse_comma_separated(ps, x->parse_import_atsym(x, false)) emit(ps, mark, word) elseif word in KSet"import using" parse_imports(ps) @@ -2372,43 +2413,12 @@ function _is_valid_macro_name(peektok) return !is_error(peektok.kind) && (peektok.is_leaf || peektok.kind == K"var") end -function fix_macro_name_kind!(ps::ParseState, macro_name_position, name_kind=nothing) - k = peek_behind(ps, macro_name_position).kind - if k == K"var" - macro_name_position = first_child_position(ps, macro_name_position) - k = peek_behind(ps, macro_name_position).kind - elseif k == K"parens" - # @(A) x ==> (macrocall (parens @A) x) - macro_name_position = first_child_position(ps, macro_name_position) - if macro_name_position == NO_POSITION - return - end - k = peek_behind(ps, macro_name_position).kind - elseif k == K"error" - # Error already reported in parse_macro_name - return - end - if isnothing(name_kind) - name_kind = _is_valid_macro_name(peek_behind(ps, macro_name_position)) ? - K"MacroName" : K"error" - if name_kind == K"error" - # TODO: This isn't quite accurate - emit_diagnostic(ps, macro_name_position, macro_name_position, - error="invalid macro name") - end - end - reset_node!(ps, macro_name_position, kind=name_kind) -end - -# If remap_kind is false, the kind will be remapped by parse_call_chain after -# it discovers which component of the macro's module path is the macro name. -# # flisp: parse-macro-name function parse_macro_name(ps::ParseState) # @! x ==> (macrocall @! x) - # @.. x ==> (macrocall @.. x) - # @$ x ==> (macrocall @$ x) - # @var"#" x ==> (macrocall (var @#) x) + # @.. x ==> (macrocall (macro_name ..) x) + # @$ x ==> (macrocall (macro_name $) x) + # @var"#" x ==> (macrocall (macro_name (var #)) x) bump_disallowed_space(ps) mark = position(ps) parse_atom(ps, false) @@ -2417,7 +2427,7 @@ function parse_macro_name(ps::ParseState) emit_diagnostic(ps, mark, warning="parenthesizing macro names is unnecessary") elseif !_is_valid_macro_name(b) - # @[x] y z ==> (macrocall (error (vect x)) y z) + # @[x] y z ==> (macrocall (macro_name (error (vect x))) y z) emit(ps, mark, K"error", error="invalid macro name") end end @@ -2425,15 +2435,16 @@ end # Parse an identifier, interpolation or @-prefixed symbol # # flisp: parse-atsym -function parse_atsym(ps::ParseState, allow_quotes=true) +function parse_import_atsym(ps::ParseState, allow_quotes=true) bump_trivia(ps) if peek(ps) == K"@" - # export @a ==> (export @a) - # export @var"'" ==> (export (var @')) - # export a, \n @b ==> (export a @b) + mark = position(ps) + # export @a ==> (export (macro_name a)) + # export @var"'" ==> (export (macro_name (var '))) + # export a, \n @b ==> (export a (macro_name b)) bump(ps, TRIVIA_FLAG) parse_macro_name(ps) - fix_macro_name_kind!(ps, position(ps)) + emit(ps, mark, K"macro_name") else # export a ==> (export a) # export \n a ==> (export a) @@ -2538,7 +2549,7 @@ function parse_import(ps::ParseState, word, has_import_prefix) # import A: x as y ==> (import (: (importpath A) (as (importpath x) y))) # using A: x as y ==> (using (: (importpath A) (as (importpath x) y))) bump(ps, TRIVIA_FLAG) - parse_atsym(ps, false) + parse_import_atsym(ps, false) emit(ps, mark, K"as") if word == K"using" && !has_import_prefix # using A as B ==> (using (error (as (importpath A) B))) @@ -2587,15 +2598,15 @@ function parse_import_path(ps::ParseState) end first_dot = false end - # import @x ==> (import (importpath @x)) + # import @x ==> (import (importpath (macro_name x))) # import $A ==> (import (importpath ($ A))) - parse_atsym(ps, false) + parse_import_atsym(ps, false) while true t = peek_token(ps) k = kind(t) if k == K"." # import A.B ==> (import (importpath A B)) - # import $A.@x ==> (import (importpath ($ A) @x)) + # import $A.@x ==> (import (importpath ($ A) (macro_name x))) # import A.B.C ==> (import (importpath A B C)) # import A.⋆.f ==> (import (importpath A ⋆ f)) next_tok = peek_token(ps, 2) @@ -2611,7 +2622,7 @@ function parse_import_path(ps::ParseState) bump_disallowed_space(ps) end bump(ps, TRIVIA_FLAG) - parse_atsym(ps) + parse_import_atsym(ps) elseif k == K"..." # Import the .. operator # import A... ==> (import (importpath A ..)) @@ -3651,7 +3662,7 @@ function parse_atom(ps::ParseState, check_identifiers=true, has_unary_prefix=fal emit_braces(ps, mark, ckind, cflags, dim) elseif leading_kind == K"@" # macro call # Macro names can be keywords - # @end x ==> (macrocall @end x) + # @end x ==> (macrocall (macro_name end) x) bump(ps, TRIVIA_FLAG) parse_macro_name(ps) parse_call_chain(ps, mark, true) diff --git a/src/julia/tokenize.jl b/src/julia/tokenize.jl index 24c96870..2bd0f56d 100644 --- a/src/julia/tokenize.jl +++ b/src/julia/tokenize.jl @@ -1168,30 +1168,21 @@ end function lex_dot(l::Lexer) if accept(l, '.') if accept(l, '.') + l.last_token == K"@" && return emit(l, K"Identifier") return emit(l, K"...") else if is_dottable_operator_start_char(peekchar(l)) readchar(l) return emit(l, K"ErrorInvalidOperator") else + l.last_token == K"@" && return emit(l, K"Identifier") return emit(l, K"..") end end elseif Base.isdigit(peekchar(l)) return lex_digit(l, K"Float") else - pc, dpc = dpeekchar(l) - # When we see a dot followed by an operator, we want to emit just the dot - # and let the next token be the operator - if is_operator_start_char(pc) || (pc == '!' && dpc == '=') - return emit(l, K".") - elseif pc == '÷' - return emit(l, K".") - elseif pc == '=' && dpc == '>' - return emit(l, K".") - elseif is_dottable_operator_start_char(pc) - return emit(l, K".") - end + l.last_token == K"@" && return emit(l, K"Identifier") return emit(l, K".") end end diff --git a/test/diagnostics.jl b/test/diagnostics.jl index 07e66abf..1397dd21 100644 --- a/test/diagnostics.jl +++ b/test/diagnostics.jl @@ -53,7 +53,7 @@ end Diagnostic(1, 9, :error, "try without catch or finally") # TODO: better range @test diagnostic("@A.\$x a") == - Diagnostic(6, 5, :error, "invalid macro name") + Diagnostic(4, 5, :error, "invalid macro name") @test diagnostic("a, , b") == Diagnostic(4, 4, :error, "unexpected `,`") diff --git a/test/expr.jl b/test/expr.jl index 7651347c..d7547848 100644 --- a/test/expr.jl +++ b/test/expr.jl @@ -554,6 +554,7 @@ # var"" @test parsestmt("@var\"#\" a") == Expr(:macrocall, Symbol("@#"), LineNumberNode(1), :a) + @test parsestmt("@var\"\\\"\" a") == Expr(:macrocall, Symbol("@\""), LineNumberNode(1), :a) @test parsestmt("A.@var\"#\" a") == Expr(:macrocall, Expr(:., :A, QuoteNode(Symbol("@#"))), LineNumberNode(1), :a) # Square brackets diff --git a/test/green_node.jl b/test/green_node.jl index 727c7178..cc0294e0 100644 --- a/test/green_node.jl +++ b/test/green_node.jl @@ -33,8 +33,9 @@ 1:1 │ Identifier ✔ 2:2 │ ( 3:7 │ [macrocall] - 3:3 │ @ - 4:4 │ MacroName ✔ + 3:4 │ [macro_name] + 3:3 │ @ + 4:4 │ Identifier ✔ 5:5 │ ( 6:6 │ Identifier ✔ 7:7 │ ) @@ -50,8 +51,9 @@ 1:1 │ Identifier ✔ "f" 2:2 │ ( "(" 3:7 │ [macrocall] - 3:3 │ @ "@" - 4:4 │ MacroName ✔ "x" + 3:4 │ [macro_name] + 3:3 │ @ "@" + 4:4 │ Identifier ✔ "x" 5:5 │ ( "(" 6:6 │ Identifier ✔ "y" 7:7 │ ) ")" diff --git a/test/parser.jl b/test/parser.jl index f8400928..64ecc8ea 100644 --- a/test/parser.jl +++ b/test/parser.jl @@ -345,37 +345,37 @@ tests = [ ".&(x,y)" => "(call (. &) x y)" # parse_call_chain "f(a).g(b)" => "(call (. (call f a) g) b)" - "\$A.@x" => "(macrocall (. (\$ A) @x))" + "\$A.@x" => "(macrocall (. (\$ A) (macro_name x)))" # non-errors in space sensitive contexts "[f (x)]" => "(hcat f (parens x))" "[f x]" => "(hcat f x)" # space separated macro calls - "@foo a b" => "(macrocall @foo a b)" - "@foo (x)" => "(macrocall @foo (parens x))" - "@foo (x,y)" => "(macrocall @foo (tuple-p x y))" - "A.@foo a b" => "(macrocall (. A @foo) a b)" - "@A.foo a b" => "(macrocall (. A @foo) a b)" - "[@foo x]" => "(vect (macrocall @foo x))" - "[@foo]" => "(vect (macrocall @foo))" - "@var\"#\" a" => "(macrocall (var @#) a)" - "@(A) x" => "(macrocall (parens @A) x)" - "A.@x y" => "(macrocall (. A @x) y)" - "A.@var\"#\" a"=> "(macrocall (. A (var @#)) a)" - "@+x y" => "(macrocall @+ x y)" - "A.@.x" => "(macrocall (. A @.) x)" + "@foo a b" => "(macrocall (macro_name foo) a b)" + "@foo (x)" => "(macrocall (macro_name foo) (parens x))" + "@foo (x,y)" => "(macrocall (macro_name foo) (tuple-p x y))" + "A.@foo a b" => "(macrocall (. A (macro_name foo)) a b)" + "@A.foo a b" => "(macrocall (macro_name (. A foo)) a b)" + "[@foo x]" => "(vect (macrocall (macro_name foo) x))" + "[@foo]" => "(vect (macrocall (macro_name foo)))" + "@var\"#\" a" => "(macrocall (macro_name (var #)) a)" + "@(A) x" => "(macrocall (macro_name (parens A)) x)" + "A.@x y" => "(macrocall (. A (macro_name x)) y)" + "A.@var\"#\" a"=> "(macrocall (. A (macro_name (var #))) a)" + "@+x y" => "(macrocall (macro_name +) x y)" + "A.@.x" => "(macrocall (. A (macro_name .)) x)" # Macro names - "@! x" => "(macrocall @! x)" - "@.. x" => "(macrocall @.. x)" - "@\$ y" => "(macrocall @\$ y)" - "@[x] y z" => "(macrocall (error (vect x)) y z)" + "@! x" => "(macrocall (macro_name !) x)" + "@.. x" => "(macrocall (macro_name ..) x)" + "@\$ y" => "(macrocall (macro_name \$) y)" + "@[x] y z" => "(macrocall (macro_name (error (vect x))) y z)" # Special @doc parsing rules - "@doc x\ny" => "(macrocall @doc x y)" - "A.@doc x\ny" => "(macrocall (. A @doc) x y)" - "@A.doc x\ny" => "(macrocall (. A @doc) x y)" - "@doc x y\nz" => "(macrocall @doc x y)" - "@doc x\n\ny" => "(macrocall @doc x)" - "@doc x\nend" => "(macrocall @doc x)" + "@doc x\ny" => "(macrocall (macro_name doc) x y)" + "A.@doc x\ny" => "(macrocall (. A (macro_name doc)) x y)" + "@A.doc x\ny" => "(macrocall (macro_name (. A doc)) x y)" + "@doc x y\nz" => "(macrocall (macro_name doc) x y)" + "@doc x\n\ny" => "(macrocall (macro_name doc) x)" + "@doc x\nend" => "(macrocall (macro_name doc) x)" # calls with brackets "f(a,b)" => "(call f a b)" @@ -384,26 +384,26 @@ tests = [ "f(a; b; c)" => "(call f a (parameters b) (parameters c))" "(a=1)()" => "(call (parens (= a 1)))" "f (a)" => "(call f (error-t) a)" - "@x(a, b)" => "(macrocall-p @x a b)" - "@x(a, b,)" => "(macrocall-p-, @x a b)" - "A.@x(y)" => "(macrocall-p (. A @x) y)" - "A.@x(y).z" => "(. (macrocall-p (. A @x) y) z)" + "@x(a, b)" => "(macrocall-p (macro_name x) a b)" + "@x(a, b,)" => "(macrocall-p-, (macro_name x) a b)" + "A.@x(y)" => "(macrocall-p (. A (macro_name x)) y)" + "A.@x(y).z" => "(. (macrocall-p (. A (macro_name x)) y) z)" "f(y for x = xs; a)" => "(call f (generator y (iteration (in x xs))) (parameters a))" # do "f() do\nend" => "(call f (do (tuple) (block)))" "f() do ; body end" => "(call f (do (tuple) (block body)))" "f() do x, y\n body end" => "(call f (do (tuple x y) (block body)))" "f(x) do y body end" => "(call f x (do (tuple y) (block body)))" - "@f(x) do y body end" => "(macrocall-p @f x (do (tuple y) (block body)))" + "@f(x) do y body end" => "(macrocall-p (macro_name f) x (do (tuple y) (block body)))" # square brackets - "@S[a,b]" => "(macrocall @S (vect a b))" - "@S[a b]" => "(macrocall @S (hcat a b))" - "@S[a; b]" => "(macrocall @S (vcat a b))" - "A.@S[a]" => "(macrocall (. A @S) (vect a))" - "@S[a].b" => "(. (macrocall @S (vect a)) b)" - ((v=v"1.7",), "@S[a ;; b]") => "(macrocall @S (ncat-2 a b))" - ((v=v"1.6",), "@S[a ;; b]") => "(macrocall @S (error (ncat-2 a b)))" + "@S[a,b]" => "(macrocall (macro_name S) (vect a b))" + "@S[a b]" => "(macrocall (macro_name S) (hcat a b))" + "@S[a; b]" => "(macrocall (macro_name S) (vcat a b))" + "A.@S[a]" => "(macrocall (. A (macro_name S)) (vect a))" + "@S[a].b" => "(. (macrocall (macro_name S) (vect a)) b)" + ((v=v"1.7",), "@S[a ;; b]") => "(macrocall (macro_name S) (ncat-2 a b))" + ((v=v"1.6",), "@S[a ;; b]") => "(macrocall (macro_name S) (error (ncat-2 a b)))" "a[i]" => "(ref a i)" "a [i]" => "(ref a (error-t) i)" "a[i,j]" => "(ref a i j)" @@ -419,10 +419,10 @@ tests = [ # Dotted forms # Allow `@` in macrocall only in first and last position - "A.B.@x" => "(macrocall (. (. A B) @x))" - "@A.B.x" => "(macrocall (. (. A B) @x))" - "A.@B.x" => "(macrocall (. (. A B) (error-t) @x))" - "@M.(x)" => "(macrocall (dotcall @M (error-t) x))" + "A.B.@x" => "(macrocall (. (. A B) (macro_name x)))" + "@A.B.x" => "(macrocall (macro_name (. (. A B) x)))" + "A.@B.x" => "(macrocall (. (. A (error-t) B) (macro_name (error-t) x)))" + "@M.(x)" => "(macrocall (dotcall (macro_name M) (error-t) x))" "f.(a,b)" => "(dotcall f a b)" "f.(a,b,)" => "(dotcall-, f a b)" "f.(a=1; b=2)" => "(dotcall f (= a 1) (parameters (= b 2)))" @@ -434,11 +434,11 @@ tests = [ "A.: +" => "(. A (quote-: (error-t) +))" "f.\$x" => "(. f (\$ x))" "f.\$(x+y)" => "(. f (\$ (parens (call-i x + y))))" - "A.\$B.@x" => "(macrocall (. (. A (\$ B)) @x))" - "@A.\$x a" => "(macrocall (. A (error x)) a)" - "A.@x" => "(macrocall (. A @x))" - "A.@x a" => "(macrocall (. A @x) a)" - "@A.B.@x a" => "(macrocall (. (. A B) (error-t) @x) a)" + "A.\$B.@x" => "(macrocall (. (. A (\$ B)) (macro_name x)))" + "@A.\$x a" => "(macrocall (macro_name (. A (error x))) a)" + "A.@x" => "(macrocall (. A (macro_name x)))" + "A.@x a" => "(macrocall (. A (macro_name x)) a)" + "@A.B.@x a" => "(macrocall (macro_name (. (. A B) (error-t) x)) a)" # .' discontinued "f.'" => "(dotcall-post f (error '))" # Field/property syntax @@ -451,35 +451,40 @@ tests = [ "f'ᵀ" => "(call-post f 'ᵀ)" # Curly calls "S {a}" => "(curly S (error-t) a)" - "A.@S{a}" => "(macrocall (. A @S) (braces a))" - "@S{a,b}" => "(macrocall @S (braces a b))" - "A.@S{a}" => "(macrocall (. A @S) (braces a))" - "@S{a}.b" => "(. (macrocall @S (braces a)) b)" + "A.@S{a}" => "(macrocall (. A (macro_name S)) (braces a))" + "@S{a,b}" => "(macrocall (macro_name S) (braces a b))" + "A.@S{a}" => "(macrocall (. A (macro_name S)) (braces a))" + "@S{a}.b" => "(. (macrocall (macro_name S) (braces a)) b)" + # Macro calls with chained operations + "@a[b][c]" => "(ref (macrocall (macro_name a) (vect b)) c)" + "@a{b}{c}" => "(curly (macrocall (macro_name a) (braces b)) c)" + "@a[b]{c}" => "(curly (macrocall (macro_name a) (vect b)) c)" + "@a{b}[c]" => "(ref (macrocall (macro_name a) (braces b)) c)" "S{a,b}" => "(curly S a b)" "T{y for x = xs; a}" => "(curly T (generator y (iteration (in x xs))) (parameters a))" # String macros - "x\"str\"" => """(macrocall @x_str (string-r "str"))""" - "x`str`" => """(macrocall @x_cmd (cmdstring-r "str"))""" - "x\"\"" => """(macrocall @x_str (string-r ""))""" - "x``" => """(macrocall @x_cmd (cmdstring-r ""))""" - "in\"str\"" => """(macrocall @in_str (string-r "str"))""" - "outer\"str\"" => """(macrocall @outer_str (string-r "str"))""" + "x\"str\"" => """(macrocall (macro_name_str x) (string-r "str"))""" + "x`str`" => """(macrocall (macro_name_cmd x) (cmdstring-r "str"))""" + "x\"\"" => """(macrocall (macro_name_str x) (string-r ""))""" + "x``" => """(macrocall (macro_name_cmd x) (cmdstring-r ""))""" + "in\"str\"" => """(macrocall (macro_name_str in) (string-r "str"))""" + "outer\"str\"" => """(macrocall (macro_name_str outer) (string-r "str"))""" # Triple quoted processing for custom strings - "r\"\"\"\nx\"\"\"" => raw"""(macrocall @r_str (string-s-r "x"))""" - "r\"\"\"\n x\n y\"\"\"" => raw"""(macrocall @r_str (string-s-r "x\n" "y"))""" - "r\"\"\"\n x\\\n y\"\"\"" => raw"""(macrocall @r_str (string-s-r "x\\\n" "y"))""" + "r\"\"\"\nx\"\"\"" => raw"""(macrocall (macro_name_str r) (string-s-r "x"))""" + "r\"\"\"\n x\n y\"\"\"" => raw"""(macrocall (macro_name_str r) (string-s-r "x\n" "y"))""" + "r\"\"\"\n x\\\n y\"\"\"" => raw"""(macrocall (macro_name_str r) (string-s-r "x\\\n" "y"))""" # Macro suffixes can include keywords and numbers - "x\"s\"y" => """(macrocall @x_str (string-r "s") "y")""" - "x\"s\"end" => """(macrocall @x_str (string-r "s") "end")""" - "x\"s\"in" => """(macrocall @x_str (string-r "s") "in")""" - "x\"s\"2" => """(macrocall @x_str (string-r "s") 2)""" - "x\"s\"10.0" => """(macrocall @x_str (string-r "s") 10.0)""" + "x\"s\"y" => """(macrocall (macro_name_str x) (string-r "s") "y")""" + "x\"s\"end" => """(macrocall (macro_name_str x) (string-r "s") "end")""" + "x\"s\"in" => """(macrocall (macro_name_str x) (string-r "s") "in")""" + "x\"s\"2" => """(macrocall (macro_name_str x) (string-r "s") 2)""" + "x\"s\"10.0" => """(macrocall (macro_name_str x) (string-r "s") 10.0)""" # Cmd macro suffixes - "x`s`y" => """(macrocall @x_cmd (cmdstring-r "s") "y")""" - "x`s`end" => """(macrocall @x_cmd (cmdstring-r "s") "end")""" - "x`s`in" => """(macrocall @x_cmd (cmdstring-r "s") "in")""" - "x`s`2" => """(macrocall @x_cmd (cmdstring-r "s") 2)""" - "x`s`10.0" => """(macrocall @x_cmd (cmdstring-r "s") 10.0)""" + "x`s`y" => """(macrocall (macro_name_cmd x) (cmdstring-r "s") "y")""" + "x`s`end" => """(macrocall (macro_name_cmd x) (cmdstring-r "s") "end")""" + "x`s`in" => """(macrocall (macro_name_cmd x) (cmdstring-r "s") "in")""" + "x`s`2" => """(macrocall (macro_name_cmd x) (cmdstring-r "s") 2)""" + "x`s`10.0" => """(macrocall (macro_name_cmd x) (cmdstring-r "s") 10.0)""" ], JuliaSyntax.parse_resword => [ # In normal_context @@ -545,9 +550,9 @@ tests = [ """module A \n "x"\na\n end""" => """(module A (block (doc (string "x") a)))""" # export "export a" => "(export a)" - "export @a" => "(export @a)" - "export @var\"'\"" => "(export (var @'))" - "export a, \n @b" => "(export a @b)" + "export @a" => "(export (macro_name a))" + "export @var\"'\"" => "(export (macro_name (var ')))" + "export a, \n @b" => "(export a (macro_name b))" "export +, ==" => "(export + ==)" "export \n a" => "(export a)" "export \$a, \$(a*b)" => "(export (\$ a) (\$ (parens (call-i a * b))))" @@ -601,9 +606,9 @@ tests = [ "function (x=1) end" => "(function (tuple-p (= x 1)) (block))" "function (;x=1) end" => "(function (tuple-p (parameters (= x 1))) (block))" "function (f(x),) end" => "(function (tuple-p-, (call f x)) (block))" - "function (@f(x);) end" => "(function (tuple-p (macrocall-p @f x) (parameters)) (block))" - "function (@f(x)...) end" => "(function (tuple-p (... (macrocall-p @f x))) (block))" - "function (@f(x)) end" => "(function (error (tuple-p (macrocall-p @f x))) (block))" + "function (@f(x);) end" => "(function (tuple-p (macrocall-p (macro_name f) x) (parameters)) (block))" + "function (@f(x)...) end" => "(function (tuple-p (... (macrocall-p (macro_name f) x))) (block))" + "function (@f(x)) end" => "(function (error (tuple-p (macrocall-p (macro_name f) x))) (block))" "function (\$f) end" => "(function (error (tuple-p (\$ f))) (block))" "function ()(x) end" => "(function (call (tuple-p) x) (block))" "function (A).f() end" => "(function (call (. (parens A) f)) (block))" @@ -647,10 +652,10 @@ tests = [ "function f() \n a \n b end" => "(function (call f) (block a b))" "function f() end" => "(function (call f) (block))" # Macrocall as sig - ((v=v"1.12",), "function @callmemacro(a::Int) \n 1 \n end") => "(function (macrocall-p @callmemacro (::-i a Int)) (block 1))" - ((v=v"1.12",), "function @callmemacro(a::T, b::T) where T <: Int64\n3\nend") => "(function (where (macrocall-p @callmemacro (::-i a T) (::-i b T)) (<: T Int64)) (block 3))" - ((v=v"1.12",), "function @callmemacro(a::Int, b::Int, c::Int)::Float64\n4\nend") => "(function (::-i (macrocall-p @callmemacro (::-i a Int) (::-i b Int) (::-i c Int)) Float64) (block 4))" - ((v=v"1.12",), "function @f()() end") => "(function (call (macrocall-p @f)) (block))" + ((v=v"1.12",), "function @callmemacro(a::Int) \n 1 \n end") => "(function (macrocall-p (macro_name callmemacro) (::-i a Int)) (block 1))" + ((v=v"1.12",), "function @callmemacro(a::T, b::T) where T <: Int64\n3\nend") => "(function (where (macrocall-p (macro_name callmemacro) (::-i a T) (::-i b T)) (<: T Int64)) (block 3))" + ((v=v"1.12",), "function @callmemacro(a::Int, b::Int, c::Int)::Float64\n4\nend") => "(function (::-i (macrocall-p (macro_name callmemacro) (::-i a Int) (::-i b Int) (::-i c Int)) Float64) (block 4))" + ((v=v"1.12",), "function @f()() end") => "(function (call (macrocall-p (macro_name f))) (block))" # Errors "function" => "(function (error (error)) (block (error)) (error-t))" ], @@ -704,9 +709,9 @@ tests = [ # Modules with operator symbol names "import .⋆" => "(import (importpath . ⋆))" # Expressions allowed in import paths - "import @x" => "(import (importpath @x))" + "import @x" => "(import (importpath (macro_name x)))" "import \$A" => "(import (importpath (\$ A)))" - "import \$A.@x" => "(import (importpath (\$ A) @x))" + "import \$A.@x" => "(import (importpath (\$ A) (macro_name x)))" "import A.B" => "(import (importpath A B))" "import A.B.C" => "(import (importpath A B C))" "import A.:+" => "(import (importpath A (quote-: +)))" @@ -893,9 +898,9 @@ tests = [ ((v=v"1.7",), "{a ;; b}") => "(bracescat (nrow-2 a b))" ((v=v"1.7",), "{a ;;;; b}") => "(bracescat (nrow-4 a b))" # Macro names can be keywords - "@end x" => "(macrocall @end x)" + "@end x" => "(macrocall (macro_name end) x)" # __dot__ macro - "@. x" => "(macrocall @. x)" + "@. x" => "(macrocall (macro_name .) x)" # cmd strings "``" => "(cmdstring-r \"\")" "`cmd`" => "(cmdstring-r \"cmd\")" @@ -1047,8 +1052,8 @@ tests = [ "public export=true foo, bar" => PARSE_ERROR # but these may be "public experimental=true foo, bar" => PARSE_ERROR # supported soon ;) "public(x::String) = false" => "(function-= (call public (::-i x String)) false)" - "module M; export @a; end" => "(module M (block (export @a)))" - "module M; public @a; end" => "(module M (block (public @a)))" + "module M; export @a; end" => "(module M (block (export (macro_name a))))" + "module M; public @a; end" => "(module M (block (public (macro_name a))))" "module M; export ⤈; end" => "(module M (block (export ⤈)))" "module M; public ⤈; end" => "(module M (block (public ⤈)))" "public = 4" => "(= public 4)" @@ -1056,7 +1061,7 @@ tests = [ "public() = 6" => "(function-= (call public) 6)" ]), JuliaSyntax.parse_stmts => [ - ((v = v"1.12",), "@callmemacro(b::Float64) = 2") => "(= (macrocall-p @callmemacro (::-i b Float64)) 2)" + ((v = v"1.12",), "@callmemacro(b::Float64) = 2") => "(= (macrocall-p (macro_name callmemacro) (::-i b Float64)) 2)" ], JuliaSyntax.parse_docstring => [ """ "notdoc" ] """ => "(string \"notdoc\")" @@ -1098,10 +1103,10 @@ parsestmt_test_specs = [ # The following may not be ideal error recovery! But at least the parser # shouldn't crash - "@(x y)" => "(macrocall (parens @x (error-t y)))" + "@(x y)" => "(macrocall (macro_name (parens x (error-t y))))" "|(&\nfunction" => "(call | (& (function (error (error)) (block (error)) (error-t))) (error-t))" - "@(" => "(macrocall (parens (error-t)))" - "x = @(" => "(= x (macrocall (parens (error-t))))" + "@(" => "(macrocall (macro_name (parens (error-t))))" + "x = @(" => "(= x (macrocall (macro_name (parens (error-t)))))" "function(where" => "(function (tuple-p where (error-t)) (block (error)) (error-t))" # Contextual keyword pairs must not be separated by newlines even within parens "(abstract\ntype X end)" => "(wrapper (parens abstract (error-t type X)) (error-t end ✘))" @@ -1189,9 +1194,9 @@ end @testset "Unicode normalization in tree conversion" begin # ɛµ normalizes to εμ @test parse_to_sexpr_str(JuliaSyntax.parse_eq, "\u025B\u00B5()") == "(call \u03B5\u03BC)" - @test parse_to_sexpr_str(JuliaSyntax.parse_eq, "@\u025B\u00B5") == "(macrocall @\u03B5\u03BC)" - @test parse_to_sexpr_str(JuliaSyntax.parse_eq, "\u025B\u00B5\"\"") == "(macrocall @\u03B5\u03BC_str (string-r \"\"))" - @test parse_to_sexpr_str(JuliaSyntax.parse_eq, "\u025B\u00B5``") == "(macrocall @\u03B5\u03BC_cmd (cmdstring-r \"\"))" + @test parse_to_sexpr_str(JuliaSyntax.parse_eq, "@\u025B\u00B5") == "(macrocall (macro_name \u03B5\u03BC))" + @test parse_to_sexpr_str(JuliaSyntax.parse_eq, "\u025B\u00B5\"\"") == "(macrocall (macro_name_str \u03B5\u03BC) (string-r \"\"))" + @test parse_to_sexpr_str(JuliaSyntax.parse_eq, "\u025B\u00B5``") == "(macrocall (macro_name_cmd \u03B5\u03BC) (cmdstring-r \"\"))" # · and · normalize to ⋅ @test parse_to_sexpr_str(JuliaSyntax.parse_eq, "a \u00B7 b") == "(call-i a \u22C5 b)" @test parse_to_sexpr_str(JuliaSyntax.parse_eq, "a \u0387 b") == "(call-i a \u22C5 b)"