Skip to content

Commit

Permalink
adjust readme
Browse files Browse the repository at this point in the history
  • Loading branch information
a committed Jun 2, 2024
1 parent 9d51a85 commit 6647645
Showing 1 changed file with 71 additions and 21 deletions.
92 changes: 71 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,38 @@ and [Metatheory.jl](https://github.com/0x0f0f0f/Metatheory.jl).

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

Returns `true` if `x` is an expression tree (an S-expression). If true, `head`
and `children` methods must be defined for `x`.
Returns `true` if `x` is an expression tree. If true, `head(x)` and `children(x)` methods must be defined for `x`.
Optionally, if `x` represents a function call, `iscall(x)` should be true, and `operation(x)` and `arguments(x)` should also be defined.

#### `iscall(x::T)`

Returns `true` if `x` is a function call expression. If true, `operation(x)`, `arguments(x)`
must also be defined for `x`.

If `iscall(x)` is true, then also `isexpr(x)` *must* be true. The other way around is not true.
(A function call is always an expression node, but not every expression tree represents a function call).

This means that, `head(x)` and `children(x)` must be defined. Together
with `operation(x)` and `arguments(x)`.

**Examples:**

In a functional language, all expression trees are function calls (e.g. SymbolicUtils.jl).
Let's say that you have an hybrid array and functional language. `iscall` on the expression `v[i]`
is `false`, and `iscall` on expression `f(x)` is `true`, but both of them are nested
expressions, and `isexpr` is `true` on both.

The same goes for Julia `Expr`. An `Expr(:block, ...)` is *not a function call*
and has no `operation` and `arguments`, but has a `head` and `children`.


The distinction between `head`/`children` and `operation`/`arguments` is needed
when dealing with languages that are *not representing function call operations as their head*.
The main example is `Expr(:call, :f, :x)`: it has both a `head` and an `operation`, which are
respectively `:call` and `:f`.

In other symbolic expression languages, such as SymbolicUtils.jl, the `head` of a node
can correspond to `operation` and `children` can correspond to `arguments`.

#### `head(x)`

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

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

#### `operation(x)`

Returns the function a function call expression is calling. `iscall(x)` must be
true as a precondition.

#### `arguments(x)`

Returns the arguments to the function call in a function call expression.
`iscall(x)` must be true as a precondition.

#### `maketerm(T, head, children, type=nothing, metadata=nothing)`

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

#### `iscall(x::T)`

Returns `true` if `x` is a function call expression. If true, `operation`, `arguments` must also be defined for `x::T`.

#### `operation(x)`

Returns the function a function call expression is calling. `iscall(x)` must be
true as a precondition.

#### `arguments(x)`

Returns the arguments to the function call in a function call expression.
`iscall(x)` must be true as a precondition.

### Optional

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

Returns the metadata attached to `x`.

#### `symtype(expr)`
#### `metadata(expr, md)`

Returns `expr` with metadata `md` attached to it.

## Examples

### Function call Julia Expressions

```julia
ex = :(f(a, b))
@test head(ex) == :call
@test children(ex) == [:f, :a, :b]
@test operation(ex) == :f
@test arguments(ex) == [:a, :b]
@test isexpr(ex)
@test iscall(ex)
@test ex == maketerm(Expr, :call, [:f, :a, :b], nothing)
```


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

<!-- TODO update examples -->
```julia
ex = :(arr[i, j])
@test head(ex) == :ref
@test_throws ErrorException operation(ex)
@test_throws ErrorException arguments(ex)
@test isexpr(ex)
@test !iscall(ex)
@test ex == maketerm(Expr, :ref, [:arr, :i, :j], nothing)
```

0 comments on commit 6647645

Please sign in to comment.