Skip to content

Commit abb871c

Browse files
authored
Merge pull request #38 from control-toolbox/35-bug-parser-exceptions
parser exceptions
2 parents 9f9cd90 + 457b0eb commit abb871c

3 files changed

Lines changed: 29 additions & 23 deletions

File tree

src/onepass.jl

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
# todo: as_range / as_vector for rg / lb, ub could be done here in p_constraint when calling PREFIX.constraint!
33
# - cannot call solve if problem not fully defined (dynamics not defined...)
44
# - doc: explain projections wrt to t0, tf, t; (...x1...x2...)(t) -> ...gensym1...gensym2... (most internal first)
5-
# - robustify repl
65
# - additional checks: when generating functions (constraints, dynamics, costs), there should not be any x or u left
76
# (but the user might indeed do so); meaning that has(ee, x/u/t) must be false (postcondition)
87
# - tests exceptions (parsing and semantics/runtime)
@@ -17,6 +16,13 @@ function set_prefix(p)
1716
return nothing
1817
end
1918

19+
const E_PREFIX = Ref(:OptimalControl) # prefix for exceptions in generated code, assumed to be evaluated within OptimalControl.jl; can be CTBase for tests
20+
21+
function set_e_prefix(p)
22+
E_PREFIX[] = p
23+
return nothing
24+
end
25+
2026
"""
2127
$(TYPEDEF)
2228
@@ -30,7 +36,7 @@ $(TYPEDEF)
3036
tf::Union{Real,Symbol,Expr,Nothing} = nothing
3137
x::Union{Symbol,Nothing} = nothing
3238
u::Union{Symbol,Nothing} = nothing
33-
is_scalar_x::Bool = false # todo: remove when allowing componentwise declaration of dynamics
39+
is_scalar_x::Bool = false # todo: remove in future, when allowing componentwise declaration of dynamics
3440
aliases::OrderedDict{Union{Symbol,Expr},Union{Real,Symbol,Expr}} = __init_aliases() # Dict ordered by Symbols *and Expr* just for scalar variable / state / control
3541
lnum::Int = 0
3642
line::String = ""
@@ -47,13 +53,13 @@ __init_aliases(; max_dim=20) = begin
4753
al[:integral] = :∫
4854
al[:(=>)] = :
4955
al[:in] = :
50-
al
56+
return al
5157
end
5258

53-
__throw(ex, n, line) = quote
54-
local info
55-
info = string("\nLine ", $n, ": ", $line)
56-
throw(CTBase.ParsingError(info * "\n" * $ex))
59+
__throw(mess, n, line) = begin
60+
e_prefix = E_PREFIX[]
61+
info = string("\nLine ", n, ": ", line, "\n", mess)
62+
return :( throw($e_prefix.ParsingError($info)) )
5763
end
5864

5965
__wrap(e, n, line) = quote
@@ -224,9 +230,6 @@ function p_alias!(p, p_ocp, a, e; log=false)
224230
a isa Symbol || return __throw("forbidden alias name: $a", p.lnum, p.line)
225231
aa = QuoteNode(a)
226232
ee = QuoteNode(e)
227-
#for i in 1:9
228-
# p.aliases[Symbol(a, CTBase.ctupperscripts(i))] = :($a^$i) # todo: remove? (cf. such aliases now removed for variable, state and control)
229-
#end
230233
p.aliases[a] = e
231234
code = :(LineNumberNode(0, "alias: " * string($aa) * " = " * string($ee)))
232235
return __wrap(code, p.lnum, p.line)
@@ -269,6 +272,7 @@ end
269272

270273
function p_time!(p, p_ocp, t, t0, tf; log=false)
271274
prefix = PREFIX[]
275+
e_prefix = E_PREFIX[]
272276
log && println("time: $t, initial time: $t0, final time: $tf")
273277
t isa Symbol || return __throw("forbidden time name: $t", p.lnum, p.line)
274278
p.t = t
@@ -283,7 +287,7 @@ function p_time!(p, p_ocp, t, t0, tf; log=false)
283287
:($v1) && if (v1 == p.v)
284288
end => quote
285289
($p_ocp.variable_dimension 1) && throw( # todo: add info (dim of var) in PreModel
286-
CTBase.ParsingError("variable must be of dimension one for a time"),
290+
$e_prefix.ParsingError("variable must be of dimension one for a time"),
287291
)
288292
$prefix.time!($p_ocp; ind0=1, tf=$tf, time_name=$tt)
289293
end
@@ -295,7 +299,7 @@ function p_time!(p, p_ocp, t, t0, tf; log=false)
295299
:($v1) && if (v1 == p.v)
296300
end => quote
297301
($p_ocp.variable_dimension 1) && throw( # todo: add info (dim of var) in PreModel
298-
CTBase.ParsingError("variable must be of dimension one for a time"),
302+
$e_prefix.ParsingError("variable must be of dimension one for a time"),
299303
)
300304
$prefix.time!($p_ocp; t0=$t0, indf=1, time_name=$tt)
301305
end
@@ -340,7 +344,7 @@ function p_state!(p, p_ocp, x, n; components_names=nothing, log=false)
340344
return __throw("the number of state components must be $nn", p.lnum, p.line)
341345
for i in 1:nn
342346
p.aliases[components_names.args[i]] = :($x[$i])
343-
# todo: add aliases for state components (scalar) derivatives
347+
# todo: in future, add aliases for state components (scalar) derivatives
344348
end # Aliases from names given by the user
345349
ss = QuoteNode(string.(components_names.args))
346350
code = :($prefix.state!($p_ocp, $n, $xx, $ss))

test/runtests.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
using Test
22
using Aqua
3-
import CTParser: CTParser, subs, replace_call, has, constraint_type, set_prefix, @def
3+
import CTParser: CTParser, subs, replace_call, has, constraint_type, set_prefix, set_e_prefix, @def
44
import CTBase: CTBase, ParsingError
55
import CTModels: CTModels, initial_time, final_time, time_name, variable_dimension, variable_components, variable_name, state_dimension, state_components, state_name, control_dimension, control_components, control_name, constraint, dynamics, mayer, lagrange, criterion, Model
66

test/test_onepass.jl

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
# todo: dim > 1 variable for t0 / tf
33
# todo: test non-autonomous and / or variable __dynamics, __constraint
44

5-
set_prefix(:CTModels) # code generated by @def is prefixed by CTModels (not by OptimalControl - the default)
5+
set_prefix(:CTModels) # code generated by @def is prefixed by CTModels (not by OptimalControl - the default) for tests
6+
7+
set_e_prefix(:CTBase) # exceptions in code generated by @def are prefixed by CTBase (not by OptimalControl - the default) for tests
68

79
function to_out_of_place(f!, n; T=Float64)
810
function f(args...; kwargs...)
@@ -367,15 +369,15 @@ function test_onepass()
367369
@test initial_time(o, [1, 2]) == 1
368370
@test final_time(o, [1, 2]) == 2
369371

370-
## @test_throws ParsingError @def o begin # debug: check time! in onepass, then uncomment
371-
## t0 ∈ R², variable
372-
## t ∈ [t0, 1], time
373-
## end
372+
## @test_throws ParsingError @def o begin # debug: check time! in onepass, then uncomment
373+
## t0 ∈ R², variable
374+
## t ∈ [t0, 1], time
375+
## end
374376
##
375-
## @test_throws ParsingError @def o begin # debug: check time! in onepass, then uncomment
376-
## tf ∈ R², variable
377-
## t ∈ [0, tf], time
378-
## end
377+
## @test_throws ParsingError @def o begin # debug: check time! in onepass, then uncomment
378+
## tf ∈ R², variable
379+
## t ∈ [0, tf], time
380+
## end
379381

380382
@test_throws ParsingError @def o begin
381383
v, variable

0 commit comments

Comments
 (0)