1111
1212"""
1313An Expr -> SyntaxTree transformation that should preserve semantics, but will
14- have low-quality provenance info (namely, each tree node will be associated with
15- the last seen LineNumberNode in the pre-order expr traversal).
14+ produce low-quality provenance info (namely, each tree node will be associated
15+ with the last seen LineNumberNode in the pre-order expr traversal).
1616
1717Last-resort option so that, for example, we can lower the output of old
1818Expr-producing macros. Always prefer re-parsing source text over using this.
1919
20- Supports parsed and/or macro-expanded exprs, but not lowered exprs
20+ Supports parsed and/or macro-expanded exprs, but not lowered exprs. Since
21+ macrocall and quote may occur in this tree, we can't throw errors for malformed
22+ syntax; we can only convert known-good exprs that have a SyntaxTree equivalent.
2123"""
2224function expr_to_syntaxtree (@nospecialize (e), lnn:: Union{LineNumberNode, Nothing} = nothing )
2325 graph = ensure_attributes! (
4648 return out
4749end
4850
49- function _expr_replace! (@nospecialize (e), replace_pred:: Function , replacer! :: Function ,
51+ function _expr_replace (@nospecialize (e), replace_pred:: Function , replacer:: Function ,
5052 recurse_pred= (@nospecialize e)-> true )
5153 if replace_pred (e)
52- replacer! (e)
53- end
54- if e isa Expr && recurse_pred (e)
55- for a in e. args
56- _expr_replace! (a, replace_pred, replacer!, recurse_pred)
57- end
54+ replacer (e)
55+ elseif e isa Expr && recurse_pred (e)
56+ Expr (e. head, [_expr_replace (a, replace_pred, replacer, recurse_pred) for a in e. args]. .. )
5857 end
5958end
6059
6160function _to_iterspec (exs:: Vector , is_generator:: Bool )
6261 if length (exs) === 1 && exs[1 ]. head === :filter
63- @assert length (exs[1 ]. args) >= 2
64- return Expr (:filter , _to_iterspec (exs[1 ]. args[2 : end ], true ), exs[1 ]. args[1 ])
62+ return if length (exs[1 ]. args) >= 2
63+ Expr (:filter , _to_iterspec (exs[1 ]. args[2 : end ], true ), exs[1 ]. args[1 ])
64+ else # invalid
65+ Expr (:filter , exs[1 ]. args[1 ])
66+ end
6567 end
6668 outex = Expr (:iteration )
6769 for e in exs
@@ -71,7 +73,7 @@ function _to_iterspec(exs::Vector, is_generator::Bool)
7173 end
7274 elseif e. head === :(= )
7375 push! (outex. args, Expr (:in , e. args... ))
74- else
76+ else # invalid. TODO : at least find something that round-trips.
7577 @assert false " unknown iterspec in $e "
7678 end
7779 end
@@ -253,14 +255,14 @@ function _insert_convert_expr(@nospecialize(e), graph::SyntaxGraph, src::SourceA
253255 elseif e. head === :comparison
254256 for i = 2 : 2 : length (child_exprs)
255257 op,op_esc = unwrap_esc (child_exprs[i])
256- @assert op isa Symbol
257- op_s = string (op)
258- if is_dotted_operator (op_s)
259- child_exprs[i] = Expr (:., op_esc (Symbol (op_s[2 : end ])))
258+ if op isa Symbol
259+ op_s = string (op)
260+ if is_dotted_operator (op_s)
261+ child_exprs[i] = Expr (:., op_esc (Symbol (op_s[2 : end ])))
262+ end
260263 end
261264 end
262- elseif e. head === :macrocall
263- @assert nargs >= 2
265+ elseif e. head === :macrocall && nargs >= 2
264266 a1,a1_esc = unwrap_esc (e. args[1 ])
265267 child_exprs = collect_expr_parameters (e, 3 )
266268 if child_exprs[2 ] isa LineNumberNode
@@ -289,8 +291,7 @@ function _insert_convert_expr(@nospecialize(e), graph::SyntaxGraph, src::SourceA
289291 elseif a1. name === Symbol (" @big_str" )
290292 end
291293 end
292- elseif e. head === Symbol (" '" )
293- @assert nargs === 1
294+ elseif e. head === Symbol (" '" ) && nargs === 1
294295 st_k = K " call"
295296 child_exprs = Any[e. head, e. args[1 ]]
296297 elseif e. head === :. && nargs === 2
@@ -302,11 +303,9 @@ function _insert_convert_expr(@nospecialize(e), graph::SyntaxGraph, src::SourceA
302303 elseif a2 isa QuoteNode
303304 child_exprs[2 ] = a2_esc (a2. value)
304305 end
305- elseif e. head === :for
306- @assert nargs === 2
306+ elseif e. head === :for && nargs === 2
307307 child_exprs = Any[_to_iterspec (Any[e. args[1 ]], false ), e. args[2 ]]
308- elseif e. head === :where
309- @assert nargs >= 2
308+ elseif e. head === :where && nargs >= 2
310309 e2,_ = unwrap_esc (e. args[2 ])
311310 if ! (e2 isa Expr && e2. head === :braces )
312311 child_exprs = Any[e. args[1 ], Expr (:braces , e. args[2 : end ]. .. )]
@@ -315,7 +314,7 @@ function _insert_convert_expr(@nospecialize(e), graph::SyntaxGraph, src::SourceA
315314 child_exprs = collect_expr_parameters (e, 1 )
316315 elseif e. head in (:curly , :ref )
317316 child_exprs = collect_expr_parameters (e, 2 )
318- elseif e. head === :try
317+ elseif e. head === :try && nargs >= 3
319318 child_exprs = Any[e. args[1 ]]
320319 # Expr:
321320 # (try (block ...) var (block ...) [block ...] [block ...])
@@ -343,22 +342,21 @@ function _insert_convert_expr(@nospecialize(e), graph::SyntaxGraph, src::SourceA
343342 st_k = K " generator"
344343 child_exprs = Any[]
345344 next = e
346- while next. head === :flatten
347- @assert next. args[1 ]. head === :generator
345+ while next. head === :flatten && length (next. args) >= 1 && next. args[1 ]. head === :generator
348346 push! (child_exprs, _to_iterspec (next. args[1 ]. args[2 : end ], true ))
349347 next = next. args[1 ]. args[1 ]
350348 end
351- @assert next. head === :generator
352- push! (child_exprs, _to_iterspec (next. args[2 : end ], true ))
353- pushfirst! (child_exprs, next. args[1 ])
349+ if next. head === :generator
350+ push! (child_exprs, _to_iterspec (next. args[2 : end ], true ))
351+ pushfirst! (child_exprs, next. args[1 ])
352+ end
354353 elseif e. head === :ncat || e. head === :nrow
355354 dim = unwrap_esc_ (popfirst! (child_exprs))
356355 st_flags |= JS. set_numeric_flags (dim)
357356 elseif e. head === :typed_ncat
358357 st_flags |= JS. set_numeric_flags (unwrap_esc_ (e. args[2 ]))
359358 deleteat! (child_exprs, 2 )
360- elseif e. head === :(-> )
361- @assert nargs === 2
359+ elseif e. head === :(-> ) && nargs === 2
362360 a1, a1_esc = unwrap_esc (e. args[1 ])
363361 if a1 isa Expr && a1. head === :block
364362 # Expr parsing fails to make :parameters here...
@@ -397,13 +395,12 @@ function _insert_convert_expr(@nospecialize(e), graph::SyntaxGraph, src::SourceA
397395 src = maybe_extract_lnn (e. args[2 ], src)
398396 child_exprs[2 ] = maybe_unwrap_arg (e. args[2 ])
399397 end
400- elseif e. head === :module
401- @assert nargs === 3
402- if ! e. args[1 ]
398+ elseif e. head === :module && nargs === 3
399+ if e. args[1 ] === false
403400 st_flags |= JS. BARE_MODULE_FLAG
404401 end
405402 child_exprs = Any[e. args[2 ], e. args[3 ]]
406- elseif e. head === :do
403+ elseif e. head === :do && nargs === 2
407404 # Expr:
408405 # (do (call f args...) (-> (tuple lam_args...) (block ...)))
409406 # SyntaxTree:
@@ -421,21 +418,19 @@ function _insert_convert_expr(@nospecialize(e), graph::SyntaxGraph, src::SourceA
421418 st_k = K " call"
422419 end
423420 child_exprs = Any[callargs... , Expr (:do_lambda , e. args[2 ]. args... )]
424- elseif e. head === :let
425- if nargs >= 1
426- a1,_ = unwrap_esc (e. args[1 ])
427- if ! (a1 isa Expr && a1. head === :block )
428- child_exprs[1 ] = Expr (:block , e. args[1 ])
429- end
421+ elseif e. head === :let && nargs >= 1
422+ a1,_ = unwrap_esc (e. args[1 ])
423+ if ! (a1 isa Expr && a1. head === :block )
424+ child_exprs[1 ] = Expr (:block , e. args[1 ])
430425 end
431- elseif e. head === :struct
426+ elseif e. head === :struct && nargs >= 1
432427 e. args[1 ] && (st_flags |= JS. MUTABLE_FLAG)
433428 child_exprs = child_exprs[2 : end ]
434429 # TODO handle docstrings after refactor
435430 elseif (e. head === :using || e. head === :import )
436- _expr_replace! (e ,
437- (e)-> (e isa Expr && e . head === :.),
438- (e) -> (e . head = :importpath ))
431+ e2 = _expr_replace (e, (e) -> (e isa Expr && e . head === :.) ,
432+ (e)-> Expr ( :importpath , e . args ... ))
433+ child_exprs = e2 . args
439434 elseif e. head === :kw
440435 st_k = K " ="
441436 elseif e. head in (:local , :global ) && nargs > 1
@@ -463,36 +458,37 @@ function _insert_convert_expr(@nospecialize(e), graph::SyntaxGraph, src::SourceA
463458 if e. args[1 ] isa Expr && e. args[1 ]. head === :purity
464459 st_k = K " meta"
465460 child_exprs = [Expr (:quoted_symbol , :purity ), Base. EffectsOverride (e. args[1 ]. args... )]
466- else
467- @assert e. args[1 ] isa Symbol
468- if e. args[1 ] === :nospecialize
469- if nargs > 2
470- st_k = K " block"
471- # Kick the can down the road (should only be simple atoms?)
472- child_exprs = map (c-> Expr (:meta , :nospecialize , c), child_exprs[2 : end ])
473- else
474- st_id, src = _insert_convert_expr (e. args[2 ], graph, src)
475- setmeta! (SyntaxTree (graph, st_id); nospecialize= true )
476- return st_id, src
477- end
478- elseif e. args[1 ] in (:inline , :noinline , :generated , :generated_only ,
479- :max_methods , :optlevel , :toplevel , :push_loc , :pop_loc ,
480- :no_constprop , :aggressive_constprop , :specialize , :compile , :infer ,
481- :nospecializeinfer , :force_compile , :propagate_inbounds , :doc )
482- # TODO : Some need to be handled in lowering
483- for (i, ma) in enumerate (e. args)
484- if ma isa Symbol
485- # @propagate_inbounds becomes (meta inline
486- # propagate_inbounds), but usually(?) only args[1] is
487- # converted here
488- child_exprs[i] = Expr (:quoted_symbol , e. args[i])
489- end
490- end
461+ elseif nargs === 0
462+ # pass
463+ elseif e. args[1 ] === :nospecialize
464+ if nargs > 2
465+ st_k = K " block"
466+ # Kick the can down the road (should only be simple atoms?)
467+ child_exprs = map (c-> Expr (:meta , :nospecialize , c), child_exprs[2 : end ])
491468 else
492- # Can't throw a hard error; it is explicitly tested that meta can take arbitrary keys.
493- @error ( " Unknown meta form at $src : ` $e ` \n $( sprint (dump, e)) " )
494- child_exprs[ 1 ] = Expr ( :quoted_symbol , e . args[ 1 ])
469+ st_id, src = _insert_convert_expr (e . args[ 2 ], graph, src)
470+ setmeta! ( SyntaxTree (graph, st_id); nospecialize = true )
471+ return st_id, src
495472 end
473+ elseif e. args[1 ] in (:inline , :noinline , :generated , :generated_only ,
474+ :max_methods , :optlevel , :toplevel , :push_loc , :pop_loc ,
475+ :no_constprop , :aggressive_constprop , :specialize , :compile , :infer ,
476+ :nospecializeinfer , :force_compile , :propagate_inbounds , :doc )
477+ # TODO : Some need to be handled in lowering
478+ for (i, ma) in enumerate (e. args)
479+ if ma isa Symbol
480+ # @propagate_inbounds becomes (meta inline
481+ # propagate_inbounds), but usually(?) only args[1] is
482+ # converted here
483+ child_exprs[i] = Expr (:quoted_symbol , e. args[i])
484+ end
485+ end
486+ else
487+ # Can't throw a hard error; it is explicitly tested that meta can
488+ # take arbitrary keys.
489+ @error (" Unknown meta form at $src : `$e `\n $(sprint (dump, e)) " )
490+ st_k = K " meta"
491+ child_exprs[1 ] = Expr (:quoted_symbol , e. args[1 ])
496492 end
497493 elseif e. head === :scope_layer
498494 @assert nargs === 2
@@ -501,27 +497,22 @@ function _insert_convert_expr(@nospecialize(e), graph::SyntaxGraph, src::SourceA
501497 st_id, src = _insert_convert_expr (e. args[1 ], graph, src)
502498 setattr! (graph, st_id, scope_layer= e. args[2 ])
503499 return st_id, src
504- elseif e. head === :symbolicgoto || e. head === :symboliclabel
505- @assert nargs === 1
500+ elseif e. head === :symbolicgoto || e. head === :symboliclabel && nargs === 1
506501 st_k = e. head === :symbolicgoto ? K " symbolic_label" : K " symbolic_goto"
507502 st_attrs[:name_val ] = string (e. args[1 ])
508503 child_exprs = nothing
509- elseif e. head in (:inline , :noinline )
510- @assert nargs === 1 && e. args[1 ] isa Bool
504+ elseif e. head in (:inline , :noinline ) && nargs === 1 && e. args[1 ] isa Bool
511505 # TODO : JuliaLowering doesn't accept this (non-:meta) form yet
512506 st_k = K " TOMBSTONE"
513507 child_exprs = nothing
514- elseif e. head === :inbounds
515- @assert nargs === 1 && typeof (e. args[1 ]) in (Symbol, Bool)
508+ elseif e. head === :inbounds && nargs === 1 && typeof (e. args[1 ]) in (Symbol, Bool)
516509 # TODO : JuliaLowering doesn't accept this form yet
517510 st_k = K " TOMBSTONE"
518511 child_exprs = nothing
519- elseif e. head === :core
520- @assert nargs === 1
521- @assert e. args[1 ] isa Symbol
512+ elseif e. head === :core && nargs === 1 && e. args[1 ] isa Symbol
522513 st_attrs[:name_val ] = string (e. args[1 ])
523514 child_exprs = nothing
524- elseif e. head === :islocal || e. head === :isglobal
515+ elseif ( e. head === :islocal || e. head === :isglobal ) && nargs === 1
525516 st_k = K " extension"
526517 child_exprs = [Expr (:quoted_symbol , e. head), e. args[1 ]]
527518 elseif e. head === :block && nargs >= 1 &&
@@ -563,10 +554,12 @@ function _insert_convert_expr(@nospecialize(e), graph::SyntaxGraph, src::SourceA
563554 end
564555
565556 # ---------------------------------------------------------------------------
566- # Throw if this function isn't complete . Finally, insert a new node into the
567- # graph and recurse on child_exprs
557+ # Omit tombstones . Unrecognized expr heads become K"expr_syntax". Finally,
558+ # insert a new node into the graph and recurse on child_exprs
568559 if st_k === K " None"
569- error (" Unknown expr head at $src : `$(e. head) `\n $(sprint (dump, e)) " )
560+ st_k = K " expr_syntax"
561+ st_attrs[:value ] = e
562+ child_exprs = nothing
570563 elseif st_k === K " TOMBSTONE"
571564 return nothing , src
572565 end
0 commit comments