Skip to content

Commit 386008e

Browse files
authored
Merge pull request #39 from JuliaSymbolics/ale/notype
Remove `type` argument, `symtype`, update docs
2 parents 3331fd2 + 9ac57a8 commit 386008e

File tree

5 files changed

+107
-55
lines changed

5 files changed

+107
-55
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "TermInterface"
22
uuid = "8ea1fca8-c5ef-4a55-8b96-4e9afe9c9a3c"
33
authors = ["Shashi Gowda <gowda@mit.edu>", "Alessandro Cheli <sudo-woodo3@protonmail.com>"]
4-
version = "0.4.1"
4+
version = "0.5.0"
55

66
[compat]
77
julia = "1"

README.md

Lines changed: 71 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,38 @@ and [Metatheory.jl](https://github.com/0x0f0f0f/Metatheory.jl).
1111

1212
#### `isexpr(x::T)`
1313

14-
Returns `true` if `x` is an expression tree (an S-expression). If true, `head`
15-
and `children` methods must be defined for `x`.
14+
Returns `true` if `x` is an expression tree. If true, `head(x)` and `children(x)` methods must be defined for `x`.
15+
Optionally, if `x` represents a function call, `iscall(x)` should be true, and `operation(x)` and `arguments(x)` should also be defined.
16+
17+
#### `iscall(x::T)`
18+
19+
Returns `true` if `x` is a function call expression. If true, `operation(x)`, `arguments(x)`
20+
must also be defined for `x`.
21+
22+
If `iscall(x)` is true, then also `isexpr(x)` *must* be true. The other way around is not true.
23+
(A function call is always an expression node, but not every expression tree represents a function call).
24+
25+
This means that, `head(x)` and `children(x)` must be defined. Together
26+
with `operation(x)` and `arguments(x)`.
27+
28+
**Examples:**
29+
30+
In a functional language, all expression trees are function calls (e.g. SymbolicUtils.jl).
31+
Let's say that you have an hybrid array and functional language. `iscall` on the expression `v[i]`
32+
is `false`, and `iscall` on expression `f(x)` is `true`, but both of them are nested
33+
expressions, and `isexpr` is `true` on both.
34+
35+
The same goes for Julia `Expr`. An `Expr(:block, ...)` is *not a function call*
36+
and has no `operation` and `arguments`, but has a `head` and `children`.
37+
38+
39+
The distinction between `head`/`children` and `operation`/`arguments` is needed
40+
when dealing with languages that are *not representing function call operations as their head*.
41+
The main example is `Expr(:call, :f, :x)`: it has both a `head` and an `operation`, which are
42+
respectively `:call` and `:f`.
43+
44+
In other symbolic expression languages, such as SymbolicUtils.jl, the `head` of a node
45+
can correspond to `operation` and `children` can correspond to `arguments`.
1646

1747
#### `head(x)`
1848

@@ -22,6 +52,16 @@ Returns the head of the S-expression.
2252

2353
Returns the children (aka tail) of the S-expression.
2454

55+
#### `operation(x)`
56+
57+
Returns the function a function call expression is calling. `iscall(x)` must be
58+
true as a precondition.
59+
60+
#### `arguments(x)`
61+
62+
Returns the arguments to the function call in a function call expression.
63+
`iscall(x)` must be true as a precondition.
64+
2565
#### `maketerm(T, head, children, type=nothing, metadata=nothing)`
2666

2767
Constructs an expression. `T` is a constructor type, `head` and `children` are
@@ -41,19 +81,6 @@ Packages providing expression types _must_ implement this method for each expres
4181
If your types do not support type information or metadata, you still need to accept
4282
these arguments and may choose to not use them.
4383

44-
#### `iscall(x::T)`
45-
46-
Returns `true` if `x` is a function call expression. If true, `operation`, `arguments` must also be defined for `x::T`.
47-
48-
#### `operation(x)`
49-
50-
Returns the function a function call expression is calling. `iscall(x)` must be
51-
true as a precondition.
52-
53-
#### `arguments(x)`
54-
55-
Returns the arguments to the function call in a function call expression.
56-
`iscall(x)` must be true as a precondition.
5784

5885
### Optional
5986

@@ -67,11 +94,34 @@ Implicitly defined if `arguments(x)` is defined.
6794

6895
Returns the metadata attached to `x`.
6996

70-
#### `symtype(expr)`
97+
#### `metadata(expr, md)`
98+
99+
Returns `expr` with metadata `md` attached to it.
100+
101+
## Examples
102+
103+
### Function call Julia Expressions
104+
105+
```julia
106+
ex = :(f(a, b))
107+
@test head(ex) == :call
108+
@test children(ex) == [:f, :a, :b]
109+
@test operation(ex) == :f
110+
@test arguments(ex) == [:a, :b]
111+
@test isexpr(ex)
112+
@test iscall(ex)
113+
@test ex == maketerm(Expr, :call, [:f, :a, :b], nothing)
114+
```
115+
71116

72-
Returns the symbolic type of `expr`. By default this is just `typeof(expr)`.
73-
Define this for your symbolic types if you want `SymbolicUtils.simplify` to apply rules
74-
specific to numbers (such as commutativity of multiplication). Or such
75-
rules that may be implemented in the future.
117+
### Non-function call Julia Expressions
76118

77-
<!-- TODO update examples -->
119+
```julia
120+
ex = :(arr[i, j])
121+
@test head(ex) == :ref
122+
@test_throws ErrorException operation(ex)
123+
@test_throws ErrorException arguments(ex)
124+
@test isexpr(ex)
125+
@test !iscall(ex)
126+
@test ex == maketerm(Expr, :ref, [:arr, :i, :j], nothing)
127+
```

src/TermInterface.jl

Lines changed: 32 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,45 @@
11
module TermInterface
22

3-
"""
4-
iscall(x)
5-
Returns `true` if `x` is a function call expression. If true, `operation`, `arguments`
6-
must also be defined for `x`.
7-
"""
8-
iscall(x) = false
9-
export iscall
10-
113
"""
124
isexpr(x)
13-
Returns `true` if `x` is an expression tree (an S-expression). If true, `head` and `children` methods must be defined for `x`.
5+
Returns `true` if `x` is an expression tree. If true, `head(x)` and `children(x)` methods must be defined for `x`.
6+
Optionally, if `x` represents a function call, `iscall(x)` should be true, and `operation(x)` and `arguments(x)` should also be defined.
147
"""
158
isexpr(x) = false
169
export isexpr
1710

1811
"""
19-
symtype(expr)
12+
iscall(x)
13+
Returns `true` if `x` is a function call expression. If true, `operation(x)`, `arguments(x)`
14+
must also be defined for `x`.
2015
21-
Returns the symbolic type of `expr`. By default this is just `typeof(expr)`.
22-
Define this for your symbolic types if you want `SymbolicUtils.simplify` to apply rules
23-
specific to numbers (such as commutativity of multiplication). Or such
24-
rules that may be implemented in the future.
25-
"""
26-
function symtype(x)
27-
typeof(x)
28-
end
29-
export symtype
16+
If `iscall(x)` is true, then also `isexpr(x)` *must* be true. The other way around is not true.
17+
(A function call is always an expression node, but not every expression tree represents a function call).
3018
31-
"""
32-
issym(x)
19+
This means that, `head(x)` and `children(x)` must be defined. Together
20+
with `operation(x)` and `arguments(x)`.
21+
22+
## Examples
23+
24+
In a functional language, all expression trees are function calls (e.g. SymbolicUtils.jl).
25+
Let's say that you have an hybrid array and functional language. `iscall` on the expression `v[i]`
26+
is `false`, and `iscall` on expression `f(x)` is `true`, but both of them are nested
27+
expressions, and `isexpr` is `true` on both.
28+
29+
The same goes for Julia `Expr`. An `Expr(:block, ...)` is *not a function call*
30+
and has no `operation` and `arguments`, but has a `head` and `children`.
3331
34-
Returns `true` if `x` is a symbol. If true, `nameof` must be defined
35-
on `x` and must return a Symbol.
32+
33+
The distinction between `head`/`children` and `operation`/`arguments` is needed
34+
when dealing with languages that are *not representing function call operations as their head*.
35+
The main example is `Expr(:call, :f, :x)`: it has both a `head` and an `operation`, which are
36+
respectively `:call` and `:f`.
37+
38+
In other symbolic expression languages, such as SymbolicUtils.jl, the `head` of a node
39+
can correspond to `operation` and `children` can correspond to `arguments`.
3640
"""
37-
issym(x) = false
38-
export issym
41+
iscall(x) = false
42+
export iscall
3943

4044
"""
4145
head(x)
@@ -109,10 +113,10 @@ function metadata end
109113

110114

111115
"""
112-
maketerm(T, head, children, type, metadata)
116+
maketerm(T, head, children, metadata)
113117
114118
Constructs an expression. `T` is a constructor type, `head` and `children` are
115-
the head and tail of the S-expression, `type` is the `type` of the S-expression.
119+
the head and tail of the S-expression.
116120
`metadata` is any metadata attached to this expression.
117121
118122
Note that `maketerm` may not necessarily return an object of type `T`. For example,
@@ -125,7 +129,7 @@ the sub-expression. `T` will be the type of the outer expression.
125129
126130
Packages providing expression types _must_ implement this method for each expression type.
127131
128-
Giving `nothing` for `type` or `metadata` results in a default being selected.
132+
Giving `nothing` for `metadata` should result in a default being selected.
129133
"""
130134

131135
function maketerm end

src/expr.jl

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,4 @@ children(e::Expr) = e.args
1010
operation(e::Expr) = iscall(e) ? first(children(e)) : error("operation called on a non-function call expression")
1111
arguments(e::Expr) = iscall(e) ? @view(e.args[2:end]) : error("arguments called on a non-function call expression")
1212

13-
function maketerm(::Type{Expr}, head, args, type, metadata)
14-
Expr(head, args...)
15-
end
13+
maketerm(::Type{Expr}, head, args, metadata) = Expr(head, args...)

test/runtests.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ using Test
99
@test arguments(ex) == [:a, :b]
1010
@test isexpr(ex)
1111
@test iscall(ex)
12-
@test ex == maketerm(Expr, :call, [:f, :a, :b], nothing, nothing)
12+
@test ex == maketerm(Expr, :call, [:f, :a, :b], nothing)
1313

1414

1515
ex = :(arr[i, j])
@@ -18,5 +18,5 @@ using Test
1818
@test_throws ErrorException arguments(ex)
1919
@test isexpr(ex)
2020
@test !iscall(ex)
21-
@test ex == maketerm(Expr, :ref, [:arr, :i, :j], nothing, nothing)
21+
@test ex == maketerm(Expr, :ref, [:arr, :i, :j], nothing)
2222
end

0 commit comments

Comments
 (0)