diff --git a/shin-asm/src/compile/hir/lower/block.rs b/shin-asm/src/compile/hir/lower/block.rs index f14ff3c..6993340 100644 --- a/shin-asm/src/compile/hir/lower/block.rs +++ b/shin-asm/src/compile/hir/lower/block.rs @@ -319,7 +319,7 @@ mod tests { ───╯ - Error: Unknown instruction: `x` + Error: Unrecognized instruction: `x` ╭─[test.sal:1:1] │ 1 │ x 96 @@ -359,7 +359,7 @@ mod tests { ───╯ - Error: Expected no more than 2 arguments + Error: Extra argument: expected no more than 2 arguments ╭─[test.sal:1:15] │ 1 │ zero $v0 aslk as @@ -386,7 +386,7 @@ mod tests { ───╯ instructions: - uo(UnaryOperation { ty: Zero, destination: $v0, source: 0 }) + uo(UnaryOperation { ty: Not16, destination: $v2, source: $v1 }) code addresses: diff --git a/shin-asm/src/compile/hir/lower/file.rs b/shin-asm/src/compile/hir/lower/file.rs index 569b02d..2005df9 100644 --- a/shin-asm/src/compile/hir/lower/file.rs +++ b/shin-asm/src/compile/hir/lower/file.rs @@ -207,7 +207,7 @@ mod tests { ───╯ - Error: Unknown instruction: `ABOBA` + Error: Unrecognized instruction: `ABOBA` ╭─[test.sal:3:5] │ 3 │ ABOBA 42 42 42 diff --git a/shin-asm/src/compile/hir/lower/instruction/from_instr_args.rs b/shin-asm/src/compile/hir/lower/instruction/from_instr_args.rs index 9b6f961..16668b0 100644 --- a/shin-asm/src/compile/hir/lower/instruction/from_instr_args.rs +++ b/shin-asm/src/compile/hir/lower/instruction/from_instr_args.rs @@ -6,65 +6,60 @@ use crate::compile::{ }, }; -pub fn expect_no_more_args( +// the non-generic part of the implementation +fn expect_opt_args_inner( collectors: &mut FromHirCollectors, - ctx: &FromHirBlockCtx, instr: hir::InstructionId, -) -> [Option; N] { - let instr = ctx.instr(instr); - if instr.args.len() > N { - collectors.emit_diagnostic( - instr.args[N].into(), - format!("Expected no more than {} arguments", N), - ); - } - - let mut args = [None; N]; - for (i, arg) in args.iter_mut().enumerate() { - *arg = instr.args.get(i).copied(); - } - - args -} - -pub fn expect_exactly_args( - collectors: &mut FromHirCollectors, - ctx: &FromHirBlockCtx, - instr: hir::InstructionId, -) -> [Option; N] { - let instr_args = &ctx.instr(instr).args; - if instr_args.len() > N { - let msg = if N > 0 { + instr_args: &[hir::ExprId], + n_m: usize, + n_o: usize, +) { + if instr_args.len() > n_m + n_o { + let msg = if n_m + n_o > 0 { format!( - "Extra argument: expected exactly {} arguments, but got {}", - N, - instr_args.len() + "Extra argument: expected no more than {} arguments", + n_m + n_o ) } else { - format!( - "Extra argument: expected no arguments, but got {}", - instr_args.len() - ) + format!("Extra argument: expected no arguments") }; - collectors.emit_diagnostic(instr_args[N].into(), msg); - } else if instr_args.len() < N { + collectors.emit_diagnostic(instr_args[n_m + n_o].into(), msg); + } + if instr_args.len() < n_m { collectors.emit_diagnostic( instr.into(), format!( - "Missing argument: expected exactly {} arguments, but got {}", - N, + "Missing argument: expected at least {} arguments, but got {}", + n_m, instr_args.len() ), ); } +} - let mut args = [None; N]; - for (i, arg) in args.iter_mut().enumerate() { +#[inline] +fn expect_opt_args( + collectors: &mut FromHirCollectors, + ctx: &FromHirBlockCtx, + instr: hir::InstructionId, +) -> ([Option; N_M], [Option; N_O]) { + let instr_args = &ctx.instr(instr).args; + // N_M is the number of mandatory arguments + // N_O is the number of optional arguments + + expect_opt_args_inner(collectors, instr, instr_args, N_M, N_O); + + let mut args_m = [None; N_M]; + for (i, arg) in args_m.iter_mut().enumerate() { *arg = instr_args.get(i).copied(); } + let mut args_o = [None; N_O]; + for (i, arg) in args_o.iter_mut().enumerate() { + *arg = instr_args.get(i + N_M).copied(); + } - args + (args_m, args_o) } pub trait FromInstrArgs: Sized { @@ -116,10 +111,7 @@ macro_rules! impl_from_instr_args_opt_tuple { ctx: &FromHirBlockCtx, instr: hir::InstructionId, ) -> Option { - let [ - $($mty,)* - $($oty,)* - ] = expect_no_more_args(collectors, ctx, instr); + let ([$($mty,)*], [$($oty,)*]) = expect_opt_args(collectors, ctx, instr); $( let $mty = $mty.and_then(|arg| { FromHirExpr::from_hir_expr(collectors, ctx, arg) diff --git a/shin-asm/src/compile/hir/lower/instruction/instr_lowerer.rs b/shin-asm/src/compile/hir/lower/instruction/instr_lowerer.rs new file mode 100644 index 0000000..8a7c0fb --- /dev/null +++ b/shin-asm/src/compile/hir/lower/instruction/instr_lowerer.rs @@ -0,0 +1,86 @@ +use shin_core::format::scenario::instructions::Instruction; + +use super::from_instr_args::FromInstrArgs; +use crate::compile::hir::lower::from_hir::{FromHirBlockCtx, FromHirCollectors}; + +pub trait InstrLowerFn { + fn lower_instr( + &self, + collectors: &mut FromHirCollectors, + ctx: &FromHirBlockCtx, + instr_name: &str, + args: Args, + ) -> Option; +} + +// TODO: refine the selection of the InstrLowerFn shapes + +impl< + Args: FromInstrArgs, + F: Fn(&mut FromHirCollectors, &FromHirBlockCtx, &str, Args) -> Option, + > InstrLowerFn<((), (), (), ()), Args> for F +{ + fn lower_instr( + &self, + collectors: &mut FromHirCollectors, + ctx: &FromHirBlockCtx, + instr_name: &str, + args: Args, + ) -> Option { + self(collectors, ctx, instr_name, args) + } +} + +impl< + Args: FromInstrArgs, + F: Fn(&mut FromHirCollectors, &FromHirBlockCtx, Args) -> Option, + > InstrLowerFn<((), (), ()), Args> for F +{ + fn lower_instr( + &self, + collectors: &mut FromHirCollectors, + ctx: &FromHirBlockCtx, + _instr_name: &str, + args: Args, + ) -> Option { + self(collectors, ctx, args) + } +} + +impl Option> InstrLowerFn<((), ()), Args> + for F +{ + fn lower_instr( + &self, + _collectors: &mut FromHirCollectors, + _ctx: &FromHirBlockCtx, + instr_name: &str, + args: Args, + ) -> Option { + self(instr_name, args) + } +} + +impl Option> InstrLowerFn<((),), Args> for F { + fn lower_instr( + &self, + _collectors: &mut FromHirCollectors, + _ctx: &FromHirBlockCtx, + _instr_name: &str, + args: Args, + ) -> Option { + self(args) + } +} + +impl Option> InstrLowerFn<(), ()> for F { + fn lower_instr( + &self, + _collectors: &mut FromHirCollectors, + _ctx: &FromHirBlockCtx, + _instr_name: &str, + _args: (), + ) -> Option { + self() + } +} diff --git a/shin-asm/src/compile/hir/lower/instruction/mod.rs b/shin-asm/src/compile/hir/lower/instruction/mod.rs index badece6..3e7e678 100644 --- a/shin-asm/src/compile/hir/lower/instruction/mod.rs +++ b/shin-asm/src/compile/hir/lower/instruction/mod.rs @@ -1,19 +1,47 @@ mod from_instr_args; +mod instr_lowerer; +mod router; use shin_core::format::scenario::{ - instruction_elements::NumberSpec, + instruction_elements::{CodeAddress, NumberSpec, Register}, instructions::{Instruction, UnaryOperation, UnaryOperationType}, }; +use self::router::{Router, RouterBuilder}; use crate::compile::{ hir, - hir::lower::{ - from_hir::{FromHirBlockCtx, FromHirCollectors}, - FromHirExpr, - }, + hir::lower::from_hir::{FromHirBlockCtx, FromHirCollectors}, }; -// fn zero() +fn zero((destination, source): (Register, Option)) -> Option { + Some(Instruction::uo(UnaryOperation { + ty: UnaryOperationType::Zero, + destination, + source: source.unwrap_or(NumberSpec::constant(0)), + })) +} + +fn unary_op( + instr_name: &str, + (destination, source): (Register, NumberSpec), +) -> Option { + let ty = match instr_name { + "not16" => UnaryOperationType::Not16, + "neg" => UnaryOperationType::Negate, + "abs" => UnaryOperationType::Abs, + _ => unreachable!(), + }; + + Some(Instruction::uo(UnaryOperation { + ty, + destination, + source, + })) +} + +fn jump((target,): (CodeAddress,)) -> Option { + Some(Instruction::j { target }) +} pub fn instruction_from_hir( collectors: &mut FromHirCollectors, @@ -24,63 +52,15 @@ pub fn instruction_from_hir( return None; }; - match name.as_str() { - "zero" => { - let [destination, source] = - from_instr_args::expect_no_more_args(collectors, ctx, instr); - - let destination = destination.map(|id| FromHirExpr::from_hir_expr(collectors, ctx, id)); - let source = source.map(|id| FromHirExpr::from_hir_expr(collectors, ctx, id)); - - if destination.is_none() { - collectors.emit_diagnostic(instr.into(), "Missing a `destination` argument".into()); - } + let router = RouterBuilder::new() + .add("zero", zero) + .add("not16", unary_op) + .add("neg", unary_op) + .add("abs", unary_op) + .add("j", jump) + .build(); - let destination = destination??; - let source = source.flatten().unwrap_or(NumberSpec::constant(0)); - - Some(Instruction::uo(UnaryOperation { - ty: UnaryOperationType::Zero, - destination, - source, - })) - } - "not16" | "neg" | "abs" => { - let [destination, source] = - from_instr_args::expect_exactly_args(collectors, ctx, instr); - - let destination = destination.map(|id| FromHirExpr::from_hir_expr(collectors, ctx, id)); - let source = source.map(|id| FromHirExpr::from_hir_expr(collectors, ctx, id)); - - let destination = destination??; - let source = source??; - - let ty = match name.as_str() { - "not16" => UnaryOperationType::Not16, - "neg" => UnaryOperationType::Negate, - "abs" => UnaryOperationType::Abs, - _ => unreachable!(), - }; - - Some(Instruction::uo(UnaryOperation { - ty, - destination, - source, - })) - } - "j" => { - let [target] = from_instr_args::expect_exactly_args(collectors, ctx, instr); - let target = target.map(|id| FromHirExpr::from_hir_expr(collectors, ctx, id)); - - let target = target??; - - Some(Instruction::j { target }) - } - _ => { - collectors.emit_diagnostic(instr.into(), format!("Unknown instruction: `{}`", name)); - None - } - } + return router.handle_instr(collectors, ctx, name, instr); } #[cfg(test)] diff --git a/shin-asm/src/compile/hir/lower/instruction/router.rs b/shin-asm/src/compile/hir/lower/instruction/router.rs new file mode 100644 index 0000000..11058ec --- /dev/null +++ b/shin-asm/src/compile/hir/lower/instruction/router.rs @@ -0,0 +1,102 @@ +use std::marker::PhantomData; + +use shin_core::format::scenario::instructions::Instruction; + +use super::{from_instr_args::FromInstrArgs, instr_lowerer::InstrLowerFn}; +use crate::compile::{ + hir, + hir::lower::from_hir::{FromHirBlockCtx, FromHirCollectors}, +}; + +pub trait Router { + fn handle_instr( + &self, + collectors: &mut FromHirCollectors, + ctx: &FromHirBlockCtx, + instr_name: &str, + instr: hir::InstructionId, + ) -> Option; +} + +pub struct SentinelRouter; + +impl Router for SentinelRouter { + #[inline] + fn handle_instr( + &self, + collectors: &mut FromHirCollectors, + _ctx: &FromHirBlockCtx, + instr_name: &str, + instr: hir::InstructionId, + ) -> Option { + collectors.emit_diagnostic( + instr.into(), + format!("Unrecognized instruction: `{}`", instr_name), + ); + None + } +} + +pub struct ConsRouter, Tail: Router> +{ + name: &'static str, + lower_fn: LowerFn, + tail: Tail, + phantom: PhantomData<(Dummy, Args)>, +} + +impl, Tail: Router> Router + for ConsRouter +{ + #[inline] + fn handle_instr( + &self, + collectors: &mut FromHirCollectors, + ctx: &FromHirBlockCtx, + instr_name: &str, + instr: hir::InstructionId, + ) -> Option { + if instr_name == self.name { + let args = Args::from_instr_args(collectors, ctx, instr)?; + self.lower_fn.lower_instr(collectors, ctx, instr_name, args) + } else { + self.tail.handle_instr(collectors, ctx, instr_name, instr) + } + } +} + +pub struct RouterBuilder { + router: S, +} + +impl RouterBuilder { + #[inline] + pub fn new() -> Self { + Self { + router: SentinelRouter, + } + } +} + +impl RouterBuilder { + #[inline] + pub fn add>( + self, + name: &'static str, + lower_fn: LowerFn, + ) -> RouterBuilder> { + RouterBuilder { + router: ConsRouter { + name, + lower_fn, + tail: self.router, + phantom: PhantomData, + }, + } + } + + #[inline] + pub fn build(self) -> S { + self.router + } +}