Skip to content
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

Allow recipes to set previous operation #292

Merged
merged 2 commits into from
Jun 10, 2024
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
28 changes: 28 additions & 0 deletions docs/src/tutorials/recipes.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,3 +79,31 @@ The use of `@latexrecipe` to redefine how an already supported type should be in


If a recipe is defined within a module, everything should just work without the need to export anything.

The special keyword argument `operation` lets you specify that a type corresponds to a specific arithmetic operation.
For instance, if we define a type
```julia
struct MyDifference
x
y
end
```
that is meant to represent the operation `x - y`, we might want to create the recipe

```julia
@latexrecipe function f(m::MyDifference)
return :($(m.y) - $(m.x))
end
```
so that the result of `latexify(MyDifference(2,3))` is ``3 - 2``.
But right now, `latexify` does not know that this represents an operation, so for instance
`@latexify $(MyDifference(2,3))*4` gives ``3 - 2 \cdot 4``, which is incorrect.
The way around this is to edit the recipe:
```julia
@latexrecipe function f(m::MyDifference)
operation := :-
return :($(m.y) - $(m.x))
end
```
Now `latexify` knows that `MyDifference` represents a subtraction, and parenthesis rules kick in:
`@latexify $(MyDifference(2,3))*4` gives ``\left( 3 - 2 \right) \cdot 4``.
15 changes: 14 additions & 1 deletion src/latexraw.jl
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,8 @@ function _latexraw(inputex::Expr; convert_unicode=true, kwargs...)
end
else
for i in 1:length(ex.args)
prevOp[i] = _getoperation(ex.args[i])
if isa(ex.args[i], Expr)
length(ex.args[i].args) > 1 && ex.args[i].args[1] isa Symbol && (prevOp[i] = ex.args[i].args[1])
ex.args[i] = recurseexp!(ex.args[i])
elseif ex.args[i] isa AbstractArray
ex.args[i] = latexraw(ex.args[i]; kwargs...)
Expand Down Expand Up @@ -175,3 +175,16 @@ environment that is capable of displaying not-maths objects. Try for example
end

_latexraw(i::Missing; kwargs...) = "\\textrm{NA}"

"""
_getoperation(x)

Check if `x` represents something that could affect the vector of previous operations.
`:none` by default, recipes can use `operation-->:something` to hack this.
"""
function _getoperation(ex::Expr)
length(ex.args) > 1 && ex.args[1] isa Symbol && return ex.args[1]
return :none
end
_getoperation(x) = :none

26 changes: 20 additions & 6 deletions src/recipes.jl
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Much of this is copied/adapted from RecipesBase.jl. Cred to everyone who has
# Much of this is copied/adapted from RecipesBase.jl. Cred to everyone who has
# worked on that package!

const _debug_recipes = Bool[false]
Expand Down Expand Up @@ -75,6 +75,7 @@ end
# and we push this block onto the series_blocks list.
# then at the end we push the main body onto the series list
function process_recipe_body!(expr::Expr)
operation = QuoteNode(:none)
for (i,e) in enumerate(expr.args)
if isa(e,Expr)

Expand All @@ -100,6 +101,11 @@ function process_recipe_body!(expr::Expr)
if e.head == :(-->)
k, v = e.args
if isa(k, Symbol)
if k == :operation
operation = v
expr.args[i] = nothing
continue
end
k = QuoteNode(k)
end

Expand All @@ -110,14 +116,14 @@ function process_recipe_body!(expr::Expr)
end

quiet = false
expr.args[i] = set_expr
expr.args[i] = set_expr

elseif e.head != :call
# we want to recursively replace the arrows, but not inside function calls
# as this might include things like Dict(1=>2)
process_recipe_body!(e)
end

if e.head == :return
if e.args[1] isa Expr
if e.args[1] isa Tuple
Expand All @@ -131,6 +137,7 @@ function process_recipe_body!(expr::Expr)
end
end
end
return operation
end

macro latexrecipe(funcexpr)
Expand All @@ -150,8 +157,7 @@ macro latexrecipe(funcexpr)

args, kw_body, kw_dict = create_kw_body(func_signature)
func = get_function_def(func_signature, args)
process_recipe_body!(func_body)

operation = process_recipe_body!(func_body)

# now build a function definition for apply_recipe
funcdef = Expr(:function, func, esc(quote
Expand All @@ -164,6 +170,14 @@ macro latexrecipe(funcexpr)
$func_body
end))

return funcdef
getopdef = Expr(:(=), copy(func), esc(operation))
signature = getopdef.args[1]
while Meta.isexpr(signature, :where)
# Get through any layers of `where`
signature = signature.args[1]
end
signature.args[1] = :(Latexify._getoperation)

return Expr(:block, funcdef, getopdef)
end

12 changes: 12 additions & 0 deletions test/recipe_test.jl
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,15 @@ end
return [myfloat.x for myfloat in vec.vec1], [myfloat.x for myfloat in vec.vec2]
end

struct MySum
x
y
end
@latexrecipe function f(s::MySum)
operation --> :+
return :($(s.x) + $(s.y))
end

end

using .MyModule
Expand Down Expand Up @@ -202,4 +211,7 @@ raw"\begin{equation}
\end{equation}
", "\r\n"=>"\n")

sum1 = MyModule.MySum(3, 4)
@test latexify(:(2 + $(sum1)^2)) == raw"$2 + \left( 3 + 4 \right)^{2}$"
@test latexify(:(2 - $(sum1))) == raw"$2 - \left( 3 + 4 \right)$"

Loading