Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/dfdx/espresso.jl
Expression transformation package
https://github.com/dfdx/espresso.jl
expression-transformation macros metaprogramming pattern-matching symbolic-manipulation
Last synced: about 1 month ago
JSON representation
Expression transformation package
- Host: GitHub
- URL: https://github.com/dfdx/espresso.jl
- Owner: dfdx
- License: other
- Created: 2016-07-27T21:26:59.000Z (over 8 years ago)
- Default Branch: master
- Last Pushed: 2024-09-09T07:27:50.000Z (3 months ago)
- Last Synced: 2024-10-13T23:30:52.354Z (2 months ago)
- Topics: expression-transformation, macros, metaprogramming, pattern-matching, symbolic-manipulation
- Language: Julia
- Homepage:
- Size: 791 KB
- Stars: 57
- Watchers: 10
- Forks: 8
- Open Issues: 2
-
Metadata Files:
- Readme: README.md
- License: LICENSE.md
Awesome Lists containing this project
README
# Espresso
[![Build Status](https://travis-ci.org/dfdx/Espresso.jl.svg?branch=master)](https://travis-ci.org/dfdx/Espresso.jl)
Expression transformation package.
## Symbolic manipulation
Espresso provides functions for finding, matching, substituting and rewriting Julia AST. A few examples:
Match power expression and extract its first argument
```julia
pat = :(_x ^ 2) # anything starting with `_` is a placeholder, placeholder matches everything
ex = :(A ^ 2)
matchex(pat, ex)
# ==> Dict{Symbol,Any} with 1 entry:
# ==> :_x => :A -- placeholder _x captured symbol :A
```Find all function calls with any number of arguments:
```julia
pat = :(_f(_a...)) # `_a...` will match 0 or more arguments
ex = quote
x = foo(3, 5)
y = bar(x)
z = baz(y)
endfindex(pat, ex)
# ==> 3-element Array{Any,1}:
# ==> :(foo(3, 5))
# ==> :(bar(x))
# ==> :(baz(y))
```
Substitute symbol `y` with `quux(x)`:```julia
ex = :(z = 2x + y)
subs(ex, Dict(:y => :(quux(x))))
# ==> :(z = 2x + quux(x))
```Rewrite all function calls with corresponding broadcasting:
```julia
ex = :(z = foo(x) + bar(y)) # take this expression
pat = :(_f(_a...)) # match recursively to this pattern
rpat = :(_f.(_a...)) # and rewrite to this pattern
rewrite_all(ex, pat, rpat)
# ==> :(z = (+).(foo.(x), bar.(y)))
```
See [rewrite.jl](https://github.com/dfdx/Espresso.jl/blob/master/src/rewrite.jl) for more expression transformation functions and their parameters.## Expression graph
Sometimes we need more sophisticated transformations including those depending on argument types. Espresso can parse expressions into a graph of basic calls and assignments using `ExGraph` type, e.g.:
```julia
ex = :(z = x ^ 2 * (y + x ^ 2))
g = ExGraph(ex; x=3.0, y=2.0); # `x` and `y` are example values from which ExGraphs learns types of these vars
evaluate!(g) # evaluate all expressions to fill values of intermediate nodes
g
# ==> ExGraph
# ==> ExNode{input}(x = x | 3.0)
# ==> ExNode{input}(y = y | 2.0)
# ==> ExNode{constant}(tmp390 = 2 | 2)
# ==> ExNode{call}(tmp391 = x ^ tmp390 | 9.0)
# ==> ExNode{constant}(tmp392 = 2 | 2)
# ==> ExNode{call}(tmp393 = x ^ tmp392 | 9.0)
# ==> ExNode{call}(tmp394 = y + tmp393 | 11.0)
# ==> ExNode{call}(z = tmp391 * tmp394 | 99.0)
```
Such representation, although somewhat cryptic, is more flexible. For example, using it we can easily get rid of common subexpressions (`x ^ 2`):```julia
g2 = eliminate_common(g)
# ==> ExGraph
# ==> ExNode{input}(x = x | 3.0)
# ==> ExNode{input}(y = y | 2.0)
# ==> ExNode{constant}(tmp390 = 2 | 2)
# ==> ExNode{call}(tmp391 = x ^ tmp390 | 9.0)
# ==> ExNode{call}(tmp394 = y + tmp391 | 11.0)
# ==> ExNode{call}(z = tmp391 * tmp394 | 99.0)
```
`to_expr` and `to_expr_kw` construct a Julia expression back from `ExGraph`:```julia
to_expr_kw(g2)
# ==> quote
# ==> tmp390 = 2
# ==> tmp391 = x ^ tmp390
# ==> tmp394 = y + tmp391
# ==> z = tmp391 * tmp394
# ==> end
```## (Somewhat outdated) documentation
[![](https://img.shields.io/badge/docs-stable-blue.svg)](https://dfdx.github.io/Espresso.jl/stable)
[![](https://img.shields.io/badge/docs-latest-blue.svg)](https://dfdx.github.io/Espresso.jl/latest)