use kataan::parser::Parser;
use kataan::{Ctx, Interp, NanBox};
fn main() {
let mut interp = Interp::new();
interp.register_global_fn("hypot", 2, |cx: &mut Ctx, _this, args| {
let a = cx.to_number(args.first().copied().unwrap_or(cx.undefined()))?;
let b = cx.to_number(args.get(1).copied().unwrap_or(cx.undefined()))?;
Ok(cx.number((a * a + b * b).sqrt()))
});
interp.register_global_fn("parseHex", 1, |cx: &mut Ctx, _this, args| {
let s = cx.to_string(args.first().copied().unwrap_or(cx.undefined()))?;
match i64::from_str_radix(s.trim_start_matches("0x"), 16) {
Ok(n) => {
let obj = cx.new_object();
let dec = cx.number(n as f64);
cx.set(obj, "decimal", dec);
let hex = cx.string(&s);
cx.set(obj, "source", hex);
Ok(obj)
}
Err(_) => Err(cx.range_error(&format!("not hex: {s}"))),
}
});
interp.register_global_fn("times", 2, |cx: &mut Ctx, _this, args| {
let f = args.first().copied().unwrap_or(cx.undefined());
let n = cx.to_number(args.get(1).copied().unwrap_or(cx.undefined()))? as i64;
let mut acc: Vec<NanBox> = Vec::new();
for i in 0..n {
let idx = cx.number(i as f64);
acc.push(cx.call(f, cx.undefined(), &[idx])?);
}
Ok(cx.new_array(acc))
});
interp.register_global_fn("describe", 1, |cx: &mut Ctx, _this, args| {
let v = args.first().copied().unwrap_or(cx.undefined());
let s = if cx.is_array(v) {
let n = cx.array_len(v).unwrap_or(0);
let mut sum = 0.0;
for i in 0..n {
let e = cx.array_get(v, i);
sum += cx.to_number(e).unwrap_or(0.0);
}
format!("array of {n}, sum {sum}")
} else if cx.is_object(v) {
format!("object with keys [{}]", cx.own_keys(v).join(", "))
} else {
format!("{} {}", cx.type_of(v), cx.to_string(v)?)
};
Ok(cx.string(&s))
});
interp.register_global_fn("originOf", 1, |cx: &mut Ctx, _this, args| {
let ctor = args.first().copied().unwrap_or(cx.undefined());
if !cx.is_constructor(ctor) {
return Err(cx.type_error("originOf expects a constructor"));
}
let zero = cx.number(0.0);
let p = cx.construct(ctor, &[zero, zero])?;
cx.get(p, "x")
});
interp.register_global_fn("makeCounter", 0, |cx: &mut Ctx, _this, _args| {
let o = cx.new_object();
cx.set_native_state(o, 0u32);
Ok(o)
});
interp.register_global_fn("bump", 1, |cx: &mut Ctx, _this, args| {
let o = args.first().copied().unwrap_or(cx.undefined());
let next = cx.native_state::<u32>(o).copied().unwrap_or(0) + 1;
cx.set_native_state(o, next);
Ok(cx.number(f64::from(next)))
});
let src = r#"
var out = [];
out.push("hypot(3,4) = " + hypot(3, 4));
out.push("parseHex('0xff') = " + JSON.stringify(parseHex("0xff")));
try { parseHex("nope"); } catch (e) { out.push("threw: " + e.message); }
out.push("times(sq,5) = " + JSON.stringify(times(function (i) { return i * i; }, 5)));
out.push("describe([1,2,3]) = " + describe([1, 2, 3]));
out.push("describe({a:1,b:2}) = " + describe({ a: 1, b: 2 }));
out.push("describe('hi') = " + describe("hi"));
class Point { constructor(x, y) { this.x = x; this.y = y; } }
out.push("originOf(Point) = " + originOf(Point));
var c = makeCounter();
out.push("counter = " + bump(c) + "," + bump(c) + "," + bump(c));
out.join("\n");
"#;
let program = Parser::parse_program(src).expect("parse");
let value = interp.run(&program).expect("run");
println!("{}", interp.realm().to_display_string(value));
}