From 2403432194f27596889f9b71fba9c469835ae3e2 Mon Sep 17 00:00:00 2001 From: archanarw <47085068+archanarw@users.noreply.github.com> Date: Sun, 23 Jul 2023 21:18:10 -0400 Subject: [PATCH] first draft of polystr. models --- src/base/aexpr.jl | 3 + src/base/sexpr.jl | 3 + src/interpreter/interpret.jl | 118 +++++++++++++++++++++++++++++- src/interpreter/interpretutils.jl | 6 +- test/polystr_model_examples.jl | 111 ++++++++++++++++++++++++++++ 5 files changed, 237 insertions(+), 4 deletions(-) create mode 100644 test/polystr_model_examples.jl diff --git a/src/base/aexpr.jl b/src/base/aexpr.jl index 2f0c060..6fe5e6d 100644 --- a/src/base/aexpr.jl +++ b/src/base/aexpr.jl @@ -117,6 +117,9 @@ function showstring(aexpr::AExpr) Expr(:object, name, args...) => "(object $(showstring(name)) $(join(map(showstring, args), " ")))" Expr(:on, event, upd) => "(on $(showstring(event)) $(showstring(upd)))" Expr(:deriv, x, val) => "(deriv $(showstring(x)) $(showstring(val)))" + Expr(:in, x, args...) => "(in $(showstring(x)) $(showstring(args...)))" + Expr(:runner, x) => "(runner $(showstring(x)))" + Expr(:run, x) => "(run $(showstring(x)))" x => "Fail $x" end end diff --git a/src/base/sexpr.jl b/src/base/sexpr.jl index 4dcab75..c03f3d1 100644 --- a/src/base/sexpr.jl +++ b/src/base/sexpr.jl @@ -54,6 +54,9 @@ function parseau(sexpr::AbstractArray) [:list, vars...] => AExpr(:list, map(parseau, vars)...) [:.., var, field] => AExpr(:field, parseau(var), parseau(field)) [:on, args...] => AExpr(:on, map(parseau, args)...) + [:run, model::Symbol] => AExpr(:run, model) + [:in, model::Symbol, args...] => AExpr(:in, model, map(parseau, args)...) + [:runner, model::Symbol] => AExpr(:runner, model) [:object, args...] => AExpr(:object, map(parseau, args)...) [:deriv, args...] => AExpr(:deriv, args[1], parseau(args[2])) [f, xs...] => AExpr(:call, parseau(f), map(parseau, xs)...) diff --git a/src/interpreter/interpret.jl b/src/interpreter/interpret.jl index 6ff797a..683cf45 100644 --- a/src/interpreter/interpret.jl +++ b/src/interpreter/interpret.jl @@ -4,9 +4,122 @@ using ..AExpressions: AExpr using ..AutumnStandardLibrary using ..SExpr using Random -export empty_env, Environment, std_env, start, step, run, interpret_program, interpret_over_time, interpret_over_time_observations, interpret_over_time_observations_and_env +export empty_env, Environment, std_env, start, step, run, interpret_program, interpret_over_time, interpret_over_time_observations, interpret_over_time_observations_and_env, polystr_interpret_over_time import MLStyle +"""Interpret program for given number of time steps, returning final environment""" +function polystr_interpret_over_time(aex::AExpr, iters, user_events=[]; show_rules=-1)::Env + run_line = filter(l -> l.head == :run, aex.args) + isempty(run_line) && return interpret_over_time(aex, iters, user_events, show_rules = show_rules) # Did this to keep the old interpreter intact + run_model = run_line[1].args[1] + interpret_over_time_runner(aex, run_model, iters, user_events, show_rules = show_rules) +end + +function interpret_over_time_runner(aex::AExpr, run_model::Symbol, iters, user_events=[]; show_rules=-1)::Env + new_aex, env_ = start_runner(aex, run_model, show_rules=show_rules) + for i in 1:iters + env_ = (user_events == []) ? step(new_aex, env_) : step(new_aex, env_, user_events[i]) + end + env_ +end + +function start_runner(aex::AExpr, run_model::Symbol, rng=Random.GLOBAL_RNG; show_rules=-1) + aex.head == :program || error("Must be a program aex") + env = Env(false, false, false, false, nothing, Dict(), State(0, 0, rng, Scene([], "white"), Dict(), Dict()), show_rules) + + lines = aex.args + + # reorder program lines + grid_params_and_object_type_lines = filter(l -> !(l.head in (:assign, :on, :deriv, :runner, :in, :run)), lines) # || (l.head == :assign && l.args[1] in [:GRID_SIZE, :background]) + initnext_lines = filter(l -> l.head == :assign && (l.args[2] isa AExpr && l.args[2].head == :initnext), lines) + lifted_lines = filter(l -> l.head == :assign && (!(l.args[2] isa AExpr) || l.args[2].head != :initnext), lines) # GRID_SIZE and background here + deriv_lines = filter(l -> l.head == :deriv, lines) + on_clause_lines = filter(l -> l.head == :on, lines) + + in_lines_ = filter(l -> l.head == :in && l.args[1] == run_model, lines) + in_lines = vcat((l.args[2:end] for l in in_lines_)...) + + push!(grid_params_and_object_type_lines, filter(l -> !(l.head in (:assign, :on, :deriv, :runner, :in, :run)), in_lines)...) + push!(initnext_lines, filter(l -> l.head == :assign && (l.args[2] isa AExpr && l.args[2].head == :initnext), in_lines)...) + push!(lifted_lines, filter(l -> l.head == :assign && (!(l.args[2] isa AExpr) || l.args[2].head != :initnext), in_lines)...) + push!(deriv_lines, filter(l -> l.head == :deriv, in_lines)...) + push!(on_clause_lines, filter(l -> l.head == :on, in_lines)...) + + default_on_clause_lines = [] + for line in initnext_lines + var_name = line.args[1] + next_clause = line.args[2].args[2] + if !(next_clause isa AExpr && next_clause.head == :call && length(next_clause.args) == 2 && next_clause.args[1] == :prev && next_clause.args[2] == var_name) + new_on_clause = AExpr(:on, Symbol("true"), AExpr(:assign, var_name, next_clause)) + push!(default_on_clause_lines, new_on_clause) + end + end + + # ----- START deriv handling ----- + deriv_on_clause_lines = [] + for line in deriv_lines + new_on_clause = AExpr(:on, Symbol("true"), line) + push!(deriv_on_clause_lines, new_on_clause) + end + + on_clause_lines_ = [default_on_clause_lines..., deriv_on_clause_lines..., on_clause_lines...] + + on_clause_lines = [] + for oc in on_clause_lines_ + if oc.args[2].head == :deriv + var = oc.args[2].args[1] + update = oc.args[2].args[2] + new_oc = AExpr(:on, oc.args[1], parseautumn("""(= $(var) (+ $(var) (* (/ 1 2) $(repr(update)))))""")) + push!(on_clause_lines, new_oc) + else + push!(on_clause_lines, oc) + end + end + # ----- END deriv handling ----- + + reordered_lines_init = vcat(grid_params_and_object_type_lines, + initnext_lines, + on_clause_lines, + # lifted_lines + ) + + # following initialization, we no longer need initnext lines + reordered_lines = on_clause_lines + + # add prev functions and variable history to state for lifted variables + for line in lifted_lines + var_name = line.args[1] + # construct history variable in state + env.state.histories[var_name] = Dict() + # construct prev function + # env.current_var_values[Symbol(string(:prev, uppercasefirst(string(var_name))))] = [AExpr(:list, [:state]), AExpr(:call, :get, env.state.histories[var_name], AExpr(:call, :-, AExpr(:field, :state, :time), 1), var_name)] + # _, env = interpret(AExpr(:assign, Symbol(string(:prev, uppercasefirst(string(var_name)))), parseautumn("""(fn () (get (.. (.. state histories) $(string(var_name))) (- (.. state time) 1) $(var_name)))""")), env) + end + + # add background to scene + background_assignments = filter(l -> l.args[1] == :background, lifted_lines) + background = background_assignments != [] ? background_assignments[end].args[2] : "#ffffff00" + env.state.scene.background = background + + # initialize lifted variables + for line in lifted_lines + var_name = line.args[1] + # env.lifted[var_name] = line.args[2] + if var_name in [:GRID_SIZE, :background] + env.current_var_values[var_name] = interpret(line.args[2], env)[1] + end + end + + new_aex = AExpr(:program, reordered_lines_init...) # try interpreting the init_next's before on for the first time step (init) + @show new_aex + aex_, env_ = interpret_program(new_aex, env) + + # update state (time, histories, scene) + env_ = update_state(env_) + + AExpr(:program, reordered_lines...), env_ +end + """Interpret program for given number of time steps, returning final environment""" function interpret_over_time(aex::AExpr, iters, user_events=[]; show_rules=-1)::Env new_aex, env_ = start(aex, show_rules=show_rules) @@ -24,12 +137,11 @@ function start(aex::AExpr, rng=Random.GLOBAL_RNG; show_rules=-1) lines = aex.args # reorder program lines - grid_params_and_object_type_lines = filter(l -> !(l.head in (:assign, :on, :deriv)), lines) # || (l.head == :assign && l.args[1] in [:GRID_SIZE, :background]) + grid_params_and_object_type_lines = filter(l -> !(l.head in (:assign, :on, :deriv, :runner, :in, :run)), lines) # || (l.head == :assign && l.args[1] in [:GRID_SIZE, :background]) initnext_lines = filter(l -> l.head == :assign && (l.args[2] isa AExpr && l.args[2].head == :initnext), lines) lifted_lines = filter(l -> l.head == :assign && (!(l.args[2] isa AExpr) || l.args[2].head != :initnext), lines) # GRID_SIZE and background here deriv_lines = filter(l -> l.head == :deriv, lines) on_clause_lines = filter(l -> l.head == :on, lines) - default_on_clause_lines = [] for line in initnext_lines var_name = line.args[1] diff --git a/src/interpreter/interpretutils.jl b/src/interpreter/interpretutils.jl index 2007310..0d4af3b 100644 --- a/src/interpreter/interpretutils.jl +++ b/src/interpreter/interpretutils.jl @@ -3,7 +3,7 @@ using ..AExpressions: AExpr using ..SExpr using ..AutumnStandardLibrary using Setfield -export interpret, interpret_let, interpret_call, interpret_init_next, interpret_object, interpret_object_call, interpret_on, Environment, empty_env, std_env, update, primapl, isprim, update +export interpret, interpret_let, interpret_call, interpret_init_next, interpret_object, interpret_object_call, interpret_on, Environment, empty_env, std_env, update, primapl, isprim, update, +₂ import MLStyle using MappedArrays @@ -48,6 +48,10 @@ function update(Γ::Object, x::Symbol, v)::Object Γ end +# function +₂(x, y) +# (x + y)%2 +# end + # primitive function handling const prim_to_func = let prims = (:+, :-, :*, :/, :&, :!, :|, :>, :>=, :<, :<=, :(==), :%, :!=,) NamedTuple{prims}(getproperty.(Ref(Base), prims)) diff --git a/test/polystr_model_examples.jl b/test/polystr_model_examples.jl new file mode 100644 index 0000000..9972ce1 --- /dev/null +++ b/test/polystr_model_examples.jl @@ -0,0 +1,111 @@ +using Autumn + +arithmatic = au"""(program + (: x Int) + (: y Int) + (: z Int) + (= x (initnext 0 (+ (prev x) 2))) + (= y (initnext 1 (+ 1 (prev y)))) + (= z (initnext (+ x y) (+ x y))) + + (runner R) + (in R (: z Bool)) + (in R (= z (initnext (== (% (+ x y) 2) 1) (== (% (+ x y) 2) 1)))) + (run R) +)""" + +env = polystr_interpret_over_time(arithmatic, 4, []) + +light_switch = au"""(program + (= GRID_SIZE 16) + (in HIGH_LEVEL + (: light Bool) + (= light (initnext false (prev light))) + ) + + (: switch Bool) + (= switch (initnext false (prev switch))) + + (on clicked (= switch (! switch))) + (on switch (= light true)) + (on (! switch) (= light false)) + + (runner R) + (in R + (: light Bool) + (= light (initnext false (prev light))) + (: powercut Bool) + (= powercut (initnext false (prev powercut))) + (on switch (= light (! powercut))) + ) + + (runner P) + (in P + (: light Int) + (= light (initnext 0 (prev light))) + (: volt Int) + (= volt (initnext 7 (prev volt))) + (on switch (= light volt)) + ) + + (run P) +)""" + +env = polystr_interpret_over_time(light_switch, 4, [(click=Autumn.AutumnStandardLibrary.Click(5,5),), empty_env(), (click=Autumn.AutumnStandardLibrary.Click(9,9),), empty_env()]) + +a = au"""(program + (= GRID_SIZE 16) + + (object Lamp (: on Bool) (map (--> pos (Cell pos (if on then "gold" else "gray"))) (vcat (list (Position 0 0)) + (rect (Position -1 1) (Position 1 1)) + (rect (Position -2 2) (Position 2 3)) + (rect (Position 0 4) (Position 0 6)) + (rect (Position -2 7) (Position 2 7)) + )) ) + (object Switch (: on Bool) (Cell 0 0 (if on then "red" else "black"))) + (object Outlet (: powerOut Bool) (map (--> pos (Cell pos "darkorange")) (rect (Position 0 -1) (Position 0 1)))) + (object Wire (: attached Bool) (if attached then (map (--> pos (Cell pos "brown")) (vcat (rect (Position 0 0) (Position 4 0)) (rect (Position 4 -2) (Position 4 -1)) (Position 5 -2))) + else (map (--> pos (Cell pos "brown")) (vcat (rect (Position 0 0) (Position 4 0)) (rect (Position 4 1) (Position 4 2)) (Position 5 2))) + )) + + (: lamp Lamp) + (= lamp (initnext (Lamp false (Position 5 5)) (prev lamp))) + + (: switch Switch) + (= switch (initnext (Switch false (Position 3 11)) (prev switch))) + + (: outlet Outlet) + (= outlet (initnext (Outlet false (Position 14 10)) (prev outlet))) + + (: wire Wire) + (= wire (initnext (Wire true (Position 8 12)) (prev wire))) + + (on (clicked outlet) (= outlet (updateObj outlet "powerOut" (! (.. outlet powerOut))))) + (on (clicked switch) (= switch (updateObj switch "on" (! (.. switch on))))) + (on (clicked wire) (= wire (updateObj wire "attached" (! (.. wire attached))))) + (on (& (! (.. outlet powerOut)) (& (.. switch on) (.. wire attached))) (= lamp (updateObj lamp "on" true))) + (on (! (& (! (.. outlet powerOut)) (& (.. switch on) (.. wire attached)))) (= lamp (updateObj lamp "on" false))) +)""" + +env = polystr_interpret_over_time(a, 4, [(click=Autumn.AutumnStandardLibrary.Click(5,5),), empty_env(), (click=Autumn.AutumnStandardLibrary.Click(9,9),), empty_env()]) + +particles = au"""(program + (= GRID_SIZE 16) + + (= particleSize (initnext 1 (prev particleSize))) + + (runner R1) + (in R1 (= particleSize (initnext 2 (prev particleSize)))) + + (runner R2) + (in R2 (= particleSize (initnext 3 (prev particleSize)))) + + (object Particle (: size Int) (map (--> pos (Cell pos "blue")) (rect (Position 0 0) (Position (- size 1) (- size 1))))) + + (= particles (initnext (list) (updateObj (prev particles) (--> obj (uniformChoice (list (moveNoCollision obj (- 0 particleSize) 0) (moveNoCollision obj particleSize 0) (moveNoCollision obj 0 particleSize) (moveNoCollision obj 0 (- 0 particleSize))) )))))  + (on clicked (= particles (addObj (prev particles) (Particle particleSize (Position (- (.. click x) (% (.. click x) particleSize)) (- (.. click y) (% (.. click y) particleSize)) ))))) + + (run R1) +)""" + +env = polystr_interpret_over_time(particles, 4, [(click=Autumn.AutumnStandardLibrary.Click(5,5),), empty_env(), (click=Autumn.AutumnStandardLibrary.Click(9,9),), empty_env()])