Skip to content

Commit 4f6dbe5

Browse files
authored
Cleanup parsing of macros (#66)
* Cleanup parsing of macros The symbol macro W"" no longer supports $ interpolation The expression macro W`` supports $ interpolation, but now does it directly rather than converting via text * fix printing of exponents * update README
1 parent 873050b commit 4f6dbe5

File tree

6 files changed

+149
-40
lines changed

6 files changed

+149
-40
lines changed

README.md

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,16 @@ julia> W"Sin"
2020
W"Sin"
2121

2222
julia> sin1 = W"Sin"(1.0)
23-
W"Sin"(1.0)
23+
W`Sin[1.0]`
2424

2525
julia> sinx = W"Sin"(W"x")
26-
W"Sin"(W"x")
26+
W`Sin[x]`
2727
```
2828

2929
To parse an expression in the Wolfram Language, you can use the `W` cmd macro (note the backticks):
3030
```julia
3131
julia> W`Sin[1]`
32-
W"Sin"(1)
32+
W`Sin[1]`
3333
```
3434

3535
`weval` evaluates an expression:
@@ -38,10 +38,10 @@ julia> weval(sin1)
3838
0.8414709848078965
3939

4040
julia> weval(sinx)
41-
W"Sin"(W"x")
41+
W`Sin[x]`
4242

4343
julia> weval(W"Integrate"(sinx, (W"x", 0, 1)))
44-
W"Plus"(1, W"Times"(-1, W"Cos"(1)))
44+
W`Plus[1, Times[-1, Cos[1]]]`
4545
```
4646

4747
Keyword arguments can be used to pass local variables
@@ -58,24 +58,25 @@ MathLink also overloads the `+`, `-`, `*`, `/` operations.
5858
julia> using MathLink
5959

6060
julia> W"a"+W"b"
61-
W"Plus"(W"a",W"b")
61+
W`Plus[a, b]`
6262

6363
julia> W"a"+W"a"
64-
W"Plus"(W"a",W"a")
64+
W`Plus[a, a]`
6565

6666
julia> W"a"-W"a"
67-
W"Plus"(W"a",W"Minus"(W"a"))
67+
W`Plus[a, Minus[a]]`
6868
```
6969

7070
One can toggle automatic use of `weval` on-and-off using `set_GreedyEval(x::Bool)`
7171

7272
```julia
73-
julia>set_GreedyEval(true)
73+
julia> set_GreedyEval(true);
74+
7475
julia> W"a"+W"b"
75-
W"Plus"(W"a",W"b")
76+
W`Plus[a, b]`
7677

7778
julia> W"a"+W"a"
78-
W"Times"(2,W"a")
79+
W`Times[2, a]`
7980

8081
julia> W"a"-W"a"
8182
0
@@ -88,23 +89,23 @@ The package also contains extensions to handle fractions.
8889

8990
```julia
9091
julia> weval(1//2)
91-
W"Rational"(1, 2)
92+
W`Rational[1, 2]`
9293

9394
julia> (4//5)*W"a"
94-
W"Times"(W"Rational"(4, 5), W"a")
95+
W`Times[Rational[4, 5], a]`
9596

9697
julia> W"a"/(4//5)
97-
W"Times"(W"Rational"(5, 4), W"a")
98+
W`Times[Rational[5, 4], a]`
9899
```
99100

100101
and complex numbers
101102

102103
```julia
103104
julia> im*W"a"
104-
W"Times"(W"Complex"(0, 1), W"a")
105+
W`Times[Complex[0, 1], a]`
105106

106107
julia> im*(im*W"c")
107-
W"Times"(-1, W"c")
108+
W`Times[-1, c]`
108109
```
109110

110111

src/display.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ export HasGraphicsHead, HasRecursiveGraphicsHead, W2Tex
2626

2727

2828
#### Code to produce LaTex strings
29-
W2Tex(x::WTypes) = weval(W`ToString@TeXForm[#]&`(x))
29+
W2Tex(x::WTypes) = weval(W"ToString"(W"TeXForm"(x)))
3030

3131
#### Allow latex string to be shown when supported. Relevant for the jupyter notebook.
3232

src/eval.jl

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,55 @@ end
5959
wevalstr(T, expr) = wevalstr(_defaultlink(), T, expr)
6060
wevalstr(expr) = wevalstr(Any, expr)
6161

62+
"""
63+
MathLink.parseexpr(str::AbstractString)
64+
65+
Parse a string `str` as a Wolfram Language expression.
66+
"""
6267
function parseexpr(str::AbstractString)
6368
r = weval(W"ToExpression"(str, W"StandardForm", W"Hold"))
6469
r.args[1]
6570
end
6671

72+
"""
73+
W`expr`
74+
75+
Parse a string `expr` as a Wolfram Language expression.
76+
"""
6777
macro W_cmd(str)
68-
quote parseexpr($(esc(Meta.parse("\"$(escape_string(str))\"")))) end
78+
#quote parseexpr($(esc(Meta.parse("\"$(escape_string(str))\"")))) end
79+
string_expr = Meta.parse("\"$(escape_string(str))\"")
80+
subst_dict = Dict{WSymbol,Any}()
81+
if string_expr isa String
82+
string = string_expr
83+
elseif string_expr isa Expr && string_expr.head == :string
84+
for i in eachindex(string_expr.args)
85+
arg = string_expr.args[i]
86+
if !(arg isa String)
87+
sym = weval(W"Unique"(W"JuliaWSTP"))
88+
subst_dict[sym] = arg
89+
string_expr.args[i] = "($(sym.name))"
90+
end
91+
end
92+
string = join(string_expr.args)
93+
else
94+
error("Invalid string expression: $string_expr")
95+
end
96+
wexpr = parseexpr(string)
97+
to_julia_expr(wexpr, subst_dict)
98+
end
99+
100+
function to_julia_expr(wexpr::WExpr, subst_dict)
101+
head = to_julia_expr(wexpr.head, subst_dict)
102+
args = map(x->to_julia_expr(x, subst_dict), wexpr.args)
103+
:(WExpr($head, [$(args...)]))
69104
end
105+
function to_julia_expr(wsym::WSymbol, dict)
106+
if haskey(dict, wsym)
107+
return esc(dict[wsym])
108+
else
109+
return wsym
110+
end
111+
end
112+
to_julia_expr(val, dict) = val
113+

src/link.jl

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,23 @@ function open!(link::Link, args::AbstractString)
3535
return link
3636
end
3737

38+
function open!(link::Link, args::Vector)
39+
# MLOpenString
40+
# local link
41+
err = Ref{Cint}()
42+
ptr = ccall((:MLOpenArgcArgv, mlib), CLink,
43+
(Env, Cint, Ptr{Cstring}, Ptr{Cint}),
44+
env, length(args), args, err)
45+
if err[] != 0
46+
error("Could not open MathLink link")
47+
end
48+
link.ptr = ptr
49+
50+
refcount_inc()
51+
finalizer(close, link)
52+
return link
53+
end
54+
3855
open(args::AbstractString) = open!(Link(C_NULL), args)
3956

4057
function Base.close(link::Link)
@@ -45,9 +62,37 @@ function Base.close(link::Link)
4562
end
4663
end
4764

65+
function getname(link::Link)
66+
# WSLINK WSGetLinkName(WSLINK link)
67+
ptr = ccall((:MLUTF8LinkName, mlib), Cstring, (CLink,), link)
68+
ptr == C_NULL && throw(MathLinkError(link))
69+
str = unsafe_string(ptr)
70+
ccall((:MLReleaseUTF8LinkName, mlib), Cvoid, (CLink,Cstring), link, ptr)
71+
return str
72+
end
73+
74+
function duplicate(link::Link, name::AbstractString)
75+
# WSLINK WSDuplicateLink(WSLINK link, int *errp)
76+
err = Ref{Cint}()
77+
ptr = ccall((:MLDuplicateLink, mlib), CLink, (CLink, Cstring, Ptr{Cint}), link, name, err)
78+
if err[] != 0
79+
throw(MathLinkError(link))
80+
end
81+
newlink = Link(ptr)
82+
refcount_inc()
83+
finalizer(close, newlink)
84+
return newlink
85+
end
86+
87+
4888
function _defaultlink()
4989
if defaultlink.ptr == C_NULL
50-
args = "-linkname '\"$mker\" -mathlink' -linkmode launch"
90+
args = [
91+
"-linkname",
92+
"\"$mker\" -mathlink",
93+
"-linkmode",
94+
"launch",
95+
]
5196
open!(defaultlink, args)
5297

5398
# Ignore first input packet

src/types.jl

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ Base.show(io::IO, s::WSymbol) = print(io, 'W', '"', s.name, '"')
4040
Base.:(==)(a::WSymbol, b::WSymbol) = a.name == b.name
4141

4242
macro W_str(str)
43-
quote WSymbol($(esc(Meta.parse("\"$(escape_string(str))\"")))) end
43+
:(WSymbol($str))
4444
end
4545

4646
"""
@@ -81,7 +81,7 @@ julia> weval(W`Function[x,x+1]`(2))
8181
3
8282
```
8383
"""
84-
struct WExpr <: WTypes
84+
mutable struct WExpr <: WTypes
8585
head
8686
args
8787
end
@@ -94,20 +94,40 @@ function Base.:(==)(a::WExpr, b::WExpr)
9494
return true
9595
end
9696

97-
function Base.show(io::IO, w::WExpr)
98-
show(io, w.head)
99-
print(io, '(')
100-
isfirst = true
101-
for arg in w.args
102-
if !isfirst
97+
function Base.show(io::IO, wexpr::WExpr)
98+
print(io, "W`")
99+
print_wexpr(io, wexpr)
100+
print(io, "`")
101+
end
102+
103+
function print_wexpr(io::IO, wexpr::WExpr)
104+
print_wexpr(io, wexpr.head)
105+
print(io, '[')
106+
if length(wexpr.args) > 0
107+
print_wexpr(io, wexpr.args[1])
108+
for arg in wexpr.args[2:end]
103109
print(io, ", ")
104-
else
105-
isfirst = false
110+
print_wexpr(io, arg)
106111
end
107-
show(io, arg)
108112
end
109-
print(io, ')')
113+
print(io, ']')
114+
end
115+
print_wexpr(io::IO, wsym::WSymbol) = print(io, wsym.name)
116+
print_wexpr(io::IO, wreal::WReal) = print(io, wreal.value)
117+
print_wexpr(io::IO, wint::WInteger) = print(io, wint.value)
118+
function print_wexpr(io::IO, x::Float64)
119+
s = split(string(x),'e')
120+
if length(s) == 1
121+
print(io, x)
122+
else
123+
print(io, s[1], "*^", s[2])
124+
end
110125
end
126+
print_wexpr(io::IO, x::Int) = print(io, x)
127+
print_wexpr(io::IO, x::String) = show(io, x)
128+
print_wexpr(io::IO, x) = print(io, "\$(", x, ")")
129+
130+
111131

112132

113133
function (w::WSymbol)(args...; kwargs...)

test/runtests.jl

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -137,9 +137,11 @@ end
137137

138138
@testset "interpolation" begin
139139
x = exp(1)
140-
@test W`Sin[$x]` == W"Sin"(x)
141-
140+
@test W`Sin[$x]` == W"Sin"(x)
142141
@test W`Cos[$(log(2))]` == W"Cos"(log(2))
142+
143+
@test W`Sin[$(1.2e19)]` == W`Sin[1.2*^19]`
144+
@test string(W`Sin[$(1.2e19)]`) == "W`Sin[1.2*^19]`"
143145
end
144146

145147

@@ -284,15 +286,12 @@ end
284286

285287
###Testing that MIME form exists for the text/latex option of show.
286288
io = IOBuffer();
287-
ioc = IOContext(io, :limit => true, :displaysize => (10, 10))
288-
show(ioc,"text/plain",W"a")
289-
@test String(take!(io)) == "W\"a\""
290-
show(ioc,"text/plain",W"a"+W"b")
291-
@test String(take!(io)) == "W\"Plus\"(W\"a\", W\"b\")"
289+
context = IOContext(io, :limit => true, :displaysize => (10, 10))
290+
@test sprint(show,"text/plain",W"a"; context) == "W\"a\""
291+
@test sprint(show,"text/plain",W"a"+W"b"; context) == "W`Plus[a, b]`"
292292

293293
set_texOutput(true)
294-
show(ioc,"text/latex",W"a"+W"b")
295-
@test String(take!(io)) == "\$a+b\$"
294+
@test sprint(show,"text/latex",W"a"+W"b") == "\$a+b\$"
296295
@test showable("text/latex",W"a"+W"b")
297296
set_texOutput(false)
298297
@test !showable("text/latex",W"a"+W"b")

0 commit comments

Comments
 (0)