@@ -426,13 +426,26 @@ module SymbolicUnits
426426
427427 import .. UNIT_SYMBOLS
428428 import .. CONSTANT_SYMBOLS
429+ import .. ALL_MAPPING
429430 import .. SymbolicDimensionsSingleton
430431 import .. constructorof
431432 import .. DEFAULT_SYMBOLIC_QUANTITY_TYPE
432433 import .. DEFAULT_SYMBOLIC_QUANTITY_OUTPUT_TYPE
433434 import .. DEFAULT_VALUE_TYPE
434435 import .. DEFAULT_DIM_BASE_TYPE
436+ import .. INDEX_TYPE
437+ import .. UnionAbstractQuantity
435438 import .. WriteOnceReadMany
439+ import .. disambiguate_constant_symbol
440+
441+ symbolic_unit_from_symbol (unit:: Symbol ) = constructorof (DEFAULT_SYMBOLIC_QUANTITY_TYPE)(
442+ DEFAULT_VALUE_TYPE (1.0 ),
443+ SymbolicDimensionsSingleton {DEFAULT_DIM_BASE_TYPE} (unit)
444+ )
445+ symbolic_constant_from_symbol (unit:: Symbol ) = constructorof (DEFAULT_SYMBOLIC_QUANTITY_TYPE)(
446+ DEFAULT_VALUE_TYPE (1.0 ),
447+ SymbolicDimensionsSingleton {DEFAULT_DIM_BASE_TYPE} (disambiguate_constant_symbol (unit))
448+ )
436449
437450 # Lazily create unit symbols (since there are so many)
438451 module Constants
@@ -479,11 +492,7 @@ module SymbolicUnits
479492 # Non-eval version of `update_symbolic_unit_values!` for registering units in
480493 # an external module.
481494 function update_external_symbolic_unit_value (unit)
482- unit = constructorof (DEFAULT_SYMBOLIC_QUANTITY_TYPE)(
483- DEFAULT_VALUE_TYPE (1.0 ),
484- SymbolicDimensionsSingleton {DEFAULT_DIM_BASE_TYPE} (unit)
485- )
486- push! (SYMBOLIC_UNIT_VALUES, unit)
495+ push! (SYMBOLIC_UNIT_VALUES, symbolic_unit_from_symbol (unit))
487496 end
488497
489498 """
@@ -512,39 +521,50 @@ module SymbolicUnits
512521 as_quantity (x:: Number ) = convert (DEFAULT_SYMBOLIC_QUANTITY_OUTPUT_TYPE, x)
513522 as_quantity (x) = error (" Unexpected type evaluated: $(typeof (x)) " )
514523
515- @unstable function map_to_scope (ex:: Expr )
524+ @unstable map_to_scope (ex:: Expr ) = map_to_scope (@__MODULE__ , ex)
525+ @unstable function map_to_scope (mod:: Module , ex:: Expr )
516526 if ! (ex. head == :call ) && ! (ex. head == :. && ex. args[1 ] == :Constants )
517527 throw (ArgumentError (" Unexpected expression: $ex . Only `:call` and `:.` (for `SymbolicConstants`) are expected." ))
518528 end
519529 if ex. head == :call
520- ex. args[2 : end ] = map (map_to_scope, ex. args[2 : end ])
530+ ex. args[2 : end ] = map (arg -> map_to_scope (mod, arg) , ex. args[2 : end ])
521531 return ex
522532 else # if ex.head == :. && ex.args[1] == :Constants
523533 @assert ex. args[2 ] isa QuoteNode
524- return lookup_constant (ex. args[2 ]. value)
534+ return Expr ( :call , GlobalRef ( @__MODULE__ , : lookup_constant), QuoteNode (ex. args[2 ]. value) )
525535 end
526536 end
527- function map_to_scope (sym:: Symbol )
528- if sym in UNIT_SYMBOLS
537+ map_to_scope (sym:: Symbol ) = map_to_scope (@__MODULE__ , sym)
538+ function map_to_scope (mod:: Module , sym:: Symbol )
539+ if sym in UNIT_SYMBOLS || _has_quantity_binding (mod, sym)
529540 # return at end
530541 elseif sym in CONSTANT_SYMBOLS
531542 throw (ArgumentError (" Symbol $sym found in `Constants` but not `Units`. Please use `us\" Constants.$sym \" ` instead." ))
532543 else
533544 throw (ArgumentError (" Symbol $sym not found in `Units` or `Constants`." ))
534545 end
535- return lookup_unit (sym)
546+ return Expr ( :call , GlobalRef ( @__MODULE__ , : lookup_unit), mod, QuoteNode (sym) )
536547 end
537- function map_to_scope (ex)
538- return ex
548+ map_to_scope (ex) = ex
549+ map_to_scope (:: Module , ex) = ex
550+
551+ function _has_quantity_binding (mod:: Module , sym:: Symbol )
552+ return isdefined (mod, sym) && Base. invokelatest (getproperty, mod, sym) isa UnionAbstractQuantity
539553 end
540- function lookup_unit (ex:: Symbol )
541- i = findfirst (== (ex), UNIT_SYMBOLS):: Int
542- return as_quantity (SYMBOLIC_UNIT_VALUES[i])
554+ function _ensure_registered (mod:: Module , sym:: Symbol )
555+ if iszero (get (ALL_MAPPING, sym, INDEX_TYPE (0 ))) && _has_quantity_binding (mod, sym)
556+ update_all_values = getfield (parentmodule (@__MODULE__ ), :update_all_values )
557+ unit = Base. invokelatest (getproperty, mod, sym)
558+ Base. invokelatest (update_all_values, sym, unit)
559+ end
560+ return nothing
543561 end
544- function lookup_constant ( ex:: Symbol )
545- i = findfirst ( == (ex), CONSTANT_SYMBOLS) :: Int
546- return as_quantity (SYMBOLIC_CONSTANT_VALUES[i] )
562+ function lookup_unit (mod :: Module , ex:: Symbol )
563+ _ensure_registered (mod, ex)
564+ return as_quantity (symbolic_unit_from_symbol (ex) )
547565 end
566+ lookup_unit (ex:: Symbol ) = lookup_unit (@__MODULE__ , ex)
567+ lookup_constant (ex:: Symbol ) = as_quantity (symbolic_constant_from_symbol (ex))
548568end
549569
550570import . SymbolicUnits: as_quantity, sym_uparse, SymbolicConstants, map_to_scope
@@ -565,7 +585,7 @@ module. So, for example, `us"Constants.c^2 * Hz^2"` would evaluate to
565585namespace collisions, a few physical constants are automatically converted.
566586"""
567587macro us_str (s)
568- ex = map_to_scope (Meta. parse (s))
588+ ex = map_to_scope (__module__, Meta. parse (s))
569589 ex = :($ as_quantity ($ ex))
570590 return esc (ex)
571591end
0 commit comments