use std::collections::BTreeSet;
use weft_lang::pack::Package;
use weft_lang::{Def, EffectKind, PrimOp, Term, Ty};
fn rec_ty(fields: Vec<(&str, Ty)>) -> Ty {
Ty::Record(
fields
.into_iter()
.map(|(k, v)| (k.to_string(), v))
.collect(),
)
}
fn int(i: i64) -> Term {
Term::Int(i)
}
fn add(a: Term, b: Term) -> Term {
Term::Prim(PrimOp::Add, vec![a, b])
}
fn sub(a: Term, b: Term) -> Term {
Term::Prim(PrimOp::Sub, vec![a, b])
}
fn mul(a: Term, b: Term) -> Term {
Term::Prim(PrimOp::Mul, vec![a, b])
}
fn div(a: Term, b: Term) -> Term {
Term::Prim(PrimOp::Div, vec![a, b])
}
fn modt(x: Term, m: i64) -> Term {
sub(x.clone(), mul(div(x, int(m)), int(m)))
}
fn main() {
let state_ty = rec_ty(vec![("beats", Ty::Int)]);
let out_ty = rec_ty(vec![
("actions", Ty::List(Box::new(Ty::Action))),
("state", state_ty.clone()),
]);
let beats = || Term::Var(0);
let clock_action = Term::Effect(
EffectKind::SetState,
[
("placement".to_string(), Term::Text("clock".into())),
(
"text".to_string(),
Term::Prim(
PrimOp::Concat,
vec![
Term::Prim(
PrimOp::Concat,
vec![
Term::Text("The Atrium has been alive for ".into()),
Term::Prim(PrimOp::ToText, vec![div(beats(), int(4))]),
],
),
Term::Text(" seconds.\nWoven code, beating.".into()),
],
),
),
]
.into(),
);
let cm_to_m = |t: Term| {
Term::Prim(
PrimOp::FDiv,
vec![
Term::Prim(PrimOp::FixOfInt, vec![t]),
Term::Prim(PrimOp::FixOfInt, vec![int(100)]),
],
)
};
let mote_action = Term::Effect(
EffectKind::Spawn,
[
("builtin".to_string(), Term::Text("sphere".into())),
(
"x".to_string(),
cm_to_m(sub(modt(mul(beats(), int(137)), 1800), int(900))),
),
(
"y".to_string(),
cm_to_m(add(int(220), modt(mul(beats(), int(71)), 320))),
),
(
"z".to_string(),
cm_to_m(sub(modt(mul(beats(), int(53)), 1800), int(900))),
),
("scale".to_string(), Term::Fix(140_000)), (
"r".to_string(),
add(int(110), modt(mul(beats(), int(97)), 130)),
),
(
"g".to_string(),
add(int(150), modt(mul(beats(), int(61)), 100)),
),
("b".to_string(), int(255)),
("animate".to_string(), Term::Text("bob".into())),
("speed".to_string(), Term::Fix(700_000)), ("amp".to_string(), Term::Fix(180_000)), ]
.into(),
);
let form: Package = serde_json::from_str(
&std::fs::read_to_string("packages/weft-form/weft-form.weftpack.json")
.expect("run gen_weft_form first"),
)
.expect("weft-form parses");
form.verify().expect("weft-form verifies");
let ring = form.export("ring").expect("weft-form exports ring");
let ring_actions = Term::Map {
cap: 16,
list: Box::new(Term::Call(ring, vec![int(12), Term::Fix(6_000_000)])), body: Box::new(Term::Effect(
EffectKind::Spawn,
[
("builtin".to_string(), Term::Text("sphere".into())),
(
"x".to_string(),
Term::Get(Box::new(Term::Var(0)), "x".into()),
),
(
"y".to_string(),
Term::Prim(
PrimOp::FAdd,
vec![
Term::Get(Box::new(Term::Var(0)), "y".into()),
Term::Fix(2_600_000),
],
),
),
(
"z".to_string(),
Term::Get(Box::new(Term::Var(0)), "z".into()),
),
("scale".to_string(), Term::Fix(120_000)),
("r".to_string(), int(255)),
("g".to_string(), int(210)),
("b".to_string(), int(120)),
("animate".to_string(), Term::Text("bob".into())),
("speed".to_string(), Term::Fix(500_000)),
("amp".to_string(), Term::Fix(120_000)),
]
.into(),
)),
};
let body = Term::Let(
Box::new(add(
Term::Get(Box::new(Term::Var(1)), "beats".into()),
int(1),
)),
Box::new(Term::Rec(
[
(
"actions".to_string(),
Term::If(
Box::new(Term::Prim(PrimOp::EqInt, vec![modt(beats(), 200), int(0)])),
Box::new(ring_actions),
Box::new(Term::If(
Box::new(Term::Prim(PrimOp::EqInt, vec![modt(beats(), 40), int(0)])),
Box::new(Term::ListNew(vec![clock_action.clone(), mote_action])),
Box::new(Term::ListNew(vec![clock_action])),
)),
),
),
(
"state".to_string(),
Term::Rec([("beats".to_string(), beats())].into()),
),
]
.into(),
)),
);
let def = Def {
params: vec![state_ty, rec_ty(vec![])],
ret: out_ty,
effects: BTreeSet::from([EffectKind::SetState, EffectKind::Spawn]),
body,
pre: None,
post: None,
};
let module = weft_lang::pack::link(&[form], vec![def], 0).expect("clock links");
weft_lang::verify_module(&module).expect("the clock must verify — nothing unverified ships");
let pkg = weft_lang::pack::Package {
name: "weft-clock".to_string(),
exports: [("clock".to_string(), module.entry)].into(),
defs: module.defs,
};
pkg.verify().expect("the clock package verifies");
let json = serde_json::to_string_pretty(&pkg).unwrap();
let out = "worlds/wiki.pixygon.io/weft-clock.weftpack.json";
std::fs::write(out, &json).expect("write package");
let _ = std::fs::remove_file("worlds/wiki.pixygon.io/clock.weft.json");
println!(
"wrote {out} ({} bytes, verified) — exports: clock",
json.len()
);
}