Skip to content

Commit 64b545d

Browse files
authored
Merge branch 'master' into compathelper/new_version/2024-07-23-01-07-55-653-01054105936
2 parents 490d277 + f650e28 commit 64b545d

24 files changed

+1267
-95
lines changed

.github/workflows/CI.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,13 +80,16 @@ jobs:
8080
- uses: julia-actions/cache@v1
8181
- uses: julia-actions/julia-buildpkg@v1
8282
- name: Run tests
83+
id: run-tests
84+
continue-on-error: ${{ matrix.test_name == 'enzyme' }}
8385
run: |
8486
julia --color=yes -e 'import Pkg; Pkg.add("Coverage")'
8587
SR_TEST=${{ matrix.test_name }} julia --color=yes --threads=auto --check-bounds=yes --depwarn=yes --code-coverage=user -e 'import Coverage; import Pkg; Pkg.activate("."); Pkg.test(coverage=true)'
8688
julia --color=yes coverage.jl
8789
shell: bash
8890
- name: Coveralls
8991
uses: coverallsapp/github-action@v2
92+
if: steps.run-tests.outcome == 'success'
9093
with:
9194
parallel: true
9295
path-to-lcov: lcov.info

docs/Project.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
[deps]
22
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
33
DynamicExpressions = "a40a106e-89c9-4ca8-8020-a735e8728b6b"
4+
Interfaces = "85a1e053-f937-4924-92a5-1367d23b7b87"
5+
Literate = "98b081ad-f1c9-55d3-8b20-4c87d4299306"

docs/make.jl

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,18 @@
11
using Documenter
22
using DynamicExpressions
33
using Random: AbstractRNG
4+
using Literate: markdown
5+
6+
####################################
7+
# Literate #########################
8+
####################################
9+
10+
include("utils.jl")
11+
process_literate_blocks()
12+
13+
####################################
14+
# index.md #########################
15+
####################################
416

517
readme = joinpath(@__DIR__, "..", "README.md")
618

@@ -28,9 +40,6 @@ index_content = let r = read(readme, String)
2840
bottom_part = """
2941
## Contents
3042
31-
```@contents
32-
Pages = ["utils.md", "api.md", "eval.md"]
33-
```
3443
"""
3544

3645
join((top_part, r, bottom_part), "\n")
@@ -41,13 +50,26 @@ open(index_md, "w") do f
4150
write(f, index_content)
4251
end
4352

53+
####################################
54+
4455
makedocs(;
4556
sitename="DynamicExpressions.jl",
4657
authors="Miles Cranmer",
47-
doctest=false,
4858
clean=true,
49-
format=Documenter.HTML(),
50-
warnonly=true,
59+
format=Documenter.HTML(;
60+
canonical="https://symbolicml.org/DynamicExpressions.jl/stable"
61+
),
62+
pages=[
63+
"Home" => "index.md",
64+
"Examples" => [
65+
"examples/base_operations.md", # Defined by `test/test_base_2.jl`
66+
"examples/expression.md", # Defined by `test/test_expression.jl`
67+
"examples/structured_expression.md", # Defined by `test/test_structured_expression.jl`
68+
],
69+
"Eval" => "eval.md",
70+
"Utils" => "utils.md",
71+
"API" => "api.md",
72+
],
5173
)
5274

5375
# Forward links from old docs:

docs/src/api.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,33 @@ ParametricExpression
166166
ParametricNode
167167
```
168168

169+
Another example is the `StructuredExpression` type, for defining rigid
170+
predefined operations in an expression tree:
171+
172+
```@docs
173+
StructuredExpression
174+
```
175+
176+
You may use operators directly on `AbstractExpression` objects to create a new object
177+
containing the combined expression tree, so long as those objects have identical operators
178+
in their metadata.
179+
180+
You can extract and set contents and metadata with a few utility functions, including:
181+
182+
```@docs
183+
get_contents
184+
with_contents
185+
get_metadata
186+
with_metadata
187+
get_tree
188+
```
189+
190+
To declare a new operator for expressions, you may use:
191+
192+
```@docs
193+
@declare_expression_operator
194+
```
195+
169196
## Interfaces
170197

171198
The interfaces for `AbstractExpression` and `AbstractExpressionNode` are

docs/utils.jl

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
2+
# Function to process literate blocks in test files
3+
function process_literate_blocks()
4+
test_dir = joinpath(@__DIR__, "..", "test")
5+
for file in readdir(test_dir)
6+
if endswith(file, ".jl")
7+
process_file(joinpath(test_dir, file))
8+
end
9+
end
10+
end
11+
12+
function process_file(filepath)
13+
content = read(filepath, String)
14+
blocks = match_literate_blocks(content)
15+
for (output_file, block_content) in blocks
16+
process_literate_block(output_file, block_content, filepath)
17+
end
18+
end
19+
20+
function match_literate_blocks(content)
21+
pattern = r"^(\s*)#literate_begin\s+file=\"(.*?)\"\n(.*?)#literate_end"sm
22+
matches = collect(eachmatch(pattern, content))
23+
return Dict(
24+
m.captures[2] => process_block_content(m.captures[1], m.captures[3]) for
25+
m in matches
26+
)
27+
end
28+
29+
function process_block_content(indent, block_content)
30+
if isempty(block_content)
31+
return ""
32+
end
33+
indent_length = length(indent)
34+
lines = split(block_content, '\n')
35+
stripped_lines = [
36+
if length(line) > indent_length
37+
line[(indent_length + 1):end]
38+
else
39+
""
40+
end for line in lines
41+
]
42+
return strip(join(stripped_lines, '\n'))
43+
end
44+
45+
function process_literate_block(output_file, content, source_file)
46+
# Create a temporary .jl file
47+
temp_file = tempname() * ".jl"
48+
write(temp_file, content)
49+
50+
# Process the temporary file with Literate.markdown
51+
output_dir = joinpath(@__DIR__, "src", "examples")
52+
base_name = first(splitext(basename(output_file))) # Remove any existing extension
53+
54+
markdown(temp_file, output_dir; name=base_name, documenter=true)
55+
56+
# Generate the relative path for EditURL
57+
edit_path = relpath(source_file, output_dir)
58+
59+
# Read the generated markdown file
60+
md_file = joinpath(output_dir, base_name * ".md")
61+
md_content = read(md_file, String)
62+
63+
# Replace the existing EditURL with the correct one
64+
new_content = replace(md_content, r"EditURL = .*" => "EditURL = \"$edit_path\"")
65+
66+
# Write the updated content back to the file
67+
write(md_file, new_content)
68+
69+
@info "Processed literate block to $md_file with EditURL set to $edit_path"
70+
end

src/DynamicExpressions.jl

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,11 @@ using DispatchDoctor: @stable, @unstable
1717
include("Simplify.jl")
1818
include("OperatorEnumConstruction.jl")
1919
include("Expression.jl")
20+
include("ExpressionAlgebra.jl")
2021
include("Random.jl")
2122
include("Parse.jl")
2223
include("ParametricExpression.jl")
24+
include("StructuredExpression.jl")
2325
end
2426

2527
import PackageExtensionCompat: @require_extensions
@@ -76,12 +78,20 @@ import .NodeModule:
7678
@reexport import .ExtensionInterfaceModule: node_to_symbolic, symbolic_to_node
7779
@reexport import .RandomModule: NodeSampler
7880
@reexport import .ExpressionModule:
79-
AbstractExpression, Expression, with_contents, with_metadata, get_contents, get_metadata
81+
AbstractExpression,
82+
Expression,
83+
with_contents,
84+
with_metadata,
85+
get_contents,
86+
get_metadata,
87+
get_tree
8088
import .ExpressionModule:
81-
get_tree, get_operators, get_variable_names, Metadata, default_node_type, node_type
89+
get_operators, get_variable_names, Metadata, default_node_type, node_type
90+
@reexport import .ExpressionAlgebraModule: @declare_expression_operator
8291
@reexport import .ParseModule: @parse_expression, parse_expression
8392
import .ParseModule: parse_leaf
8493
@reexport import .ParametricExpressionModule: ParametricExpression, ParametricNode
94+
@reexport import .StructuredExpressionModule: StructuredExpression
8595

8696
@stable default_mode = "disable" begin
8797
include("Interfaces.jl")

src/Expression.jl

Lines changed: 50 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,13 @@ Base.propertynames(x::Metadata) = propertynames(_data(x))
3636
@unstable @inline Base.getproperty(x::Metadata, f::Symbol) = getproperty(_data(x), f)
3737
Base.show(io::IO, x::Metadata) = print(io, "Metadata(", _data(x), ")")
3838
@inline _copy(x) = copy(x)
39+
@inline _copy(x::NamedTuple) = copy_named_tuple(x)
3940
@inline _copy(x::Nothing) = nothing
41+
@inline function copy_named_tuple(nt::NamedTuple)
42+
return NamedTuple{keys(nt)}(map(_copy, values(nt)))
43+
end
4044
@inline function Base.copy(metadata::Metadata)
41-
nt = _data(metadata)
42-
copied_nt = NamedTuple{keys(nt)}(map(_copy, values(nt)))
43-
return Metadata(copied_nt)
45+
return Metadata(_copy(_data(metadata)))
4446
end
4547
@inline Base.:(==)(x::Metadata, y::Metadata) = _data(x) == _data(y)
4648
@inline Base.hash(x::Metadata, h::UInt) = hash(_data(x), h)
@@ -101,26 +103,11 @@ default_node_type(::Type{<:AbstractExpression{T}}) where {T} = Node{T}
101103
########################################################
102104
# Abstract interface ###################################
103105
########################################################
104-
"""
105-
get_operators(ex::AbstractExpression, operators::Union{Nothing,Any})
106-
107-
which will return the operators to be passed to internal functions
108-
such as `eval_tree_array` or `string_tree`, either from the expression itself,
109-
or `cur_operators` if it is not `nothing`. If left as default,
110-
it requires `cur_operators` to not be `nothing`.
111-
`cur_operators` would typically be an `OperatorEnum`.
112-
"""
113106
function get_operators(
114107
ex::AbstractExpression, operators::Union{AbstractOperatorEnum,Nothing}=nothing
115108
)
116109
return error("`get_operators` function must be implemented for $(typeof(ex)) types.")
117110
end
118-
119-
"""
120-
get_variable_names(ex::AbstractExpression, variable_names::Union{Nothing,AbstractVector{<:AbstractString}})
121-
122-
The same as `operators`, but for variable names.
123-
"""
124111
function get_variable_names(
125112
ex::AbstractExpression,
126113
variable_names::Union{Nothing,AbstractVector{<:AbstractString}}=nothing,
@@ -130,12 +117,6 @@ function get_variable_names(
130117
)
131118
end
132119

133-
"""
134-
get_tree(ex::AbstractExpression)
135-
136-
A method that extracts the expression tree from `AbstractExpression`
137-
and should return an `AbstractExpressionNode`.
138-
"""
139120
function get_tree(ex::AbstractExpression)
140121
return error("`get_tree` function must be implemented for $(typeof(ex)) types.")
141122
end
@@ -167,6 +148,50 @@ function get_metadata(ex::AbstractExpression)
167148
end
168149
########################################################
169150

151+
"""
152+
get_operators(ex::AbstractExpression, operators::Union{Nothing,Any})
153+
154+
which will return the operators to be passed to internal functions
155+
such as `eval_tree_array` or `string_tree`, either from the expression itself,
156+
or `cur_operators` if it is not `nothing`. If left as default,
157+
it requires `cur_operators` to not be `nothing`.
158+
`cur_operators` would typically be an `OperatorEnum`.
159+
"""
160+
get_operators
161+
162+
"""
163+
get_variable_names(ex::AbstractExpression, variable_names::Union{Nothing,AbstractVector{<:AbstractString}})
164+
165+
The same as `operators`, but for variable names.
166+
"""
167+
get_variable_names
168+
169+
"""
170+
get_tree(ex::AbstractExpression)
171+
172+
A method that extracts the expression tree from `AbstractExpression`
173+
and should return an `AbstractExpressionNode`.
174+
"""
175+
get_tree
176+
177+
"""
178+
get_contents(ex::AbstractExpression)
179+
180+
Get the contents of the expression, which might be a plain
181+
`AbstractExpressionNode`, or some combination of them, or other data.
182+
This should include everything other than that returned by [`get_metadata`](@ref).
183+
"""
184+
get_contents
185+
186+
"""
187+
get_metadata(ex::AbstractExpression)
188+
189+
Get the metadata of the expression, which might be a plain
190+
`NamedTuple`, or some combination of them, or other data.
191+
This should include everything other than that returned by [`get_contents`](@ref).
192+
"""
193+
get_metadata
194+
170195
"""
171196
with_contents(ex::AbstractExpression, tree::AbstractExpressionNode)
172197
with_contents(ex::AbstractExpression, tree::AbstractExpression)
@@ -317,7 +342,7 @@ for io in ((), (:(io::IO),))
317342
end
318343
end
319344

320-
function Base.show(io::IO, ::MIME"text/plain", ex::AbstractExpression)
345+
function Base.show(io::IO, ex::AbstractExpression)
321346
return print(io, string_tree(ex))
322347
end
323348

0 commit comments

Comments
 (0)