Skip to content

Commit

Permalink
top-level clock definitions!
Browse files Browse the repository at this point in the history
  • Loading branch information
jmgrosen committed Jun 7, 2024
1 parent 89b76ea commit 6004672
Show file tree
Hide file tree
Showing 14 changed files with 8,408 additions and 7,787 deletions.
45 changes: 22 additions & 23 deletions runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,6 @@ pub struct Stream {
tail: *const Closure,
}

#[repr(C)]
pub struct ClockData {
clock: *const Clock,
which_clocks: *const ClockSet,
}

#[no_mangle]
pub unsafe extern "C" fn hd_stream(s: *const Stream) -> f32 {
*(*s).head
Expand Down Expand Up @@ -63,21 +57,26 @@ pub unsafe extern "C" fn apply_clock(clos: *const Closure, clk: *const ()) -> *c
#[repr(C)]
struct SinceLastTickClosure {
clos: Closure,
clock: *const ClockData,
clock: *const ClockSet,
}

unsafe fn since_last_clock_set_tick(clock_set: &ClockSet) -> f32 {
// TODO: should find more efficient method
clock_set.iter().map(|clk_id| SCHEDULER.clocks[clk_id].since_last_tick()).reduce(f32::min).unwrap()
}

unsafe extern "C" fn since_last_tick_closure(self_: *const SinceLastTickClosure) -> *const Stream {
let st: *mut Stream = mem::transmute(alloc(mem::size_of::<Stream>() as u32));
let val: *mut f32 = mem::transmute(alloc(mem::size_of::<f32>() as u32));
// i love indirection!!!!!!
*val = (*(*(*self_).clock).clock).since_last_tick();
let clock_set: &ClockSet = &*(*self_).clock;
*val = since_last_clock_set_tick(clock_set);
(*st).head = val;
(*st).tail = mem::transmute(self_);
st
}

#[no_mangle]
pub unsafe extern "C" fn since_last_tick_stream(clock: *const ClockData) -> *const Stream {
pub unsafe extern "C" fn since_last_tick_stream(clock: *const ClockSet) -> *const Stream {
let clos: *mut SinceLastTickClosure = mem::transmute(alloc(mem::size_of::<SinceLastTickClosure>() as u32));
(*clos).clos.func = mem::transmute(since_last_tick_closure as unsafe extern "C" fn(*const SinceLastTickClosure) -> *const Stream);
(*clos).clos.arity = 0;
Expand Down Expand Up @@ -114,7 +113,7 @@ pub static UNIT_CLOSURE: Closure = Closure {
};

#[no_mangle]
pub unsafe extern "C" fn wait_closure(_clk: *const ClockData) -> *const Closure {
pub unsafe extern "C" fn wait_closure(_clk: *const ClockSet) -> *const Closure {
// TODO: why doesn't &UNIT_CLOSURE work?
Box::into_raw(Box::new(Closure {
func: unit_closure as ClockyFunc,
Expand Down Expand Up @@ -151,7 +150,7 @@ struct DelayedValue {
// target_clock, but if they are triggered by the same clock, the
// closure really should run first...
#[no_mangle]
pub unsafe extern "C" fn schedule(source_clock: *const ClockData, _target_clock: *const ClockData, clos: *const Closure) -> *const Closure {
pub unsafe extern "C" fn schedule(source_clock: *const ClockSet, _target_clock: *const ClockSet, clos: *const Closure) -> *const Closure {
let sched_clos = alloc(mem::size_of::<ScheduledClosure>() as u32) as *mut ScheduledClosure;
(*sched_clos).func = scheduled_closure_func;
(*sched_clos).n_args = 0;
Expand All @@ -161,7 +160,7 @@ pub unsafe extern "C" fn schedule(source_clock: *const ClockData, _target_clock:
(*sched_clos).cell_to_fill = target_cell;

SCHEDULER.scheduled_tasks.push(Task {
triggering_clocks: (*(*source_clock).which_clocks).clone(),
triggering_clocks: (*source_clock).clone(),
clos: sched_clos as *const Closure,
});

Expand All @@ -176,14 +175,8 @@ pub unsafe extern "C" fn schedule(source_clock: *const ClockData, _target_clock:
}

#[no_mangle]
pub unsafe extern "C" fn get_clock_data(clk_id: ClockId) -> *const ClockData {
// TODO: aaaaaaaaaaa this is obviously bad if we make new clocks
let clock = &SCHEDULER.clocks[clk_id] as *const Clock;
let which_clocks = Box::into_raw(Box::new(iter::once(clk_id).collect::<ClockSet>()));
Box::into_raw(Box::new(ClockData {
clock,
which_clocks,
}))
pub unsafe extern "C" fn get_clock_set(clk_id: ClockId) -> *const ClockSet {
Box::into_raw(Box::new(iter::once(clk_id).collect()))
}

enum Clock {
Expand Down Expand Up @@ -249,9 +242,15 @@ static mut SCHEDULER: Scheduler = Scheduler {

#[no_mangle]
pub unsafe extern "C" fn init_scheduler() {
// two default clocks for now
SCHEDULER.clocks.push(Clock::Audio);
SCHEDULER.clocks.push(Clock::Periodic { period: 0.5, remaining: 0.5 });
}

#[no_mangle]
pub unsafe extern "C" fn make_clock(freq: f32) -> *const ClockSet {
let clock_id = SCHEDULER.clocks.len();
let period = 1. / freq;
SCHEDULER.clocks.push(Clock::Periodic { period, remaining: period });
get_clock_set(clock_id)
}

unsafe fn step_scheduler(dur: f32) {
Expand Down
6 changes: 1 addition & 5 deletions sketching/processor.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,7 @@ class ClockyStreamProcessor extends AudioWorkletProcessor {
console.log("instantiated");
this.instance = instance;
this.first = true;
instance.exports.init_scheduler()
const clock0 = instance.exports.get_clock_data(0);
const intermediate = instance.exports.apply_clock(instance.exports.main, clock0);
const clock1 = instance.exports.get_clock_data(1);
this.stream = instance.exports.apply_clock(intermediate, clock1);
this.stream = instance.exports.main;
this.samples_ptr = instance.exports.alloc(4 * 128);
});
};
Expand Down
93 changes: 75 additions & 18 deletions src/bindings.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
use core::fmt;
use std::iter;
use std::error::Error;
use std::path::Path;
use std::process::ExitCode;
use std::{collections::HashMap, path::PathBuf, fs::File};
use std::{collections::{HashMap, HashSet}, path::PathBuf, fs::File};
use std::io::{Read, Write};

use crate::expr::{Expr, Symbol};
use crate::expr::{Expr, Symbol, TopLevelDefBody};
use num::One;
use string_interner::{StringInterner, DefaultStringInterner};

Expand All @@ -27,6 +28,7 @@ struct TopLevel<'a> {
interner: DefaultStringInterner,
arena: &'a Arena<Expr<'a, tree_sitter::Range>>,
globals: Globals,
global_clocks: HashSet<Symbol>,
}

impl<'a> TopLevel<'a> {
Expand All @@ -36,7 +38,12 @@ impl<'a> TopLevel<'a> {
}

fn make_typechecker<'b>(&'b mut self) -> Typechecker<'b, 'a, tree_sitter::Range> {
Typechecker { arena: self.arena, globals: &mut self.globals, interner: &mut self.interner }
Typechecker {
arena: self.arena,
globals: &mut self.globals,
global_clocks: &self.global_clocks,
interner: &mut self.interner,
}
}
}

Expand All @@ -51,6 +58,22 @@ enum TopLevelError<'a> {
WavError(hound::Error),
}

// TODO: move this out
#[derive(Clone, Copy, Hash, PartialEq, Eq)]
enum Name {
Term(Symbol),
Clock(Symbol),
}

impl Name {
fn symbol(&self) -> Symbol {
match *self {
Name::Term(x) => x,
Name::Clock(x) => x,
}
}
}

#[wasm_bindgen]
pub fn compile(code: String) -> Result<Vec<u8>, String> {
let annot_arena = Arena::new();
Expand Down Expand Up @@ -102,16 +125,19 @@ pub fn compile(code: String) -> Result<Vec<u8>, String> {
))
)));

let mut toplevel = TopLevel { arena: &annot_arena, interner, globals };
let mut global_clock_names = HashSet::new();
let audio = interner.get_or_intern_static("audio");
global_clock_names.insert(audio);

let mut toplevel = TopLevel { arena: &annot_arena, interner, globals, global_clocks: global_clock_names };

let parsed_file = match toplevel.make_parser().parse_file(&code) {
Ok(parsed_file) => parsed_file,
Err(e) => { return Err(format!("{:?}", TopLevelError::ParseError(code, e))); }
};
let elabbed_file = toplevel.make_typechecker().check_file(&parsed_file).map_err(|e| format!("{:?}", TopLevelError::TypeError(code, e)))?;

let arena = Arena::new();
let defs: Vec<(Symbol, &Expr<'_, ()>)> = elabbed_file.defs.iter().map(|def| (def.name, &*arena.alloc(def.body.map_ext(&arena, &(|_| ()))))).collect();
let defs = elabbed_file.defs;

let builtins = make_builtins(&mut toplevel.interner);
let mut builtin_globals = HashMap::new();
Expand All @@ -126,8 +152,20 @@ pub fn compile(code: String) -> Result<Vec<u8>, String> {
});
}

for &(name, _) in defs.iter() {
builtin_globals.insert(name, ir1::Global(global_defs.len() as u32));
let mut global_clocks = HashMap::new();
global_clocks.insert(audio, ir1::Global(global_defs.len() as u32));
global_defs.push(ir2::GlobalDef::ClosedExpr {
body: &ir2::Expr::Var(ir1::DebruijnIndex(0)),
});
for def in defs.iter() {
match def.body {
TopLevelDefBody::Def { .. } => {
builtin_globals.insert(def.name, ir1::Global(global_defs.len() as u32));
}
TopLevelDefBody::Clock { .. } => {
global_clocks.insert(def.name, ir1::Global(global_defs.len() as u32));
}
}
// push a dummy def that we'll replace later, to reserve the space
global_defs.push(ir2::GlobalDef::ClosedExpr {
body: &ir2::Expr::Var(ir1::DebruijnIndex(0)),
Expand All @@ -137,16 +175,32 @@ pub fn compile(code: String) -> Result<Vec<u8>, String> {
let expr_under_arena = Arena::new();
let expr_ptr_arena = Arena::new();
let expr_arena = util::ArenaPlus { arena: &expr_under_arena, ptr_arena: &expr_ptr_arena };
let translator = ir1::Translator { globals: builtin_globals, global_clocks: HashMap::new(), arena: &expr_arena };
let translator = ir1::Translator { globals: builtin_globals, global_clocks, arena: &expr_arena };


let mut main = None;
let defs_ir1: HashMap<Symbol, &ir1::Expr<'_>> = defs.iter().map(|&(name, expr)| {
let expr_ir1 = expr_under_arena.alloc(translator.translate(ir1::Ctx::Empty.into(), expr));
let (annotated, _) = translator.annotate_used_vars(expr_ir1);
let shifted = translator.shift(annotated, 0, 0, &imbl::HashMap::new());
(name, shifted)
}).collect();
let mut defs_ir1: HashMap<Name, &ir1::Expr<'_>> = iter::once({
let clock_expr = &*expr_under_arena.alloc(
ir1::Expr::Op(ir1::Op::GetClock(0), &[])
);
(Name::Clock(audio), clock_expr)
}).chain(defs.iter().map(|def| {
match def.body {
TopLevelDefBody::Def { expr, .. } => {
println!("compiling {}", toplevel.interner.resolve(def.name).unwrap());
let expr_ir1 = expr_under_arena.alloc(translator.translate(ir1::Ctx::Empty.into(), expr));
let (annotated, _) = translator.annotate_used_vars(expr_ir1);
let shifted = translator.shift(annotated, 0, 0, &imbl::HashMap::new());
(Name::Term(def.name), shifted)
},
TopLevelDefBody::Clock { freq } => {
let clock_expr = &*expr_under_arena.alloc(
ir1::Expr::Op(ir1::Op::MakeClock(freq), &[])
);
(Name::Clock(def.name), clock_expr)
},
}
})).collect();

let expr2_under_arena = Arena::new();
let expr2_ptr_arena = Arena::new();
Expand All @@ -155,12 +209,15 @@ pub fn compile(code: String) -> Result<Vec<u8>, String> {

for (name, expr) in defs_ir1 {
let expr_ir2 = translator2.translate(expr);
let def_idx = translator.globals[&name].0 as usize;
if name == toplevel.interner.get_or_intern_static("main") {
println!("found main: {def_idx}");
let def_idx = match name {
Name::Term(sym) => translator.globals[&sym].0 as usize,
Name::Clock(sym) => translator.global_clocks[&sym].0 as usize,
};
if name == Name::Term(toplevel.interner.get_or_intern_static("main")) {
main = Some(def_idx);
}
translator2.globals[def_idx] = ir2::GlobalDef::ClosedExpr { body: expr_ir2 };
println!("{}: {:?}", toplevel.interner.resolve(name.symbol()).unwrap(), expr_ir2);
}

let mut global_defs = translator2.globals;
Expand Down
51 changes: 46 additions & 5 deletions src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -272,19 +272,48 @@ impl<'a, 'b, R> fmt::Display for PrettyExpr<'a, 'b, R> {
}
}

#[derive(Clone, Copy, Debug)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum TopLevelDefKind {
Let,
Def,
}

impl fmt::Display for TopLevelDefKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
TopLevelDefKind::Let => write!(f, "let"),
TopLevelDefKind::Def => write!(f, "def"),
}
}
}

#[derive(Clone, Debug)]
pub enum TopLevelDefBody<'a, R> {
Def { kind: TopLevelDefKind, type_: Type, expr: &'a Expr<'a, R> },
Clock { freq: f32 },
}

impl<'a, R> TopLevelDefBody<'a, R> {
pub fn get_expr(&self) -> Option<&'a Expr<'a, R>> {
match *self {
TopLevelDefBody::Def { expr, .. } => Some(expr),
_ => None,
}
}

pub fn get_type(&self) -> Option<&Type> {
match *self {
TopLevelDefBody::Def { ref type_, .. } => Some(type_),
_ => None,
}
}
}

#[derive(Clone, Debug)]
pub struct TopLevelDef<'a, R> {
pub kind: TopLevelDefKind,
pub name: Symbol,
pub type_: Type,
pub body: &'a Expr<'a, R>,
pub range: R,
pub body: TopLevelDefBody<'a, R>,
}

pub struct PrettyTopLevelLet<'a, R> {
Expand All @@ -294,7 +323,19 @@ pub struct PrettyTopLevelLet<'a, R> {

impl<'a, R> fmt::Display for PrettyTopLevelLet<'a, R> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "def {}: {} = {}", self.interner.resolve(self.def.name).unwrap(), self.def.type_.pretty(self.interner), self.def.body.pretty(self.interner))
let name = self.interner.resolve(self.def.name).unwrap();
match self.def.body {
TopLevelDefBody::Def { kind, ref type_, expr: body } =>
write!(f, "{} {}: {} = {};;",
kind,
name,
type_.pretty(self.interner),
body.pretty(self.interner)),
TopLevelDefBody::Clock { freq } =>
write!(f, "clock {} of frequency {} Hz;;",
name,
freq),
}
}
}

Expand Down
4 changes: 4 additions & 0 deletions src/ir1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,8 @@ pub enum Op {
Advance,
Wait,
Schedule,
MakeClock(f32),
GetClock(u32),
}

impl Op {
Expand Down Expand Up @@ -212,6 +214,8 @@ impl Op {
Op::Advance => Some(1),
Op::Wait => Some(1),
Op::Schedule => Some(3),
Op::MakeClock(_) => Some(0),
Op::GetClock(_) => Some(0),
}
}
}
Expand Down
Loading

0 comments on commit 6004672

Please sign in to comment.