diff --git a/README.md b/README.md index b73d6c8..9cc48db 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ # How to Use + ``` Usage: rust_compiler_construction.exe [OPTIONS] [INPUT] @@ -11,43 +12,76 @@ Options: -V, --version Print version ``` + # Examples -These examples demonstrate how to build and run the compiler using Cargo. -Note that our language returns its expression as the exit code, which is why we use `echo $?`. + +These examples demonstrate how to build and run the compiler using Cargo. Run with input from stdin: + ```sh -echo "(+ 1 2)" | cargo run && ./output ; echo $? +echo "fn main() { print(42); }" | cargo run && ./output ``` + Run with specified input path, without specifying an output path: + ```sh -cargo run -- example.jj && ./example ; echo $? +cargo run -- example.jj && ./example ``` + Run with specified input path and specified output path: + ```sh -cargo run -- input.jj -o output && ./output ; echo $? +cargo run -- input.jj -o output && ./output ``` +# Language Features + +* Literals +* Var +* BinaryOp +* UnaryOp +* Let +* If +* Functions + * Return +* Loop + * Break + * Continue +* While +* Sequences +* Structs + +# AoC Wishlist for Christmas + +* Fixing that not very nice bug. +* Pass raw strings for testing (allows testing AoC problems). +* Compile + run + # Fixes + * [ ] Updated README, with 3 new colors! * [ ] Add documentation where necessary. * [x] Improve error handling for parsing pass. * [x] Improve error handling for type checking pass. * [ ] Make errors prettier. * [ ] Improve algorithm for colouring the interference graph. -* [ ] Add read and write functionality to the bencher to update locally. +* [x] Add read and write functionality to the bencher to update locally. * [x] Lots, and lots, of refactoring! * [x] Write test input in comments. # Upcoming Language Features + * [x] Type inference. * [x] Implement comments in code. * [ ] Algebraic Data Types. * [x] Structs. * [ ] Enums. * [ ] First-class functions. +* [ ] Constants. # Upcoming Optimizations + * [ ] Dead code. * [ ] Constant folding. * [ ] Improve prologue and epilogue. @@ -55,6 +89,7 @@ cargo run -- input.jj -o output && ./output ; echo $? * [ ] And probably more... # Lofty Goals + * [ ] Make the compiler suggest hints. * [ ] Nested definitions. * [ ] Match statements. diff --git a/compiler/src/interpreter.rs b/compiler/src/interpreter.rs index 661e9ac..2fcb0b1 100644 --- a/compiler/src/interpreter.rs +++ b/compiler/src/interpreter.rs @@ -6,7 +6,7 @@ use std::collections::HashMap; use std::vec::IntoIter; pub trait IO { - fn read(&mut self) -> TLit; + fn read(&mut self) -> Option; fn print(&mut self, v: TLit); } @@ -29,10 +29,8 @@ impl TestIO { } impl IO for TestIO { - fn read(&mut self) -> TLit { - self.inputs - .next() - .expect("Test tried to read more input than were available.") + fn read(&mut self) -> Option { + self.inputs.next() } fn print(&mut self, v: TLit) { diff --git a/compiler/src/lib.rs b/compiler/src/lib.rs index 8c4e8ce..e163c2c 100644 --- a/compiler/src/lib.rs +++ b/compiler/src/lib.rs @@ -5,10 +5,21 @@ pub mod passes; pub mod utils; use crate::passes::parse::parse::parse_program; +use clap::ValueEnum; use miette::{IntoDiagnostic, NamedSource, Report}; use std::fs::File; use std::path::Path; +#[derive(ValueEnum, Clone, Debug)] +pub enum Pass { + Parse, + Validate, + Reveal, + Atomize, + Explicate, + Select, +} + pub fn compile(program: &str, filename: &str, output: &Path) -> miette::Result<()> { let add_source = |error| Report::with_source_code(error, NamedSource::new(filename, program.to_string())); @@ -32,3 +43,42 @@ pub fn compile(program: &str, filename: &str, output: &Path) -> miette::Result<( Ok(()) } + +pub fn display(program: &str, filename: &str, pass: Pass) -> miette::Result<()> { + let add_source = + |error| Report::with_source_code(error, NamedSource::new(filename, program.to_string())); + + let prg_parsed = display!( + parse_program(program) + .map_err(Into::into) + .map_err(add_source)?, + pass, + Parse + ); + let prg_validated = display!( + prg_parsed + .validate() + .map_err(Into::into) + .map_err(add_source)?, + pass, + Validate + ); + let prg_revealed = display!(prg_validated.reveal(), pass, Reveal); + let prg_atomized = display!(prg_revealed.atomize(), pass, Atomize); + let prg_explicated = display!(prg_atomized.explicate(), pass, Explicate); + let _prg_select = display!(prg_explicated.eliminate().select(), pass, Select); + + Ok(()) +} + +#[macro_export] +macro_rules! display { + ($prg:expr, $pass:expr, $pat:ident) => { + if matches!($pass, $crate::Pass::$pat) { + println!("{}", $prg); + return Ok(()); + } else { + $prg + } + }; +} diff --git a/compiler/src/main.rs b/compiler/src/main.rs index 4ab59fe..4b22664 100644 --- a/compiler/src/main.rs +++ b/compiler/src/main.rs @@ -1,7 +1,7 @@ use clap::Parser; -use compiler::compile; use compiler::passes::parse::parse::PrettyParseError; use compiler::passes::validate::error::TypeError; +use compiler::{compile, display, Pass}; use miette::{Diagnostic, IntoDiagnostic}; use std::fs; use std::io::Read; @@ -26,8 +26,11 @@ struct Args { /// Specifies the path to an output file. If None, it uses the input filename. /// If that's also None, it defaults to "output". - #[arg(short, long)] + #[arg(short, long, value_name = "FILE")] output: Option, + + #[arg(value_enum, short, long, value_name = "PASS")] + display: Option, } fn read_from_stdin() -> Result { @@ -44,7 +47,11 @@ fn main() -> miette::Result<()> { Some(file) => (fs::read_to_string(file).into_diagnostic()?, file.as_str()), }; - let output: &str = args.output.as_deref().unwrap_or_else(|| { + if let Some(pass) = args.display { + return display(&program, filename, pass); + } + + let output = args.output.as_deref().unwrap_or_else(|| { args.input.as_ref().map_or_else( || "output", |s| Path::new(s).file_stem().unwrap().to_str().unwrap(), diff --git a/compiler/src/passes/assign/assign.rs b/compiler/src/passes/assign/assign.rs index ea3400e..cd45b7b 100644 --- a/compiler/src/passes/assign/assign.rs +++ b/compiler/src/passes/assign/assign.rs @@ -1,7 +1,6 @@ use crate::passes::assign::{Arg, X86Assigned}; -use crate::passes::select::{Block, Instr, VarArg, X86Selected}; +use crate::passes::select::{Block, Instr, InstrSelected, VarArg, X86Selected}; use crate::utils::gen_sym::UniqueSym; -use crate::*; use std::collections::HashMap; impl<'p> X86Selected<'p> { @@ -27,7 +26,7 @@ impl<'p> X86Selected<'p> { } fn assign_block<'p>( - block: Block<'p, VarArg>, + block: Block<'p, VarArg>>, color_map: &HashMap, ) -> Block<'p, Arg> { Block { @@ -40,10 +39,10 @@ fn assign_block<'p>( } fn assign_instr<'p>( - instr: Instr<'p, VarArg>, + instr: InstrSelected<'p>, color_map: &HashMap, -) -> Instr<'p, Arg> { - let map = |arg: VarArg| -> Arg { +) -> Instr> { + let map = |arg: VarArg>| -> Arg { match arg { VarArg::Imm { val } => Arg::Imm { val }, VarArg::Reg { reg } => Arg::Reg { reg }, @@ -51,28 +50,5 @@ fn assign_instr<'p>( VarArg::XVar { sym } => color_map[&sym].clone(), } }; - - match instr { - Instr::Addq { src, dst } => addq!(map(src), map(dst)), - Instr::Subq { src, dst } => subq!(map(src), map(dst)), - Instr::Divq { divisor } => divq!(map(divisor)), - Instr::Mulq { src } => mulq!(map(src)), - Instr::Negq { dst } => negq!(map(dst)), - Instr::Movq { src, dst } => movq!(map(src), map(dst)), - Instr::Pushq { src } => pushq!(map(src)), - Instr::Popq { dst } => popq!(map(dst)), - Instr::CallqDirect { lbl, arity } => callq_direct!(lbl, arity), - Instr::Retq => retq!(), - Instr::Syscall { arity } => syscall!(arity), - Instr::Jmp { lbl } => jmp!(lbl), - Instr::Jcc { lbl, cnd } => jcc!(lbl, cnd), - Instr::Cmpq { src, dst } => cmpq!(map(src), map(dst)), - Instr::Andq { src, dst } => andq!(map(src), map(dst)), - Instr::Orq { src, dst } => orq!(map(src), map(dst)), - Instr::Xorq { src, dst } => xorq!(map(src), map(dst)), - Instr::Notq { dst } => notq!(map(dst)), - Instr::Setcc { cnd } => setcc!(cnd), - Instr::LoadLbl { sym, dst } => load_lbl!(sym, map(dst)), - Instr::CallqIndirect { src, arity } => callq_indirect!(map(src), arity), - } + instr.fmap(map) } diff --git a/compiler/src/passes/assign/color_interference.rs b/compiler/src/passes/assign/color_interference.rs index 4ec7197..3a7dada 100644 --- a/compiler/src/passes/assign/color_interference.rs +++ b/compiler/src/passes/assign/color_interference.rs @@ -68,7 +68,7 @@ impl<'p> InterferenceGraph<'p> { .max() .unwrap_or_default() as usize; - let stack_space = (8 * used_vars).div_ceil(16) * 16; + let stack_space = (8 * used_vars).div_ceil(16) * 16 + 256; let colors = node_map .into_iter() @@ -107,7 +107,7 @@ fn arg_from_color(i: isize) -> Arg { ); Arg::Deref { reg: Reg::RBP, - off: (-8 * (i - 10)) as i64, + off: (-8 * (i - 10)) as i64 - 256, } } } diff --git a/compiler/src/passes/assign/include_liveness.rs b/compiler/src/passes/assign/include_liveness.rs index f57fd65..ee8a8bb 100644 --- a/compiler/src/passes/assign/include_liveness.rs +++ b/compiler/src/passes/assign/include_liveness.rs @@ -1,7 +1,9 @@ use crate::utils::gen_sym::UniqueSym; use crate::passes::assign::{LArg, LBlock, LX86VarProgram}; -use crate::passes::select::{Block, Instr, Reg, VarArg, X86Selected, CALLER_SAVED, SYSCALL_REGS}; +use crate::passes::select::{ + Block, Instr, InstrSelected, Reg, VarArg, X86Selected, CALLER_SAVED, SYSCALL_REGS, +}; use std::collections::hash_map::Entry; use std::collections::{HashMap, HashSet}; @@ -58,7 +60,7 @@ impl<'p> X86Selected<'p> { } fn block_liveness<'p>( - block: &Block<'p, VarArg<'p>>, + block: &Block<'p, VarArg>>, before_map: &HashMap, HashSet>>, ) -> (LBlock<'p>, HashSet>) { let mut instrs = Vec::new(); @@ -100,9 +102,9 @@ pub enum ReadWriteOp { } pub fn handle_instr<'p>( - instr: &Instr<'p, VarArg<'p>>, + instr: &InstrSelected<'p>, before_map: &HashMap, HashSet>>, - mut arg: impl FnMut(&VarArg<'p>, ReadWriteOp), + mut arg: impl FnMut(&VarArg>, ReadWriteOp), ) { use ReadWriteOp::Read as R; use ReadWriteOp::ReadWrite as RW; diff --git a/compiler/src/passes/assign/mod.rs b/compiler/src/passes/assign/mod.rs index a1cafe9..9c060b2 100644 --- a/compiler/src/passes/assign/mod.rs +++ b/compiler/src/passes/assign/mod.rs @@ -6,7 +6,7 @@ mod include_liveness; mod tests; use crate::passes::select::std_lib::Std; -use crate::passes::select::{Block, Instr, Reg, VarArg, X86Selected}; +use crate::passes::select::{Block, InstrSelected, Reg, VarArg, X86Selected}; use crate::utils::gen_sym::UniqueSym; use derive_more::Display; use functor_derive::Functor; @@ -41,7 +41,7 @@ pub struct LX86VarProgram<'p> { #[derive(PartialEq)] pub struct LBlock<'p> { - pub instrs: Vec<(Instr<'p, VarArg<'p>>, HashSet>)>, + pub instrs: Vec<(InstrSelected<'p>, HashSet>)>, } #[derive(Hash, Clone, Copy, Eq, Ord, PartialEq, PartialOrd)] @@ -50,7 +50,7 @@ pub enum LArg<'p> { Reg { reg: Reg }, } -impl<'p> From> for VarArg<'p> { +impl<'p> From> for VarArg> { fn from(val: LArg<'p>) -> Self { match val { LArg::Var { sym } => VarArg::XVar { sym }, @@ -59,7 +59,7 @@ impl<'p> From> for VarArg<'p> { } } -impl<'p> From> for Block<'p, VarArg<'p>> { +impl<'p> From> for Block<'p, VarArg>> { fn from(value: LBlock<'p>) -> Self { Block { instrs: value.instrs.into_iter().map(|(instr, _)| instr).collect(), @@ -67,7 +67,7 @@ impl<'p> From> for Block<'p, VarArg<'p>> { } } -impl<'p> From for VarArg<'p> { +impl<'p> From for VarArg> { fn from(value: Arg) -> Self { match value { Arg::Imm { val } => VarArg::Imm { val }, diff --git a/compiler/src/passes/atomize/atomize.rs b/compiler/src/passes/atomize/atomize.rs index de6774e..014b3e3 100644 --- a/compiler/src/passes/atomize/atomize.rs +++ b/compiler/src/passes/atomize/atomize.rs @@ -3,6 +3,7 @@ use crate::passes::parse::types::Type; use crate::passes::parse::{Meta, Typed}; use crate::passes::reveal::{DefRevealed, PrgRevealed, RExpr}; use crate::utils::gen_sym::{gen_sym, UniqueSym}; +use crate::utils::push_map::PushMap; impl<'p> PrgRevealed<'p> { #[must_use] @@ -26,17 +27,25 @@ fn atomize_def(def: DefRevealed) -> DefAtomized { params, typ, bdy, - } => DefAtomized::Fn { - sym, - params, - typ, - bdy: atomize_expr(bdy), - }, + } => { + let mut scope = + PushMap::from_iter(params.iter().map(|param| (param.sym, param.mutable))); + + DefAtomized::Fn { + sym, + params, + typ, + bdy: atomize_expr(bdy, &mut scope), + } + } DefRevealed::TypeDef { sym, def } => DefAtomized::TypeDef { sym, def }, } } -fn atomize_expr<'p>(expr: Typed<'p, RExpr<'p>>) -> Typed<'p, AExpr<'p>> { +fn atomize_expr<'p>( + expr: Typed<'p, RExpr<'p>>, + scope: &mut PushMap, bool>, +) -> Typed<'p, AExpr<'p>> { // Keep track of all the priors. These are bindings that should come before evaluating the expression. let mut priors = Vec::new(); @@ -54,23 +63,29 @@ fn atomize_expr<'p>(expr: Typed<'p, RExpr<'p>>) -> Typed<'p, AExpr<'p>> { } => AExpr::BinaryOp { op, exprs: [ - atomize_atom(*lhs, &mut priors), - atomize_atom(*rhs, &mut priors), + atomize_atom(*lhs, &mut priors, scope), + atomize_atom(*rhs, &mut priors, scope), ], }, RExpr::UnaryOp { op, expr: arg } => AExpr::UnaryOp { op, - expr: atomize_atom(*arg, &mut priors), + expr: atomize_atom(*arg, &mut priors, scope), }, - RExpr::Let { sym, bnd, bdy } => AExpr::Let { + RExpr::Let { sym, - bnd: Box::new(atomize_expr(*bnd)), - bdy: Box::new(atomize_expr(*bdy)), - }, + mutable, + bnd, + bdy, + } => { + let bnd = Box::new(atomize_expr(*bnd, scope)); + let bdy = Box::new(scope.push(sym, mutable, |scope| atomize_expr(*bdy, scope))); + + AExpr::Let { sym, bnd, bdy } + } RExpr::If { cnd, thn, els } => AExpr::If { - cnd: Box::new(atomize_expr(*cnd)), - thn: Box::new(atomize_expr(*thn)), - els: Box::new(atomize_expr(*els)), + cnd: Box::new(atomize_expr(*cnd, scope)), + thn: Box::new(atomize_expr(*thn, scope)), + els: Box::new(atomize_expr(*els, scope)), }, RExpr::Apply { fun, args } => { let Type::Fn { params, .. } = fun.meta.clone() else { @@ -78,47 +93,48 @@ fn atomize_expr<'p>(expr: Typed<'p, RExpr<'p>>) -> Typed<'p, AExpr<'p>> { }; AExpr::Apply { - fun: atomize_atom(*fun, &mut priors), + fun: atomize_atom(*fun, &mut priors, scope), args: args .into_iter() - .map(|arg| atomize_atom(arg, &mut priors)) + .map(|arg| atomize_atom(arg, &mut priors, scope)) .zip(params) .collect(), } } RExpr::FunRef { sym } => AExpr::FunRef { sym }, RExpr::Loop { bdy } => AExpr::Loop { - bdy: Box::new(atomize_expr(*bdy)), + bdy: Box::new(atomize_expr(*bdy, scope)), }, RExpr::Break { bdy } => AExpr::Break { - bdy: Box::new(atomize_expr(*bdy)), + bdy: Box::new(atomize_expr(*bdy, scope)), }, RExpr::Seq { stmt, cnt } => AExpr::Seq { - stmt: Box::new(atomize_expr(*stmt)), - cnt: Box::new(atomize_expr(*cnt)), + stmt: Box::new(atomize_expr(*stmt, scope)), + cnt: Box::new(atomize_expr(*cnt, scope)), }, RExpr::Assign { sym, bnd } => AExpr::Assign { sym, - bnd: Box::new(atomize_expr(*bnd)), + bnd: Box::new(atomize_expr(*bnd, scope)), }, RExpr::Continue => AExpr::Continue, RExpr::Return { bdy } => AExpr::Return { - bdy: Box::new(atomize_expr(*bdy)), + bdy: Box::new(atomize_expr(*bdy, scope)), }, RExpr::Struct { sym, fields } => AExpr::Struct { sym, fields: fields .into_iter() .map(|(sym, expr)| { - let field = atomize_atom(expr, &mut priors); + let field = atomize_atom(expr, &mut priors, scope); (sym, field) }) .collect(), }, RExpr::AccessField { strct, field } => AExpr::AccessField { - strct: atomize_atom(*strct, &mut priors), + strct: atomize_atom(*strct, &mut priors, scope), field, }, + RExpr::Asm { instrs } => AExpr::Asm { instrs }, }; // Chains all the priors with the atomized expression as the body. @@ -142,12 +158,15 @@ fn atomize_expr<'p>(expr: Typed<'p, RExpr<'p>>) -> Typed<'p, AExpr<'p>> { fn atomize_atom<'p>( expr: Typed<'p, RExpr<'p>>, priors: &mut Vec<(UniqueSym<'p>, Typed<'p, AExpr<'p>>)>, + scope: &mut PushMap, bool>, ) -> Atom<'p> { - if let RExpr::Lit { val } = expr.inner { - Atom::Val { val } - } else { - let tmp = gen_sym("tmp"); - priors.push((tmp, atomize_expr(expr))); - Atom::Var { sym: tmp } + match expr.inner { + RExpr::Lit { val } => Atom::Val { val }, + RExpr::Var { sym } if !scope[&sym] => Atom::Var { sym }, + _ => { + let tmp = gen_sym("tmp"); + priors.push((tmp, atomize_expr(expr, scope))); + Atom::Var { sym: tmp } + } } } diff --git a/compiler/src/passes/atomize/display.rs b/compiler/src/passes/atomize/display.rs new file mode 100644 index 0000000..330ad33 --- /dev/null +++ b/compiler/src/passes/atomize/display.rs @@ -0,0 +1,78 @@ +use crate::passes::atomize::AExpr; +use indenter::indented; +use itertools::Itertools; +use std::fmt::Write; +use std::fmt::{Display, Formatter}; + +impl Display for AExpr<'_> { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + AExpr::Atom { atm } => { + write!(f, "{atm}") + } + AExpr::FunRef { sym } => { + write!(f, "{sym}") + } + AExpr::UnaryOp { op, expr } => { + write!(f, "({op}{expr})") + } + AExpr::BinaryOp { + op, + exprs: [e1, e2], + } => { + write!(f, "({e1} {op} {e2})") + } + AExpr::Let { sym, bnd, bdy } => { + writeln!(f, "(let {sym} = {bnd};",)?; + write!(f, "{bdy})") + } + AExpr::If { cnd, thn, els } => { + writeln!(f, "if {cnd} {{")?; + writeln!(indented(f), "{thn}")?; + writeln!(f, "}} else {{")?; + writeln!(indented(f), "{els}")?; + write!(f, "}}") + } + AExpr::Apply { fun, args } => { + write!(f, "{fun}({})", args.iter().map(|(atm, _)| atm).format(", ")) + } + AExpr::Loop { bdy } => { + writeln!(f, "loop {{")?; + writeln!(indented(f), "{bdy}")?; + write!(f, "}}") + } + AExpr::Break { bdy } => { + write!(f, "break {bdy}") + } + AExpr::Continue => { + write!(f, "continue") + } + AExpr::Return { bdy } => { + write!(f, "return {bdy}") + } + AExpr::Seq { stmt, cnt } => { + writeln!(f, "{stmt};")?; + write!(f, "{cnt}") + } + AExpr::Assign { sym, bnd } => { + write!(f, "{sym} = {bnd}") + } + AExpr::Struct { sym, fields } => { + writeln!(f, "{sym} {{")?; + writeln!( + indented(f), + "{}", + fields + .iter() + .map(|(sym, bnd)| format!("{sym}: {bnd},")) + .format("\n") + )?; + write!(f, "}}") + } + AExpr::AccessField { strct, field } => { + write!(f, "{strct}.{field}") + } + AExpr::Asm { .. } => todo!(), + } + } +} diff --git a/compiler/src/passes/atomize/mod.rs b/compiler/src/passes/atomize/mod.rs index 09a668c..60ef3b3 100644 --- a/compiler/src/passes/atomize/mod.rs +++ b/compiler/src/passes/atomize/mod.rs @@ -1,14 +1,18 @@ pub mod atomize; -#[cfg(test)] -mod tests; +mod display; use crate::passes::parse::types::Type; -use crate::passes::parse::{BinaryOp, Def, Meta, Typed, UnaryOp}; +use crate::passes::parse::{BinaryOp, Def, Typed, UnaryOp}; use crate::passes::select::std_lib::Std; -use crate::passes::validate::{DefValidated, ExprValidated, PrgValidated, TLit}; +use crate::passes::select::InstrSelected; +use crate::passes::validate::TLit; use crate::utils::gen_sym::UniqueSym; +use derive_more::Display; +use itertools::Itertools; use std::collections::HashMap; +#[derive(Display)] +#[display(fmt = "{}", r#"defs.values().format("\n")"#)] pub struct PrgAtomized<'p> { pub defs: HashMap, DefAtomized<'p>>, pub entry: UniqueSym<'p>, @@ -72,9 +76,12 @@ pub enum AExpr<'p> { strct: Atom<'p>, field: &'p str, }, + Asm { + instrs: Vec>, + }, } -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Display)] pub enum Atom<'p> { Val { val: TLit }, Var { sym: UniqueSym<'p> }, @@ -89,116 +96,3 @@ impl<'p> Atom<'p> { } } } - -impl<'p> From> for PrgValidated<'p> { - fn from(value: PrgAtomized<'p>) -> Self { - PrgValidated { - defs: value - .defs - .into_iter() - .map(|(sym, def)| (sym, def.into())) - .collect(), - entry: value.entry, - std: value.std, - } - } -} - -impl<'p> From> for DefValidated<'p> { - fn from(value: DefAtomized<'p>) -> Self { - match value { - DefAtomized::Fn { - sym, - params, - typ, - bdy, - } => DefValidated::Fn { - sym, - params, - typ, - bdy: bdy.into(), - }, - DefAtomized::TypeDef { sym, def } => DefValidated::TypeDef { sym, def }, - } - } -} - -impl<'p> From>> for Typed<'p, ExprValidated<'p>> { - fn from(value: Typed<'p, AExpr<'p>>) -> Self { - let inner = match value.inner { - AExpr::Atom { atm, .. } => return atm.into(), - AExpr::BinaryOp { op, exprs } => ExprValidated::BinaryOp { - op, - exprs: exprs.map(|a| Box::new(a.into())), - }, - AExpr::UnaryOp { op, expr } => ExprValidated::UnaryOp { - op, - expr: Box::new(expr.into()), - }, - AExpr::Let { sym, bnd, bdy } => ExprValidated::Let { - sym, - mutable: true, - typ: None, - bnd: Box::new((*bnd).into()), - bdy: Box::new((*bdy).into()), - }, - AExpr::If { cnd, thn, els } => ExprValidated::If { - cnd: Box::new((*cnd).into()), - thn: Box::new((*thn).into()), - els: Box::new((*els).into()), - }, - AExpr::Apply { fun, args, .. } => ExprValidated::Apply { - fun: Box::new(fun.into()), - args: args.into_iter().map(|(a, _)| a.into()).collect(), - }, - AExpr::FunRef { sym } => ExprValidated::Var { sym }, - AExpr::Loop { bdy } => ExprValidated::Loop { - bdy: Box::new((*bdy).into()), - }, - AExpr::Break { bdy } => ExprValidated::Break { - bdy: Box::new((*bdy).into()), - }, - AExpr::Seq { stmt, cnt } => ExprValidated::Seq { - stmt: Box::new((*stmt).into()), - cnt: Box::new((*cnt).into()), - }, - AExpr::Assign { sym, bnd } => ExprValidated::Assign { - sym, - bnd: Box::new((*bnd).into()), - }, - AExpr::Continue => ExprValidated::Continue, - AExpr::Return { bdy } => ExprValidated::Return { - bdy: Box::new((*bdy).into()), - }, - AExpr::Struct { sym, fields } => ExprValidated::Struct { - sym, - fields: fields - .into_iter() - .map(|(sym, atm)| (sym, atm.into())) - .collect(), - }, - AExpr::AccessField { strct, field } => ExprValidated::AccessField { - strct: Box::new(strct.into()), - field, - }, - }; - - Meta { - inner, - meta: value.meta, - } - } -} - -// Note that casting to Never here is safe because this `From` is only used by the interpreter which doesn't care about the type information. -impl<'p> From> for Typed<'p, ExprValidated<'p>> { - fn from(value: Atom<'p>) -> Self { - Meta { - meta: Type::Never, - inner: match value { - Atom::Val { val } => ExprValidated::Lit { val }, - Atom::Var { sym } => ExprValidated::Var { sym }, - }, - } - } -} diff --git a/compiler/src/passes/atomize/tests.rs b/compiler/src/passes/atomize/tests.rs deleted file mode 100644 index 53b50d8..0000000 --- a/compiler/src/passes/atomize/tests.rs +++ /dev/null @@ -1,24 +0,0 @@ -use crate::interpreter::TestIO; -use crate::passes::parse::parse::parse_program; -use crate::passes::validate::PrgValidated; -use crate::utils::split_test::split_test; -use test_each_file::test_each_file; - -fn atomize([test]: [&str; 1]) { - let (input, expected_output, expected_return, _) = split_test(test); - - let program: PrgValidated = parse_program(test) - .unwrap() - .validate() - .unwrap() - .reveal() - .atomize() - .into(); - let mut io = TestIO::new(input); - let result = program.interpret(&mut io); - - assert_eq!(result, expected_return.into(), "Incorrect program result."); - assert_eq!(io.outputs(), &expected_output, "Incorrect program output."); -} - -test_each_file! { for ["test"] in "./programs/good" as atomize => atomize } diff --git a/compiler/src/passes/eliminate/eliminate_expr.rs b/compiler/src/passes/eliminate/eliminate_expr.rs index 4c2b19e..d71c638 100644 --- a/compiler/src/passes/eliminate/eliminate_expr.rs +++ b/compiler/src/passes/eliminate/eliminate_expr.rs @@ -12,6 +12,7 @@ pub fn eliminate_expr(e: CExpr) -> EExpr { args: args.fmap(|(arg, _)| arg), }, CExpr::FunRef { sym } => EExpr::FunRef { sym }, + CExpr::Asm { instrs } => EExpr::Asm { instrs }, CExpr::Struct { .. } | CExpr::AccessField { .. } => unreachable!(), } } diff --git a/compiler/src/passes/eliminate/eliminate_seq.rs b/compiler/src/passes/eliminate/eliminate_seq.rs index 14b42a9..bfeb7c3 100644 --- a/compiler/src/passes/eliminate/eliminate_seq.rs +++ b/compiler/src/passes/eliminate/eliminate_seq.rs @@ -130,7 +130,8 @@ pub fn eliminate_seq<'p>( CExpr::BinaryOp { .. } | CExpr::UnaryOp { .. } | CExpr::FunRef { .. } - | CExpr::AccessField { .. } => { + | CExpr::AccessField { .. } + | CExpr::Asm { .. } => { unreachable!() } }, diff --git a/compiler/src/passes/eliminate/interpreter.rs b/compiler/src/passes/eliminate/interpreter.rs deleted file mode 100644 index b4d79e4..0000000 --- a/compiler/src/passes/eliminate/interpreter.rs +++ /dev/null @@ -1,174 +0,0 @@ -use crate::interpreter::Val; -use crate::interpreter::IO; -use crate::passes::atomize::Atom; -use crate::passes::eliminate::{EExpr, ETail, PrgEliminated}; -use crate::passes::parse::{BinaryOp, UnaryOp}; -use crate::passes::validate::TLit; -use crate::utils::gen_sym::UniqueSym; -use crate::utils::push_map::PushMap; - -impl<'p> PrgEliminated<'p> { - pub fn interpret(&self, io: &mut impl IO) -> Vec> { - let std_iter = self - .std - .iter() - .map(|(_, &def)| (def, Val::StdlibFunction { sym: def.sym })); - - self.interpret_tail( - &self.blocks[&self.entry], - &mut PushMap::from_iter(std_iter), - io, - ) - } - - fn interpret_tail( - &self, - tail: &ETail<'p>, - scope: &mut PushMap, Val<'p>>, - io: &mut impl IO, - ) -> Vec> { - match tail { - ETail::Return { exprs: expr } => expr - .iter() - .map(|atm| self.interpret_atom(atm, scope)) - .collect(), - ETail::Seq { syms, bnd, tail } => { - let bnds = syms - .iter() - .cloned() - .zip(self.interpret_expr(&bnd.inner, scope, io)); - scope.push_iter(bnds, |scope| self.interpret_tail(tail, scope, io)) - } - ETail::IfStmt { cnd, thn, els } => { - if self.interpret_expr(cnd, scope, io)[0].bool() { - self.interpret_tail(&self.blocks[thn], scope, io) - } else { - self.interpret_tail(&self.blocks[els], scope, io) - } - } - ETail::Goto { lbl } => self.interpret_tail(&self.blocks[lbl], scope, io), - } - } - - pub fn interpret_expr( - &self, - expr: &EExpr<'p>, - scope: &mut PushMap, Val<'p>>, - io: &mut impl IO, - ) -> Vec> { - let val = match expr { - EExpr::BinaryOp { - op, - exprs: [lhs, rhs], - } => { - let lhs = self.interpret_atom(lhs, scope); - let rhs = self.interpret_atom(rhs, scope); - match op { - BinaryOp::Add => Val::Int { - val: lhs.int() + rhs.int(), - }, - BinaryOp::Sub => Val::Int { - val: lhs.int() - rhs.int(), - }, - BinaryOp::Mul => Val::Int { - val: lhs.int() * rhs.int(), - }, - BinaryOp::Div => Val::Int { - val: lhs.int() / rhs.int(), - }, - BinaryOp::Mod => Val::Int { - val: lhs.int() % rhs.int(), - }, - BinaryOp::Xor => Val::Bool { - val: lhs.bool() ^ rhs.bool(), - }, - BinaryOp::GT => Val::Bool { - val: lhs.int() > rhs.int(), - }, - BinaryOp::GE => Val::Bool { - val: lhs.int() >= rhs.int(), - }, - BinaryOp::EQ => Val::Bool { val: lhs == rhs }, - BinaryOp::LE => Val::Bool { - val: lhs.int() <= rhs.int(), - }, - BinaryOp::LT => Val::Bool { - val: lhs.int() < rhs.int(), - }, - BinaryOp::NE => Val::Bool { val: lhs != rhs }, - BinaryOp::LAnd => Val::Bool { - val: lhs.bool() && rhs.bool(), - }, - BinaryOp::LOr => Val::Bool { - val: lhs.bool() || rhs.bool(), - }, - } - } - EExpr::UnaryOp { op, expr } => { - let expr = self.interpret_atom(expr, scope); - match op { - UnaryOp::Neg => Val::Int { val: -expr.int() }, - UnaryOp::Not => Val::Bool { val: !expr.bool() }, - } - } - EExpr::Atom { atm, .. } => self.interpret_atom(atm, scope), - EExpr::FunRef { sym, .. } => { - if self.std.contains_key(sym.sym) { - Val::StdlibFunction { sym: sym.sym } - } else { - Val::Function { sym: *sym } - } - } - EExpr::Apply { fun, args, .. } => { - let fun = self.interpret_atom(fun, scope); - - let mut fn_args = Vec::new(); - for atm in args { - fn_args.push(self.interpret_atom(atm, scope)); - } - - match fun { - Val::StdlibFunction { sym } => { - match sym { - "exit" => { - unreachable!("Validated programs should not have an explicit call to exit yet.") - } - "print" => { - let val = fn_args[0].clone(); - io.print(TLit::I64 { val: val.int() }); - val - } - "read" => io.read().into(), - unknown => unreachable!( - "Encountered an undefined standard library function '{unknown}'" - ), - } - } - Val::Function { sym } => { - let args = self.fn_params[&sym] - .iter() - .zip(fn_args) - .map(|(param, val)| (param.sym, val)); - return scope.push_iter(args, |scope| { - self.interpret_tail(&self.blocks[&sym], scope, io) - }); - } - _ => unreachable!("The symbol did not refer to a function."), - } - } - }; - vec![val] - } - - #[must_use] - pub fn interpret_atom( - &self, - atom: &Atom<'p>, - scope: &PushMap, Val<'p>>, - ) -> Val<'p> { - match atom { - Atom::Val { val } => (*val).into(), - Atom::Var { sym } => scope[sym].clone(), - } - } -} diff --git a/compiler/src/passes/eliminate/mod.rs b/compiler/src/passes/eliminate/mod.rs index df12a3a..4eb61e9 100644 --- a/compiler/src/passes/eliminate/mod.rs +++ b/compiler/src/passes/eliminate/mod.rs @@ -3,14 +3,12 @@ mod eliminate_expr; mod eliminate_params; mod eliminate_seq; mod eliminate_tail; -mod interpreter; -#[cfg(test)] -mod tests; use crate::passes::atomize::Atom; use crate::passes::parse::types::Type; use crate::passes::parse::{BinaryOp, Meta, Param, TypeDef, UnaryOp}; use crate::passes::select::std_lib::Std; +use crate::passes::select::InstrSelected; use crate::utils::gen_sym::UniqueSym; use std::collections::HashMap; @@ -47,4 +45,5 @@ pub enum EExpr<'p> { UnaryOp { op: UnaryOp, expr: Atom<'p> }, Apply { fun: Atom<'p>, args: Vec> }, FunRef { sym: UniqueSym<'p> }, + Asm { instrs: Vec> }, } diff --git a/compiler/src/passes/eliminate/tests.rs b/compiler/src/passes/eliminate/tests.rs deleted file mode 100644 index 3134e49..0000000 --- a/compiler/src/passes/eliminate/tests.rs +++ /dev/null @@ -1,25 +0,0 @@ -use crate::interpreter::TestIO; -use crate::passes::parse::parse::parse_program; -use crate::utils::split_test::split_test; -use test_each_file::test_each_file; - -fn eliminate([test]: [&str; 1]) { - let (input, expected_output, expected_return, _) = split_test(test); - - let program = parse_program(test) - .unwrap() - .validate() - .unwrap() - .reveal() - .atomize() - .explicate() - .eliminate(); - - let mut io = TestIO::new(input); - let result = program.interpret(&mut io)[0].clone(); - - assert_eq!(result, expected_return.into(), "Incorrect program result."); - assert_eq!(io.outputs(), &expected_output, "Incorrect program output."); -} - -test_each_file! { for ["test"] in "./programs/good" as eliminate => eliminate } diff --git a/compiler/src/passes/emit/elf/header.rs b/compiler/src/passes/emit/elf/header.rs index 5f2a57d..1c4b36c 100644 --- a/compiler/src/passes/emit/elf/header.rs +++ b/compiler/src/passes/emit/elf/header.rs @@ -1,5 +1,6 @@ use crate::passes::emit::elf::program::ProgramHeader; use crate::passes::emit::elf::section::SectionHeader; +use std::clone::Clone; use std::mem::size_of; use zerocopy::AsBytes; @@ -83,7 +84,7 @@ enum Machine { RISCV = 0xF3, } -const ELF_MAGIC: [u8; 4] = [0x7F, b'E', b'L', b'F']; +const ELF_MAGIC: [u8; 4] = *b"\x7FELF"; impl ElfIdentifier { fn new() -> Self { diff --git a/compiler/src/passes/emit/mod.rs b/compiler/src/passes/emit/mod.rs index 2e79024..b5b8171 100644 --- a/compiler/src/passes/emit/mod.rs +++ b/compiler/src/passes/emit/mod.rs @@ -64,7 +64,7 @@ fn emit_block<'p>( } fn emit_instr<'p>( - instr: &Instr<'p, Arg>, + instr: &Instr>, machine_code: &mut Vec, rel_jumps: &mut HashMap>, abs_jumps: &mut HashMap>, diff --git a/compiler/src/passes/explicate/display.rs b/compiler/src/passes/explicate/display.rs new file mode 100644 index 0000000..f6c17ef --- /dev/null +++ b/compiler/src/passes/explicate/display.rs @@ -0,0 +1,72 @@ +use crate::passes::explicate::{CExpr, CTail}; +use indenter::indented; +use itertools::Itertools; +use std::fmt::Write; +use std::fmt::{Display, Formatter}; + +impl Display for CTail<'_> { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + CTail::Return { expr } => { + write!(indented(f), "{expr}") + } + CTail::Seq { sym, bnd, tail } => { + writeln!(indented(f), "{sym} = {bnd};")?; + write!(f, "{tail}") + } + CTail::IfStmt { cnd, thn, els } => { + write!(indented(f), "if {cnd} {{ jmp {thn} }} else {{ jmp {els} }}") + } + CTail::Goto { lbl } => { + write!(indented(f), "jmp {lbl}") + } + } + } +} + +impl Display for CExpr<'_> { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + CExpr::Atom { atm } => { + write!(f, "{atm}") + } + CExpr::BinaryOp { + op, + exprs: [lhs, rhs], + } => { + write!(f, "{lhs} {op} {rhs}") + } + CExpr::UnaryOp { op, expr } => { + write!(f, "{op}{expr}") + } + CExpr::Apply { fun, args } => { + write!(f, "{fun}({})", args.iter().map(|(arg, _)| arg).format(", ")) + } + CExpr::FunRef { sym } => { + write!(f, "{sym}") + } + CExpr::Struct { sym, fields } => { + writeln!(f, "{sym} {{")?; + writeln!( + indented(f), + "{}", + fields + .iter() + .map(|(sym, bnd)| format!("{sym}: {bnd},")) + .format("\n") + )?; + write!(f, "}}") + } + CExpr::AccessField { strct, field } => { + write!(f, "{strct}.{field}") + } + CExpr::Asm { instrs } => { + writeln!(f, "asm {{")?; + for instr in instrs { + writeln!(indented(f), "{instr}")?; + } + write!(f, "}}") + } + } + } +} diff --git a/compiler/src/passes/explicate/explicate_assign.rs b/compiler/src/passes/explicate/explicate_assign.rs index 495e046..905db52 100644 --- a/compiler/src/passes/explicate/explicate_assign.rs +++ b/compiler/src/passes/explicate/explicate_assign.rs @@ -156,5 +156,13 @@ pub fn explicate_assign<'p>( }, tail: Box::new(tail), }, + AExpr::Asm { instrs } => CTail::Seq { + sym, + bnd: Meta { + meta: bnd.meta, + inner: CExpr::Asm { instrs }, + }, + tail: Box::new(tail), + }, } } diff --git a/compiler/src/passes/explicate/explicate_pred.rs b/compiler/src/passes/explicate/explicate_pred.rs index 36b5cbc..3d501b6 100644 --- a/compiler/src/passes/explicate/explicate_pred.rs +++ b/compiler/src/passes/explicate/explicate_pred.rs @@ -188,6 +188,7 @@ pub fn explicate_pred<'p>( | AExpr::Break { .. } | AExpr::Continue { .. } | AExpr::Return { .. } - | AExpr::Struct { .. } => unreachable!(), + | AExpr::Struct { .. } + | AExpr::Asm { .. } => unreachable!(), } } diff --git a/compiler/src/passes/explicate/interpreter.rs b/compiler/src/passes/explicate/interpreter.rs deleted file mode 100644 index c451f4c..0000000 --- a/compiler/src/passes/explicate/interpreter.rs +++ /dev/null @@ -1,182 +0,0 @@ -use crate::interpreter::Val; -use crate::interpreter::IO; -use crate::passes::atomize::Atom; -use crate::passes::explicate::{CExpr, CTail, PrgExplicated}; - -use crate::passes::parse::{BinaryOp, UnaryOp}; -use crate::passes::validate::TLit; -use crate::utils::gen_sym::UniqueSym; -use crate::utils::push_map::PushMap; -use std::collections::HashMap; - -impl<'p> PrgExplicated<'p> { - pub fn interpret(&self, io: &mut impl IO) -> Val<'p> { - let std_iter = self - .std - .iter() - .map(|(_, &def)| (def, Val::StdlibFunction { sym: def.sym })); - - self.interpret_tail( - &self.blocks[&self.entry], - &mut PushMap::from_iter(std_iter), - io, - ) - } - - fn interpret_tail( - &self, - tail: &CTail<'p>, - scope: &mut PushMap, Val<'p>>, - io: &mut impl IO, - ) -> Val<'p> { - match tail { - CTail::Return { expr, .. } => self.interpret_atom(&expr.inner, scope), - CTail::Seq { sym, bnd, tail } => { - let bnd = self.interpret_expr(&bnd.inner, scope, io); - scope.push(*sym, bnd, |scope| self.interpret_tail(tail, scope, io)) - } - CTail::IfStmt { cnd, thn, els } => { - if self.interpret_expr(cnd, scope, io).bool() { - self.interpret_tail(&self.blocks[thn], scope, io) - } else { - self.interpret_tail(&self.blocks[els], scope, io) - } - } - CTail::Goto { lbl } => self.interpret_tail(&self.blocks[lbl], scope, io), - } - } - - pub fn interpret_expr( - &self, - expr: &CExpr<'p>, - scope: &mut PushMap, Val<'p>>, - io: &mut impl IO, - ) -> Val<'p> { - match &expr { - CExpr::BinaryOp { - op, - exprs: [lhs, rhs], - } => { - let lhs = self.interpret_atom(lhs, scope); - let rhs = self.interpret_atom(rhs, scope); - match op { - BinaryOp::Add => Val::Int { - val: lhs.int() + rhs.int(), - }, - BinaryOp::Sub => Val::Int { - val: lhs.int() - rhs.int(), - }, - BinaryOp::Mul => Val::Int { - val: lhs.int() * rhs.int(), - }, - BinaryOp::Div => Val::Int { - val: lhs.int() / rhs.int(), - }, - BinaryOp::Mod => Val::Int { - val: lhs.int() % rhs.int(), - }, - BinaryOp::Xor => Val::Bool { - val: lhs.bool() ^ rhs.bool(), - }, - BinaryOp::GT => Val::Bool { - val: lhs.int() > rhs.int(), - }, - BinaryOp::GE => Val::Bool { - val: lhs.int() >= rhs.int(), - }, - BinaryOp::EQ => Val::Bool { val: lhs == rhs }, - BinaryOp::LE => Val::Bool { - val: lhs.int() <= rhs.int(), - }, - BinaryOp::LT => Val::Bool { - val: lhs.int() < rhs.int(), - }, - BinaryOp::NE => Val::Bool { val: lhs != rhs }, - BinaryOp::LAnd => Val::Bool { - val: lhs.bool() && rhs.bool(), - }, - BinaryOp::LOr => Val::Bool { - val: lhs.bool() || rhs.bool(), - }, - } - } - CExpr::UnaryOp { op, expr } => { - let expr = self.interpret_atom(expr, scope); - match op { - UnaryOp::Neg => Val::Int { val: -expr.int() }, - UnaryOp::Not => Val::Bool { val: !expr.bool() }, - } - } - CExpr::Atom { atm, .. } => self.interpret_atom(atm, scope), - CExpr::FunRef { sym, .. } => { - if self.std.contains_key(sym.sym) { - Val::StdlibFunction { sym: sym.sym } - } else { - Val::Function { sym: *sym } - } - } - CExpr::Apply { fun, args, .. } => { - let fun = self.interpret_atom(fun, scope); - - let mut fn_args = Vec::new(); - for (atm, _) in args { - fn_args.push(self.interpret_atom(atm, scope)); - } - - match fun { - Val::StdlibFunction { sym } => { - match sym { - "exit" => { - unreachable!("Validated programs should not have an explicit call to exit yet.") - } - "print" => { - let val = fn_args[0].clone(); - io.print(TLit::I64 { val: val.int() }); - val - } - "read" => io.read().into(), - unknown => unreachable!( - "Encountered an undefined standard library function '{unknown}'" - ), - } - } - Val::Function { sym } => { - let args = self.fn_params[&sym] - .iter() - .zip(fn_args) - .map(|(param, val)| (param.sym, val)); - scope.push_iter(args, |scope| { - self.interpret_tail(&self.blocks[&sym], scope, io) - }) - } - _ => unreachable!("The symbol did not refer to a function."), - } - } - CExpr::Struct { fields, .. } => { - let mut field_values = HashMap::new(); - for (sym, field) in fields { - field_values.insert(*sym, self.interpret_atom(field, scope)); - } - Val::StructInstance { - fields: field_values, - } - } - CExpr::AccessField { strct, field, .. } => { - let s = self.interpret_atom(strct, scope); - s.strct()[field].clone() - } - } - } - - #[must_use] - pub fn interpret_atom( - &self, - atom: &Atom<'p>, - scope: &PushMap, Val<'p>>, - ) -> Val<'p> { - match atom { - Atom::Val { val } => (*val).into(), - Atom::Var { sym } => scope[sym].clone(), - } - } -} diff --git a/compiler/src/passes/explicate/mod.rs b/compiler/src/passes/explicate/mod.rs index e0ac0de..5eea330 100644 --- a/compiler/src/passes/explicate/mod.rs +++ b/compiler/src/passes/explicate/mod.rs @@ -1,19 +1,25 @@ +mod display; pub mod explicate; mod explicate_assign; mod explicate_def; mod explicate_pred; mod explicate_tail; -pub mod interpreter; -#[cfg(test)] -mod tests; use crate::passes::atomize::Atom; use crate::passes::parse::types::Type; use crate::passes::parse::{BinaryOp, Param, TypeDef, Typed, UnaryOp}; use crate::passes::select::std_lib::Std; +use crate::passes::select::InstrSelected; use crate::utils::gen_sym::UniqueSym; +use derive_more::Display; +use itertools::Itertools; use std::collections::HashMap; +#[derive(Display)] +#[display( + fmt = "{}", + r#"blocks.iter().map(|(sym, tail)| format!("{sym}:\n{tail}")).format("\n")"# +)] pub struct PrgExplicated<'p> { pub blocks: HashMap, CTail<'p>>, pub fn_params: HashMap, Vec>>>, @@ -69,4 +75,7 @@ pub enum CExpr<'p> { strct: Atom<'p>, field: &'p str, }, + Asm { + instrs: Vec>, + }, } diff --git a/compiler/src/passes/explicate/tests.rs b/compiler/src/passes/explicate/tests.rs deleted file mode 100644 index 0a4b8b7..0000000 --- a/compiler/src/passes/explicate/tests.rs +++ /dev/null @@ -1,24 +0,0 @@ -use crate::interpreter::TestIO; -use crate::passes::parse::parse::parse_program; -use crate::utils::split_test::split_test; -use test_each_file::test_each_file; - -fn explicated([test]: [&str; 1]) { - let (input, expected_output, expected_return, _) = split_test(test); - - let program = parse_program(test) - .unwrap() - .validate() - .unwrap() - .reveal() - .atomize() - .explicate(); - - let mut io = TestIO::new(input); - let result = program.interpret(&mut io); - - assert_eq!(result, expected_return.into(), "Incorrect program result."); - assert_eq!(io.outputs(), &expected_output, "Incorrect program output."); -} - -test_each_file! { for ["test"] in "./programs/good" as explicate => explicated } diff --git a/compiler/src/passes/parse/display.rs b/compiler/src/passes/parse/display.rs index 940e30d..2ebcca1 100644 --- a/compiler/src/passes/parse/display.rs +++ b/compiler/src/passes/parse/display.rs @@ -124,6 +124,7 @@ impl Display } Expr::Variant { .. } => todo!(), Expr::Switch { .. } => todo!(), + Expr::Asm { .. } => todo!(), } } } diff --git a/compiler/src/passes/parse/grammar.lalrpop b/compiler/src/passes/parse/grammar.lalrpop index 0761c98..47ded7c 100644 --- a/compiler/src/passes/parse/grammar.lalrpop +++ b/compiler/src/passes/parse/grammar.lalrpop @@ -1,7 +1,7 @@ use crate::passes::parse::{ - BinaryOp, DefParsed, ExprParsed, Lit, Meta, Param, PrgParsed, Spanned, Type, TypeDef, UnaryOp, + BinaryOp, DefParsed, ExprParsed, InstrParsed, Lit, Meta, Param, PrgParsed, Spanned, Type, TypeDef, UnaryOp, }; -use crate::passes::validate::partial_type::PartialType; +use crate::passes::select::{VarArg, Reg}; use functor_derive::Functor; grammar; @@ -21,12 +21,50 @@ match { "struct", "enum", "switch", + "asm", + + // Asm instructions + "addq", + "subq", + "mulq", + "divq", + "negq", + "movq", + "pushq", + "popq", + "retq", + "andq", + "orq", + "xorq", + "notq", + "cmpq", + "syscall", + + // Asm registers + "%RSP", + "%RBP", + "%RAX", + "%RBX", + "%RCX", + "%RDX", + "%RSI", + "%RDI", + "%R8", + "%R9", + "%R10", + "%R11", + "%R12", + "%R13", + "%R14", + "%R15", // Structural tokens "(", ")", "{", "}", + "[", + "]", "->", "=", ";", @@ -35,6 +73,7 @@ match { ".", "::", "=>", + "$", // Identifier r"[_a-zA-Z][_a-zA-Z0-9]*" => identifier, @@ -57,9 +96,7 @@ match { "true", "false", "unit", - r"[0-9]+" => integer, - r"[0-9]+i64" => i64, - r"[0-9]+u64" => u64, + r"[0-9]([a-zA-Z0-9_]*[a-zA-Z0-9])?|b'(\\)?[^'[[:cntrl:]]]'([iu]64)?" => integer, // Logical operators "^", @@ -162,11 +199,7 @@ ExprInStmt: ExprParsed<'input> = { sym, bnd: Box::new(bnd), }, - "if" >> "{" > "}" > "}")?> => ExprParsed::If { - cnd: Box::new(cnd), - thn: Box::new(thn), - els: Box::new(els.unwrap_or(Meta { meta: (l, r - l), inner: ExprParsed::Lit { val: Lit::Unit }})), - }, + ExprIf, "loop" "{" > "}" => ExprParsed::Loop { bdy: Box::new(bdy), }, @@ -207,9 +240,23 @@ ExprInStmt: ExprParsed<'input> = { bdy: Box::new(bdy.fmap(|bdy| bdy.unwrap_or(ExprParsed::Lit { val: Lit::Unit }))), }, "continue" => ExprParsed::Continue, + "asm" "{" "}" => ExprParsed::Asm { instrs }, ExprLogicalOr, } +ExprIf: ExprParsed<'input> = { + "if" >> "{" > "}" >)?> => ExprParsed::If { + cnd: Box::new(cnd), + thn: Box::new(thn), + els: Box::new(els.unwrap_or(Meta { meta: (l, r - l), inner: ExprParsed::Lit { val: Lit::Unit }})), + }, +} + +ExprIfElse: ExprParsed<'input> = { + "{" "}", + ExprIf, +} + BinaryOps: ExprParsed<'input> = { >> > => ExprParsed::BinaryOp { op, @@ -278,19 +325,6 @@ ExprAtom: ExprParsed<'input> = { => ExprParsed::Lit { val: Lit::Int { val, - typ: None, - }, - }, - => ExprParsed::Lit { - val: Lit::Int { - val: val.trim_end_matches("i64"), - typ: Some(PartialType::I64), - }, - }, - => ExprParsed::Lit { - val: Lit::Int { - val: val.trim_end_matches("u64"), - typ: Some(PartialType::U64), }, }, => ExprParsed::Lit { val: Lit::Bool { val } }, @@ -312,7 +346,7 @@ Struct: ExprParsed<'input> = { }, } -StructArg : (Spanned<&'input str>, Spanned>) = { +StructArg: (Spanned<&'input str>, Spanned>) = { ":" >, => (sym.clone(), Meta { meta: (l, r - l), inner: ExprParsed::Var { sym } }) } @@ -338,3 +372,52 @@ Comma: Vec = { } Spanned: Spanned = => Meta { meta: (l, r - l), inner }; + +AsmInstr: InstrParsed<'input> = { + "addq" => InstrParsed::Addq { src, dst }, + "subq" => InstrParsed::Subq { src, dst }, + "mulq" => InstrParsed::Mulq { src }, + "divq" => InstrParsed::Divq { divisor }, + "negq" => InstrParsed::Negq { dst }, + "movq" => InstrParsed::Movq { src, dst }, + "pushq" => InstrParsed::Pushq { src }, + "popq" => InstrParsed::Popq { dst }, + "retq" => InstrParsed::Retq, + "andq" => InstrParsed::Andq { src, dst }, + "orq" => InstrParsed::Orq { src, dst }, + "xorq" => InstrParsed::Xorq { src, dst }, + "notq" => InstrParsed::Notq { dst }, + "cmpq" => InstrParsed::Cmpq { src, dst }, + "syscall" => InstrParsed::Syscall { arity: arity.parse().expect("internal compiler error :(") }, +} + +AsmArg: VarArg> = { + => VarArg::Reg { reg }, + "{" "}" => VarArg::XVar { sym }, + "$" => VarArg::Imm { + val: val.parse().expect("Internal compiler error (oh no!): We were too lazy to make a proper error for this"), + }, + "[" "+" "]" => VarArg::Deref { + reg, + off: off.parse().expect("Internal compiler error (oh no!): We were too lazy to make a proper error for this"), + }, +} + +AsmReg: Reg = { + "%RSP" => Reg::RSP, + "%RBP" => Reg::RBP, + "%RAX" => Reg::RAX, + "%RBX" => Reg::RBX, + "%RCX" => Reg::RCX, + "%RDX" => Reg::RDX, + "%RSI" => Reg::RSI, + "%RDI" => Reg::RDI, + "%R8" => Reg::R8 , + "%R9" => Reg::R9 , + "%R10" => Reg::R10, + "%R11" => Reg::R11, + "%R12" => Reg::R12, + "%R13" => Reg::R13, + "%R14" => Reg::R14, + "%R15" => Reg::R15, +} diff --git a/compiler/src/passes/parse/mod.rs b/compiler/src/passes/parse/mod.rs index c05fdf4..0dc7425 100644 --- a/compiler/src/passes/parse/mod.rs +++ b/compiler/src/passes/parse/mod.rs @@ -10,7 +10,8 @@ pub mod parse; mod tests; pub mod types; -use crate::passes::validate::partial_type::PartialType; +use crate::passes::select::{Instr, VarArg}; +use crate::passes::validate::MetaConstrained; use crate::utils::gen_sym::UniqueSym; use derive_more::Display; use functor_derive::Functor; @@ -50,6 +51,7 @@ pub enum Def { pub type DefParsed<'p> = Def, Spanned<&'p str>, Spanned>>; pub type ExprParsed<'p> = Expr, Spanned<&'p str>, Lit<'p>, Span>; +pub type InstrParsed<'p> = Instr>, Spanned<&'p str>>; #[derive(Clone, Debug)] pub enum TypeDef { @@ -78,7 +80,7 @@ impl Def { /// A parameter used in functions. /// /// Parameters are generic and can use symbols that are either `&str` or -/// [`UniqueSym`](crate::utils::gen_sym::UniqueSym) for all passes after yeet. +/// [`UniqueSym`](UniqueSym) for all passes after uniquify. #[derive(Clone, Display, Debug)] #[display(bound = "A: Display")] #[display(fmt = "{}{sym}: {typ}", r#"if *mutable { "mut " } else { "" }"#)] @@ -94,9 +96,9 @@ pub struct Param { /// An expression. /// /// Expressions are generic and can use symbols that are either `&str` or -/// [`UniqueSym`](crate::utils::gen_sym::UniqueSym) for all passes after yeet. +/// [`UniqueSym`](UniqueSym) for all passes after uniquify. #[derive(Debug)] -pub enum Expr { +pub enum Expr { /// A literal value. See [`Lit`]. Lit { /// Value of the literal. See [`Lit`]. @@ -234,6 +236,9 @@ pub enum Expr { enm: Box>>, arms: Vec>, }, + Asm { + instrs: Vec, IdentVars>>, + }, } pub type SwitchArm = ( @@ -251,6 +256,7 @@ pub struct Meta { } pub type Spanned = Meta; +pub type Constrained = Meta; pub type Typed<'p, T> = Meta>, T>; impl Functor for Meta { @@ -329,10 +335,7 @@ pub enum BinaryOp { pub enum Lit<'p> { /// Integer literal, representing a signed 64-bit number. #[display(fmt = "{val}")] - Int { - val: &'p str, - typ: Option>, - }, + Int { val: &'p str }, /// Boolean literal, representing a value of *true* or *false*. #[display(fmt = "{}", r#"if *val { "true" } else { "false" }"#)] Bool { val: bool }, diff --git a/compiler/src/passes/parse/types.rs b/compiler/src/passes/parse/types.rs index c698c30..61bd63b 100644 --- a/compiler/src/passes/parse/types.rs +++ b/compiler/src/passes/parse/types.rs @@ -2,7 +2,6 @@ use derive_more::Display; use itertools::Itertools; use std::fmt::Display; -//TODO generic not needed #[derive(Debug, Clone, Display)] #[display(bound = "A: Display")] pub enum Type { diff --git a/compiler/src/passes/patch/patch.rs b/compiler/src/passes/patch/patch.rs index 6095149..7d0b922 100644 --- a/compiler/src/passes/patch/patch.rs +++ b/compiler/src/passes/patch/patch.rs @@ -1,6 +1,7 @@ use crate::passes::assign::{Arg, X86Assigned}; use crate::passes::patch::X86Patched; use crate::passes::select::{Block, Instr}; +use crate::utils::gen_sym::UniqueSym; use crate::{addq, movq, reg, subq}; impl<'p> X86Assigned<'p> { @@ -29,7 +30,7 @@ fn patch_block(block: Block<'_, Arg>) -> Block<'_, Arg> { } } -fn patch_instr(instr: Instr<'_, Arg>) -> Vec> { +fn patch_instr(instr: Instr>) -> Vec>> { match instr { Instr::Addq { src, dst } => patch_args(src, dst, |src, dst| addq!(src, dst)), Instr::Subq { src, dst } => patch_args(src, dst, |src, dst| subq!(src, dst)), @@ -38,7 +39,11 @@ fn patch_instr(instr: Instr<'_, Arg>) -> Vec> { } } -fn patch_args<'p>(src: Arg, dst: Arg, op: fn(Arg, Arg) -> Instr<'p, Arg>) -> Vec> { +fn patch_args<'p>( + src: Arg, + dst: Arg, + op: fn(Arg, Arg) -> Instr>, +) -> Vec>> { match (&src, &dst) { (Arg::Deref { .. }, Arg::Deref { .. }) => vec![movq!(src, reg!(RAX)), op(reg!(RAX), dst)], _ => vec![op(src, dst)], diff --git a/compiler/src/passes/reveal/display.rs b/compiler/src/passes/reveal/display.rs new file mode 100644 index 0000000..7b60793 --- /dev/null +++ b/compiler/src/passes/reveal/display.rs @@ -0,0 +1,90 @@ +use crate::passes::reveal::RExpr; +use indenter::indented; +use itertools::Itertools; +use std::fmt::Write; +use std::fmt::{Display, Formatter}; + +impl Display for RExpr<'_> { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + RExpr::Lit { val } => { + write!(f, "{val}") + } + RExpr::Var { sym } => { + write!(f, "{sym}") + } + RExpr::FunRef { sym } => { + write!(f, "{sym}") + } + RExpr::UnaryOp { op, expr } => { + write!(f, "({op}{expr})") + } + RExpr::BinaryOp { + op, + exprs: [e1, e2], + } => { + write!(f, "({e1} {op} {e2})") + } + RExpr::Let { + sym, + mutable, + bnd, + bdy, + } => { + writeln!( + f, + "let {}{sym} = {bnd};", + if *mutable { "mut " } else { "" } + )?; + write!(f, "{bdy}") + } + RExpr::If { cnd, thn, els } => { + writeln!(f, "if {cnd} {{")?; + writeln!(indented(f), "{thn}")?; + writeln!(f, "}} else {{")?; + writeln!(indented(f), "{els}")?; + write!(f, "}}") + } + RExpr::Apply { fun, args } => { + write!(f, "{fun}({})", args.iter().format(", ")) + } + RExpr::Loop { bdy } => { + writeln!(f, "loop {{")?; + writeln!(indented(f), "{bdy}")?; + write!(f, "}}") + } + RExpr::Break { bdy } => { + write!(f, "break {bdy}") + } + RExpr::Continue => { + write!(f, "continue") + } + RExpr::Return { bdy } => { + write!(f, "return {bdy}") + } + RExpr::Seq { stmt, cnt } => { + writeln!(f, "{stmt};")?; + write!(f, "{cnt}") + } + RExpr::Assign { sym, bnd } => { + write!(f, "{sym} = {bnd}") + } + RExpr::Struct { sym, fields } => { + writeln!(f, "{sym} {{")?; + writeln!( + indented(f), + "{}", + fields + .iter() + .map(|(sym, bnd)| format!("{sym}: {bnd},")) + .format("\n") + )?; + write!(f, "}}") + } + RExpr::AccessField { strct, field } => { + write!(f, "{strct}.{field}") + } + RExpr::Asm { .. } => todo!(), + } + } +} diff --git a/compiler/src/passes/reveal/mod.rs b/compiler/src/passes/reveal/mod.rs index 639870e..601ed31 100644 --- a/compiler/src/passes/reveal/mod.rs +++ b/compiler/src/passes/reveal/mod.rs @@ -1,14 +1,17 @@ +mod display; pub mod reveal; -#[cfg(test)] -mod tests; -use crate::passes::parse::{BinaryOp, Def, Meta, Typed, UnaryOp}; +use crate::passes::parse::{BinaryOp, Def, Typed, UnaryOp}; use crate::passes::select::std_lib::Std; -use crate::passes::validate::{DefValidated, ExprValidated, PrgValidated, TLit}; +use crate::passes::select::InstrSelected; +use crate::passes::validate::TLit; use crate::utils::gen_sym::UniqueSym; -use functor_derive::Functor; +use derive_more::Display; +use itertools::Itertools; use std::collections::HashMap; +#[derive(Display)] +#[display(fmt = "{}", r#"defs.values().format("\n")"#)] pub struct PrgRevealed<'p> { pub defs: HashMap, DefRevealed<'p>>, pub entry: UniqueSym<'p>, @@ -37,6 +40,7 @@ pub enum RExpr<'p> { }, Let { sym: UniqueSym<'p>, + mutable: bool, bnd: Box>>, bdy: Box>>, }, @@ -75,104 +79,7 @@ pub enum RExpr<'p> { strct: Box>>, field: &'p str, }, -} - -impl<'p> From> for PrgValidated<'p> { - fn from(value: PrgRevealed<'p>) -> Self { - PrgValidated { - defs: value - .defs - .into_iter() - .map(|(sym, def)| (sym, def.into())) - .collect(), - entry: value.entry, - std: value.std, - } - } -} - -impl<'p> From> for DefValidated<'p> { - fn from(value: DefRevealed<'p>) -> Self { - match value { - DefRevealed::Fn { - sym, - params, - typ, - bdy, - } => DefValidated::Fn { - sym, - params, - typ, - bdy: bdy.into(), - }, - DefRevealed::TypeDef { sym, def } => DefValidated::TypeDef { sym, def }, - } - } -} - -impl<'p> From>> for Typed<'p, ExprValidated<'p>> { - fn from(value: Typed<'p, RExpr<'p>>) -> Self { - let inner = match value.inner { - RExpr::Lit { val } => ExprValidated::Lit { val }, - RExpr::BinaryOp { op, exprs } => ExprValidated::BinaryOp { - op, - exprs: exprs.map(|expr| expr.fmap(Into::into)), - }, - RExpr::UnaryOp { op, expr } => ExprValidated::UnaryOp { - op, - expr: expr.fmap(Into::into), - }, - RExpr::Let { sym, bnd, bdy } => ExprValidated::Let { - sym, - mutable: true, - typ: None, - bnd: Box::new((*bnd).into()), - bdy: Box::new((*bdy).into()), - }, - RExpr::If { cnd, thn, els } => ExprValidated::If { - cnd: Box::new((*cnd).into()), - thn: Box::new((*thn).into()), - els: Box::new((*els).into()), - }, - RExpr::Apply { fun, args } => ExprValidated::Apply { - fun: Box::new((*fun).into()), - args: args.into_iter().map(Into::into).collect(), - }, - RExpr::Var { sym } | RExpr::FunRef { sym } => ExprValidated::Var { sym }, - RExpr::Loop { bdy } => ExprValidated::Loop { - bdy: Box::new((*bdy).into()), - }, - RExpr::Break { bdy } => ExprValidated::Break { - bdy: Box::new((*bdy).into()), - }, - RExpr::Seq { stmt, cnt } => ExprValidated::Seq { - stmt: Box::new((*stmt).into()), - cnt: Box::new((*cnt).into()), - }, - RExpr::Assign { sym, bnd } => ExprValidated::Assign { - sym, - bnd: Box::new((*bnd).into()), - }, - RExpr::Continue => ExprValidated::Continue, - RExpr::Return { bdy } => ExprValidated::Return { - bdy: Box::new((*bdy).into()), - }, - RExpr::Struct { sym, fields } => ExprValidated::Struct { - sym, - fields: fields - .into_iter() - .map(|(sym, expr)| (sym, expr.into())) - .collect(), - }, - RExpr::AccessField { strct, field } => ExprValidated::AccessField { - strct: Box::new((*strct).into()), - field, - }, - }; - - Meta { - inner, - meta: value.meta, - } - } + Asm { + instrs: Vec>, + }, } diff --git a/compiler/src/passes/reveal/reveal.rs b/compiler/src/passes/reveal/reveal.rs index 1db1bfe..7332924 100644 --- a/compiler/src/passes/reveal/reveal.rs +++ b/compiler/src/passes/reveal/reveal.rs @@ -69,10 +69,17 @@ fn reveal_expr<'p>( Box::new(reveal_expr(*rhs, scope)), ], }, - ExprValidated::Let { sym, bnd, bdy, .. } => { + ExprValidated::Let { + sym, + mutable, + bnd, + bdy, + .. + } => { let bnd = Box::new(reveal_expr(*bnd, scope)); scope.remove(sym, |scope| RExpr::Let { sym, + mutable, bnd, bdy: Box::new(reveal_expr(*bdy, scope)), }) @@ -120,6 +127,7 @@ fn reveal_expr<'p>( }, ExprValidated::Variant { .. } => todo!(), ExprValidated::Switch { .. } => todo!(), + ExprValidated::Asm { instrs } => RExpr::Asm { instrs }, }; Meta { diff --git a/compiler/src/passes/reveal/tests.rs b/compiler/src/passes/reveal/tests.rs deleted file mode 100644 index 9bdf7f8..0000000 --- a/compiler/src/passes/reveal/tests.rs +++ /dev/null @@ -1,24 +0,0 @@ -use crate::interpreter::TestIO; -use crate::passes::parse::parse::parse_program; -use crate::passes::validate::PrgValidated; -use crate::utils::split_test::split_test; -use test_each_file::test_each_file; - -fn reveal([test]: [&str; 1]) { - let (input, expected_output, expected_return, _) = split_test(test); - - let program: PrgValidated = parse_program(test) - .unwrap() - .validate() - .unwrap() - .reveal() - .into(); - - let mut io = TestIO::new(input); - let result = program.interpret(&mut io); - - assert_eq!(result, expected_return.into(), "Incorrect program result."); - assert_eq!(io.outputs(), &expected_output, "Incorrect program output."); -} - -test_each_file! { for ["test"] in "./programs/good" as reveal => reveal } diff --git a/compiler/src/passes/select/interpreter.rs b/compiler/src/passes/select/interpreter.rs index ecaf990..268bc1a 100644 --- a/compiler/src/passes/select/interpreter.rs +++ b/compiler/src/passes/select/interpreter.rs @@ -34,7 +34,7 @@ pub struct IStats { } pub struct X86Interpreter<'p, I: IO> { - pub blocks: &'p HashMap, Block<'p, VarArg<'p>>>, + pub blocks: &'p HashMap, Block<'p, VarArg>>>, pub io: &'p mut I, pub regs: HashMap, @@ -298,7 +298,7 @@ impl<'p, I: IO> X86Interpreter<'p, I> { } } - fn get_arg(&self, a: &'p VarArg) -> i64 { + fn get_arg(&self, a: &'p VarArg>) -> i64 { match a { VarArg::Imm { val } => *val, VarArg::Reg { reg } => self.regs[reg], @@ -310,7 +310,7 @@ impl<'p, I: IO> X86Interpreter<'p, I> { } } - fn set_arg(&mut self, a: &'p VarArg, v: i64) { + fn set_arg(&mut self, a: &'p VarArg>, v: i64) { match a { VarArg::Imm { .. } => panic!("Tried to write to immediate, are u insane?"), VarArg::Reg { reg } => { @@ -333,8 +333,14 @@ impl<'p, I: IO> X86Interpreter<'p, I> { assert!(buffer_len >= 1); if self.read_buffer.is_empty() { - self.read_buffer = format!("{}\n", self.io.read().int()).into_bytes(); - self.read_buffer.reverse(); + if let Some(read) = self.io.read() { + self.read_buffer = format!("{}\n", read.int()).into_bytes(); + self.read_buffer.reverse(); + } else { + self.memory.insert(buffer, 0); + self.regs.insert(Reg::RAX, 0); + return; + } } let val = self.read_buffer.pop().unwrap(); self.memory.insert(buffer, val as i64); diff --git a/compiler/src/passes/select/mod.rs b/compiler/src/passes/select/mod.rs index b9b7e7b..f608bf8 100644 --- a/compiler/src/passes/select/mod.rs +++ b/compiler/src/passes/select/mod.rs @@ -19,18 +19,20 @@ use std::fmt::Display; r#"blocks.iter().map(|(sym, block)| format!("{sym}:\n{block}")).format("\n")"# )] pub struct X86Selected<'p> { - pub blocks: HashMap, Block<'p, VarArg<'p>>>, + pub blocks: HashMap, Block<'p, VarArg>>>, pub entry: UniqueSym<'p>, pub std: Std<'p>, } -#[derive(Clone, Display, Functor)] +#[derive(Debug, Clone, Display, Functor)] #[display(fmt = "\t{}", r#"instrs.iter().format("\n\t")"#)] pub struct Block<'p, A: Display> { - pub instrs: Vec>, + pub instrs: Vec>>, } -#[derive(Copy, Clone, PartialEq, Display)] +pub type InstrSelected<'p> = Instr>, UniqueSym<'p>>; + +#[derive(Debug, Copy, Clone, PartialEq, Display)] pub enum Cnd { Above, AboveOrEqual, @@ -52,54 +54,54 @@ pub enum Cnd { Sign, } -#[derive(Clone, PartialEq, Display, Functor)] -pub enum Instr<'p, A: Display> { +#[derive(Debug, Clone, PartialEq, Display, Functor)] +pub enum Instr { #[display(fmt = "addq\t{src}\t{dst}")] - Addq { src: A, dst: A }, + Addq { src: Arg, dst: Arg }, #[display(fmt = "subq\t{src}\t{dst}")] - Subq { src: A, dst: A }, + Subq { src: Arg, dst: Arg }, #[display(fmt = "divq\t{divisor}")] - Divq { divisor: A }, + Divq { divisor: Arg }, #[display(fmt = "mulq\t{src}")] - Mulq { src: A }, + Mulq { src: Arg }, #[display(fmt = "negq\t{dst}")] - Negq { dst: A }, + Negq { dst: Arg }, #[display(fmt = "movq\t{src}\t{dst}")] - Movq { src: A, dst: A }, + Movq { src: Arg, dst: Arg }, #[display(fmt = "pushq\t{src}")] - Pushq { src: A }, + Pushq { src: Arg }, #[display(fmt = "popq\t{dst}")] - Popq { dst: A }, + Popq { dst: Arg }, #[display(fmt = "retq")] Retq, - #[display(fmt = "syscall\t// arity: {arity}")] + #[display(fmt = "syscall\t{arity}")] Syscall { arity: usize }, #[display(fmt = "cmpq\t{src}\t{dst}")] - Cmpq { src: A, dst: A }, + Cmpq { src: Arg, dst: Arg }, #[display(fmt = "jmp\t{lbl}")] - Jmp { lbl: UniqueSym<'p> }, + Jmp { lbl: IdentVars }, #[display(fmt = "jcc\t{cnd}\t{lbl}")] - Jcc { lbl: UniqueSym<'p>, cnd: Cnd }, + Jcc { lbl: IdentVars, cnd: Cnd }, #[display(fmt = "andq {src}\t{dst}")] - Andq { src: A, dst: A }, + Andq { src: Arg, dst: Arg }, #[display(fmt = "orq {src}\t{dst}")] - Orq { src: A, dst: A }, + Orq { src: Arg, dst: Arg }, #[display(fmt = "xorq\t{src}\t{dst}")] - Xorq { src: A, dst: A }, + Xorq { src: Arg, dst: Arg }, #[display(fmt = "notq\t{dst}")] - Notq { dst: A }, + Notq { dst: Arg }, #[display(fmt = "setcc\t{cnd}")] Setcc { cnd: Cnd }, //TODO allow setting other byteregs #[display(fmt = "loadlbl\t{sym}\t{dst}")] - LoadLbl { sym: UniqueSym<'p>, dst: A }, - #[display(fmt = "call_direct\t{lbl}\t// arity: {arity}")] - CallqDirect { lbl: UniqueSym<'p>, arity: usize }, - #[display(fmt = "call_indirect\t{src}\t// arity: {arity}")] - CallqIndirect { src: A, arity: usize }, + LoadLbl { sym: IdentVars, dst: Arg }, + #[display(fmt = "call_direct\t{lbl}\t{arity}")] + CallqDirect { lbl: IdentVars, arity: usize }, + #[display(fmt = "call_indirect\t{src}\t{arity}")] + CallqIndirect { src: Arg, arity: usize }, } -#[derive(PartialEq, Clone, Display)] -pub enum VarArg<'p> { +#[derive(Debug, PartialEq, Clone, Display, Functor)] +pub enum VarArg { #[display(fmt = "${val}")] Imm { val: i64 }, #[display(fmt = "%{reg}")] @@ -107,7 +109,7 @@ pub enum VarArg<'p> { #[display(fmt = "[%{reg} + ${off}]")] Deref { reg: Reg, off: i64 }, #[display(fmt = "{sym}")] - XVar { sym: UniqueSym<'p> }, + XVar { sym: IdentVars }, } pub const CALLER_SAVED: [Reg; 9] = [ @@ -142,7 +144,7 @@ pub const SYSCALL_REGS: [Reg; 7] = [ Reg::R9, ]; -#[derive(Hash, Clone, Copy, Eq, Ord, PartialEq, PartialOrd, Display)] +#[derive(Debug, Hash, Clone, Copy, Eq, Ord, PartialEq, PartialOrd, Display)] #[allow(clippy::upper_case_acronyms)] pub enum Reg { RSP, diff --git a/compiler/src/passes/select/select.rs b/compiler/src/passes/select/select.rs index 13de1f6..14f1f87 100644 --- a/compiler/src/passes/select/select.rs +++ b/compiler/src/passes/select/select.rs @@ -4,7 +4,7 @@ use crate::passes::parse::types::Type; use crate::passes::parse::{BinaryOp, Meta, Param, UnaryOp}; use crate::passes::select::std_lib::add_std_library; use crate::passes::select::{ - Block, Cnd, Instr, VarArg, X86Selected, CALLEE_SAVED_NO_STACK, CALLER_SAVED, + Block, Cnd, InstrSelected, VarArg, X86Selected, CALLEE_SAVED_NO_STACK, CALLER_SAVED, }; use crate::utils::gen_sym::{gen_sym, UniqueSym}; use crate::*; @@ -36,7 +36,7 @@ fn select_block<'p>( sym: UniqueSym<'p>, tail: ETail<'p>, fn_params: &HashMap, Vec>>>, -) -> Block<'p, VarArg<'p>> { +) -> Block<'p, VarArg>> { let mut instrs = Vec::new(); if let Some(params) = fn_params.get(&sym) { @@ -60,7 +60,7 @@ fn select_block<'p>( Block { instrs } } -fn select_tail<'p>(tail: ETail<'p>, instrs: &mut Vec>>) { +fn select_tail<'p>(tail: ETail<'p>, instrs: &mut Vec>) { match tail { ETail::Return { exprs } => { assert!( @@ -112,7 +112,7 @@ fn select_tail<'p>(tail: ETail<'p>, instrs: &mut Vec>>) { fn select_assign<'p>( dsts: &[UniqueSym<'p>], expr: Meta>>, EExpr<'p>>, -) -> Vec>> { +) -> Vec> { let dst = var!(dsts[0]); match expr.inner { EExpr::Atom { @@ -207,10 +207,11 @@ fn select_assign<'p>( instrs } + EExpr::Asm { instrs } => instrs, } } -fn select_atom(expr: Atom) -> VarArg { +fn select_atom(expr: Atom<'_>) -> VarArg> { match expr { Atom::Val { val } => imm!(val), Atom::Var { sym } => var!(sym), diff --git a/compiler/src/passes/select/std_lib.rs b/compiler/src/passes/select/std_lib.rs index 3369af9..9031c8d 100644 --- a/compiler/src/passes/select/std_lib.rs +++ b/compiler/src/passes/select/std_lib.rs @@ -9,7 +9,10 @@ use std::collections::HashMap; pub type Std<'p> = HashMap<&'p str, UniqueSym<'p>>; -pub fn add_std_library<'p>(std: &Std<'p>, blocks: &mut HashMap, Block<'p, VarArg>>) { +pub fn add_std_library<'p>( + std: &Std<'p>, + blocks: &mut HashMap, Block<'p, VarArg>>>, +) { add_exit_block(std["exit"], blocks); add_print_block(std["print"], blocks); add_read_block(std["read"], blocks, std["exit"]); @@ -17,7 +20,7 @@ pub fn add_std_library<'p>(std: &Std<'p>, blocks: &mut HashMap, Bl fn add_exit_block<'p>( entry: UniqueSym<'p>, - blocks: &mut HashMap, Block<'p, VarArg>>, + blocks: &mut HashMap, Block<'p, VarArg>>>, ) { blocks.insert( entry, @@ -31,7 +34,7 @@ fn add_exit_block<'p>( fn add_print_block<'p>( entry: UniqueSym<'p>, - blocks: &mut HashMap, Block<'p, VarArg>>, + blocks: &mut HashMap, Block<'p, VarArg>>>, ) { let print_neg = gen_sym("print_neg"); let print_push_loop = gen_sym("print_push_loop"); @@ -94,7 +97,7 @@ fn add_print_block<'p>( fn add_read_block<'p>( entry: UniqueSym<'p>, - blocks: &mut HashMap, Block<'p, VarArg>>, + blocks: &mut HashMap, Block<'p, VarArg>>>, exit: UniqueSym<'p>, ) { let read_is_neg = gen_sym("read_is_neg"); diff --git a/compiler/src/passes/validate/constrain/access_field.rs b/compiler/src/passes/validate/constrain/access_field.rs index e9d9499..7abed4b 100644 --- a/compiler/src/passes/validate/constrain/access_field.rs +++ b/compiler/src/passes/validate/constrain/access_field.rs @@ -1,16 +1,16 @@ -use crate::passes::parse::{Meta, Span, Spanned, TypeDef}; +use crate::passes::parse::{Constrained, Span, Spanned, TypeDef}; use crate::passes::validate::constrain::expr::constrain_expr; use crate::passes::validate::constrain::uncover_globals::{Env, EnvEntry}; use crate::passes::validate::error::TypeError; use crate::passes::validate::partial_type::PartialType; -use crate::passes::validate::{CMeta, ExprConstrained, ExprUniquified}; +use crate::passes::validate::{ExprConstrained, ExprUniquified, MetaConstrained}; pub fn constrain_access_field<'p>( env: &mut Env<'_, 'p>, span: Span, strct: Spanned>, field: Spanned<&'p str>, -) -> Result>, TypeError> { +) -> Result>, TypeError> { let strct = constrain_expr(strct, env)?; let PartialType::Var { sym } = env.uf.get(strct.meta.index) else { @@ -38,8 +38,8 @@ pub fn constrain_access_field<'p>( }; let index = env.uf.type_to_index(typ.clone()); - Ok(Meta { - meta: CMeta { span, index }, + Ok(Constrained { + meta: MetaConstrained { span, index }, inner: ExprConstrained::AccessField { strct: Box::new(strct), field, diff --git a/compiler/src/passes/validate/constrain/apply.rs b/compiler/src/passes/validate/constrain/apply.rs index 61b9e16..7ad4584 100644 --- a/compiler/src/passes/validate/constrain/apply.rs +++ b/compiler/src/passes/validate/constrain/apply.rs @@ -1,9 +1,9 @@ -use crate::passes::parse::{Meta, Span, Spanned}; +use crate::passes::parse::{Constrained, Span, Spanned}; use crate::passes::validate::constrain::expr; use crate::passes::validate::constrain::uncover_globals::Env; use crate::passes::validate::error::TypeError; use crate::passes::validate::partial_type::PartialType; -use crate::passes::validate::{CMeta, ExprConstrained, ExprUniquified}; +use crate::passes::validate::{ExprConstrained, ExprUniquified, MetaConstrained}; use crate::utils::expect::expect; pub fn constrain_apply<'p>( @@ -11,7 +11,7 @@ pub fn constrain_apply<'p>( span: Span, fun: Spanned>, args: Vec>>, -) -> Result>, TypeError> { +) -> Result>, TypeError> { let fun = expr::constrain_expr(fun, env)?; let args: Vec<_> = args .into_iter() @@ -46,8 +46,8 @@ pub fn constrain_apply<'p>( })?; } - Ok(Meta { - meta: CMeta { span, index: typ }, + Ok(Constrained { + meta: MetaConstrained { span, index: typ }, inner: ExprConstrained::Apply { fun: Box::new(fun), args, diff --git a/compiler/src/passes/validate/constrain/asm.rs b/compiler/src/passes/validate/constrain/asm.rs new file mode 100644 index 0000000..d3d1f77 --- /dev/null +++ b/compiler/src/passes/validate/constrain/asm.rs @@ -0,0 +1,23 @@ +use crate::passes::parse::{Constrained, Span}; + +use crate::passes::validate::constrain::uncover_globals::Env; +use crate::passes::validate::error::TypeError; +use crate::passes::validate::partial_type::PartialType; +use crate::passes::validate::{ExprConstrained, InstrUniquified, MetaConstrained}; + +pub fn constrain_asm<'p>( + env: &mut Env<'_, 'p>, + span: Span, + instrs: Vec>, +) -> Result>, TypeError> { + //TODO mutability checks + //TODO check that all vars are nums + + Ok(Constrained { + meta: MetaConstrained { + span, + index: env.uf.add(PartialType::Unit), + }, + inner: ExprConstrained::Asm { instrs }, + }) +} diff --git a/compiler/src/passes/validate/constrain/assign.rs b/compiler/src/passes/validate/constrain/assign.rs index fc824d2..7741ba0 100644 --- a/compiler/src/passes/validate/constrain/assign.rs +++ b/compiler/src/passes/validate/constrain/assign.rs @@ -1,10 +1,10 @@ -use crate::passes::parse::{Meta, Span, Spanned}; +use crate::passes::parse::{Constrained, Span, Spanned}; use crate::passes::validate::constrain::expr; use crate::passes::validate::constrain::uncover_globals::{Env, EnvEntry}; use crate::passes::validate::error::TypeError; use crate::passes::validate::error::TypeError::MismatchedAssignBinding; use crate::passes::validate::partial_type::PartialType; -use crate::passes::validate::{CMeta, ExprConstrained, ExprUniquified}; +use crate::passes::validate::{ExprConstrained, ExprUniquified, MetaConstrained}; use crate::utils::expect::expect; use crate::utils::gen_sym::UniqueSym; @@ -13,7 +13,7 @@ pub fn constrain_assign<'p>( span: Span, sym: Spanned>, bnd: Spanned>, -) -> Result>, TypeError> { +) -> Result>, TypeError> { let bnd = expr::constrain_expr(bnd, env)?; let EnvEntry::Type { mutable, typ } = env.scope[&sym.inner] else { @@ -34,8 +34,8 @@ pub fn constrain_assign<'p>( let typ = env.uf.add(PartialType::Unit); - Ok(Meta { - meta: CMeta { span, index: typ }, + Ok(Constrained { + meta: MetaConstrained { span, index: typ }, inner: ExprConstrained::Assign { sym, bnd: Box::new(bnd), diff --git a/compiler/src/passes/validate/constrain/binary_op.rs b/compiler/src/passes/validate/constrain/binary_op.rs index 1fd251d..7dc760f 100644 --- a/compiler/src/passes/validate/constrain/binary_op.rs +++ b/compiler/src/passes/validate/constrain/binary_op.rs @@ -1,9 +1,9 @@ -use crate::passes::parse::{BinaryOp, Meta, Span, Spanned}; +use crate::passes::parse::{BinaryOp, Constrained, Span, Spanned}; use crate::passes::validate::constrain::expr; use crate::passes::validate::constrain::uncover_globals::Env; use crate::passes::validate::error::TypeError; use crate::passes::validate::partial_type::PartialType; -use crate::passes::validate::{CMeta, ExprConstrained, ExprUniquified}; +use crate::passes::validate::{ExprConstrained, ExprUniquified, MetaConstrained}; pub fn constrain_binary_op<'p>( env: &mut Env<'_, 'p>, @@ -11,7 +11,7 @@ pub fn constrain_binary_op<'p>( op: BinaryOp, lhs: Spanned>, rhs: Spanned>, -) -> Result>, TypeError> { +) -> Result>, TypeError> { // input: None = Any but equal, Some = expect this // output: None = Same as input, Some = this let (input, output) = match op { @@ -30,7 +30,7 @@ pub fn constrain_binary_op<'p>( // Check inputs satisfy constraints if let Some(input) = input { - let mut check = |expr: &Meta>| { + let mut check = |expr: &Constrained>| { env.uf .expect_partial_type(expr.meta.index, input.clone(), |got, expect| { TypeError::OperandExpect { @@ -67,8 +67,8 @@ pub fn constrain_binary_op<'p>( Some(e) => env.uf.add(e), }; - Ok(Meta { - meta: CMeta { + Ok(Constrained { + meta: MetaConstrained { span, index: output_index, }, diff --git a/compiler/src/passes/validate/constrain/break.rs b/compiler/src/passes/validate/constrain/break.rs index 5584a77..93124fd 100644 --- a/compiler/src/passes/validate/constrain/break.rs +++ b/compiler/src/passes/validate/constrain/break.rs @@ -1,15 +1,15 @@ -use crate::passes::parse::{Meta, Span, Spanned}; +use crate::passes::parse::{Constrained, Span, Spanned}; use crate::passes::validate::constrain::expr; use crate::passes::validate::constrain::uncover_globals::Env; use crate::passes::validate::error::TypeError; use crate::passes::validate::partial_type::PartialType; -use crate::passes::validate::{CMeta, ExprConstrained, ExprUniquified}; +use crate::passes::validate::{ExprConstrained, ExprUniquified, MetaConstrained}; pub fn constrain_break<'p>( env: &mut Env<'_, 'p>, span: Span, bdy: Spanned>, -) -> Result>, TypeError> { +) -> Result>, TypeError> { let Some(loop_type) = env.loop_type else { return Err(TypeError::BreakOutsideLoop { span }); }; @@ -24,8 +24,8 @@ pub fn constrain_break<'p>( } })?; - Ok(Meta { - meta: CMeta { + Ok(Constrained { + meta: MetaConstrained { span, index: env.uf.add(PartialType::Never), }, diff --git a/compiler/src/passes/validate/constrain/continue.rs b/compiler/src/passes/validate/constrain/continue.rs index f6bbbdf..21aa189 100644 --- a/compiler/src/passes/validate/constrain/continue.rs +++ b/compiler/src/passes/validate/constrain/continue.rs @@ -1,21 +1,21 @@ -use crate::passes::parse::{Meta, Span}; +use crate::passes::parse::{Constrained, Span}; use crate::passes::validate::constrain::uncover_globals::Env; use crate::passes::validate::error::TypeError; use crate::passes::validate::partial_type::PartialType; -use crate::passes::validate::{CMeta, ExprConstrained}; +use crate::passes::validate::{ExprConstrained, MetaConstrained}; use crate::utils::expect::expect; pub fn constrain_continue<'p>( env: &mut Env<'_, 'p>, span: Span, -) -> Result>, TypeError> { +) -> Result>, TypeError> { expect( env.loop_type.is_some(), TypeError::ContinueOutsideLoop { span }, )?; - Ok(Meta { - meta: CMeta { + Ok(Constrained { + meta: MetaConstrained { span, index: env.uf.add(PartialType::Never), }, diff --git a/compiler/src/passes/validate/constrain/def.rs b/compiler/src/passes/validate/constrain/def.rs index 4dfa03c..5ba6035 100644 --- a/compiler/src/passes/validate/constrain/def.rs +++ b/compiler/src/passes/validate/constrain/def.rs @@ -1,4 +1,4 @@ -use crate::passes::parse::{Def, Meta}; +use crate::passes::parse::{Def, Spanned}; use crate::passes::validate::constrain::expr::constrain_expr; use crate::passes::validate::constrain::uncover_globals::{Env, EnvEntry}; use crate::passes::validate::error::TypeError; @@ -37,7 +37,7 @@ pub fn constrain_def<'p>( uf, scope, loop_type: None, - return_type: &Meta { + return_type: &Spanned { inner: return_index, meta: sym.meta, }, // TODO replace sym.meta with return type index diff --git a/compiler/src/passes/validate/constrain/expr.rs b/compiler/src/passes/validate/constrain/expr.rs index faa82a2..edd9efd 100644 --- a/compiler/src/passes/validate/constrain/expr.rs +++ b/compiler/src/passes/validate/constrain/expr.rs @@ -1,6 +1,8 @@ -use crate::passes::parse::{Expr, Meta, Spanned}; +use crate::passes::parse::{Constrained, Expr, Spanned}; use crate::passes::validate::constrain::access_field::constrain_access_field; use crate::passes::validate::constrain::apply::constrain_apply; + +use crate::passes::validate::constrain::asm::constrain_asm; use crate::passes::validate::constrain::assign::constrain_assign; use crate::passes::validate::constrain::binary_op::constrain_binary_op; use crate::passes::validate::constrain::lit::constrain_lit; @@ -16,12 +18,12 @@ use crate::passes::validate::constrain::unary_op::constrain_unary_op; use crate::passes::validate::constrain::uncover_globals::Env; use crate::passes::validate::constrain::var::constrain_var; use crate::passes::validate::error::TypeError; -use crate::passes::validate::{CMeta, ExprConstrained, ExprUniquified}; +use crate::passes::validate::{ExprConstrained, ExprUniquified}; pub fn constrain_expr<'p>( expr: Spanned>, env: &mut Env<'_, 'p>, -) -> Result>, TypeError> { +) -> Result>, TypeError> { let span = expr.meta; match expr.inner { @@ -49,6 +51,7 @@ pub fn constrain_expr<'p>( Expr::Assign { sym, bnd } => constrain_assign(env, span, sym, *bnd), Expr::Struct { sym, fields } => constrain_struct(env, span, sym, fields), Expr::AccessField { strct, field } => constrain_access_field(env, span, *strct, field), + Expr::Asm { instrs } => constrain_asm(env, span, instrs), Expr::Variant { .. } => todo!(), Expr::Switch { .. } => todo!(), } diff --git a/compiler/src/passes/validate/constrain/if.rs b/compiler/src/passes/validate/constrain/if.rs index 1a912b6..985bbc7 100644 --- a/compiler/src/passes/validate/constrain/if.rs +++ b/compiler/src/passes/validate/constrain/if.rs @@ -1,9 +1,9 @@ use crate::passes::parse::types::Type; -use crate::passes::parse::{Meta, Span, Spanned}; +use crate::passes::parse::{Constrained, Span, Spanned}; use crate::passes::validate::constrain::expr; use crate::passes::validate::constrain::uncover_globals::Env; use crate::passes::validate::error::TypeError; -use crate::passes::validate::{CMeta, ExprConstrained, ExprUniquified}; +use crate::passes::validate::{ExprConstrained, ExprUniquified, MetaConstrained}; pub fn constrain_if<'p>( env: &mut Env<'_, 'p>, @@ -11,7 +11,7 @@ pub fn constrain_if<'p>( cnd: Spanned>, thn: Spanned>, els: Spanned>, -) -> Result>, TypeError> { +) -> Result>, TypeError> { let cnd = expr::constrain_expr(cnd, env)?; env.uf.expect_type(cnd.meta.index, Type::Bool, |got, _| { @@ -35,8 +35,8 @@ pub fn constrain_if<'p>( } })?; - Ok(Meta { - meta: CMeta { + Ok(Constrained { + meta: MetaConstrained { span, index: out_index, }, diff --git a/compiler/src/passes/validate/constrain/let.rs b/compiler/src/passes/validate/constrain/let.rs index c87fa30..da1f2ab 100644 --- a/compiler/src/passes/validate/constrain/let.rs +++ b/compiler/src/passes/validate/constrain/let.rs @@ -1,9 +1,9 @@ use crate::passes::parse::types::Type; -use crate::passes::parse::{Meta, Span, Spanned}; +use crate::passes::parse::{Constrained, Span, Spanned}; use crate::passes::validate::constrain::expr; use crate::passes::validate::constrain::uncover_globals::{Env, EnvEntry}; use crate::passes::validate::error::TypeError; -use crate::passes::validate::{CMeta, ExprConstrained, ExprUniquified}; +use crate::passes::validate::{ExprConstrained, ExprUniquified, MetaConstrained}; use crate::utils::gen_sym::UniqueSym; pub fn constrain_let<'p>( @@ -14,7 +14,7 @@ pub fn constrain_let<'p>( typ: Option>>>, bnd: Spanned>, bdy: Spanned>, -) -> Result>, TypeError> { +) -> Result>, TypeError> { let bnd = expr::constrain_expr(bnd, env)?; if let Some(typ) = &typ { @@ -36,8 +36,8 @@ pub fn constrain_let<'p>( ); let bdy = expr::constrain_expr(bdy, env)?; - Ok(Meta { - meta: CMeta { + Ok(Constrained { + meta: MetaConstrained { span, index: bdy.meta.index, }, diff --git a/compiler/src/passes/validate/constrain/lit.rs b/compiler/src/passes/validate/constrain/lit.rs index 94ff485..273a7b7 100644 --- a/compiler/src/passes/validate/constrain/lit.rs +++ b/compiler/src/passes/validate/constrain/lit.rs @@ -1,20 +1,19 @@ -use crate::passes::parse::{Lit, Meta, Span}; +use crate::passes::parse::{Constrained, Lit, Span}; use crate::passes::validate::constrain::uncover_globals::Env; use crate::passes::validate::error::TypeError; use crate::passes::validate::partial_type::PartialType; -use crate::passes::validate::{CMeta, ExprConstrained}; +use crate::passes::validate::{ExprConstrained, MetaConstrained}; pub fn constrain_lit<'p>( env: &mut Env<'_, 'p>, span: Span, val: Lit<'p>, -) -> Result>, TypeError> { +) -> Result>, TypeError> { // Get the type of the literal. let typ = match &val { - Lit::Int { typ, .. } => { - // If no type hint is given, use the generic `Int`. - typ.clone().unwrap_or(PartialType::Int) - } + Lit::Int { val } if val.ends_with("i64") => PartialType::I64, + Lit::Int { val } if val.ends_with("u64") => PartialType::U64, + Lit::Int { .. } => PartialType::Int, Lit::Bool { .. } => PartialType::Bool, Lit::Unit => PartialType::Unit, }; @@ -22,8 +21,8 @@ pub fn constrain_lit<'p>( // Add the type to the constraints. let index = env.uf.add(typ); - Ok(Meta { - meta: CMeta { span, index }, + Ok(Constrained { + meta: MetaConstrained { span, index }, inner: ExprConstrained::Lit { val }, }) } diff --git a/compiler/src/passes/validate/constrain/loop.rs b/compiler/src/passes/validate/constrain/loop.rs index 7ca0d54..09b97ab 100644 --- a/compiler/src/passes/validate/constrain/loop.rs +++ b/compiler/src/passes/validate/constrain/loop.rs @@ -1,15 +1,15 @@ -use crate::passes::parse::{Meta, Span, Spanned}; +use crate::passes::parse::{Constrained, Span, Spanned}; use crate::passes::validate::constrain::expr; use crate::passes::validate::constrain::uncover_globals::Env; use crate::passes::validate::error::TypeError; use crate::passes::validate::partial_type::PartialType; -use crate::passes::validate::{CMeta, ExprConstrained, ExprUniquified}; +use crate::passes::validate::{ExprConstrained, ExprUniquified, MetaConstrained}; pub fn constrain_loop<'p>( env: &mut Env<'_, 'p>, span: Span, bdy: Spanned>, -) -> Result>, TypeError> { +) -> Result>, TypeError> { let loop_type = env.uf.add(PartialType::Never); let mut env = Env { @@ -21,8 +21,8 @@ pub fn constrain_loop<'p>( let bdy = expr::constrain_expr(bdy, &mut env)?; - Ok(Meta { - meta: CMeta { + Ok(Constrained { + meta: MetaConstrained { span, index: loop_type, }, diff --git a/compiler/src/passes/validate/constrain/mod.rs b/compiler/src/passes/validate/constrain/mod.rs index 45fb39e..9ed47b5 100644 --- a/compiler/src/passes/validate/constrain/mod.rs +++ b/compiler/src/passes/validate/constrain/mod.rs @@ -11,6 +11,7 @@ use uncover_globals::uncover_globals; mod access_field; mod apply; +mod asm; mod assign; mod binary_op; mod r#break; diff --git a/compiler/src/passes/validate/constrain/return.rs b/compiler/src/passes/validate/constrain/return.rs index 63ee2b4..e51a88d 100644 --- a/compiler/src/passes/validate/constrain/return.rs +++ b/compiler/src/passes/validate/constrain/return.rs @@ -1,15 +1,15 @@ -use crate::passes::parse::{Meta, Span, Spanned}; +use crate::passes::parse::{Constrained, Span, Spanned}; use crate::passes::validate::constrain::expr; use crate::passes::validate::constrain::uncover_globals::Env; use crate::passes::validate::error::TypeError; use crate::passes::validate::partial_type::PartialType; -use crate::passes::validate::{CMeta, ExprConstrained, ExprUniquified}; +use crate::passes::validate::{ExprConstrained, ExprUniquified, MetaConstrained}; pub fn constrain_return<'p>( env: &mut Env<'_, 'p>, span: Span, bdy: Spanned>, -) -> Result>, TypeError> { +) -> Result>, TypeError> { let bdy = expr::constrain_expr(bdy, env)?; env.uf @@ -22,8 +22,8 @@ pub fn constrain_return<'p>( } })?; - Ok(Meta { - meta: CMeta { + Ok(Constrained { + meta: MetaConstrained { span, index: env.uf.add(PartialType::Never), }, diff --git a/compiler/src/passes/validate/constrain/seq.rs b/compiler/src/passes/validate/constrain/seq.rs index 4f9d85e..829de29 100644 --- a/compiler/src/passes/validate/constrain/seq.rs +++ b/compiler/src/passes/validate/constrain/seq.rs @@ -1,20 +1,20 @@ -use crate::passes::parse::{Meta, Span, Spanned}; +use crate::passes::parse::{Constrained, Span, Spanned}; use crate::passes::validate::constrain::expr; use crate::passes::validate::constrain::uncover_globals::Env; use crate::passes::validate::error::TypeError; -use crate::passes::validate::{CMeta, ExprConstrained, ExprUniquified}; +use crate::passes::validate::{ExprConstrained, ExprUniquified, MetaConstrained}; pub fn constrain_seq<'p>( env: &mut Env<'_, 'p>, span: Span, stmt: Spanned>, cnt: Spanned>, -) -> Result>, TypeError> { +) -> Result>, TypeError> { let stmt = expr::constrain_expr(stmt, env)?; let cnt = expr::constrain_expr(cnt, env)?; - Ok(Meta { - meta: CMeta { + Ok(Constrained { + meta: MetaConstrained { span, index: cnt.meta.index, }, diff --git a/compiler/src/passes/validate/constrain/struct.rs b/compiler/src/passes/validate/constrain/struct.rs index 69679a5..fd9c099 100644 --- a/compiler/src/passes/validate/constrain/struct.rs +++ b/compiler/src/passes/validate/constrain/struct.rs @@ -1,9 +1,9 @@ -use crate::passes::parse::{Meta, Span, Spanned, TypeDef}; +use crate::passes::parse::{Constrained, Span, Spanned, TypeDef}; use crate::passes::validate::constrain::expr; use crate::passes::validate::constrain::uncover_globals::{Env, EnvEntry}; use crate::passes::validate::error::TypeError; use crate::passes::validate::partial_type::PartialType; -use crate::passes::validate::{CMeta, ExprConstrained, ExprUniquified}; +use crate::passes::validate::{ExprConstrained, ExprUniquified, MetaConstrained}; use crate::utils::expect::expect; use crate::utils::gen_sym::UniqueSym; use std::collections::{HashMap, HashSet}; @@ -13,9 +13,9 @@ pub fn constrain_struct<'p>( span: Span, sym: Spanned>, fields: Vec<(Spanned<&'p str>, Spanned>)>, -) -> Result>, TypeError> { +) -> Result>, TypeError> { // Get the `EnvEntry` from the scope. - // This should exist after yeet, but could potentially not be a struct definition. + // This should exist after uniquify, but could potentially not be a struct definition. let EnvEntry::Def { def: TypeDef::Struct { fields: def_fields }, } = &env.scope[&sym.inner] @@ -80,8 +80,8 @@ pub fn constrain_struct<'p>( let index = env.uf.add(PartialType::Var { sym: sym.inner }); - Ok(Meta { - meta: CMeta { span, index }, + Ok(Constrained { + meta: MetaConstrained { span, index }, inner: ExprConstrained::Struct { sym, fields }, }) } diff --git a/compiler/src/passes/validate/constrain/unary_op.rs b/compiler/src/passes/validate/constrain/unary_op.rs index 55e8e36..f446d31 100644 --- a/compiler/src/passes/validate/constrain/unary_op.rs +++ b/compiler/src/passes/validate/constrain/unary_op.rs @@ -1,16 +1,16 @@ use crate::passes::parse::types::Type; -use crate::passes::parse::{Meta, Span, Spanned, UnaryOp}; +use crate::passes::parse::{Constrained, Span, Spanned, UnaryOp}; use crate::passes::validate::constrain::expr; use crate::passes::validate::constrain::uncover_globals::Env; use crate::passes::validate::error::TypeError; -use crate::passes::validate::{CMeta, ExprConstrained, ExprUniquified}; +use crate::passes::validate::{ExprConstrained, ExprUniquified, MetaConstrained}; pub fn constrain_unary_op<'p>( env: &mut Env<'_, 'p>, span: Span, op: UnaryOp, expr: Spanned>, -) -> Result>, TypeError> { +) -> Result>, TypeError> { let typ = match op { UnaryOp::Neg => Type::I64, UnaryOp::Not => Type::Bool, @@ -27,8 +27,8 @@ pub fn constrain_unary_op<'p>( } })?; - Ok(Meta { - meta: CMeta { + Ok(Constrained { + meta: MetaConstrained { span, index: expr.meta.index, }, diff --git a/compiler/src/passes/validate/constrain/var.rs b/compiler/src/passes/validate/constrain/var.rs index 031c7d6..eb1dba1 100644 --- a/compiler/src/passes/validate/constrain/var.rs +++ b/compiler/src/passes/validate/constrain/var.rs @@ -1,19 +1,19 @@ -use crate::passes::parse::{Meta, Span, Spanned}; +use crate::passes::parse::{Constrained, Span, Spanned}; use crate::passes::validate::constrain::uncover_globals::{Env, EnvEntry}; use crate::passes::validate::error::TypeError; -use crate::passes::validate::{CMeta, ExprConstrained}; +use crate::passes::validate::{ExprConstrained, MetaConstrained}; use crate::utils::gen_sym::UniqueSym; pub fn constrain_var<'p>( env: &Env<'_, 'p>, span: Span, sym: Spanned>, -) -> Result>, TypeError> { +) -> Result>, TypeError> { let EnvEntry::Type { typ, .. } = env.scope[&sym.inner] else { return Err(TypeError::SymbolShouldBeVariable { span }); }; - Ok(Meta { - meta: CMeta { span, index: typ }, + Ok(Constrained { + meta: MetaConstrained { span, index: typ }, inner: ExprConstrained::Var { sym }, }) } diff --git a/compiler/src/passes/validate/error.rs b/compiler/src/passes/validate/error.rs index f9b997f..eadb104 100644 --- a/compiler/src/passes/validate/error.rs +++ b/compiler/src/passes/validate/error.rs @@ -85,11 +85,26 @@ pub enum TypeError { #[label = "The type definition `{sym}` is not sized."] span: (usize, usize), }, - #[error("Integer out of bounds.")] - IntegerOutOfBounds { - #[label = "This number does not fit in type `{typ}`"] + #[error("Could not parse integer.")] + InvalidInteger { + #[label = "`{val}` is not a valid {typ}: {err}"] span: (usize, usize), + val: String, typ: &'static str, + err: String, + }, + #[error("Could not parse integer.")] + InvalidByteLit { + #[label = "`{val}` is not a valid {typ}"] + span: (usize, usize), + val: String, + typ: &'static str, + }, + #[error("Could not parse integer.")] + InvalidEscape { + #[label = "`{val}` is not an escape sequence (or has not been added to the compiler as an escape sequence!)"] + span: (usize, usize), + val: String, }, #[error("Integer ambiguous.")] IntegerAmbiguous { diff --git a/compiler/src/passes/validate/interpreter.rs b/compiler/src/passes/validate/interpreter.rs deleted file mode 100644 index 542c919..0000000 --- a/compiler/src/passes/validate/interpreter.rs +++ /dev/null @@ -1,225 +0,0 @@ -use crate::interpreter::{Val, IO}; -use crate::passes::parse::types::Type; -use crate::passes::parse::{BinaryOp, Def, Meta, UnaryOp}; -use crate::passes::validate::{ExprValidated, PrgValidated, TLit}; -use crate::utils::gen_sym::UniqueSym; -use crate::utils::push_map::PushMap; -use std::collections::HashMap; - -#[derive(Clone)] -pub enum ControlFlow<'p> { - Val(Val<'p>), - Break(Val<'p>), - Return(Val<'p>), - Continue, -} - -/// This macro unwraps values and bubbles up continues, breaks and returns. -macro_rules! b { - ($e: expr) => {{ - let e = $e; - match e { - ControlFlow::Val(val) => val, - ControlFlow::Break(_) | ControlFlow::Return(_) | ControlFlow::Continue => return e, - } - }}; -} - -impl<'p> PrgValidated<'p> { - pub fn interpret(&'p self, io: &mut impl IO) -> Val<'p> { - let std_iter = self - .std - .iter() - .map(|(_, &def)| (def, Val::StdlibFunction { sym: def.sym })); - - // Create a scope with all global definitions. - let mut scope = PushMap::from_iter( - self.defs - .iter() - .map(|(&sym, _)| (sym, Val::Function { sym })) - // Include the standard library in the scope. - .chain(std_iter), - ); - - // Interpret the program starting from the entry point. - self.interpret_fn(self.entry, Vec::new(), &mut scope, io) - } - - fn interpret_fn( - &'p self, - sym: UniqueSym<'p>, - args: Vec>, - scope: &mut PushMap, Val<'p>>, - io: &mut impl IO, - ) -> Val<'p> { - match &self.defs[&sym] { - Def::Fn { params, bdy, .. } => scope.push_iter( - params - .iter() - .zip(args.iter()) - .map(|(param, v)| (param.sym, v.clone())), - |scope| match self.interpret_expr(bdy, scope, io) { - ControlFlow::Return(val) | ControlFlow::Val(val) => val, - ControlFlow::Continue | ControlFlow::Break(_) => unreachable!(), - }, - ), - Def::TypeDef { .. } => unreachable!(), - } - } - - pub fn interpret_expr( - &'p self, - expr: &'p Meta, ExprValidated<'p>>, - scope: &mut PushMap, Val<'p>>, - io: &mut impl IO, - ) -> ControlFlow<'p> { - ControlFlow::Val(match &expr.inner { - ExprValidated::Lit { val, .. } => (*val).into(), - ExprValidated::Var { sym, .. } => scope[sym].clone(), - ExprValidated::BinaryOp { - op, - exprs: [lhs, rhs], - } => { - let lhs = b!(self.interpret_expr(lhs, scope, io)); - let mut rhs = || self.interpret_expr(rhs, scope, io); - match op { - BinaryOp::Add => Val::Int { - val: lhs.int() + b!(rhs()).int(), - }, - BinaryOp::Sub => Val::Int { - val: lhs.int() - b!(rhs()).int(), - }, - BinaryOp::Mul => Val::Int { - val: lhs.int() * b!(rhs()).int(), - }, - BinaryOp::Div => Val::Int { - val: lhs.int() / b!(rhs()).int(), - }, - BinaryOp::Mod => Val::Int { - val: lhs.int() % b!(rhs()).int(), - }, - BinaryOp::Xor => Val::Bool { - val: lhs.bool() ^ b!(rhs()).bool(), - }, - BinaryOp::GT => Val::Bool { - val: lhs.int() > b!(rhs()).int(), - }, - BinaryOp::GE => Val::Bool { - val: lhs.int() >= b!(rhs()).int(), - }, - BinaryOp::EQ => Val::Bool { - val: lhs == b!(rhs()), - }, - BinaryOp::LE => Val::Bool { - val: lhs.int() <= b!(rhs()).int(), - }, - BinaryOp::LT => Val::Bool { - val: lhs.int() < b!(rhs()).int(), - }, - BinaryOp::NE => Val::Bool { - val: lhs != b!(rhs()), - }, - BinaryOp::LAnd => { - // Short-circuit logical AND. - if !lhs.bool() { - return ControlFlow::Val(lhs); - } - b!(rhs()) - } - BinaryOp::LOr => { - // Short-circuit logical OR. - if lhs.bool() { - return ControlFlow::Val(lhs); - } - b!(rhs()) - } - } - } - ExprValidated::UnaryOp { op, expr } => { - let expr = b!(self.interpret_expr(expr, scope, io)); - match op { - UnaryOp::Neg => Val::Int { val: -expr.int() }, - UnaryOp::Not => Val::Bool { val: !expr.bool() }, - } - } - ExprValidated::Let { sym, bnd, bdy, .. } => { - let bnd = b!(self.interpret_expr(bnd, scope, io)); - b!(scope.push(*sym, bnd, |scope| self.interpret_expr(bdy, scope, io))) - } - ExprValidated::If { cnd, thn, els, .. } => { - if b!(self.interpret_expr(cnd, scope, io)).bool() { - b!(self.interpret_expr(thn, scope, io)) - } else { - b!(self.interpret_expr(els, scope, io)) - } - } - ExprValidated::Apply { fun, args, .. } => { - let fun = b!(self.interpret_expr(fun, scope, io)); - - let mut fn_args = Vec::new(); - for arg in args { - fn_args.push(b!(self.interpret_expr(arg, scope, io))); - } - - match fun { - Val::StdlibFunction { sym } => { - match sym { - "exit" => { - unreachable!("Validated programs should not have an explicit call to exit yet.") - } - "print" => { - let val = fn_args[0].clone(); - io.print(TLit::I64 { val: val.int() }); - val - } - "read" => io.read().into(), - unknown => unreachable!( - "Encountered an undefined standard library function '{unknown}'" - ), - } - } - Val::Function { sym } => self.interpret_fn(sym, fn_args, scope, io), - _ => unreachable!("The symbol did not refer to a function."), - } - } - ExprValidated::Loop { bdy, .. } => loop { - match self.interpret_expr(bdy, scope, io) { - ControlFlow::Return(val) => return ControlFlow::Return(val), - ControlFlow::Break(val) => return ControlFlow::Val(val), - ControlFlow::Continue | ControlFlow::Val(_) => {} - } - }, - ExprValidated::Break { bdy, .. } => { - return ControlFlow::Break(b!(self.interpret_expr(bdy, scope, io))) - } - ExprValidated::Seq { stmt, cnt, .. } => { - b!(self.interpret_expr(stmt, scope, io)); - b!(self.interpret_expr(cnt, scope, io)) - } - ExprValidated::Assign { sym, bnd, .. } => { - let bnd = b!(self.interpret_expr(bnd, scope, io)); - scope.0.insert(*sym, bnd); - Val::Unit - } - ExprValidated::Continue { .. } => return ControlFlow::Continue, - ExprValidated::Return { bdy, .. } => { - return ControlFlow::Return(b!(self.interpret_expr(bdy, scope, io))) - } - ExprValidated::Struct { fields, .. } => { - let mut field_values = HashMap::new(); - for (sym, field) in fields { - field_values.insert(*sym, b!(self.interpret_expr(field, scope, io))); - } - Val::StructInstance { - fields: field_values, - } - } - ExprValidated::AccessField { strct, field, .. } => { - let s = b!(self.interpret_expr(strct, scope, io)); - s.strct()[field].clone() - } - ExprValidated::Variant { .. } => todo!(), - ExprValidated::Switch { .. } => todo!(), - }) - } -} diff --git a/compiler/src/passes/validate/mod.rs b/compiler/src/passes/validate/mod.rs index 8d19a5c..e23531c 100644 --- a/compiler/src/passes/validate/mod.rs +++ b/compiler/src/passes/validate/mod.rs @@ -1,7 +1,6 @@ mod check_sized; mod constrain; pub mod error; -pub mod interpreter; pub mod partial_type; mod resolve; #[cfg(test)] @@ -10,16 +9,19 @@ mod uniquify; pub mod validate; use crate::passes::parse::types::Type; -use crate::passes::parse::{Def, Expr, Lit, Meta, Span, Spanned, Typed}; +use crate::passes::parse::{Constrained, Def, Expr, Lit, Span, Spanned, Typed}; use crate::passes::select::std_lib::Std; +use crate::passes::select::{Instr, VarArg}; use crate::utils::gen_sym::UniqueSym; use crate::utils::union_find::{UnionFind, UnionIndex}; use derive_more::Display; +use itertools::Itertools; use partial_type::PartialType; use std::collections::HashMap; use std::str::FromStr; -#[derive(Debug)] +#[derive(Debug, Display)] +#[display(fmt = "{}", r#"defs.values().format("\n")"#)] pub struct PrgValidated<'p> { pub defs: HashMap, DefValidated<'p>>, pub entry: UniqueSym<'p>, @@ -37,14 +39,16 @@ pub type DefValidated<'p> = Def, &'p str, Typed<'p, ExprValidated< pub type ExprValidated<'p> = Expr, &'p str, TLit, Type>>; pub type DefConstrained<'p> = - Def>, Spanned<&'p str>, Meta>>; -pub type ExprConstrained<'p> = Expr>, Spanned<&'p str>, Lit<'p>, CMeta>; + Def>, Spanned<&'p str>, Constrained>>; +pub type ExprConstrained<'p> = + Expr>, Spanned<&'p str>, Lit<'p>, MetaConstrained>; pub type DefUniquified<'p> = Def>, Spanned<&'p str>, Spanned>>; pub type ExprUniquified<'p> = Expr>, Spanned<&'p str>, Lit<'p>, Span>; +pub type InstrUniquified<'p> = Instr>>, Spanned>>; -pub struct CMeta { +pub struct MetaConstrained { pub span: Span, pub index: UnionIndex, } diff --git a/compiler/src/passes/validate/resolve.rs b/compiler/src/passes/validate/resolve.rs index 148c933..9e7b136 100644 --- a/compiler/src/passes/validate/resolve.rs +++ b/compiler/src/passes/validate/resolve.rs @@ -1,14 +1,17 @@ use crate::passes::parse::types::Type; -use crate::passes::parse::{Expr, Lit, Meta, Param, Spanned, TypeDef, Typed}; +use crate::passes::parse::{Constrained, Expr, Lit, Meta, Param, Span, Spanned, TypeDef, Typed}; +use crate::passes::select::{Instr, InstrSelected, VarArg}; use crate::passes::validate::error::TypeError; use crate::passes::validate::partial_type::PartialType; use crate::passes::validate::{ - CMeta, DefConstrained, DefValidated, ExprConstrained, ExprValidated, PrgConstrained, - PrgValidated, TLit, + DefConstrained, DefValidated, ExprConstrained, ExprValidated, PrgConstrained, PrgValidated, + TLit, }; use crate::utils::gen_sym::UniqueSym; use crate::utils::union_find::{UnionFind, UnionIndex}; +use crate::*; use functor_derive::Functor; +use std::num::ParseIntError; impl<'p> PrgConstrained<'p> { pub fn resolve(mut self) -> Result, TypeError> { @@ -103,8 +106,60 @@ fn partial_type_to_type<'p>( }) } +fn resolve_int_lit>( + original_val: &str, + span: Span, + from_radix: fn(&str, u32) -> Result, +) -> Result { + let mut val = original_val; + if val.ends_with("i64") || val.ends_with("u64") { + val = &val[..val.len() - 3]; + } + + let (base, val) = match val { + s if s.starts_with('b') => { + let mut s = s[1..].chars(); + + let int = match (s.next(), s.next(), s.next(), s.next(), s.next()) { + (Some('\''), Some(s), Some('\''), None, None) => T::from(s as u8), + (Some('\''), Some('\\'), Some(s), Some('\''), None) => { + let s = match s { + 'n' => '\n', + 'r' => '\r', + '\\' => '\\', + '"' => '"', + '\'' => '\'', + '0' => '\0', + s => { + return Err(TypeError::InvalidEscape { + span, + val: format!("\\{s}"), + }) + } + }; + T::from(s as u8) + } + _ => unreachable!("Congrats you made an invalid byte lit, plx tell us how"), + }; + + return Ok(int); + } + s if s.starts_with("0b") => (2, &s[2..]), + s if s.starts_with("0o") => (8, &s[2..]), + s if s.starts_with("0x") => (16, &s[2..]), + s => (10, s), + }; + + from_radix(&val.replace('_', ""), base).map_err(|error| TypeError::InvalidInteger { + span, + val: original_val.to_string(), + typ: "I64", + err: error.to_string(), + }) +} + fn resolve_expr<'p>( - expr: Meta>, + expr: Constrained>, uf: &mut UnionFind>, ) -> Result>, TypeError> { // Type of the expression, if `None` then type is still ambiguous. @@ -121,16 +176,10 @@ fn resolve_expr<'p>( } Some(typ) => match typ { Type::I64 => TLit::I64 { - val: val.parse().map_err(|_| TypeError::IntegerOutOfBounds { - span: expr.meta.span, - typ: "I64", - })?, + val: resolve_int_lit(val, expr.meta.span, i64::from_str_radix)?, }, Type::U64 => TLit::U64 { - val: val.parse().map_err(|_| TypeError::IntegerOutOfBounds { - span: expr.meta.span, - typ: "U64", - })?, + val: resolve_int_lit(val, expr.meta.span, u64::from_str_radix)?, }, _ => unreachable!(), }, @@ -213,6 +262,9 @@ fn resolve_expr<'p>( }, Expr::Variant { .. } => todo!(), Expr::Switch { .. } => todo!(), + ExprConstrained::Asm { instrs } => ExprValidated::Asm { + instrs: instrs.into_iter().map(resolve_instr).collect(), + }, }; Ok(Meta { @@ -220,3 +272,38 @@ fn resolve_expr<'p>( inner: expr, }) } + +pub fn resolve_instr<'p>( + instr: Instr>>, Spanned>>, +) -> InstrSelected<'p> { + let map = |arg: VarArg>>| match arg { + VarArg::Imm { val } => VarArg::Imm { val }, + VarArg::Reg { reg } => VarArg::Reg { reg }, + VarArg::Deref { reg, off } => VarArg::Deref { reg, off }, + VarArg::XVar { sym } => VarArg::XVar { sym: sym.inner }, + }; + + match instr { + Instr::Addq { src, dst } => addq!(map(src), map(dst)), + Instr::Subq { src, dst } => subq!(map(src), map(dst)), + Instr::Divq { divisor } => divq!(map(divisor)), + Instr::Mulq { src } => mulq!(map(src)), + Instr::Negq { dst } => negq!(map(dst)), + Instr::Movq { src, dst } => movq!(map(src), map(dst)), + Instr::Pushq { src } => pushq!(map(src)), + Instr::Popq { dst } => popq!(map(dst)), + Instr::Retq => retq!(), + Instr::Syscall { arity } => syscall!(arity), + Instr::Cmpq { src, dst } => cmpq!(map(src), map(dst)), + Instr::Andq { src, dst } => andq!(map(src), map(dst)), + Instr::Orq { src, dst } => orq!(map(src), map(dst)), + Instr::Xorq { src, dst } => xorq!(map(src), map(dst)), + Instr::Notq { dst } => notq!(map(dst)), + Instr::Setcc { cnd } => setcc!(cnd), + Instr::CallqDirect { .. } => todo!(), + Instr::Jmp { .. } => todo!(), + Instr::Jcc { .. } => todo!(), + Instr::LoadLbl { .. } => todo!(), + Instr::CallqIndirect { .. } => todo!(), + } +} diff --git a/compiler/src/passes/validate/tests.rs b/compiler/src/passes/validate/tests.rs index 5091f2c..6ee40f2 100644 --- a/compiler/src/passes/validate/tests.rs +++ b/compiler/src/passes/validate/tests.rs @@ -1,4 +1,3 @@ -use crate::interpreter::TestIO; use crate::passes::parse::parse::parse_program; use crate::utils::split_test::split_test; use derive_name::VariantName; @@ -6,19 +5,13 @@ use miette::{NamedSource, Report}; use test_each_file::test_each_file; fn validate([test]: [&str; 1], good: bool) { - let (input, expected_output, expected_return, expected_error) = split_test(test); + let (_input, _expected_output, _expected_return, expected_error) = split_test(test); assert_eq!(good, expected_error.is_none()); let result = parse_program(test).unwrap().validate(); match (result, expected_error) { - (Ok(program), None) => { - let mut io = TestIO::new(input); - let result = program.interpret(&mut io); - - assert_eq!(result, expected_return.into(), "Incorrect program result."); - assert_eq!(io.outputs(), &expected_output, "Incorrect program output."); - } + (Ok(_), None) => {} (Ok(_), Some(expected_error)) => { panic!("Expected validation to fail with: {expected_error}.") } diff --git a/compiler/src/passes/validate/uniquify/expr.rs b/compiler/src/passes/validate/uniquify/expr.rs index a509735..87c1668 100644 --- a/compiler/src/passes/validate/uniquify/expr.rs +++ b/compiler/src/passes/validate/uniquify/expr.rs @@ -1,10 +1,12 @@ -use crate::passes::parse::{Expr, ExprParsed, Meta, Spanned}; +use crate::passes::parse::{Expr, ExprParsed, InstrParsed, Meta, Spanned}; +use crate::passes::select::VarArg; use crate::passes::validate::error::TypeError; -use crate::passes::validate::uniquify::gen_spanned_sym; use crate::passes::validate::uniquify::r#type::uniquify_type; -use crate::passes::validate::{uniquify, ExprUniquified}; +use crate::passes::validate::uniquify::{gen_spanned_sym, try_get}; +use crate::passes::validate::{uniquify, ExprUniquified, InstrUniquified}; use crate::utils::gen_sym::UniqueSym; use crate::utils::push_map::PushMap; +use crate::*; pub fn uniquify_expr<'p>( expr: Spanned>, @@ -91,6 +93,12 @@ pub fn uniquify_expr<'p>( }, Expr::Variant { .. } => todo!(), Expr::Switch { .. } => todo!(), + ExprParsed::Asm { instrs } => ExprUniquified::Asm { + instrs: instrs + .into_iter() + .map(|instr| uniquify_instr(instr, scope)) + .collect::>()?, + }, }; Ok(Meta { @@ -98,3 +106,45 @@ pub fn uniquify_expr<'p>( meta: expr.meta, }) } + +fn uniquify_instr<'p>( + instr: InstrParsed<'p>, + scope: &PushMap<&'p str, UniqueSym<'p>>, +) -> Result, TypeError> { + let map = |arg: VarArg>| { + Ok(match arg { + VarArg::Imm { val } => VarArg::Imm { val }, + VarArg::Reg { reg } => VarArg::Reg { reg }, + VarArg::Deref { reg, off } => VarArg::Deref { reg, off }, + VarArg::XVar { sym } => VarArg::XVar { + sym: try_get(sym, scope)?, + }, + }) + }; + + let instr = match instr { + InstrParsed::Addq { src, dst } => addq!(map(src)?, map(dst)?), + InstrParsed::Subq { src, dst } => subq!(map(src)?, map(dst)?), + InstrParsed::Divq { divisor } => divq!(map(divisor)?), + InstrParsed::Mulq { src } => mulq!(map(src)?), + InstrParsed::Negq { dst } => negq!(map(dst)?), + InstrParsed::Movq { src, dst } => movq!(map(src)?, map(dst)?), + InstrParsed::Pushq { src } => pushq!(map(src)?), + InstrParsed::Popq { dst } => popq!(map(dst)?), + InstrParsed::Retq => retq!(), + InstrParsed::Syscall { arity } => syscall!(arity), + InstrParsed::Cmpq { src, dst } => cmpq!(map(src)?, map(dst)?), + InstrParsed::Andq { src, dst } => andq!(map(src)?, map(dst)?), + InstrParsed::Orq { src, dst } => orq!(map(src)?, map(dst)?), + InstrParsed::Xorq { src, dst } => xorq!(map(src)?, map(dst)?), + InstrParsed::Notq { dst } => notq!(map(dst)?), + InstrParsed::Setcc { cnd } => setcc!(cnd), + InstrParsed::CallqDirect { .. } => todo!(), + InstrParsed::Jmp { .. } => todo!(), + InstrParsed::Jcc { .. } => todo!(), + InstrParsed::LoadLbl { .. } => todo!(), + InstrParsed::CallqIndirect { .. } => todo!(), + }; + + Ok(instr) +} diff --git a/compiler/src/passes/validate/uniquify/fn.rs b/compiler/src/passes/validate/uniquify/fn.rs index 6bc4219..5262ec4 100644 --- a/compiler/src/passes/validate/uniquify/fn.rs +++ b/compiler/src/passes/validate/uniquify/fn.rs @@ -21,7 +21,7 @@ pub fn uniquify_fn<'p>( .iter() .map(|param| (param.sym.inner, gen_spanned_sym(param.sym.clone()).inner)); - // Push the parameters into scope and yeet the function. + // Push the parameters into scope and uniquify the function. scope.push_iter(iterator, |scope| { // Collect uniquified parameters. let params = params diff --git a/input.txt b/input.txt new file mode 100644 index 0000000..fba06e7 --- /dev/null +++ b/input.txt @@ -0,0 +1,1000 @@ +9cbncbxclbvkmfzdnldc +jjn1drdffhs +3six7 +38rgtnqqxtc +rxszdkkv3j8kjhbm +54nzzddht8ninelrkkseightseven6 +6fourmnvkgnthjtnjqkr +nineninezsstmkone4sjnlbldcrj4eight +3rdnfkvrx4twoqgeightqkgmn +6mlhponeglrzrvbsseven +five8spgxz8 +3three2dfourfour58 +9q +two45 +five8mpkpdfiveeightfourseven +93threefive3three92 +6jdzeightnineone1sclhzrnrjxfive +6nseven16lbztpbbzthree8five +eight6mntljvbfrmftsffchsix4 +4twosixvnhjl +8d +eightsevenvqvzlqxkbm6rqhsgqpnine7twonex +seven96 +ck8tv6 +2mcddqpnbxssmrc6 +zsfbkrjjqpbbone6six2 +fourone8hfrkxrr +7ninetphdpcx +9gcn9six4mfnjgtcdc +6onevsevenseven4three +ldssggpknine7 +twolltbfkhltwo892jngx +rsh1five3ninefourmfk +xtrh534nqxr +ggllvfjthreefcckbninekspmf6bnpkvt2 +kng16 +kqgthcleightsixeight96nine69 +nchcdqbsxmeighttwo65onevtqrznxmnl +chphzzqb6threeqhspbgkrn6 +8threevbqxl4 +99eight2hgdvcqdqxsnmkxctskvxxvqxjt +9qb95oneightsf +tfmpcthbdbnzhldnbj96 +9mzbn +twoseven9rmssixbvbjpsbjbh3two +7fcmfjjrtz5six4 +hpspsgtfxvxtmdsqcninelcjhfb2mhffpvxkdxdlvkqxnine +fivexrhxhtfivesevenone3d +jvc6twofourbgfgrthree9fourmhz +8xfvjdqlgfrlhzds4 +5271ninexfthree +fbm1jd3onenine +2jm7fchdklthreedzfg +6lgrrzfkj +eight6sixthree +eightsixzng8dxxnfbqdjkjt1two +fivesix92j +1kfrjjq +tjjthblvjfspbcshfivesix5fivesixf +nrlx5vqzcfive +doneightthtpmjlzhgpxdc18229twofive +6shtlqlbsxnine7snthree +oneonengtbjmlq4 +sixhxvzpshrhbbk3qdxrbq +23onezbvbrlnseven239 +one883mztbhfvqqq3srfptz +zxlglzxgkltc8four6kzmqf5eightstzlqcvxt +3four452jclt +sixmone3phrxxdninetwosix +3nine39 +578nine4 +442ffhxfrxb +shnnn3nqcgfbgpzzfrtchbseven5dk3 +ninenine5xrxrhcr +ninefourdlckvqzz6oneseven +fivetwoqcnqhbs9 +2sevenbgp8one +twohqbp7fnndbjpn +2nctn1pbbxmns +46fourhjppdlkr +cbfour3seven7dctlone +sevenjxbbplfour488trzv +5qqfb9twooneonethree +7zzlgxrfmlpxrbbnjt7five5mxhd4 +99vdnnzcdsqxgbonefoursevenjjjgc1 +4threeeighttkqdxnkrgblvnine5 +sevendtlm5onetwo +threeonejvxqb15nnjndprnn5cdxb9 +hkr1mtcmqqqsixxbfjnlqqlb +six2five3jkknzf5 +9xgkspdhrfivesix15zj +nsztwo4jxfzxbtrsjjnj8 +1twofive2sixqxxrjpgbdfive +41sixgmgxzdseveneightmnmxj3 +8qrvxvfourseven1 +lms268eight +7ninethreeksjz +qeightwobrfvcssthreeeight3167 +ninetwoone532fqhggszllnfcmfs6 +838lcrhzs2 +ninedjpteighthqrgvklln3cxbt +8lfxxeightone4526six +1lmvvbl +fvkxpcjm7tzzgkxfqoneeightone7nbxzskngd5 +twompjtfbt2 +twoeight4six +zmpml5six54fourfour +pndklmzmsseven9pncxgjrnine1three7pkjvv +88kggbsqnfjngqffkbp5fivetwo5 +1xrvkpqvspt +1eightxhpd5bsctj3three +twothreexjvsxklsxeight4twofour +tvqsngkbl7eightzxhxjmrhgs +5zkxqxx +7eightfour +3pjsrdp +fivermk75ninetghvdrltz +prfthbhsftpmfcn7ronesix +three9pvxmhkscqv +sixninetqhj5two +nsoneight3eight +nineqdprvhtqb4fbone98nineeight +ktfgnssfourdqktfflninephpntvmmm139 +qvxfzrdnineonenkpgxnnmqpsxrlcvjq5onehx +cq57sixeightwosvx +225vntfqdzqjgmtkdgbxrsr6three +56981stx6xdjkhnvgq +xksevendzeight2sixone +863 +onethreetwofive5sevenfdmrmczlqs +28qcdzgrbdkh +6two43four +fourseven4 +1jgone +qmpsfscxxtsix872 +6sixthreeeight +1bzxjhv2x +onecs5one7five +vr4phpdtmk3fivebtltwom +4onefourqqrpvxn1two +3sixsixjkfssevennpjfzhhsg1eightgj +seven1eightsevennspvrr9 +5fiveseven7ninepkzzj +xkjpxsqsfiveeightjgv2onelhdqjd2 +sone7rhbtvlttsix +vrc6five6 +twoseven7 +onetwo9onehndznine +5six6sixr +vfiveeight6hdxnpfktttstnvhjks46 +fiveninefour8zpptwoonefjzvmnhnvq +five53 +seventpfztfccgm8bxjsjxgmz +eighttwo4tktgpbj +6426 +jjkfournqbdqkddhlninekvd56bdshtbvn +onefive1onehkhsix +7sevenm9twofourqp +26sevenseven3twonelq +46threethree9seven +9pnzsix648kgngnpxttzlqlmnine +seven7onenine +two3mtklsvrrbthtxsrgrtwo +gkpfrq4vfddfqxzppmvthree733 +fq2qbmone +threecvone7eightsevenxnmfbtp +fivetmbnqlchhtqmbcsssvxjzvlxdvznlbfive7 +fvqzlcp5 +qhgjmzntonefnlxfqqrxx5three7twofr +rczhlkcqpfkgcjmcggztjlqsgxmdxstwo88bdxmqjvlfl +npqxcdnhltbjmpjvdvfvvthreennsnbfljq2 +four9gl +gjrf94three14 +5xrpnzlneightsevenlkkltrpsqgzbzffpjhrkc +twozmxqcsgkvjtkshttglxcx234xtzhht5 +fournine3rkxhdvjlzfour +4onesnfive1sixeightwof +threeone6sjnfive +4ninefournrphscbjjc +6xsvn6twofourseven4four +khbqhgqpph5krdsrs35kfbnqdbb6 +7kdn5 +4scmksvgl8twoltonetwofivejl +mmmkthreedmflzpmxqtccfz7 +lbkdqjfcf58hpdtgjnnvhsixfive +chfbqldb52qkpzlkx +98gbxklnbcb +ghqrqplpone2ljzseven9 +fxqfour2njrxrz79 +rcbcmlvqc57btvhphxqbxvxxkjtlcvjffgxsdtjfb +crgvfkklfivesixsevenngm52jl +8rhvh5gdvx24 +qcnlc62 +pmcdbsvzgtqgczdvzln6 +fivernine29 +skslcfqkgk47four +9twonine5sevenone +ccltdxdfksnninekpzccsvtgrtseighteight9pgvlbkvs +1nine7dqxdgknlz75 +fourninebbpthvqntf6759foursix +qsvpn4mvhxgmzsevenpnkmtzrg +82fqxx6oneg +sixhnrk34 +nine5five6dkqxmnc +qhdzhhqfbfjnfmglvxctkjm59 +1threeeightbchlm +1drjnqoneninennhqt +56six6two76bs +7onefoursixhkhpdns +4threectphcxfmmksdcsvhgfx +5jsqbninelphjdmsnjl9eightql +5vkkmjkrxpntmgfkfxmg4 +6sjxrtfnjkthree +pmeight418 +vgldhpczvgr2twofour784 +5ndkknqfjjxfiveddfiveb +five1tmv +four37fourninethree34 +rcnvxqrsevenjttxd9fiverqzblpnrcjhbc4 +tdhvhhjxbbmjnls1vn8six +two4three9 +2ckvlpznbqbqblqbr +6nineseven8zfqfptcjxtfmmkqpj +2jdxnqttrjvhmbbxqqmeight +zloneightjzsxtsxhbgtwokdfour2pqmfkkqksxlfv +8sixfive3 +qxgzh3gdjmdqlpdnfgdxvbblpnqtsevenseveneighthvmqqdmr +8jjmbqnfive3bbxdzctxxn9five +fivethree38four +pdzbkmcvhbvfivexv5five5hzvvg4 +4eightkmsrlqsfcnmzvprdf4rcxxqtvpqcfjfptmk +twortrlbqqkrnqgxhgseven5 +8sevenltzbjsfjxdkdjncm +1nineqqbjsoneeightmzvfn +threeeightsevenfour3jhkcthree +nine265sixthree +nnmbqhf6three +4one5fivefivehbfktxgrdkdrgp +twothreetwoseventwo8 +bkggrdbngtjfmhone6fmvfzpjldzb +bpbg4tjqnine56zklbtkzlrs3 +4twokmxgqbgqgsseven8oneseven +hvdv74ninej +dfknvxfmczqrgdbqsph823 +3twoeightnine +foursixeightbfhlczrpjfxfive7two +fcpkrvmtzxkrfsmqcbzeight7gfourfbkthreeoneightvm +four18vgkmj2gxmtxsbnvxthree +415threecshnzmmx +sevenseven44lkfourgmqtrs +jnzsqsgznvcnjjfbblkteight8 +497three5eight8d +88onethreexzsbgprp1 +eightszseven9htqlxb +1lqqgzdrxt4qlqklftlsqzm +rqbjqpfhzfeight6oneonelllcmbrdxqhmttptg +fpqxqfourthreefqgdsmhjfk2rmb4 +qnshr9threeonefour +xshsbkqxpltjsd2fivexvtrmnlpvtwo +446 +nine2pvn12five7 +fourfourninemvfvztpkbb9nine +3fhtlpone7965four +qkpdgtrfprttrzc69 +1mfsltcsxvlcxfzdh3 +fgjfltjcsnps8three +tbeightdsdtzmncv5pdcsk +rsjvlhxtn7six +7mdtl +four3three6sgxdtdmtnfive3 +3eightninenineeightlxtqtspmklrxbknftrbh +onenine92sixrnine6tggjndsrfd +oneqdfhrfzlteighttftvfcrmmzz43lbmlbg +35bthreernjskthzrs1two +threesixeight7rjpcxnzmzfgngjpkk6eight +six9ninerxfnldpsevenone1threenine +5sixhjzkknthreenxklxbvgxfouroneseven +1zkx93two9sixseven8 +sixtwocdnqdn2vdbnblzlvffmrninetzdtdpjjsrsix +twoeighteight437nrscj +six31vtcphkpltgbcprdhvdfivefoureightwonvh +8pz +hzrggpcmpnqzzgnxjfqrlllgnqks496five +7glkjtgbsnqhnplzcp9rllkkznffnznngthree4seven +one7fivetwo45 +4nxzxtnhb5foureighttwo +threeone56lgzgdklhtwo +rjnmxbflvn5oneightpj +fs7tpvvf +fhfkcx1hnzzjjh +6onexqrfvldeightfourkcvpngj +1threeeightxxcnjfl3zbxfgmfsx +9327six +three5p86sixcxdsjjvn9eight +four1cfrxsdgnjtwo +rxjmtgsixone87 +49sevennbgqf +sixone42 +three3jblhchr +hlroneight3hsjhkl +sgtrvtcq22 +8seven985 +qrhnjnkstwo4fourthree +8phnkkdrghbmtql +two9sixncgqpsseventhreefourthree +one3six3 +1nine4rtvxxgddzhf2 +2cfp84 +khgkvntthree2 +vsznj15ninehhlfiveone +mmrxqjbjmrtwo7 +oneone7ninekmzgq +eightone84lzktckgrbkzjzkqqxlgqn +btzdblrrpfxljsix69 +82sixseven +two5nineseveneight2eighttlmvfkf +vlbnccpl1 +four5gsbz7 +418ncqrk651 +bkbbncm9eight6eightnine +3sksvljxpkz4vcxsdnztfxeight8 +fivecfrlnh51glx2dsjqseven +hnflgpth64threefour +mjxthlr1six4tdplzrnhklnz +ddrkrnssxtlkhbrjvkxpb39 +2tfoureightgvnl +79six +sixvktfvkmv5xhfgnine +86szmzzfntxxltmkffnczrjvthreethreethree4 +threethree1sixdkmjpmxtwoonezktwo +tlp189jnmskmcnkhvmn65 +41nqvvc +4xrlpc7c82oneightk +six1threecxxtttdthreeeightjc +gbhkzvnrxfourndtfc6 +fivesevengjmnbpdvcdeight6vj6 +vfjdp8 +8dfrttfrtrtfour35 +887 +714eight1tdnt29 +xbmhsbn3 +gjktjcn3kf +1327five1eighttwobfhtqcjjms +pc615 +one1rthreefive +fcjsktwo9 +lhgqvtxcntrljlnhdllthree1 +5xxmtwo1dkjpchmzfz9mndbrcbzkh6 +6zxdxzxt62three37 +cveightwo6 +ninezdvzbkggh4one63 +three7fivesix +3vcdrd881vlmglkfone +2ninenjcvq4 +5jltkfhl5onehthree3 +8eight4rgfrhseven +6vqjgxltqd18two7onelkmc +xffld1jjlqxfz7xjvxfspzrkztsdtsone +9szzhnfpssevenoneeightfour42two +four91mfnrxkcckd6vbhtxvvmzhpqqrkzxv +eight2three6ksfive +94eightslkpbf5zc5tm +4twottthree91sevenone +kjgfgq226fiveseven2 +kjrrgfmthree36ck +sevensixfour24one8 +dqj8eightthreeqzcz8 +xtwo5pcdl3eight7 +one131xnghfczdpvsczkrxhjt3 +j4qqx +hncrcntcmmpkn6sixzvjzkgr7 +9nnzltv7mclrhhgnmq2nine2 +6sixeight9 +5jgnhjmdc6xsdsl1five +onerdtqzhpbdflxvrhnpjqdqzn39 +rcroneighttvnfcngrfblvmeight7sevenonevknxtpfour3 +mcfjn569 +44 +84six +nineeight3 +one916 +eightfourseven5four +nine1gpmqdxkzmr6six +two4fouronesevensix1nscgll1 +eightthree44vxbjmvbpfleightvgbjxcgrjonesix +6vmgscbtpsnkktmbdjpmmlv +1one1 +ttwo9sevenninetwothreephpfrtjztn +cxlzcpdd32dhvvcsgdcc49rhhrqpkxtwo +8fourkkrbjsqt +rxndgg4lhkcphmtmjvtqkc9qsevenpcmftwo +jpgrzfvdx3858fourthreesix7 +7krxc9mjhgtonetqd +eight2qhspjfm +gsxplbone2pffltsp4two +hqtwonethree939zknfxrqjpn4x +eightsevenone4sevenmhxbbqrtwopgvvgn +6three5tlffrcdszg384 +frqhjmqntneightxgtldk9qpdnncxgnmqzbzkqsxz5 +vsf42twolzmftmbbb346 +7eight47 +9four94kpmvbtblbxthreefourone +2eightctvgglxjgxvghmsbbfivedbnn +qxkvzrpkj79lkqtt +jfjgzhrkpc77nine4nine8 +prqrpms3ninefiveqnhtngpbsix +gqxbtbtvdoneznqsfpf9threelmhn +four7sixjrrpqdjtbsfivetvcngclhrprmhftwo +9sevenntmbflxvf1eight3nine9four +twoqgxklrmdzntrbtvcfsix6 +zzt8kt2 +ninethddninexszntlm9jnqnbpb4 +lcsfourxdsk9297 +f2v96nqfour +2tqsdl +7onethreegeight +47q667gtgj2sm +8four7sevenfseveneightninettlqkc +5ppkgf +61threetwohxlxzbrlfjk +6fcfbvhpzgl57 +five2tjkc5three5onepqlgv7 +five925ltvrgpzm +jjstljfs47threebcg +37seven +8three47eight2pqxcvbn9 +threeeightrdndbtfpbszscbqzlhtq84 +xvqtwoeight4twoone2seven +fourpcqvbcfvz8onefivev7xvtxdpbnone +3956fivethree +sevenlmsksix55 +eightnine17 +5hgqmlb +eight91pfnngffzl8dxvlnnninektlmqq6 +5one8two9hh7 +qfourlvclhf8four +9cfgrkdmeight +foursevennvxcmjzzpjtwoone4onezlht +njpjsceighthqqeight33two8ppjf +four5djlmjfive99eightonefour +jcf2s6gmzrnjrjkvfkgone6kbhjc +threeninedbskzbqlb8jpnine7 +onekbddzcdx5eighttwo3 +3xms +mmvvxmpthreesixone96 +3djjfsixtwo7threeeight1 +339 +3seven6 +six48nine533oneightv +dkvvxngfrvhktzx3sixvqtkpqztzs49 +1three5ldzvllvqeight +9hn8mdcpvzqlzctwo922 +164k7 +sixsix4c1tnkmzkqconejvdkcdfntpfkpsdm +31qvdnvdqthreetwofour8two +htwone6fkzkrdfcpqlnxlone +2875 +6eighthj +7lcchpx3x96fivefoursix +kone1ptnkjhks65sixrsseight +23sixninefour5 +xdnzfbpq2 +hbznxkpmktwo3six6sevenfourfive +tbbcrzdtfive42eightsixfourthree +5mjgvsrrdlnrvlr6ninevlzrksfj2five +cftlmrsptdjtsndl2eight +snmkjqprhk57ninetm383six +krxqxdgdp7fkgtmdtbx11fivenqncrnhcone +3sixmcfnf +7jkfgvjlbkfvlmf9 +sixgljmbvxoneseveneightpfvvnl8 +523eight6 +seventwoone3rnxghtb9sixbkqch +5hjzzvkbls9qrcqtrrk19hbrhszxfive +8kbhdp483four +seven7five1 +dklfjdmzc15zchbgmbqkgzgkn1sixsixeightwovz +8sixqbpgkppjktwooneninethree +seven16 +sdqvhfour2glrqkmj5fiveonegzqsdtgqskjcmgg +twofiveoneseven1rqjvrrxtwonen +rmgdlx9cskhdjlmtwobvpnjrcxbfftd5 +93mcfoursix4 +gst66jphtlbtngqdqdnonefoursevenseven +seventwotntksixjrczhp557 +svjgzfkbj3five +threeninetgqdgnmr1xxnxfxlninertwo +jjhpnine33hjnine +twojgcp9 +vgkbmkzrbbvlhv267 +fournine1eightfourcdvvdgkmkndbkrheightone +1onefourthreecqbffjdvtzbxdc +mmgvhrmftnmtdkqgf29 +7qnsfqj3one +qhr933three5 +pqblftsix3 +one7sgsqvszkd6ztwovcrmqbjthdthree +1qgqtnfive487czvxsjk +37jrggmdknnine +3sevenfour7sblhjqg9fourgchqkdg +nfxdxlssvgxxvzgksrkxqtwoffpphxdqjzh9seven +4qjhllxzdb8sgbgksxbblsmftd +twofivegddmhcplptf13five +twosix55 +3zcftpvkbmzpffpjrfouroneseven +6vpsixhprqhzthree +eight7ninemsmfbnqkzmqfd +nbrtxpkpb1733 +nine33jbrkqlnf1 +14lgl +eightjjvclfxp9 +fivetnmksk8cp6 +dpxcqhbkvlhccb1fntmcrjjgccnszct +lbjppg3hrgpzstfqgbcrzbmn66two +dxbxkzmpzzthreestcvtvhftgzctnvnshzgqtbgxlrqkthreefgxdrfmm7 +31lklpfour +nineonezcmrfppsvbg7seven316 +9cxgcgjsd8ctgbh7 +9vdnine4five +2jfkrtdxvzq +qgzgclpt8jltqzkpvddtm +614two +pjb92two5sevenfkb6three +twosix766five71 +86vbnpsixthreetwonevng +onel2one +4ninesevenpgbqgpfgkfzdsixmmfive4 +7xgcmqfqmk7twothreeninepdt5 +sdonefour77one +fourmfhmsznxffzpgdonegck6nine6 +rxjspvttx6nine1knbsl +pljd3fourone8 +8onevhpeightz94seven +jtmdhqjn5eight +onefshb1sixone2b +seventwo4gnrsrpnfppseven2 +147fourfour +9hfjjmgrzntssjpxcvbzpvmqzgsd54twonine +mprnmlhxsdtntbknine1 +98onefivethreesix +gd6ninejhsrhsevenksvkcone5 +fourd9xkcsrncpdsbqhcqg34twoneb +pbx74kfivefourvmslqvfbml +dmxmpl71 +594shxctmq6qkmnbrm +5qrhd8jmmthjkdzhrxf6two +four4six4three +hlrsmmjjvshzztxrnznmseveneightfive9sjddhlfvftbtd +fivesbcklfdrvz4pcxsvdcqpeightgj1tqkfnv8 +tthree4one9 +nine4sevensixone +eight5threethreesevendrctkthree2 +4ngzcbtwo451bpvbtqdvk +9two1tdjdlrflshfourjlkdctfp +sevenseven6132sxqfspmvxjfvh +fourfour28562 +seven6gsevenrtcsldgztqthree +sixtfmvfnkqxlp3sfsmdlfgh5nine +tjeightwornmddbpcsckjrzdtvzrxfivef4 +gcv42jfcdftseven1 +166eight73 +5tlcxlscsgmrznhlgfgkqdpksjllz +2one7twofivesevenqxrdvbczfmt +1bdqfdmtfrtx2svdltfzknnqssvxvsdkzvd3six +rzztvfourfive754fournine +71vfsixrjhdqqj +one63csbsvkqmkjt +3seven4fourthreeseven36 +bmkr6threefour +5fmr6four6mxqj +3five1one93 +5nrtfrdhfv4mkkhnine4 +mklgxhrpp3two +hpfrfpkddsdqfgbhtgfourthreetvdvjrfr5 +five6ttmzjcs +tlbzoneninesixsevenpddssz2 +prceightwo1 +vvktl7eightltmtpfcrkndtkglhndhvrbtfv6tjgrzqv2 +sjpzgmvmddttrcmnvzseventwocgzc4 +seventvxrsbbbzrkfbkbcrbdsdtv456 +ttwone12threesix +85nine +hdgxncxv83 +58sqpgnine +65mpmptlg +threefourxvmdkhlqd2three8xfphd8 +rdptwo22threergcfdntchfqbsseven +98jbmmhznxvrqkdxseven1onem9 +eighttwo1twothree3sixjbjqgzx9 +9klnfhhx +7three7five +jlvrcqflvfivenine7cnx +fourrr2ktmdqhsteightwot +bkjgtqxg9fivenrsktmkmxcfbg4 +eightjj95six +6threeonebmpqrnqgdqlkkqc +seven5bbzlsnvjchbjgxh +lbsptf1threefour9 +37threeeightcgfxdonebhkrdnfive4 +379263 +24vmbb +9v +1seven15 +slxpsix3threeeight64jlrnkmkrqr +ninedjlvkxqh24 +sneightpssteightpdfzqjcjgsmseven7one +sevenninehf6bxjtfntwo +four2cknjgdkbqdvl4 +qxrzdhsjjkmtggt42 +tgsjddrgtkthreefzlvgrsix32onetwo +3zldxonenrghfnhhmptbpgcl973 +8ckzrgrzbone4sixdspxtwo +xvxzprh1mbpspkrv +lgchfzs6sixthreeksix3khvhldq +sixonefour2mxslvpdhk7 +6sixsixbghxppnztxfive +jpdbbeight57seven +2onethree61jtnjjcq +63lfsznprjddqpfourcrkthree +ninepqmvc2xtwotcjcfcvht +1bcrpone +pntsrhj725xblt2seven +12nine1 +8twooneseven22 +hfrbxdlfzmjvdslseventhmtqzbtcmsqbeight5fiveqlglhclrh +three6htff5nlzslroneightxm +hzcdc6threeseveneightmckdvg4 +tgpjgvlq6nineseven8six2foureightwozbz +three83bbbfsg +75bpmqvdseventwo9four +2nine8vjdcgvfns +rjndbbfmppgd67eight3hztlrqv6 +onefourfour6 +two5six1 +954ninetghsfnnine12cmp +6one8nfzstqlfive +seven1sevenjrslmhrfivexsk +nineqjgmh1onekqblsvjkdn2five +eight94mzsevenvtkkv1cdqgv +xsix4sixnine +3pbxzxrsd +77four9 +8zcthreethreeninettdb +c4eightsevenxqnhvclfour +gxbkjmfhhzlzrbzcmjmmnqxlsevenninefour4jcbgttq +9zptjlhzkls1fdfzxvpssleightsdcmfour +8oneone9one +1threefive4zkslknvmr +9seven4jninesix834 +sixdpgkdnnt4kjzgrjtcqlqq +1qmcgnclrdqrc +5xhlzgzpzbgzjcsthreefive +mrgcpmnnsixeightfrtjfourjlbcdt3 +129twokqmtmdj +fourskzt382mnznt17eightwocx +9hnx914mtsnhnxtgn6 +81psmkhzk3fiveltthree +lhbtwonetwo3dvrtmpjxk +x859zhjsbzjpjrtrp6 +73mqrrpdrs +43d73rbddvrz +rnmrrcndm6 +x6rrnsmpgqmp3three1 +pbtnndfj2onetrvmvcftf4 +four6rfoursixfour5three +69nine2szkcnjbmcvjtbjbgnmssghfdlninetwo +one92phhclzfsfxqg +hzc2 +fivethree8threedttx +pjdkcdbxt5 +trgggsgx92six +9ninethreeknlvmb +eighttbbxhxcmrjqxkbjrdcjv5nineeighttffour +9cghvbkpmkt +sevenzhlcljtqcntthp7rr2 +3fxgjqpbp +7nineqxqvhgf1plh4 +2fivethreeeightsixfiveqz +twoeight8 +fvvjphpmqffqjchvtfivemseven8jmlrk6troneightvf +three46 +tktkqsqskrpxl7thfrqnpdkzcjxvfmmbfourone +9twosxgk +g3scthxjb33nppgcone +mxmr17fvcgh4nine +vz82 +2threefivenineeightqmjlcsbqrcg4 +xptfourthree7mcfeight +bdthlfbqbczmlh56 +onesixlpqxqlkxxkqldhd5twop36 +seventvptphkhhjvslhtphntwo2cfnn358 +shjlthfxpfive4rzm6eight +three565nine4 +xfjoneightsix47sevendvtxtfive6 +9717one +rkmhlxzpxgfourthree7qhh69pfds +3threepvknzvbmbrvljtcx13three2 +1zbhchxzlccgfvbdtlmfr +28hqpxjvxcmnqtxhhgninethree +eight3phxhpsxnz6nvgqznb9h9 +2klvscxmt94fourthreekgmqgjbhnzrxtwo +7eighthmbvjlfseven5 +k45ls +1qkvvbvsixsix8threeshqknine +4928plcrzs6 +gsskhbkmfzq8 +6crvcnine3six7eight1six +fourfive311 +lqdjctn82sevenjdm6twoonesix +8three8pqhmjjc +hgn6mnpmdmcpzceighteightnxkvjjfrninejmpkrfzcgv3 +ln7pqvg3sevenkqtztcpjrxeight +dqbrjj9fourseven +4sevenone16 +9cxtchrmsd3fflmkdzdgp +6349 +sixsevenlqgzcsvnd1 +kjfcbgeightthreejvqgk874 +2vkrvzpmfv4eightwob +mhjrqbvlmgsixthree74 +fivekldbzshd37 +seven11fourcgvnqr7four +fztzttvmx4vtwoone288 +5fivevlbqczq1 +twoncljq5 +jljz388kklzbronetmmf +92twonmjmkhgqfhx66 +2rh9nine14seven98 +7hgzjblbxpkltmjmlpscd4svtwo1two +xcchzd8fourhkrstwo9three +lxgxlsdkvcfxsclj4cqzjgjgvtmkjlhxfnmfc3 +86hzhn8eight6 +qhsevenvpg4hzffldbrvpxxpthreeqpvvdndv +five6eight15 +mhl8rcdkxx8 +3sevenv +one131eight86fourk +7hsnjrnlcnb8twosevennineeight1 +2khhfivejgbknv65 +8lmfd298dlnmszrrfive +one5ppczgjzzsix +nine6fchgqxsdjhqtwoqpqffhn6ncjxx +bfivehtndrmm62 +829fourbjpmbfqkqgsixtwo +xvcdsix3five +sixhqthreeninethree7 +gpsdvj3zpztgcndvcxz12 +seven3fglvdzxzcqfive7four9five +3five4gzgjbpptwo +nine44four8 +4143two +fourpcjhfjrxdhvzf2dkmszvtjx +ninecrxdkznbg8p72nine2 +sevenrbzhktn7five7sixvklcksmb +rndrrfhvl4zscdjmxcbrfvrxxjxcc1jjfsjntxzqp6eight +sbnkdhrjrthreecvxqrfxccdpmqsix5jjpkrxfqlnxbzlzskcfgr +two4xdtsdjtneight +teightwo5k4eightmtheight +eightzlc6lgtgxsvckm56 +qzrzhnchttjnjxjnine4seven611 +ninefiveninefqtd37eighthzghljhhv9 +bcbbbneight1rqjtnnzrv +ninegldjhplfthreetnqcbrllpvjtlthn9xkbqkfourthree +nineeight4 +4zvghdrbl7onemsbffzkjrb265 +lthreesix7two +vvkfivefmkdsbst66 +3rxdgrfgdtwofourseven +onegzbdndnqkkckpsh1vftnlhnqjcvrm4 +gpjld3lcvtzckpqrdghlpz +3ngvnzone3 +sixfive8eightnine +tscdrcsvztsnktrj495sg +9twojbzsqv64smmhbqc +8ninefour +ttlrjjxm41four +qtwonekvlm9tnmzlvpzzs1 +twotwo1fiveninelcl +8rfvffourtwotwosixpqmthjsrgj3 +nineeight2seven +n9 +two7fivenrgdqshs +nine5foureightwom +4tworvqpsxzhn +18ghsmdrjlq8rfpmvqtgcl3fournine +8three7qfskg +nine82 +41zb5 +5cdpnqzfbthree +247pgfzbhpbkxsk88three +xsnjsqrrpck8two7 +qmlbtk79vqnzqv +pkcmsixjhqtfzvjv23 +1nine1lnzeightlpxpssdgtsgbvqmftmnv2 +threefourhkfckgvqn721 +onethreegzninenineone31 +kttwo4gcpxpvrp +4six4632 +7vmkqlqfbbzkksix3 +jsixpckt8six142nine +two753six22f +twotwokjgftjhnhp6onefour7hqzlcbkz8 +ghmjbfv49pls68 +three1889twogbcjkkzc6seven +twothreethreevkmlsplbgninenine4six +seven7vhjqfmklxsvc +pxnxlqg91 +fzrbbvfsqrgcgsjjrd5 +skcm59mfgkjvcffourhqzsbpt12five +three9eightvfksbfcf18vlvgsixnine +oneonefour9eight +zjrxbvxtjbqtqqeight75rf +flk6gjsixtwo74five +4jncmngfmqxs1four9 +1fivefivefournine58 +274 +gjjzjfplnninetwofourlgpjfpnkhsixthree3 +4jnthree75tdxnhpm7bv +three82jgvgjthreev +sevenfour8 +threeqfrhnkfkj2jhzllpn +one859one +8xtnbtqqh41fourmgxzpdv957 +37126 +zzsixsevenfrszzvchj9hxhdcxmxqqckclmfm +sixsix79nine +s2mkfdqmcznztqtgddtwo +hpvpdczjvkf8two +6two73hdnbrkgblgfiveblgzksljjjskfpthree +7kndzrhvcnstgfxjlff9twoninervrknsffmfzmdhtth +three81kqcfplcf7 +fivegtfbnspddkeightmmv4bzksixeighteight +596ninefqrfvpfs +sfpzkhxqp7 +kqd5 +1sevenljlmfh6 +75sevenxpzv8ngffsnm8 +rxqnshcskglgkrlhzone4 +1threespckmrpdnxfoursgqsevennnrhjkcrlbnine +hffpvhxzznnkg86one61two +7bqm5sevenkdnmqpqfvvtbsct +3sevensixnineone5nine +two1five23dgmqsgzftflfmjseven +jzmltz1jtlnsbgtsix +nine1sevenjltnln8fivenine +svmzvnr8tfxqlxnc1 +sixrmcsqtphlk5 +fourjnpshskvmsqscq8threethree8six +3threetrqfhnbtsbckkvf +5fcmhxjhpr75zkcbqgltq93zcdm +eightlzjjtzk3xlbvgnsfoureight8 +hrrxxnj6nine1two +zbnj6txxhqgdtq21fvcjxvkkrsrrdmkrmvtbjhs +threex86three +onesevenone6four6twogjtqp +92onethree52 +qgzcfour9eightbsrseven5 +rnrlmtqdqlb6 +sixeightzgbf788five +sixgnlbxrfrc1jseightsixeight +nzxgkcfive44tkblnn58jbmdg +49fivefgsk +one8zqztmlmss6fiveznccsnnnzqk +6fivesevenfiveqrvlmdjvjxzvsix +sxdgthcx9 +93qlthreefivezmk +xvsrgcsqbsqhdnqhbmcgn39ninesixszgtlslqb5 +7sevenfourrpfqkkrm5 +vcmfthdpkzsjpjt5six +zqtrk35lvsg1jkdfour5 +7threegqzhcnxcmcrpgjkttbmxq5 +2four25seveneight9 +3h +xcspfcvfive37three7three +nbsnvhg197csnine8gfttndsf +xzztbq981vfbcmrv6ddrhrnprrnj +5kv672fpd +two2nine4fourl9foursix +ghngfb2four67qsfhpsb +4ninetwobcr49sixfive5 +2threethree7onefour4 +824zpsjjm +four6twosevenqbmzone4fourph +ctvmbrvksix18kz +eightfive9twokrpsltfhkjkjkdqlszzs4glxxtgktsx +2two7hxrncxqeight +6121two46 +fivefourthreefivemkmb5fourmqfhmlrxmm +fiveonefivevbtbzrone9tsix +oneonethree5sgs9 +cdvrrsm53qfvdzhvnlprvfjcx +jtflvmkdnineone62klbvbzzltscsvmbsbp7 +3mjmrxl3fourqfqhrknblcthreelfmm +seven1vjlvfmh5flmnhfzsixfbdsjdkxqhvmj +sixfourninefiveoneklpsgrtthree5zcpxs +eight73pjvcbb +gdgbgzdlgjt9hklsxglkrtwo1nchtbvltmxnzn +87sixkfmrdmdx +jrpjxhqkleightttpkqqrtzvb5threerx +xdrjhrrnmnblnbtone8one +618gnvslkkmm8 +tqrctvjjdone39 +6zs9lninekjbrm1twosix +23mxsmvfthreefourkhvxmqg +onemkhn4seven3mtphqnqb +one5foursevenfivezpgvjmdhl +2mskpg1threevdjjlzbrbsevengfgmqvd +1hrrxgxgzhj2eightfive +rlleightwo2vfq +2gss +4lndrzf7vcfffcb +four5one +three5lxddbbpccp6kkpgxm +sixcbqfivesixzfgbzszq3tqcfqk +8xpsixrcfoursixfkxh +rqrtwojhcfive89fourthree +5ninesixknjmlcjmnsmv2 +18zgjpthfsix2ncmdfkcsq3 +tsxbfgzhjr55seventhreesxnnjhninefive +three61rqljxqdbch +threelhqzdvxnsgkmthmbd7 +54two5dnhx5 +btc4ggbfmtfscgzfourtwothreeqptmrn +rvqkbtvg331nine +2fourkrgdhvkgzrhvqonefive +7twoxkcpbfxthree +bmzczfl54oneeightqslllrcjkm +sbjgbkn89 +9rpq83seven +fiveqmqklgxkqrrckkjqsxkkfjzzdml87seven +74one27 +45four3 +pbhcrscll27five5 +7bbmjqlxv1dbdsvsc +sevenrhqt9sixthreethree +4sr8five +kc1 +1eightlkjrvmpt4sixtwo +1oneddzcqhmgd6 +feight6tmqcj4four +five9mj7mgjqfhshvhxskjsix8 +six99eightg42 +vfhvmxnvhxnonekrhfzqpseven27 +866three +eight5one118 +clqlseveneight8dbrqdlcf7 +9mnlgvcfmlrsrxfp7six +8mzlgnnqddptwoone +3bqckbeighteightthreesevensltsixf +eightfive37688eighteightwof +sixfour2one4zzqtdp +mkx1twofourxseven67 +1jhcktvgninethreexdmfqdbjxltzj7rhkdbvf3 +two16 +5443 +1three52nine7threemszlkc +jsgqfbhmcmtjmxkq1brl9eight6 +fzvgmbbeight8 +861snhtzkvcpnr4 +b6kpxgcv71six1 +c5four7vcnqhd84threefsklxc +lcpdthree2three9three +kljtp912sevenctg +241zlv59sevenqdbvddmrhtfgrhxgxmb +hksp3gdmcldnvbts1 +xvcpr86btlptpnphhsix5fivenine +fivehgtmtxtwothree1seven +fpjpnrtpthreegjmfsjpcsix4twoonejqrr9 +2hsrckv4639six3 +fourtwofour3 +2kggzqfourfourseven +three526seven6gbmjbqkncg +crstsqhvt98rhzvhsshthreebj +xbhcfqvplfive9one77 +7256hlnmqlhth +5rttvcfhbjzoneqdbhvtwoneb +7mxgmsvxzzsevenclzbcq3twos +xcmpjgk9 +eight5cnntwopzjrmgbhq +rvbfnddhg25lpthcsfxfdkmseven +eight82cppmnpvkvthreesevenseven8h +fourhmpknkfdtwogfhrgthree84 +6six3599fivejhmjzdzsr +sevenbngxnjljfivegcvtmnt5k1 +twoone58nine +29jfctbqssvrtwothreeg +84kkjdjjp +threektqgtgcrccbsnsqpcfxtb3vxtdfour2hgvdg +6fivejttmkvvpntvqlfpbjbcfkcztltwosix +twoseven7qkhzqlx9four +ktnxrj2sixsevenrcnqbksgbgdfxrdqgz +236four +grbhpnjrtvrbslnfgthree47vbpncxqfourfp +5sevenkrlmnrjsix4 +5fivekgsxtbvkk +2two4 +lkrjlsz7mgv9525p1 +nineonebmfdxxfqvvkrblrd9 +5six6cvmqttbsxkzg +42seven13four4 diff --git a/print b/print new file mode 100755 index 0000000..50d855a Binary files /dev/null and b/print differ diff --git a/programs/aoc/y2022d01.test b/programs/aoc/y2022d01.test new file mode 100644 index 0000000..06914d1 --- /dev/null +++ b/programs/aoc/y2022d01.test @@ -0,0 +1,52 @@ +fn read_char() -> I64 { + let mut v = 0; + let mut res = 0; + asm { + subq $8 %RSP // allocate stack space for reading char + movq $0 %RAX // read + movq $0 %RDI // stdin + movq %RSP %RSI // put read char at top of stack + movq $1 %RDX // read 1 byte + syscall 4 // arity of 4 + movq %RAX {res} // result of system call + popq {v} // pop read char + }; + if res == 0 { + return res + }; + v +} + +fn main() { + let ASCII_NEWLINE = 10; + let ASCII_ZERO = 48; + let ASCII_NULL = 0; + + let mut next = read_char(); + + let mut best = 0; + let mut sum = 0; + let mut current = 0; + let mut last_was_newline = false; + + while next != ASCII_NULL { + if next == ASCII_NEWLINE { + if last_was_newline { + // Found empty line + if sum > best { + best = sum; + }; + sum = 0; + } else { + last_was_newline = true; + sum = sum + current; + current = 0; + } + } else { + last_was_newline = false; + current = current * 10 + next - ASCII_ZERO; + }; + next = read_char(); + }; + print(best); +} diff --git a/programs/aoc/y2023d01p1.test b/programs/aoc/y2023d01p1.test new file mode 100644 index 0000000..7a39c31 --- /dev/null +++ b/programs/aoc/y2023d01p1.test @@ -0,0 +1,40 @@ +fn read_char() -> I64 { + let mut v = 0; + let mut res = 0; + asm { + subq $8 %RSP // allocate stack space for reading char + movq $0 %RAX // read + movq $0 %RDI // stdin + movq %RSP %RSI // put read char at top of stack + movq $1 %RDX // read 1 byte + syscall 4 // arity of 4 + movq %RAX {res} // result of system call + popq {v} // pop read char + }; + if res == 0 { + return res + }; + v +} + +fn main() { + let mut total = 0; + let mut first = 0; + let mut last = 0; + + let mut next = 0; + while (next = read_char(); next != b'\0') { + if next == b'\n' { + total = total + first * 10 + last; + first = 0; + }; + next = next - b'0'; + if next >= 0 && next < 10 { + if first == 0 { + first = next; + }; + last = next; + }; + }; + print(total); +} diff --git a/programs/aoc/y2023d01p2.test b/programs/aoc/y2023d01p2.test new file mode 100644 index 0000000..df8fc1d --- /dev/null +++ b/programs/aoc/y2023d01p2.test @@ -0,0 +1,63 @@ +fn read_char() -> I64 { + let mut v = 0; + let mut res = 0; + asm { + subq $8 %RSP // allocate stack space for reading char + movq $0 %RAX // read + movq $0 %RDI // stdin + movq %RSP %RSI // put read char at top of stack + movq $1 %RDX // read 1 byte + syscall 4 // arity of 4 + movq %RAX {res} // result of system call + popq {v} // pop read char + }; + if res == 0 { + return res + }; + v +} + +fn main() { + let mut total = 0; + let mut first = 0; + let mut last = 0; + + let mut x0 = 0; + let mut x1 = 0; + let mut x2 = 0; + let mut x3 = 0; + let mut x4 = 0; + + let mut next = 0; + while (next = read_char(); next != b'\0') { + x0 = x1; + x1 = x2; + x2 = x3; + x3 = x4; + x4 = next; + + if next == b'\n' { + total = total + first * 10 + last; + first = 0; + continue + }; + + let digit = (if x2 == b'o' && x3 == b'n' && x4 == b'e' { 1 } + else if x2 == b't' && x3 == b'w' && x4 == b'o' { 2 } + else if x0 == b't' && x1 == b'h' && x2 == b'r' && x3 == b'e' && x4 == b'e' { 3 } + else if x1 == b'f' && x2 == b'o' && x3 == b'u' && x4 == b'r' { 4 } + else if x1 == b'f' && x2 == b'i' && x3 == b'v' && x4 == b'e' { 5 } + else if x2 == b's' && x3 == b'i' && x4 == b'x' { 6 } + else if x0 == b's' && x1 == b'e' && x2 == b'v' && x3 == b'e' && x4 == b'n' { 7 } + else if x0 == b'e' && x1 == b'i' && x2 == b'g' && x3 == b'h' && x4 == b't' { 8 } + else if x1 == b'n' && x2 == b'i' && x3 == b'n' && x4 == b'e' { 9 } + else if next >= b'0' && next <= b'9' { next - b'0' } + else { continue }); + + if first == 0 { + first = digit; + }; + last = digit; + }; + print(total); +} diff --git a/programs/fail/validate/invalid_escape.test b/programs/fail/validate/invalid_escape.test new file mode 100644 index 0000000..c0ea59b --- /dev/null +++ b/programs/fail/validate/invalid_escape.test @@ -0,0 +1,4 @@ +//* err: InvalidEscape +fn main() { + let x: I64 = b'\a'; +} diff --git a/programs/fail/validate/integer_out_of_bounds.test b/programs/fail/validate/invalid_integer_1.test similarity index 77% rename from programs/fail/validate/integer_out_of_bounds.test rename to programs/fail/validate/invalid_integer_1.test index 053ba31..95b9b3f 100644 --- a/programs/fail/validate/integer_out_of_bounds.test +++ b/programs/fail/validate/invalid_integer_1.test @@ -1,4 +1,4 @@ -//* err: IntegerOutOfBounds +//* err: InvalidInteger fn main() { // 9_223_372_036_854_775_807 is the max for an I64. 9223372036854775808i64; diff --git a/programs/fail/validate/invalid_integer_2.test b/programs/fail/validate/invalid_integer_2.test new file mode 100644 index 0000000..fef7760 --- /dev/null +++ b/programs/fail/validate/invalid_integer_2.test @@ -0,0 +1,4 @@ +//* err: InvalidInteger +fn main() { + 1a2i64; +} diff --git a/programs/fail/validate/invalid_integer_3.test b/programs/fail/validate/invalid_integer_3.test new file mode 100644 index 0000000..e382efa --- /dev/null +++ b/programs/fail/validate/invalid_integer_3.test @@ -0,0 +1,4 @@ +//* err: InvalidInteger +fn main() { + 0b3i64; +} diff --git a/programs/good/asm/exit.test b/programs/good/asm/exit.test new file mode 100644 index 0000000..635f0e5 --- /dev/null +++ b/programs/good/asm/exit.test @@ -0,0 +1,15 @@ +//* out: 0 +//* ret: 42 +fn exit_asm(exit_code: I64) { + asm { + movq {exit_code} %RDI + movq $60 %RAX + syscall 2 + }; +} + +fn main() { + print(0); + exit_asm(42); + print(1); +} diff --git a/programs/good/asm/maths.test b/programs/good/asm/maths.test new file mode 100644 index 0000000..7f9f78f --- /dev/null +++ b/programs/good/asm/maths.test @@ -0,0 +1,12 @@ +//* ret: 8 +fn main() -> I64 { + let mut x = 1; + let y = 4i64; + asm { + addq $1 {x} + movq {x} %RAX + mulq {y} + movq %RAX {x} + }; + x +} diff --git a/programs/good/asm/print.test b/programs/good/asm/print.test new file mode 100644 index 0000000..369cfce --- /dev/null +++ b/programs/good/asm/print.test @@ -0,0 +1,27 @@ +//* out: 42 +fn print_backwards(mut v: I64) { + let ASCII_ZERO = 48; + while v != 0 { + let unit_digit = v % 10; + print_char(unit_digit + ASCII_ZERO); + v = v / 10; + }; + let ASCII_NEWLINE = 10; + print_char(ASCII_NEWLINE); +} + +fn print_char(v: I64) { + asm { + pushq {v} + movq $1 %RAX // write + movq $1 %RDI // stdout + movq %RSP %RSI // print char on top of stack + movq $1 %RDX // print 1 byte + syscall 4 // arity of 4 + addq $8 %RSP // reset stack + }; +} + +fn main() { + print_backwards(24); +} diff --git a/programs/good/asm/read.test b/programs/good/asm/read.test new file mode 100644 index 0000000..a3de48c --- /dev/null +++ b/programs/good/asm/read.test @@ -0,0 +1,47 @@ +//* inp: 42 +//* out: 42 +fn read_asm() -> I64 { + let ASCII_NEWLINE = 10; + let ASCII_DASH = 45; + let ASCII_ZERO = 48; + + let mut next = read_char(); + let negative = next == ASCII_DASH; + + if negative { + next = read_char(); + }; + + let mut total = 0; + + // Exhaust all characters until a newline is hit. + while next != ASCII_NEWLINE { + total = total * 10 + next - ASCII_ZERO; + next = read_char(); + }; + + // If the number was negative, negate it. + if negative { + total = -total; + }; + + total +} + +fn read_char() -> I64 { + let mut v = 0; + asm { + subq $8 %RSP // allocate stack space for reading char + movq $0 %RAX // read + movq $0 %RDI // stdin + movq %RSP %RSI // put read char at top of stack + movq $1 %RDX // read 1 byte + syscall 4 // arity of 4 + popq {v} // pop read char + }; + v +} + +fn main() { + print(read_asm()); +} diff --git a/programs/good/asm/simple.test b/programs/good/asm/simple.test new file mode 100644 index 0000000..c6c8273 --- /dev/null +++ b/programs/good/asm/simple.test @@ -0,0 +1,8 @@ +//* ret: 1 +fn main() -> I64 { + let mut x = 0; + asm { + movq $1 {x} + }; + x +} diff --git a/programs/good/booleans/else_if_1.test b/programs/good/booleans/else_if_1.test new file mode 100644 index 0000000..d25ca71 --- /dev/null +++ b/programs/good/booleans/else_if_1.test @@ -0,0 +1,11 @@ +//* ret: 42 +fn main() -> I64 { + let x = 42; + if x < 42 { + 0 + } else if x > 42 { + 100 + } else { + x + } +} diff --git a/programs/good/booleans/else_if_2.test b/programs/good/booleans/else_if_2.test new file mode 100644 index 0000000..f400074 --- /dev/null +++ b/programs/good/booleans/else_if_2.test @@ -0,0 +1,11 @@ +//* ret: 100 +fn main() -> I64 { + let x = 43; + if x < 42 { + 0 + } else if x > 42 { + 100 + } else { + x + } +} diff --git a/programs/good/integration/is_prime.test b/programs/good/integration/is_prime.test index 0559158..8945bed 100644 --- a/programs/good/integration/is_prime.test +++ b/programs/good/integration/is_prime.test @@ -4,10 +4,10 @@ fn is_prime(n: I64) -> Bool { let mut i = 2; loop { if i == n { - break true; + break true } else { if n % i == 0 { - break false; + break false } else { i = i + 1; } @@ -19,7 +19,7 @@ fn main() { loop { let n = read(); if n == 0 { - break; + break } else { if is_prime(n) { print(1); diff --git a/programs/good/mutability/semicolons.test b/programs/good/mutability/semicolons.test index 2b0237a..1df3406 100644 --- a/programs/good/mutability/semicolons.test +++ b/programs/good/mutability/semicolons.test @@ -1,5 +1,5 @@ fn main() -> Unit { 5i64; - 6u64; + 6i64; 7i64; } diff --git a/programs/good/simple/wacky_literals.test b/programs/good/simple/wacky_literals.test new file mode 100644 index 0000000..371f691 --- /dev/null +++ b/programs/good/simple/wacky_literals.test @@ -0,0 +1,15 @@ +fn main() { + let a: I64 = 4_2; + let b: I64 = b'*'; + let c: I64 = 0x2A; + let d: I64 = 0x2a; + let e: I64 = 0x2_A; + let f: I64 = 0b101010; + let g: I64 = 0b101_010; + let h: I64 = 0b1_0_1_0_1_0; // please do not do this, we will find you and we will [redacted] + let i: I64 = 0o52; + let j: I64 = 0o5_2; + let k: I64 = b'\n'; // sadly this is not 42 + let l = b'*'i64; + let m = 4_2i64; +} diff --git a/read b/read new file mode 100755 index 0000000..af3c66e Binary files /dev/null and b/read differ