-
Notifications
You must be signed in to change notification settings - Fork 15
/
uparse.jl
97 lines (85 loc) · 2.91 KB
/
uparse.jl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
module UnitsParse
import ..constructorof
import ..DEFAULT_QUANTITY_TYPE
import ..DEFAULT_DIM_TYPE
import ..DEFAULT_VALUE_TYPE
import ..Units: UNIT_SYMBOLS, UNIT_VALUES
import ..Constants: CONSTANT_SYMBOLS, CONSTANT_VALUES
import ..Constants
function _generate_units_import()
import_expr = :(import ..Units: _)
deleteat!(first(import_expr.args).args, 2)
for symb in UNIT_SYMBOLS
push!(first(import_expr.args).args, Expr(:., symb))
end
return import_expr
end
macro generate_units_import()
return _generate_units_import()
end
@generate_units_import
"""
uparse(s::AbstractString)
Parse a string containing an expression of units and return the
corresponding `Quantity` object with `Float64` value. For example,
`uparse("m/s")` would be parsed to `Quantity(1.0, length=1, time=-1)`.
Note that inside this expression, you also have access to the `Constants`
module. So, for example, `uparse("Constants.c^2 * Hz^2")` would evaluate to
the quantity corresponding to the speed of light multiplied by Hertz,
squared.
"""
function uparse(s::AbstractString)
ex = map_to_scope(Meta.parse(s))
ex = :($as_quantity($ex))
return eval(ex)::DEFAULT_QUANTITY_TYPE
end
as_quantity(q::DEFAULT_QUANTITY_TYPE) = q
as_quantity(x::Number) = convert(DEFAULT_QUANTITY_TYPE, x)
as_quantity(x) = error("Unexpected type evaluated: $(typeof(x))")
"""
u"[unit expression]"
Parse a string containing an expression of units and return the
corresponding `Quantity` object with `Float64` value. For example,
`u"km/s^2"` would be parsed to `Quantity(1000.0, length=1, time=-2)`.
Note that inside this expression, you also have access to the `Constants`
module. So, for example, `u"Constants.c^2 * Hz^2"` would evaluate to
the quantity corresponding to the speed of light multiplied by Hertz,
squared.
"""
macro u_str(s)
ex = map_to_scope(Meta.parse(s))
ex = :($as_quantity($ex))
return esc(ex)
end
function map_to_scope(ex::Expr)
if ex.head == :call
ex.args[2:end] = map(map_to_scope, ex.args[2:end])
return ex
elseif ex.head == :. && ex.args[1] == :Constants
@assert ex.args[2] isa QuoteNode
return lookup_constant(ex.args[2].value)
else
throw(ArgumentError("Unexpected expression: $ex. Only `:call` and `:.` (for `Constants`) are expected."))
end
end
function map_to_scope(sym::Symbol)
if sym in UNIT_SYMBOLS
return lookup_unit(sym)
elseif sym in CONSTANT_SYMBOLS
throw(ArgumentError("Symbol $sym found in `Constants` but not `Units`. Please use `u\"Constants.$sym\"` instead."))
else
throw(ArgumentError("Symbol $sym not found in `Units` or `Constants`."))
end
end
function map_to_scope(ex)
return ex
end
function lookup_unit(ex::Symbol)
i = findfirst(==(ex), UNIT_SYMBOLS)::Int
return UNIT_VALUES[i]
end
function lookup_constant(ex::Symbol)
i = findfirst(==(ex), CONSTANT_SYMBOLS)::Int
return CONSTANT_VALUES[i]
end
end