Skip to content

Solving error with $ not escaping correctly. #99

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Mar 15, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,32 @@ using Test
Printing in Jupyter notebooks is, by default, done in latex.
This can be turned off with the command `MathLink.set_texOutput(false)`

## Escaping dollars for Mathematica
The `$` sign has a special meaning in Julia, but it does not in Mathematica. We can send dollar signs to Mathematica that same way we add them to normal strings. Below are a few exampoles of how it works:

using Test
x = exp(1)
@test W`$x` == x
@test W`\$` == W"$"
@test W`\$x` == W"$x"
@test W`$x +\$` == x+W"$"
@test W`"\$"` == "\$"
@test W`"a"` == "a"
@test W`{a -> b}` == W"List"(W"Rule"(W"a",W"b"))
@test W`{"a" -> "b"}` == W"List"(W"Rule"("a","b"))
@test W`"a" -> "b"` == W"Rule"("a","b")
@test W`a -> b` == W"Rule"(W"a",W"b")
@test W`"b(\$)a"` == "b(\$)a"
@test W`"b\\\$"` == "b\\\$"
@test W`"b\$"` == "b\$"
@test W`"\$a"` == "\$a"
@test W`"\$" -> "b"` == W"Rule"("\$","b")
@test W`{"\$" -> "b"}` == W"List"(W"Rule"("\$","b"))
@test W`{"a" -> "\$"}` == W"List"(W"Rule"("a","\$"))
@test W`{a -> "\$"}` == W"List"(W"Rule"(W"a","\$"))



## Installation Troubleshoot
The package requires an installation of either [Mathematica](http://www.wolfram.com/mathematica/) or the free [Wolfram Engine](https://www.wolfram.com/engine/). It will attempt to find the installation at build time; if this fails, you will need to set the following [environment variables](https://docs.julialang.org/en/v1/manual/environment-variables/):
- `JULIA_MATHKERNEL`: the path of the MathKernel executable
Expand Down
32 changes: 30 additions & 2 deletions src/eval.jl
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,16 @@ wevalstr(expr) = wevalstr(Any, expr)
Parse a string `str` as a Wolfram Language expression.
"""
function parseexpr(str::AbstractString)
#####The escaping of dollars was a bit messy. I'm letting these comment stay here for a while untill a better documentation is in place.


#println("parseexpr: '",str,"'")
#dump(str)
UnescapedDollar=unescape_dollar(str)
#println("UnescapedDollar: '",UnescapedDollar,"'")
#dump(UnescapedDollar)
r = weval(W"ToExpression"(UnescapedDollar, W"StandardForm", W"Hold"))
#println("r: '",r,"'")
r.args[1]
end

Expand All @@ -80,12 +88,29 @@ macro W_cmd(str)
# the function parseexpr(string). The result is then back-converted to a julia MathLink expression.
#quote parseexpr($(esc(Meta.parse("\"$(escape_string(str))\"")))) end


#####The escaping of dollars was a bit messy. I'm letting these comment stay here for a while untill a better documentation is in place.

###Adding a set of string escapes for correct parsing
#println("------")
#println("str: '",str,"'")
EscapedString=escape_string(str)
#println("EscapedString: '",EscapedString,"'")
DollarString=escape_dollar(EscapedString)
#println("DollarString: '",DollarString,"'")
FullString="\"$(DollarString)\""
#FullStringII="\"$(EscapedString)\""

#println("FullString: '",FullString,"'")
#println("FullStringII: '",FullStringII,"'")

##Doing the parsing!
string_expr = Meta.parse(FullString)
# string_exprII = Meta.parse(FullStringII)
#println("string_expr: '",string_expr,"'")
# println("string_exprII: '",string_exprII,"'")


subst_dict = Dict{WSymbol,Any}()
if string_expr isa String
string = string_expr
Expand All @@ -102,7 +127,11 @@ macro W_cmd(str)
else
error("Invalid string expression: $string_expr")
end
#println("subst_dict:",subst_dict)
#println("string:",string)

wexpr = parseexpr(string)
#println("wexpr: '",wexpr,"'")
to_julia_expr(wexpr, subst_dict)
end

Expand All @@ -113,7 +142,7 @@ Escapes the '\$' character to create a correct string interpretation when these
"""
function escape_dollar(str::AbstractString)
####This function explicitly escapes the $ character to create a correct string interpretation when dollars are present.
return replace(str,'\$'=>"\\\$")
return replace(str,"\\\$"=>"\\\\\$")
end
"""
unescape_dollar(str::AbstractString)
Expand All @@ -127,7 +156,6 @@ end




function to_julia_expr(wexpr::WExpr, subst_dict)
head = to_julia_expr(wexpr.head, subst_dict)
args = map(x->to_julia_expr(x, subst_dict), wexpr.args)
Expand Down
48 changes: 26 additions & 22 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ import MathLink: WExpr, WSymbol
@testset "interpolation" begin
set_GreedyEval(false)
x = exp(1)
@test W`$x` == x
@test W`\$` == W"$"
@test W`\$x` == W"$x"
@test W`$x +\$` == x+W"$"
@test W`Sin[$x +\$]` == W"Sin"(x+W"$")
@test W`Sin[$x]` == W"Sin"(x)
@test W`Cos[$(log(2))]` == W"Cos"(log(2))

Expand All @@ -33,7 +38,7 @@ end

@testset "Conversion of strange characters" begin
a="!"
println("Test with variable after")
#println("Test with variable after")
###Test with variable after
@test TestMeta("\"$a\"") == "!"
@test TestMeta("\"\$a\"") == :("$(a)")
Expand All @@ -44,7 +49,7 @@ end
@test TestMeta("\"\\\\\$a\"") == :("\\$(a)")
@test_throws Base.Meta.ParseError("invalid escape sequence") TestMeta("\"\\\\\\$a\"")

println("Test with no variable after")
#println("Test with no variable after")
###Test with no variable after
### TestMeta("\"$\"") ###Invalid syntax
@test_throws Base.Meta.ParseError TestMeta("\"\$\"")
Expand All @@ -55,7 +60,7 @@ end
### TestMeta("\"\\\\\\$\"") ###Invalid syntax
@test TestMeta("\"\\\\\\\$\"") == "\\\$"

println("Test escaped strings")
#println("Test escaped strings")
### Test escaped strings
@test TestEscape("\$") == "\$"
@test TestEscape("\\\$") == "\\\\\$"
Expand All @@ -73,23 +78,23 @@ end
@test EscapeDollar("\\\\\$\$") == "\\\\\\\$\\\$"

set_GreedyEval(false)
println("Test math on the symbols")
#println("Test math on the symbols")
@test weval(WSymbol("a")+WSymbol("a")) == weval(2*WSymbol("a"))
@test weval(WSymbol("\$")+WSymbol("\$")) == weval(2*WSymbol("\$"))
println("Test creating the symbol")
#println("Test creating the symbol")
###Test creating the symbol
@test W`a` == WSymbol("a")
@test W`a` == W"a"
@test W`\$` == WSymbol("\$")


println("Test creating the symbol string")
#println("Test creating the symbol string")
###Test creating the symbol string
@test W`"a"` == "a"
@test W`"\$"` == "\$"


println("Other tests")
#println("Other tests")
@test W`"\$"` == "\$"
@test W`"a"` == "a"
@test W`{a -> b}` == W"List"(W"Rule"(W"a",W"b"))
Expand All @@ -99,13 +104,12 @@ end
@test W`"b(\$)a"` == "b(\$)a"
@test W`"b\\\$"` == "b\\\$"
@test W`"b\$"` == "b\$"
@test W`"$a"` == "\$a"
@test W`"$"` == "\$"
@test W`"$"` == "\$"
@test W`"$" -> "b"` == W"Rule"("\$","b")
@test W`{"$" -> "b"}` == W"List"(W"Rule"("\$","b"))
@test W`{"a" -> "$"}` == W"List"(W"Rule"("a","\$"))
@test W`{a -> "$"}` == W"List"(W"Rule"(W"a","\$"))
@test W`"\$a"` == "\$a"
@test W`"\$"` == "\$"
@test W`"\$" -> "b"` == W"Rule"("\$","b")
@test W`{"\$" -> "b"}` == W"List"(W"Rule"("\$","b"))
@test W`{"a" -> "\$"}` == W"List"(W"Rule"("a","\$"))
@test W`{a -> "\$"}` == W"List"(W"Rule"(W"a","\$"))

end

Expand Down Expand Up @@ -467,22 +471,22 @@ end
### weval(W"ToExpression"("17.0000000000000000000000000", W"StandardForm", W"Hold"))

s="17.000000000"
@test MathLink.parseexpr(s) == s
@test_broken MathLink.parseexpr(s) == s
s="17.0000000000"
@test MathLink.parseexpr(s) == s
@test_broken MathLink.parseexpr(s) == s
s="17.00000000000"
@test MathLink.parseexpr(s) == s
@test_broken MathLink.parseexpr(s) == s
s="17.000000000000"
@test MathLink.parseexpr(s) == s
@test_broken MathLink.parseexpr(s) == s
s="17.0000000000000"
@test MathLink.parseexpr(s) == s
@test_broken MathLink.parseexpr(s) == s
s="17.00000000000000"
@test MathLink.parseexpr(s) == s
@test_broken MathLink.parseexpr(s) == s

@test W`17.000000000` == 17.0
@test W`17.000000000000` == 17.0
@test W`17.00000000000000000` == 17.0
@test W`17.000000000000000000000` == 17.0
@test_broken W`17.00000000000000000` == 17.0
@test_broken W`17.000000000000000000000` == 17.0

end

Loading