From 0867ccb75d3f3bac5049d128677bb5f973da71b5 Mon Sep 17 00:00:00 2001 From: Jessie Grosen Date: Fri, 7 Jun 2024 16:44:58 -0400 Subject: [PATCH] rip out interpreter --- src/builtin.rs | 40 ++------- src/expr.rs | 21 +---- src/interp.rs | 217 ------------------------------------------------- src/ir1.rs | 2 - src/lib.rs | 1 - src/main.rs | 157 +---------------------------------- src/typing.rs | 1 - 7 files changed, 8 insertions(+), 431 deletions(-) delete mode 100644 src/interp.rs diff --git a/src/builtin.rs b/src/builtin.rs index be8b236..e3c8cc2 100644 --- a/src/builtin.rs +++ b/src/builtin.rs @@ -2,42 +2,27 @@ use std::collections::HashMap; use string_interner::DefaultStringInterner; -use crate::expr::{Symbol, Value}; +use crate::expr::Symbol; use crate::ir1::{DebruijnIndex, Op}; use crate::ir2; -type BuiltinFn = for<'a> fn(&[Value<'a>]) -> Result, &'static str>; - #[derive(Clone, Copy, Debug)] pub struct Builtin { pub n_args: usize, - pub body: BuiltinFn, pub ir2_expr: &'static ir2::Expr<'static>, } impl Builtin { - const fn new(n_args: usize, body: BuiltinFn, ir2_expr: &'static ir2::Expr<'static>) -> Builtin { - Builtin { n_args, body, ir2_expr } + const fn new(n_args: usize, ir2_expr: &'static ir2::Expr<'static>) -> Builtin { + Builtin { n_args, ir2_expr } } } macro_rules! builtins { - ( $($name:ident [ $nargs:literal ] { $pat:pat => $e:expr } [ $ir2e:expr ] ),* $(,)? ) => { - mod builtins { - use super::*; - - $( - pub fn $name<'a>(args: &[Value<'a>]) -> Result, &'static str> { - match args { - $pat => $e, - _ => Err(concat!("args passed to ", stringify!($name), " do not match the pattern ", stringify!($pat))), - } - } - )* - } + ( $($name:ident [ $nargs:literal ] [ $ir2e:expr ] ),* $(,)? ) => { const BUILTINS: &[(&'static str, Builtin)] = &[ $( - (stringify!($name), Builtin::new($nargs, builtins::$name, $ir2e)), + (stringify!($name), Builtin::new($nargs, $ir2e)), )* ]; } @@ -45,51 +30,36 @@ macro_rules! builtins { builtins!( pi[0] - { &[] => Ok(Value::Sample(std::f32::consts::PI)) } [ &ir2::Expr::Op(Op::Pi, &[]) ], sin[1] - { &[Value::Sample(s)] => Ok(Value::Sample(s.sin())) } [ &ir2::Expr::Op(Op::Sin, &[&ir2::Expr::Var(DebruijnIndex(0))]) ], cos[1] - { &[Value::Sample(s)] => Ok(Value::Sample(s.cos())) } [ &ir2::Expr::Op(Op::Cos, &[&ir2::Expr::Var(DebruijnIndex(0))]) ], add[2] - { &[Value::Sample(s1), Value::Sample(s2)] => Ok(Value::Sample(s1 + s2)) } [ &ir2::Expr::Op(Op::FAdd, &[&ir2::Expr::Var(DebruijnIndex(0)), &ir2::Expr::Var(DebruijnIndex(1))]) ], sub[2] - { &[Value::Sample(s1), Value::Sample(s2)] => Ok(Value::Sample(s1 - s2)) } // TODO: make sure this order is correct? [ &ir2::Expr::Op(Op::FSub, &[&ir2::Expr::Var(DebruijnIndex(0)), &ir2::Expr::Var(DebruijnIndex(1))]) ], mul[2] - { &[Value::Sample(s1), Value::Sample(s2)] => Ok(Value::Sample(s1 * s2)) } [ &ir2::Expr::Op(Op::FMul, &[&ir2::Expr::Var(DebruijnIndex(0)), &ir2::Expr::Var(DebruijnIndex(1))]) ], div[2] - { &[Value::Sample(s1), Value::Sample(s2)] => Ok(Value::Sample(s1 / s2)) } [ &ir2::Expr::Op(Op::FDiv, &[&ir2::Expr::Var(DebruijnIndex(0)), &ir2::Expr::Var(DebruijnIndex(1))]) ], addone[1] - { &[Value::Sample(s)] => Ok(Value::Sample(s + 1.0)) } [ &ir2::Expr::Op(Op::FAdd, &[&ir2::Expr::Var(DebruijnIndex(0)), &ir2::Expr::Op(Op::Const(crate::ir1::Value::Sample(1.0)), &[])]) ], reinterpi[1] - { &[Value::Index(i)] => Ok(Value::Sample(f32::from_bits(i as u32))) } [ &ir2::Expr::Op(Op::ReinterpI2F, &[&ir2::Expr::Var(DebruijnIndex(0))]) ], reinterpf[1] - { &[Value::Sample(x)] => Ok(Value::Index(x.to_bits() as usize)) } [ &ir2::Expr::Op(Op::ReinterpF2I, &[&ir2::Expr::Var(DebruijnIndex(0))]) ], cast[1] - { &[Value::Index(i)] => Ok(Value::Sample(i as f32)) } [ &ir2::Expr::Op(Op::CastI2F, &[&ir2::Expr::Var(DebruijnIndex(0))]) ], since_tick[1] - { &[_] => panic!("oops no interpreter") } [ &ir2::Expr::Op(Op::SinceLastTickStream, &[&ir2::Expr::Var(DebruijnIndex(0))]) ], wait[1] - { &[_] => panic!("oops no interpreter") } [ &ir2::Expr::Op(Op::Wait, &[&ir2::Expr::Var(DebruijnIndex(0))]) ], sched[3] - { &[_] => panic!("oops no interpreter") } [ &ir2::Expr::Op(Op::Schedule, &[&ir2::Expr::Var(DebruijnIndex(2)), &ir2::Expr::Var(DebruijnIndex(1)), &ir2::Expr::Var(DebruijnIndex(0))]) ], ); - pub type BuiltinsMap = HashMap; pub fn make_builtins(interner: &mut DefaultStringInterner) -> BuiltinsMap { diff --git a/src/expr.rs b/src/expr.rs index eb074fd..6a7c3d6 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -1,36 +1,19 @@ -use std::collections::HashMap; use std::fmt; use string_interner::{DefaultStringInterner, DefaultSymbol}; use typed_arena::Arena; -use crate::builtin::Builtin; use crate::typing::{Clock, Type, PrettyClock, PrettyType}; pub type Symbol = DefaultSymbol; #[derive(Debug, Clone)] -pub enum Value<'a> { +pub enum Value { Unit, Sample(f32), Index(usize), - Pair(Box>, Box>), - InL(Box>), - InR(Box>), - Gen(Box>, Box>), - Closure(Env<'a>, Symbol, &'a Expr<'a, ()>), - Suspend(Env<'a>, &'a Expr<'a, ()>), - BuiltinPartial(Builtin, Box<[Value<'a>]>), - Array(Box<[Value<'a>]>), - Box(Env<'a>, &'a Expr<'a, ()>), - // TODO: this is a bit of a hack - BoxDelay(Env<'a>, &'a Expr<'a, ()>), } -// TODO: use a better type here... eventually we should resolve symbols and just use de bruijn offsets or similar... -pub type Env<'a> = HashMap>; -// type Env<'a> = imbl::HashMap>; - #[derive(Debug, Clone, Copy)] pub enum Binop { FMul, @@ -84,7 +67,7 @@ impl Binop { #[derive(Debug, Clone)] pub enum Expr<'a, R> { Var(R, Symbol), - Val(R, Value<'a>), + Val(R, Value), Annotate(R, &'a Expr<'a, R>, Type), Lam(R, Symbol, &'a Expr<'a, R>), App(R, &'a Expr<'a, R>, &'a Expr<'a, R>), diff --git a/src/interp.rs b/src/interp.rs deleted file mode 100644 index 12c3c8b..0000000 --- a/src/interp.rs +++ /dev/null @@ -1,217 +0,0 @@ -use std::collections::HashMap; - -use crate::expr::{Binop, Expr, Value, Env, Symbol}; -use crate::builtin::BuiltinsMap; - -pub struct InterpretationContext<'a, 'b> { - pub builtins: &'b BuiltinsMap, - pub defs: &'b HashMap>, - pub env: Env<'a>, -} - -impl<'a, 'b> InterpretationContext<'a, 'b> { - fn with_env(&self, new_env: Env<'a>) -> InterpretationContext<'a, 'b> { - InterpretationContext { env: new_env, ..*self } - } -} - -fn interp_binop<'a>(op: Binop, v1: Value<'a>, v2: Value<'a>) -> Result, &'static str> { - match (v1, v2) { - (Value::Sample(x1), Value::Sample(x2)) => - Ok(Value::Sample(match op { - Binop::FMul => x1 * x2, - Binop::FDiv => x1 / x2, - Binop::FAdd => x1 + x2, - Binop::FSub => x1 - x2, - _ => return Err("cannot do some weird binop on two samples"), - })), - (Value::Index(i1), Value::Index(i2)) => - Ok(Value::Index(match op { - Binop::IMul => i1 * i2, - Binop::IDiv => i1 / i2, - Binop::IAdd => i1 + i2, - Binop::ISub => i1 - i2, - Binop::Shl => i1 << i2, - Binop::Shr => i1 >> i2, - Binop::And => i1 & i2, - Binop::Xor => i1 ^ i2, - Binop::Or => i1 | i2, - _ => return Err("cannot do some weird binop on two indices"), - })), - (_, _) => - Err("bad binop combo"), - } -} - -pub fn interp<'a, 'b>(ctx: &InterpretationContext<'a, 'b>, expr: &'a Expr<'a, ()>) -> Result, &'static str> { - match *expr { - Expr::Var(_, ref x) => { - if let Some(v) = ctx.env.get(x) { - Ok(v.clone()) - } else if let Some(e) = ctx.defs.get(x) { - interp(&ctx.with_env(Env::new()), e) - } else if let Some(&builtin) = ctx.builtins.get(x) { - if builtin.n_args == 0 { - (builtin.body)(&[]) - } else { - Ok(Value::BuiltinPartial(builtin, [].into())) - } - } else { - Err("couldn't find the var in locals or builtins") - } - }, - Expr::Val(_, ref v) => - Ok(v.clone()), - Expr::Annotate(_, e, _) => - interp(ctx, e), - Expr::Lam(_, ref x, ref e) => - Ok(Value::Closure(ctx.env.clone(), x.clone(), *e)), - Expr::App(_, ref e1, ref e2) => { - let v1 = interp(ctx, &*e1)?; - let v2 = interp(ctx, &*e2)?; - match v1 { - Value::Closure(env1, x, ebody) => { - let mut new_env = env1.clone(); - new_env.insert(x, v2); - // TODO: i don't think rust has TCE and this can't be - // TCE'd anyway bc new_env must be deallocated. - interp(&ctx.with_env(new_env), &*ebody) - }, - Value::BuiltinPartial(builtin, args_so_far) => { - let mut new_args = Vec::with_capacity(args_so_far.len() + 1); - new_args.extend(args_so_far.iter().cloned()); - new_args.push(v2); - if builtin.n_args == new_args.len() { - (builtin.body)(&new_args[..]) - } else { - Ok(Value::BuiltinPartial(builtin, new_args.into())) - } - }, - _ => - Err("don't call something that's not a closure!!"), - } - }, - Expr::Adv(_, ref e) => { - let v = interp(ctx, &*e)?; - let Value::Suspend(env1, ebody) = v else { - return Err("don't force something that's not a suspension!!"); - }; - interp(&ctx.with_env(env1), &*ebody) - }, - Expr::Lob(_, _, s, ref e) => { - // TODO: is this really the right semantics? - let boxed = Value::BoxDelay(ctx.env.clone(), expr); - let mut new_env = ctx.env.clone(); - new_env.insert(s, boxed); - interp(&ctx.with_env(new_env), e) - }, - Expr::Gen(_, ref eh, ref et) => { - let vh = interp(ctx, eh)?; - // now this is expected to result in some sort of fix expression or smth - let vt = interp(ctx, et)?; - Ok(Value::Gen(Box::new(vh), Box::new(vt))) - }, - Expr::LetIn(_, x, _, e1, e2) => { - let v = interp(ctx, e1)?; - let mut new_env = ctx.env.clone(); - new_env.insert(x, v); - interp(&ctx.with_env(new_env), e2) - }, - Expr::Pair(_, e1, e2) => { - let v1 = interp(ctx, e1)?; - let v2 = interp(ctx, e2)?; - Ok(Value::Pair(Box::new(v1), Box::new(v2))) - }, - Expr::UnPair(_, x1, x2, e0, e) => { - let Value::Pair(v1, v2) = interp(ctx, e0)? else { - return Err("tried to unpair a non-pair!"); - }; - let mut new_env = ctx.env.clone(); - new_env.insert(x1, *v1); - new_env.insert(x2, *v2); - interp(&ctx.with_env(new_env), e) - }, - Expr::InL(_, e) => - Ok(Value::InL(Box::new(interp(ctx, e)?))), - Expr::InR(_, e) => - Ok(Value::InR(Box::new(interp(ctx, e)?))), - Expr::Case(_, e0, x1, e1, x2, e2) => - match interp(ctx, e0)? { - Value::InL(v) => { - let mut new_env = ctx.env.clone(); - new_env.insert(x1, *v); - interp(&ctx.with_env(new_env), e1) - }, - Value::InR(v) => { - let mut new_env = ctx.env.clone(); - new_env.insert(x2, *v); - interp(&ctx.with_env(new_env), e2) - }, - _ => - Err("tried to case on a non-sum"), - }, - Expr::Array(_, ref es) => { - let mut vs = Vec::with_capacity(es.len()); - for e in es.iter() { - vs.push(interp(ctx, e)?); - } - Ok(Value::Array(vs.into())) - }, - Expr::UnGen(_, e) => - match interp(ctx, e)? { - Value::Gen(v_hd, v_tl) => - Ok(Value::Pair(v_hd, v_tl)), - _ => - Err("tried to ungen a non-gen"), - }, - Expr::Delay(_, e) => - Ok(Value::Suspend(ctx.env.clone(), e)), - Expr::Box(_, e) => - Ok(Value::Box(ctx.env.clone(), e)), - Expr::Unbox(_, e) => - match interp(ctx, e)? { - Value::Box(new_env, e_body) => - interp(&ctx.with_env(new_env), e_body), - Value::BoxDelay(new_env, e_body) => - Ok(Value::Suspend(new_env, e_body)), - _ => - Err("tried to unbox a non-box"), - }, - Expr::ClockApp(_, e, _) => - interp(ctx, e), - Expr::TypeApp(_, e, _) => - interp(ctx, e), - Expr::Binop(_, op, e1, e2) => - interp_binop(op, interp(ctx, e1)?, interp(ctx, e2)?), - Expr::ExIntro(_, _, _) | - Expr::ExElim(_, _, _, _, _) | - Expr::ClockLam(_, _, _) => - panic!("lol i've abandoned the interpreter"), - } -} - -pub fn get_samples<'a>(builtins: &'a BuiltinsMap, defs: &'a HashMap>, mut expr: &'a Expr<'a, ()>, out: &mut [f32]) -> Result<(), String> { - let mut ctx = InterpretationContext { builtins, defs, env: Env::new() }; - for (i, s_out) in out.iter_mut().enumerate() { - match interp(&ctx, expr) { - Ok(Value::Gen(head, tail)) => { - match (*head, *tail) { - (Value::Sample(s), Value::Suspend(new_env, next_expr)) => { - ctx.env = new_env; - *s_out = s; - expr = next_expr; - }, - (h, t) => - return Err(format!("on index {i}, evaluation succeeded with a Gen but got head {h:?} and tail {t:?}")), - } - }, - Ok(v) => { - return Err(format!("on index {i}, evaluation succeeded but got {v:?}")); - }, - Err(e) => { - return Err(format!("on index {i}, evaluation failed with error {e:?}")); - }, - } - } - Ok(()) -} diff --git a/src/ir1.rs b/src/ir1.rs index b25e3b3..b4d6ff2 100644 --- a/src/ir1.rs +++ b/src/ir1.rs @@ -386,8 +386,6 @@ impl<'a> Translator<'a> { Expr::Val(Value::Sample(x)), HExpr::Val(_, HValue::Index(i)) => Expr::Val(Value::Index(i)), - HExpr::Val(_, _) => - panic!("weird value??"), HExpr::Annotate(_, next, _) => self.translate(ctx, next), HExpr::Lam(_, x, next) => { diff --git a/src/lib.rs b/src/lib.rs index bc114f6..34660f7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,6 @@ pub mod expr; pub mod builtin; pub mod parse; -pub mod interp; pub mod typing; pub mod ir1; pub mod ir2; diff --git a/src/main.rs b/src/main.rs index 8c98f81..f9d80db 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,24 +1,15 @@ use std::path::Path; use std::process::ExitCode; -use std::{collections::HashMap, path::PathBuf, fs::File}; +use std::{path::PathBuf, fs::File}; use std::io::{Read, Write}; -use clocky::expr::{Expr, Symbol}; -use num::One; - use typed_arena::Arena; use clap::Parser as CliParser; -use clocky::builtin::make_builtins; -use clocky::interp::get_samples; use clocky::parse; -use clocky::typing::{self, Ctx}; -use clocky::interp; use clocky::toplevel::{compile, TopLevel, TopLevelError, TopLevelResult}; -use clocky::typing::{Type, TopLevelTypeError, Kind, Clock}; - #[derive(CliParser, Debug)] struct Args { #[command(subcommand)] @@ -40,26 +31,6 @@ enum Command { /// Code file to use file: Option, }, - /// Interpret the given program. - Interpret { - /// Code file to use - file: Option, - }, - /// Launch a REPL - Repl, - /// Write out a WAV file, sampling the stream specified in the code - Sample { - /// Path to WAV file to be written - #[arg(short='o')] - out: PathBuf, - - /// How long (in milliseconds at 48kHz) to sample for - #[arg(short='l', default_value_t=1000)] - length: usize, - - /// Code file to use - file: Option, - }, Compile { /// Code file to use file: Option, @@ -116,129 +87,6 @@ fn cmd_typecheck<'a>(toplevel: &mut TopLevel<'a>, file: Option) -> TopL Ok(()) } -fn cmd_interpret<'a>(toplevel: &mut TopLevel<'a>, file: Option) -> TopLevelResult<'a, ()> { - let code = read_file(file.as_deref())?; - let parsed_file = match toplevel.make_parser().parse_file(&code) { - Ok(parsed_file) => parsed_file, - Err(e) => { return Err(TopLevelError::ParseError(code, e)); } - }; - let elabbed_file = toplevel.make_typechecker().check_file(&parsed_file).map_err(|e| TopLevelError::TypeError(code, e))?; - - let arena = Arena::new(); - let defs: HashMap> = elabbed_file.defs.iter().map(|def| (def.name, &*arena.alloc(def.body.get_expr().unwrap().map_ext(&arena, &(|_| ()))))).collect(); - let main = *defs.get(&parsed_file.defs.last().unwrap().name).unwrap(); - let builtins = make_builtins(&mut toplevel.interner); - let interp_ctx = interp::InterpretationContext { builtins: &builtins, defs: &defs, env: HashMap::new() }; - - match interp::interp(&interp_ctx, &main) { - Ok(v) => println!("{:?}", v), - Err(e) => return Err(TopLevelError::InterpError(e.into())), - } - - Ok(()) -} - -fn repl_one<'a>(toplevel: &mut TopLevel<'a>, interp_ctx: &interp::InterpretationContext<'a, '_>, code: String) -> TopLevelResult<'a, ()> { - // this seems like a hack, but it seems like tree-sitter doesn't - // support parsing specific rules yet... - let code = format!("def main: unit = {}", code); - let expr = match toplevel.make_parser().parse_file(&code) { - Ok(parsed_file) => parsed_file.defs[0].body.get_expr().unwrap(), - Err(e) => { return Err(TopLevelError::ParseError(code, e)); } - }; - let empty_symbol = toplevel.interner.get_or_intern_static(""); - let (expr_elab, ty) = toplevel - .make_typechecker() - .synthesize(&Ctx::Empty, expr) - .map_err(|e| TopLevelError::TypeError(code, typing::FileTypeErrors { errs: vec![TopLevelTypeError::TypeError(empty_symbol, e)] } ))?; - println!("synthesized type: {}", ty.pretty(&toplevel.interner)); - - let arena = Arena::new(); - let expr_unannotated = expr_elab.map_ext(&arena, &(|_| ())); - - match interp::interp(&interp_ctx, &expr_unannotated) { - Ok(v) => println!("{:?}", v), - Err(e) => return Err(TopLevelError::InterpError(e.into())), - } - - Ok(()) -} - -fn cmd_repl<'a>(toplevel: &mut TopLevel<'a>) -> TopLevelResult<'a, ()> { - let builtins = make_builtins(&mut toplevel.interner); - let interp_ctx = interp::InterpretationContext { builtins: &builtins, defs: &HashMap::new(), env: HashMap::new() }; - - for line in std::io::stdin().lines() { - match repl_one(toplevel, &interp_ctx, line?) { - Ok(()) => { }, - Err(err @ TopLevelError::IoError(_)) => { return Err(err); }, - Err(TopLevelError::TypeError(code, e)) => { - let TopLevelTypeError::TypeError(_, ref err) = e.errs[0] else { panic!() }; - println!("{}", err.pretty(&toplevel.interner, &code)); - }, - Err(err) => { println!("{:?}", err); }, - } - } - - Ok(()) -} - -fn verify_sample_type<'a>(type_: &Type) -> TopLevelResult<'a, ()> { - match *type_ { - Type::Forall(x, Kind::Clock, ref st) => - match **st { - Type::Stream(Clock { coeff, var }, ref inner) - if x == var && coeff.is_one() => - match **inner { - Type::Sample => - Ok(()), - _ => Err(TopLevelError::CannotSample(type_.clone())), - }, - _ => - Err(TopLevelError::CannotSample(type_.clone())), - }, - _ => - Err(TopLevelError::CannotSample(type_.clone())), - } -} - -fn cmd_sample<'a>(toplevel: &mut TopLevel<'a>, file: Option, length: usize, out: PathBuf) -> TopLevelResult<'a, ()> { - let code = read_file(file.as_deref())?; - let parsed_file = match toplevel.make_parser().parse_file(&code) { - Ok(parsed_file) => parsed_file, - Err(e) => { return Err(TopLevelError::ParseError(code, e)); } - }; - let elabbed_file = toplevel.make_typechecker().check_file(&parsed_file).map_err(|e| TopLevelError::TypeError(code, e))?; - - let arena = Arena::new(); - let defs: HashMap> = elabbed_file.defs.iter().map(|def| (def.name, &*arena.alloc(def.body.get_expr().unwrap().map_ext(&arena, &(|_| ()))))).collect(); - let main_sym = toplevel.interner.get_or_intern_static("main"); - let main_def = parsed_file.defs.iter().filter(|x| x.name == main_sym).next().unwrap(); - verify_sample_type(&main_def.body.get_type().unwrap())?; - let main = *defs.get(&parsed_file.defs.last().unwrap().name).unwrap(); - let builtins = make_builtins(&mut toplevel.interner); - let mut samples = [0.0].repeat(48 * length); - - match get_samples(&builtins, &defs, main, &mut samples[..]) { - Ok(()) => { }, - Err(e) => return Err(TopLevelError::InterpError(e)), - } - - let wav_spec = hound::WavSpec { - channels: 1, - sample_rate: 48000, - bits_per_sample: 32, - sample_format: hound::SampleFormat::Float, - }; - let mut hound_writer = hound::WavWriter::create(out, wav_spec)?; - // is this really the only way hound will let me do this?? - for samp in samples { - hound_writer.write_sample(samp)?; - } - - Ok(()) -} - fn cmd_compile<'a>(toplevel: &mut TopLevel<'a>, file: Option, out: Option) -> TopLevelResult<'a, ()> { let code = read_file(file.as_deref())?; let wasm_bytes = compile(toplevel, code)?; @@ -366,9 +214,6 @@ fn main() -> Result<(), ExitCode> { let res = match args.cmd { Command::Parse { file, dump_to } => cmd_parse(&mut toplevel, file, dump_to), Command::Typecheck { file } => cmd_typecheck(&mut toplevel, file), - Command::Interpret { file } => cmd_interpret(&mut toplevel, file), - Command::Repl => cmd_repl(&mut toplevel), - Command::Sample { out, file, length } => cmd_sample(&mut toplevel, file, length, out), Command::Compile { file, out } => cmd_compile(&mut toplevel, file, out), }; diff --git a/src/typing.rs b/src/typing.rs index d62309a..aa1b3d5 100644 --- a/src/typing.rs +++ b/src/typing.rs @@ -991,7 +991,6 @@ impl<'a, 'b, R: Clone> Typechecker<'a, 'b, R> { Value::Unit => Ok((self.alloc(Expr::Val(r.clone(), Value::Unit)), Type::Unit)), Value::Sample(s) => Ok((self.alloc(Expr::Val(r.clone(), Value::Sample(s))), Type::Sample)), Value::Index(i) => Ok((self.alloc(Expr::Val(r.clone(), Value::Index(i))), Type::Index)), - _ => panic!("trying to type {v:?} but that kind of value shouldn't be created yet?"), }, &Expr::Var(ref r, x) => if let Some((timing, ty)) = ctx.lookup_term_var(x) {