diff --git a/crates/codegen/Cargo.toml b/crates/codegen/Cargo.toml index a2e86d34..e9dea30f 100644 --- a/crates/codegen/Cargo.toml +++ b/crates/codegen/Cargo.toml @@ -17,6 +17,6 @@ keywords = ["compiler", "evm", "wasm", "smart-contract"] [dependencies] cranelift-entity = "0.104" smallvec = "1.7.0" -rustc-hash = "1.1.0" +rustc-hash = "2.0.0" sonatina-ir = { path = "../ir", version = "0.0.3-alpha" } sonatina-triple = { path = "../triple", version = "0.0.3-alpha" } diff --git a/crates/codegen/src/critical_edge.rs b/crates/codegen/src/critical_edge.rs index 83b87442..eacbb511 100644 --- a/crates/codegen/src/critical_edge.rs +++ b/crates/codegen/src/critical_edge.rs @@ -148,7 +148,7 @@ mod tests { CriticalEdgeSplitter::new().run(func, &mut cfg); assert_eq!( - dump_func(func), + dump_func(&module, func_ref), "func public %test_func() -> void { block0: br 1.i32 block3 block1; @@ -166,6 +166,7 @@ mod tests { " ); + let func = &mut module.funcs[func_ref]; let mut cfg_split = ControlFlowGraph::default(); cfg_split.compute(func); assert_eq!(cfg, cfg_split); @@ -207,7 +208,7 @@ mod tests { CriticalEdgeSplitter::new().run(func, &mut cfg); assert_eq!( - dump_func(func), + dump_func(&module, func_ref), "func public %test_func() -> void { block0: br 1.i8 block5 block1; @@ -234,6 +235,7 @@ mod tests { " ); + let func = &mut module.funcs[func_ref]; let mut cfg_split = ControlFlowGraph::default(); cfg_split.compute(func); assert_eq!(cfg, cfg_split); @@ -269,7 +271,7 @@ mod tests { CriticalEdgeSplitter::new().run(func, &mut cfg); assert_eq!( - dump_func(func), + dump_func(&module, func_ref), "func public %test_func() -> void { block0: jump block1; @@ -289,6 +291,7 @@ mod tests { " ); + let func = &mut module.funcs[func_ref]; let mut cfg_split = ControlFlowGraph::default(); cfg_split.compute(func); assert_eq!(cfg, cfg_split); @@ -332,7 +335,7 @@ mod tests { CriticalEdgeSplitter::new().run(func, &mut cfg); assert_eq!( - dump_func(func), + dump_func(&module, func_ref), "func public %test_func() -> void { block0: br 1.i1 block5 block6; @@ -362,6 +365,7 @@ mod tests { " ); + let func = &mut module.funcs[func_ref]; let mut cfg_split = ControlFlowGraph::default(); cfg_split.compute(func); assert_eq!(cfg, cfg_split); diff --git a/crates/filecheck/src/lib.rs b/crates/filecheck/src/lib.rs index c18a31ee..d9e20f60 100644 --- a/crates/filecheck/src/lib.rs +++ b/crates/filecheck/src/lib.rs @@ -145,10 +145,12 @@ impl<'a> FileChecker<'a> { func_ref: FuncRef, ) -> FileCheckResult { let func = &mut parsed_module.module.funcs[func_ref]; - let comments = &parsed_module.func_comments[func_ref]; + let comments = &parsed_module.debug.func_comments[func_ref]; self.transformer.transform(func); - let func_ir = FuncWriter::new(func).dump_string().unwrap(); + let func_ir = FuncWriter::new(func_ref, func, Some(&parsed_module.debug)) + .dump_string() + .unwrap(); let checker = self.build_checker(comments); @@ -171,7 +173,7 @@ impl<'a> FileChecker<'a> { Err(errs) => { let mut v = vec![]; for e in errs { - e.print(&mut v, self.file_path.to_str().unwrap(), &input) + e.print(&mut v, self.file_path.to_str().unwrap(), &input, true) .unwrap() } Err(String::from_utf8(v).unwrap()) diff --git a/crates/interpreter/src/state.rs b/crates/interpreter/src/state.rs index 63d36034..1a662665 100644 --- a/crates/interpreter/src/state.rs +++ b/crates/interpreter/src/state.rs @@ -277,7 +277,7 @@ mod test { Ok(pm) => pm.module, Err(errs) => { for err in errs { - eprintln!("{}", err.print_to_string("[test]", input)); + eprintln!("{}", err.print_to_string("[test]", input, true)); } panic!("parsing failed"); } diff --git a/crates/ir/Cargo.toml b/crates/ir/Cargo.toml index 677b6ff4..2911073c 100644 --- a/crates/ir/Cargo.toml +++ b/crates/ir/Cargo.toml @@ -17,7 +17,7 @@ keywords = ["compiler", "evm", "wasm", "smart-contract"] primitive-types = { version = "0.12", default-features = false } cranelift-entity = "0.104" smallvec = "1.7.0" -rustc-hash = "1.1.0" +rustc-hash = "2.0.0" dyn-clone = "1.0.4" sonatina-triple = { path = "../triple", version = "0.0.3-alpha" } indexmap = "2.0.0" diff --git a/crates/ir/src/builder/func_builder.rs b/crates/ir/src/builder/func_builder.rs index 66ec6d11..a40e6626 100644 --- a/crates/ir/src/builder/func_builder.rs +++ b/crates/ir/src/builder/func_builder.rs @@ -4,7 +4,7 @@ use crate::{ func_cursor::{CursorLocation, FuncCursor}, insn::{BinaryOp, CastOp, DataLocationKind, InsnData, UnaryOp}, module::FuncRef, - Block, Function, GlobalVariable, Immediate, Type, Value, ValueData, + Block, Function, GlobalVariable, Immediate, Type, Value, }; use super::{ @@ -18,7 +18,6 @@ pub struct FunctionBuilder { func_ref: FuncRef, pub cursor: C, ssa_builder: SsaBuilder, - undefined: Vec, } macro_rules! impl_binary_insn { @@ -49,7 +48,6 @@ where func_ref, cursor, ssa_builder: SsaBuilder::new(), - undefined: Default::default(), } } @@ -64,12 +62,9 @@ where mut module_builder, func, func_ref, - undefined, .. } = self; - debug_assert!(undefined.is_empty()); // xxx - module_builder.funcs[func_ref] = func; module_builder } @@ -93,34 +88,6 @@ where self.cursor.set_location(CursorLocation::BlockBottom(block)); } - pub fn name_value(&mut self, value: Value, name: &str) { - if let Some(v) = self.func.value_names.get_by_right(name) { - if let Some(pos) = self.undefined.iter().position(|u| u == v) { - self.func.dfg.change_to_alias(*v, value); - // self.func.dfg.values[*v] = ValueData::Alias { alias: value }; - self.undefined.remove(pos); - } else { - panic!("value names must be unique"); - } - } - self.func.value_names.insert(value, name.into()); - } - - pub fn get_named_value(&mut self, name: &str) -> Value { - if let Some(v) = self.func.value_names.get_by_right(name).copied() { - v - } else { - let v = self.func.dfg.make_value(ValueData::Immediate { - imm: Immediate::I128(424242), - ty: Type::I128, - }); - - self.undefined.push(v); - self.name_value(v, name); - v - } - } - pub fn make_imm_value(&mut self, imm: Imm) -> Value where Imm: Into, @@ -428,7 +395,7 @@ mod tests { let module = builder.finish().build(); let func_ref = module.iter_functions().next().unwrap(); assert_eq!( - dump_func(&module.funcs[func_ref]), + dump_func(&module, func_ref), "func public %test_func() -> void { block0: v2.i8 = add 1.i8 2.i8; @@ -458,7 +425,7 @@ mod tests { let module = builder.finish().build(); let func_ref = module.iter_functions().next().unwrap(); assert_eq!( - dump_func(&module.funcs[func_ref]), + dump_func(&module, func_ref), "func public %test_func(v0.i32, v1.i64) -> void { block0: v2.i64 = sext v0; @@ -484,7 +451,7 @@ mod tests { let module = builder.finish().build(); let func_ref = module.iter_functions().next().unwrap(); assert_eq!( - dump_func(&module.funcs[func_ref]), + dump_func(&module, func_ref), "func public %test_func() -> i32 { block0: return 1.i32; @@ -526,7 +493,7 @@ mod tests { let module = builder.finish().build(); let func_ref = module.iter_functions().next().unwrap(); assert_eq!( - dump_func(&module.funcs[func_ref]), + dump_func(&module, func_ref), "func public %test_func(v0.i64) -> void { block0: br v0 block1 block2; diff --git a/crates/ir/src/builder/mod.rs b/crates/ir/src/builder/mod.rs index 76a9c95a..3b30ce02 100644 --- a/crates/ir/src/builder/mod.rs +++ b/crates/ir/src/builder/mod.rs @@ -16,8 +16,8 @@ pub mod test_util { func_cursor::InsnInserter, ir_writer::FuncWriter, isa::{IsaBuilder, TargetIsa}, - module::ModuleCtx, - Function, Linkage, Signature, Type, + module::{FuncRef, ModuleCtx}, + Linkage, Module, Signature, Type, }; pub fn build_test_isa() -> TargetIsa { @@ -34,8 +34,9 @@ pub mod test_util { mb.build_function(func_ref) } - pub fn dump_func(func: &Function) -> String { - let mut writer = FuncWriter::new(func); + pub fn dump_func(module: &Module, func_ref: FuncRef) -> String { + let func = &module.funcs[func_ref]; + let mut writer = FuncWriter::new(func_ref, func, None); writer.dump_string().unwrap() } } diff --git a/crates/ir/src/builder/ssa.rs b/crates/ir/src/builder/ssa.rs index 7cdc4159..90ab0e99 100644 --- a/crates/ir/src/builder/ssa.rs +++ b/crates/ir/src/builder/ssa.rs @@ -228,10 +228,9 @@ mod tests { let module = builder.finish().build(); let func_ref = module.iter_functions().next().unwrap(); - let func = &module.funcs[func_ref]; assert_eq!( - dump_func(func), + dump_func(&module, func_ref), "func public %test_func() -> void { block0: v1.i32 = add 1.i32 1.i32; @@ -277,10 +276,9 @@ mod tests { let module = builder.finish().build(); let func_ref = module.iter_functions().next().unwrap(); - let func = &module.funcs[func_ref]; assert_eq!( - dump_func(func), + dump_func(&module, func_ref), "func public %test_func() -> void { block0: br 1.i32 block2 block1; @@ -358,10 +356,9 @@ mod tests { let module = builder.finish().build(); let func_ref = module.iter_functions().next().unwrap(); - let func = &module.funcs[func_ref]; assert_eq!( - dump_func(func), + dump_func(&module, func_ref), "func public %test_func() -> void { block0: br 0.i32 block1 block2; @@ -432,10 +429,9 @@ mod tests { let module = builder.finish().build(); let func_ref = module.iter_functions().next().unwrap(); - let func = &module.funcs[func_ref]; assert_eq!( - dump_func(func), + dump_func(&module, func_ref), "func public %test_func() -> void { block0: jump block1; @@ -508,10 +504,9 @@ mod tests { let module = builder.finish().build(); let func_ref = module.iter_functions().next().unwrap(); - let func = &module.funcs[func_ref]; assert_eq!( - dump_func(func), + dump_func(&module, func_ref), "func public %test_func() -> void { block0: jump block1; @@ -589,10 +584,9 @@ mod tests { let module = builder.finish().build(); let func_ref = module.iter_functions().next().unwrap(); - let func = &module.funcs[func_ref]; assert_eq!( - dump_func(func), + dump_func(&module, func_ref), "func public %test_func() -> void { block0: jump block1; @@ -668,10 +662,9 @@ mod tests { let module = builder.finish().build(); let func_ref = module.iter_functions().next().unwrap(); - let func = &module.funcs[func_ref]; assert_eq!( - dump_func(func), + dump_func(&module, func_ref), "func public %test_func(v0.i32) -> i32 { block0: br_table v0 block4 (1.i32 block1) (2.i32 block2) (3.i32 block3); diff --git a/crates/ir/src/function.rs b/crates/ir/src/function.rs index ce9615e3..90b15660 100644 --- a/crates/ir/src/function.rs +++ b/crates/ir/src/function.rs @@ -1,14 +1,8 @@ use super::{module::FuncRef, DataFlowGraph, Layout, Type, Value}; use crate::{module::ModuleCtx, types::DisplayType, Linkage}; -use rustc_hash::{FxHashMap, FxHasher}; +use rustc_hash::FxHashMap; use smallvec::SmallVec; -use smol_str::SmolStr; -use std::{ - fmt::{self, Write}, - hash::BuildHasherDefault, -}; - -type Bimap = bimap::BiHashMap>; +use std::fmt::{self, Write}; #[derive(Debug, Clone)] pub struct Function { @@ -18,9 +12,6 @@ pub struct Function { pub dfg: DataFlowGraph, pub layout: Layout, - // xxx move - pub value_names: Bimap, - /// Stores signatures of all functions that are called by the function. pub callees: FxHashMap, } @@ -43,7 +34,6 @@ impl Function { arg_values, dfg, layout: Layout::default(), - value_names: Bimap::default(), callees: FxHashMap::default(), } } @@ -78,11 +68,6 @@ impl Signature { self.linkage } - // xxx remove - pub fn append_arg(&mut self, arg: Type) { - self.args.push(arg); - } - pub fn args(&self) -> &[Type] { &self.args } diff --git a/crates/ir/src/ir_writer.rs b/crates/ir/src/ir_writer.rs index c7bc5acb..663e2e7f 100644 --- a/crates/ir/src/ir_writer.rs +++ b/crates/ir/src/ir_writer.rs @@ -1,20 +1,40 @@ use std::io; use crate::{ - module::ModuleCtx, + module::{FuncRef, ModuleCtx}, types::{CompoundType, CompoundTypeData, StructData}, DataLocationKind, GlobalVariableData, Module, }; use super::{Block, Function, Insn, InsnData, Type, Value}; +pub trait DebugProvider { + fn value_name(&self, _func: FuncRef, _value: Value) -> Option<&str> { + None + } +} +impl DebugProvider for () {} + pub struct ModuleWriter<'a> { module: &'a Module, + debug: Option<&'a dyn DebugProvider>, } +impl<'a> ModuleWriter<'a> {} + impl<'a> ModuleWriter<'a> { pub fn new(module: &'a Module) -> Self { - Self { module } + Self { + module, + debug: None, + } + } + + pub fn with_debug_provider(module: &'a Module, debug: &'a dyn DebugProvider) -> Self { + Self { + module, + debug: Some(debug), + } } pub fn write(&mut self, mut w: impl io::Write) -> io::Result<()> { @@ -40,7 +60,7 @@ impl<'a> ModuleWriter<'a> { for func_ref in self.module.funcs.keys() { let func = &self.module.funcs[func_ref]; - let mut func_writer = FuncWriter::new(func); + let mut func_writer = FuncWriter::new(func_ref, func, self.debug); func_writer.write(&mut w)?; writeln!(w)?; } @@ -56,13 +76,24 @@ impl<'a> ModuleWriter<'a> { } pub struct FuncWriter<'a> { + func_ref: FuncRef, func: &'a Function, level: u8, + debug: Option<&'a dyn DebugProvider>, } impl<'a> FuncWriter<'a> { - pub fn new(func: &'a Function) -> Self { - Self { func, level: 0 } + pub fn new( + func_ref: FuncRef, + func: &'a Function, + debug: Option<&'a dyn DebugProvider>, + ) -> Self { + Self { + func_ref, + func, + level: 0, + debug, + } } pub fn write(&mut self, mut w: impl io::Write) -> io::Result<()> { @@ -106,6 +137,10 @@ impl<'a> FuncWriter<'a> { unsafe { Ok(String::from_utf8_unchecked(s)) } } + pub fn value_name(&self, value: Value) -> Option<&str> { + self.debug.and_then(|d| d.value_name(self.func_ref, value)) + } + fn write_block_with_insn(&mut self, block: Block, mut w: impl io::Write) -> io::Result<()> { self.indent(&mut w)?; block.write(self, &mut w)?; @@ -179,7 +214,7 @@ impl IrWrite for Value { writer .ctx() .with_gv_store(|s| write!(w, "%{}", s.gv_data(gv).symbol)) - } else if let Some(name) = writer.func.value_names.get_by_left(&value) { + } else if let Some(name) = writer.value_name(value) { write!(w, "{name}") } else { write!(w, "v{}", value.0) diff --git a/crates/parser/Cargo.toml b/crates/parser/Cargo.toml index fa9954c1..b7868a39 100644 --- a/crates/parser/Cargo.toml +++ b/crates/parser/Cargo.toml @@ -25,8 +25,10 @@ hex = "0.4.3" num-traits = { version = "0.2.19", default-features = false } either = { version = "1.12.0", default-features = false } annotate-snippets = "0.11.4" +rustc-hash = "2.0.0" +bimap = "0.6.3" [dev-dependencies] -dir-test = { git = "https://github.com/sbillig/dir-test", rev = "c4115dd" } +dir-test = "0.3" insta = { version = "1.38.0" } indenter = "0.3.3" diff --git a/crates/parser/build.rs b/crates/parser/build.rs new file mode 100644 index 00000000..8e048f92 --- /dev/null +++ b/crates/parser/build.rs @@ -0,0 +1,4 @@ +fn main() { + #[cfg(test)] + println!("cargo:rerun-if-changed=./test_files"); +} diff --git a/crates/parser/src/ast.rs b/crates/parser/src/ast.rs index 27d71956..389bc401 100644 --- a/crates/parser/src/ast.rs +++ b/crates/parser/src/ast.rs @@ -1,6 +1,8 @@ use super::{syntax::Node, Error}; -use crate::syntax::{FromSyntax, Parser, Rule}; -use annotate_snippets::{Level, Renderer, Snippet}; +use crate::{ + syntax::{FromSyntax, Parser, Rule, Spanned}, + Span, +}; use either::Either; use hex::FromHex; pub use ir::{ @@ -11,11 +13,9 @@ use ir::{I256, U256}; use pest::Parser as _; use smol_str::SmolStr; pub use sonatina_triple::{InvalidTriple, TargetTriple}; -use std::{io, ops::Range, str::FromStr}; +use std::str::FromStr; pub fn parse(input: &str) -> Result> { - pest::set_error_detail(true); // xxx - match Parser::parse(Rule::module, input) { Err(err) => Err(vec![Error::SyntaxError(err)]), Ok(mut pairs) => { @@ -45,7 +45,7 @@ pub struct Module { impl FromSyntax for Module { fn from_syntax(node: &mut Node) -> Self { - let target = node.single_opt(Rule::target_triple).flatten(); + let target = node.single(Rule::target_triple); let module_comments = node.map_while(|p| { if p.as_rule() == Rule::COMMENT && p.as_str().starts_with("#!") { @@ -97,7 +97,7 @@ impl FromSyntax for Option { match TargetTriple::parse(node.txt) { Ok(t) => Some(t), Err(e) => { - node.error(Error::InvalidTarget(e, node.span.clone())); + node.error(Error::InvalidTarget(e, node.span)); None } } @@ -223,6 +223,11 @@ pub struct Block { pub stmts: Vec, } +impl Block { + pub fn id(&self) -> u32 { + self.id.id.unwrap() + } +} impl FromSyntax for Block { fn from_syntax(node: &mut Node) -> Self { Self { @@ -233,20 +238,27 @@ impl FromSyntax for Block { } #[derive(Debug)] -pub struct BlockId(pub Option); +pub struct BlockId { + pub id: Option, + pub span: Span, +} impl FromSyntax for BlockId { fn from_syntax(node: &mut Node) -> Self { + let span = node.span; node.descend(); debug_assert_eq!(node.rule, Rule::block_number); - BlockId(node.txt.parse().ok()) + let id = node.txt.parse().ok(); + if id.is_none() { + node.error(Error::NumberOutOfBounds(node.span)); + } + BlockId { id, span } } } #[derive(Debug)] pub struct Stmt { pub kind: StmtKind, - // pub comments: Vec, } impl FromSyntax for Stmt { @@ -298,7 +310,13 @@ impl FromSyntax for (Value, BlockId) { } #[derive(Debug)] -pub enum Type { +pub struct Type { + pub kind: TypeKind, + pub span: Span, +} + +#[derive(Debug)] +pub enum TypeKind { Int(IntType), Ptr(Box), Array(Box, usize), @@ -310,19 +328,26 @@ pub enum Type { impl FromSyntax for Type { fn from_syntax(node: &mut Node) -> Self { node.descend(); - match node.rule { - Rule::primitive_type => Type::Int(IntType::from_str(node.txt).unwrap()), - Rule::ptr_type => Type::Ptr(Box::new(node.single(Rule::type_name))), + let kind = match node.rule { + Rule::primitive_type => TypeKind::Int(IntType::from_str(node.txt).unwrap()), + Rule::ptr_type => TypeKind::Ptr(Box::new(node.single(Rule::type_name))), Rule::array_type => { let Ok(size) = usize::from_str(node.get(Rule::array_size).as_str()) else { - node.error(Error::NumberOutOfBounds(node.span.clone())); - return Type::Error; + node.error(Error::NumberOutOfBounds(node.span)); + return Type { + kind: TypeKind::Error, + span: node.span, + }; }; - Type::Array(Box::new(node.single(Rule::type_name)), size) + TypeKind::Array(Box::new(node.single(Rule::type_name)), size) } - Rule::void_type => Type::Void, - Rule::struct_identifier => Type::Struct(node.parse_str(Rule::struct_name)), + Rule::void_type => TypeKind::Void, + Rule::struct_identifier => TypeKind::Struct(node.parse_str(Rule::struct_name)), _ => unreachable!(), + }; + Type { + kind, + span: node.span, } } } @@ -390,14 +415,20 @@ impl FromSyntax for Expr { } #[derive(Debug)] -pub struct Call(pub FunctionName, pub Vec); +pub struct Call(pub Spanned, pub Vec); #[derive(Debug)] -pub struct ValueName(pub SmolStr); +pub struct ValueName { + pub string: SmolStr, + pub span: Span, +} impl FromSyntax for ValueName { fn from_syntax(node: &mut Node) -> Self { - Self(node.txt.into()) + Self { + string: node.txt.into(), + span: node.span, + } } } @@ -411,7 +442,13 @@ impl FromSyntax for ValueDeclaration { } #[derive(Debug)] -pub enum Value { +pub struct Value { + pub kind: ValueKind, + pub span: Span, +} + +#[derive(Debug)] +pub enum ValueKind { Immediate(Immediate), Named(ValueName), Error, @@ -419,16 +456,16 @@ pub enum Value { macro_rules! parse_dec { ($node:ident, $imm:expr, $ity:ty, $uty:ty) => { - if let Ok(v) = $node + match $node .txt .parse::<$ity>() .or_else(|_| $node.txt.parse::<$uty>().map(|v| v as $ity)) { - Value::Immediate($imm(v)) - } else { - let span = $node.span.clone(); - $node.error(Error::NumberOutOfBounds(span)); - Value::Error + Ok(v) => ValueKind::Immediate($imm(v)), + Err(_) => { + $node.error(Error::NumberOutOfBounds($node.span)); + ValueKind::Error + } } }; } @@ -436,9 +473,9 @@ macro_rules! parse_dec { macro_rules! parse_hex { ($node:ident, $imm:expr, $ity:ty) => { if let Some(bytes) = hex_bytes($node.txt) { - Value::Immediate($imm(<$ity>::from_be_bytes(bytes))) + ValueKind::Immediate($imm(<$ity>::from_be_bytes(bytes))) } else { - Value::Error + ValueKind::Error } }; } @@ -446,8 +483,8 @@ macro_rules! parse_hex { impl FromSyntax for Value { fn from_syntax(node: &mut Node) -> Self { node.descend(); - match node.rule { - Rule::value_name => Value::Named(ValueName(node.txt.into())), + let kind = match node.rule { + Rule::value_name => ValueKind::Named(ValueName::from_syntax(node)), Rule::imm_number => { let ty: IntType = node.parse_str(Rule::primitive_type); node.descend(); @@ -485,8 +522,8 @@ impl FromSyntax for Value { Rule::hex => match ty { IntType::I1 => { - node.error(Error::NumberOutOfBounds(node.span.clone())); - Value::Error + node.error(Error::NumberOutOfBounds(node.span)); + ValueKind::Error } IntType::I8 => parse_hex!(node, Immediate::I8, i8), IntType::I16 => parse_hex!(node, Immediate::I16, i16), @@ -503,11 +540,10 @@ impl FromSyntax for Value { if is_negative { i256 = I256::zero().overflowing_sub(i256).0; } - Value::Immediate(Immediate::I256(i256)) + ValueKind::Immediate(Immediate::I256(i256)) } else { - let span = node.span.clone(); - node.error(Error::NumberOutOfBounds(span)); - Value::Error + node.error(Error::NumberOutOfBounds(node.span)); + ValueKind::Error } } }, @@ -515,6 +551,10 @@ impl FromSyntax for Value { } } _ => unreachable!(), + }; + Value { + kind, + span: node.span, } } } @@ -536,53 +576,16 @@ impl FromStr for IntType { } } -impl Error { - pub fn span(&self) -> Range { - match self { - Error::NumberOutOfBounds(span) => span.clone(), - Error::InvalidTarget(_, span) => span.clone(), - Error::SyntaxError(err) => match err.location { - pest::error::InputLocation::Pos(p) => p..p, - pest::error::InputLocation::Span((s, e)) => s..e, - }, - } - } - - pub fn print(&self, mut w: impl io::Write, path: &str, content: &str) -> io::Result<()> { - let label = match self { - Error::NumberOutOfBounds(_) => "number out of bounds".into(), - Error::InvalidTarget(err, _) => err.to_string(), - Error::SyntaxError(err) => err.to_string(), - }; - let snippet = Level::Error.title("parse error").snippet( - Snippet::source(content) - .line_start(0) - .origin(path) - .fold(true) - .annotation(Level::Error.span(self.span()).label(&label)), - ); - let rend = Renderer::styled(); - let disp = rend.render(snippet); - write!(w, "{}", disp) - } - - pub fn print_to_string(&self, path: &str, content: &str) -> String { - let mut v = vec![]; - self.print(&mut v, path, content).unwrap(); - String::from_utf8(v).unwrap() - } -} - -fn imm_or_err(node: &mut Node, f: F) -> Value +fn imm_or_err(node: &mut Node, f: F) -> ValueKind where F: Fn() -> Option, { let Some(imm) = f() else { - let span = node.span.clone(); + let span = node.span; node.error(Error::NumberOutOfBounds(span)); - return Value::Error; + return ValueKind::Error; }; - Value::Immediate(imm) + ValueKind::Immediate(imm) } fn hex_bytes(mut s: &str) -> Option<[u8; N]> { @@ -597,39 +600,3 @@ fn hex_bytes(mut s: &str) -> Option<[u8; N]> { out[N - bytes.len()..].copy_from_slice(&bytes); Some(out) } - -// xxx remove -// pub fn parse_immediate( -// val: &str, -// loc: Range, -// ) -> Result> { -// let mut chunks = val.split('.'); -// let num = chunks.next().unwrap(); -// let t = chunks.next().unwrap(); - -// let imm = match t { -// "i1" => Immediate::I1(parse_num(num, loc)?), -// "i8" => Immediate::I8(parse_num(num, loc)?), -// "i16" => Immediate::I16(parse_num(num, loc)?), -// "i32" => Immediate::I32(parse_num(num, loc)?), -// "i64" => Immediate::I64(parse_num(num, loc)?), -// "i128" => Immediate::I128(parse_num(num, loc)?), -// "i256" => todo!(), -// _ => { -// unreachable!() -// } -// }; -// Ok(Value::Immediate(imm)) -// } - -// pub fn parse_num( -// s: &str, -// loc: Range, -// ) -> Result> -// where -// T: FromStr, -// { -// T::from_str(s).map_err(|_| ParseError::User { -// error: Error::NumberOutOfBounds(loc), -// }) -// } diff --git a/crates/parser/src/error.rs b/crates/parser/src/error.rs new file mode 100644 index 00000000..0d81a124 --- /dev/null +++ b/crates/parser/src/error.rs @@ -0,0 +1,81 @@ +use std::io; + +use crate::{syntax::Rule, Span}; +use annotate_snippets::{Level, Renderer, Snippet}; +use smol_str::SmolStr; +use sonatina_triple::InvalidTriple; + +#[derive(Debug)] +#[allow(clippy::large_enum_variant)] +pub enum Error { + NumberOutOfBounds(Span), + InvalidTarget(InvalidTriple, Span), + SyntaxError(pest::error::Error), + Undefined(UndefinedKind, Span), + DuplicateValueName(SmolStr, Span), +} + +#[derive(Debug)] +pub enum UndefinedKind { + Block(ir::Block), + Func(SmolStr), + Type(SmolStr), + Value(SmolStr), +} + +impl Error { + pub fn span(&self) -> Span { + match self { + Error::NumberOutOfBounds(span) => *span, + Error::InvalidTarget(_, span) => *span, + Error::Undefined(_, span) => *span, + + Error::DuplicateValueName(_, span) => *span, + Error::SyntaxError(err) => match err.location { + pest::error::InputLocation::Pos(p) => Span(p as u32, p as u32), + pest::error::InputLocation::Span((s, e)) => Span(s as u32, e as u32), + }, + } + } + + pub fn print( + &self, + mut w: impl io::Write, + path: &str, + content: &str, + colors: bool, + ) -> io::Result<()> { + let label = match self { + Error::NumberOutOfBounds(_) => "number out of bounds".into(), + Error::InvalidTarget(err, _) => err.to_string(), + Error::SyntaxError(err) => err.to_string(), + Error::Undefined(kind, _) => match kind { + UndefinedKind::Block(id) => format!("undefined block: `block{}`", id.0), + UndefinedKind::Func(name) => format!("undefined function: `%{name}`"), + UndefinedKind::Type(name) => format!("undefined type: `%{name}`"), + UndefinedKind::Value(name) => format!("undefined value: `{name}`"), + }, + Error::DuplicateValueName(name, _) => format!("value name `{name}` is already defined"), + }; + let snippet = Level::Error.title("parse error").snippet( + Snippet::source(content) + .line_start(0) + .origin(path) + .fold(true) + .annotation(Level::Error.span(self.span().as_range()).label(&label)), + ); + let rend = if colors { + Renderer::styled() + } else { + Renderer::plain() + }; + let disp = rend.render(snippet); + write!(w, "{}", disp) + } + + pub fn print_to_string(&self, path: &str, content: &str, colors: bool) -> String { + let mut v = vec![]; + self.print(&mut v, path, content, colors).unwrap(); + String::from_utf8(v).unwrap() + } +} diff --git a/crates/parser/src/lib.rs b/crates/parser/src/lib.rs index ba13ba2d..2ca93875 100644 --- a/crates/parser/src/lib.rs +++ b/crates/parser/src/lib.rs @@ -1,41 +1,45 @@ -use std::ops::Range; - use ast::ValueDeclaration; use cranelift_entity::SecondaryMap; use ir::{ self, builder::{FunctionBuilder, ModuleBuilder}, func_cursor::{CursorLocation, FuncCursor, InsnInserter}, + ir_writer::DebugProvider, isa::IsaBuilder, module::{FuncRef, ModuleCtx}, Module, Signature, }; -use sonatina_triple::InvalidTriple; -use syntax::Rule; +use rustc_hash::{FxHashMap, FxHashSet, FxHasher}; +use smol_str::SmolStr; +use std::hash::BuildHasherDefault; +use syntax::Spanned; pub mod ast; +mod error; pub mod syntax; +pub use error::{Error, UndefinedKind}; +pub use syntax::Span; + +type Bimap = bimap::BiHashMap>; -#[derive(Debug)] -#[allow(clippy::large_enum_variant)] -pub enum Error { - NumberOutOfBounds(Range), - InvalidTarget(InvalidTriple, Range), - SyntaxError(pest::error::Error), +pub struct ParsedModule { + pub module: Module, + pub debug: DebugInfo, } pub fn parse_module(input: &str) -> Result> { let ast = ast::parse(input)?; - let isa = IsaBuilder::new(ast.target.unwrap()).build(); // xxx - let ctx = ModuleCtx::new(isa); - let mut builder = ModuleBuilder::new(ctx); + let isa = IsaBuilder::new(ast.target.unwrap()).build(); + let mut builder = ModuleBuilder::new(ModuleCtx::new(isa)); + + let mut ctx = BuildCtx::default(); for st in ast.struct_types { let fields = st .fields .iter() - .map(|t| build_type(&mut builder, t)) + .map(|t| ctx.type_(&mut builder, t)) .collect::>(); builder.declare_struct_type(&st.name.0, &fields, false); } @@ -44,12 +48,12 @@ pub fn parse_module(input: &str) -> Result> { let params = func .params .iter() - .map(|t| build_type(&mut builder, t)) + .map(|t| ctx.type_(&mut builder, t)) .collect::>(); let ret_ty = func .ret_type .as_ref() - .map(|t| build_type(&mut builder, t)) + .map(|t| ctx.type_(&mut builder, t)) .unwrap_or(ir::Type::Void); let sig = Signature::new(&func.name.0, func.linkage, ¶ms, ret_ty); @@ -61,13 +65,13 @@ pub fn parse_module(input: &str) -> Result> { let args = sig .params .iter() - .map(|decl| build_type(&mut builder, &decl.1)) + .map(|decl| ctx.type_(&mut builder, &decl.1)) .collect::>(); let ret_ty = sig .ret_type .as_ref() - .map(|t| build_type(&mut builder, t)) + .map(|t| ctx.type_(&mut builder, t)) .unwrap_or(ir::Type::Void); let sig = Signature::new(&sig.name.0, sig.linkage, &args, ret_ty); @@ -78,179 +82,269 @@ pub fn parse_module(input: &str) -> Result> { for func in ast.functions { let id = builder.get_func_ref(&func.signature.name.0).unwrap(); - let mut fb = builder.build_function(id); - build_func(&mut fb, &func); - fb.seal_all(); - builder = fb.finish(); + builder = ctx.build_func(builder.build_function(id), id, &func); func_comments[id] = func.comments; } - let module = builder.build(); - Ok(ParsedModule { - module, - module_comments: ast.comments, - func_comments, - }) + if ctx.errors.is_empty() { + let module = builder.build(); + Ok(ParsedModule { + module, + debug: DebugInfo { + module_comments: ast.comments, + func_comments, + value_names: ctx.value_names, + }, + }) + } else { + Err(ctx.errors) + } } -pub struct ParsedModule { - pub module: Module, +pub struct DebugInfo { pub module_comments: Vec, pub func_comments: SecondaryMap>, + pub value_names: FxHashMap>, } -fn build_func(builder: &mut FunctionBuilder, func: &ast::Func) { - for (i, ValueDeclaration(name, _ty)) in func.signature.params.iter().enumerate() { - builder.name_value(builder.func.arg_values[i], &name.0); +impl DebugProvider for DebugInfo { + fn value_name(&self, func: FuncRef, value: ir::Value) -> Option<&str> { + let names = self.value_names.get(&func)?; + names.get_by_left(&value).map(|s| s.as_str()) } +} + +#[derive(Default)] +struct BuildCtx { + errors: Vec, + blocks: FxHashSet, + undefined: FxHashMap, + value_names: FxHashMap>, + func_value_names: Bimap, +} + +impl BuildCtx { + fn build_func( + &mut self, + mut fb: FunctionBuilder, + func_ref: FuncRef, + func: &ast::Func, + ) -> ModuleBuilder { + self.blocks.clear(); + self.undefined.clear(); - // "forward declare" all block ids - if let Some(max_block_id) = func.blocks.iter().map(|b| b.id.0.unwrap()).max() { - while builder.func.dfg.blocks.len() <= max_block_id as usize { - builder.cursor.make_block(&mut builder.func); + for (i, ValueDeclaration(name, _ty)) in func.signature.params.iter().enumerate() { + let value = fb.func.arg_values[i]; + self.name_value(&mut fb.func, value, name); } - } - for block in &func.blocks { - let block_id = ir::Block(block.id.0.unwrap()); - builder.cursor.append_block(&mut builder.func, block_id); - builder - .cursor - .set_location(CursorLocation::BlockTop(block_id)); - - for stmt in &block.stmts { - match &stmt.kind { - ast::StmtKind::Define(ValueDeclaration(val, ty), expr) => { - let ty = build_type(&mut builder.module_builder, ty); - - let result_val = match expr { - ast::Expr::Binary(op, lhs, rhs) => { - let lhs = build_value(builder, lhs); - let rhs = build_value(builder, rhs); - builder.binary_op(*op, lhs, rhs) - } - ast::Expr::Unary(op, val) => { - let val = build_value(builder, val); - builder.unary_op(*op, val) - } - ast::Expr::Cast(op, val) => { - let val = build_value(builder, val); - builder.cast_op(*op, val, ty) - } - ast::Expr::Load(location, addr) => { - let addr = build_value(builder, addr); - match location { - ir::DataLocationKind::Memory => builder.memory_load(addr), - ir::DataLocationKind::Storage => builder.storage_load(addr), + // collect all defined block ids + self.blocks + .extend(func.blocks.iter().map(|b| ir::Block(b.id()))); + if let Some(max) = self.blocks.iter().max() { + while fb.func.dfg.blocks.len() <= max.0 as usize { + fb.cursor.make_block(&mut fb.func); + } + } + + for block in &func.blocks { + let block_id = ir::Block(block.id()); + fb.cursor.append_block(&mut fb.func, block_id); + fb.cursor.set_location(CursorLocation::BlockTop(block_id)); + + for stmt in &block.stmts { + match &stmt.kind { + ast::StmtKind::Define(ValueDeclaration(val, ty), expr) => { + let ty = self.type_(&mut fb.module_builder, ty); + + let result_val = match expr { + ast::Expr::Binary(op, lhs, rhs) => { + let lhs = self.value(&mut fb, lhs); + let rhs = self.value(&mut fb, rhs); + fb.binary_op(*op, lhs, rhs) } + ast::Expr::Unary(op, val) => { + let val = self.value(&mut fb, val); + fb.unary_op(*op, val) + } + ast::Expr::Cast(op, val) => { + let val = self.value(&mut fb, val); + fb.cast_op(*op, val, ty) + } + ast::Expr::Load(location, addr) => { + let addr = self.value(&mut fb, addr); + match location { + ir::DataLocationKind::Memory => fb.memory_load(addr), + ir::DataLocationKind::Storage => fb.storage_load(addr), + } + } + ast::Expr::Alloca(ty) => { + let ty = self.type_(&mut fb.module_builder, ty); + fb.alloca(ty) + } + ast::Expr::Call(ast::Call(name, args)) => { + let func = self.func_ref(&mut fb.module_builder, name); + + let args = args + .iter() + .map(|val| self.value(&mut fb, val)) + .collect::>(); + fb.call(func, &args).unwrap() + } + ast::Expr::Gep(vals) => { + let vals = vals + .iter() + .map(|val| self.value(&mut fb, val)) + .collect::>(); + fb.gep(&vals).unwrap() + } + ast::Expr::Phi(vals) => { + let args = vals + .iter() + .map(|(val, block)| { + let b = self.block(block); + let v = self.value(&mut fb, val); + (v, b) + }) + .collect::>(); + fb.phi(ty, &args) + } + }; + self.name_value(&mut fb.func, result_val, val) + } + ast::StmtKind::Store(loc, addr, val) => { + let addr = self.value(&mut fb, addr); + let val = self.value(&mut fb, val); + + match loc { + ir::DataLocationKind::Memory => fb.memory_store(addr, val), + ir::DataLocationKind::Storage => fb.storage_store(addr, val), } - ast::Expr::Alloca(ty) => { - let ty = build_type(&mut builder.module_builder, ty); - builder.alloca(ty) - } - ast::Expr::Call(ast::Call(name, args)) => { - let func_ref = builder.module_builder.get_func_ref(&name.0).unwrap(); - let args = args - .iter() - .map(|val| build_value(builder, val)) - .collect::>(); - builder.call(func_ref, &args).unwrap() - } - ast::Expr::Gep(vals) => { - let vals = vals - .iter() - .map(|val| build_value(builder, val)) - .collect::>(); - builder.gep(&vals).unwrap() - } - ast::Expr::Phi(vals) => { - let args = vals - .iter() - .map(|(val, block)| { - // xxx declare block - let b = ir::Block(block.0.unwrap()); - let v = build_value(builder, val); - (v, b) - }) - .collect::>(); - builder.phi(ty, &args) - } - }; - builder.name_value(result_val, &val.0) - } - ast::StmtKind::Store(loc, addr, val) => { - let addr = build_value(builder, addr); - let val = build_value(builder, val); + } + ast::StmtKind::Return(val) => { + let val = val.as_ref().map(|v| self.value(&mut fb, v)); + fb.ret(val); + } + ast::StmtKind::Jump(block_id) => { + let block_id = self.block(block_id); + fb.jump(block_id); + } + ast::StmtKind::Branch(cond, true_block, false_block) => { + let cond = self.value(&mut fb, cond); + let true_block = self.block(true_block); + let false_block = self.block(false_block); + fb.br(cond, true_block, false_block); + } + ast::StmtKind::BranchTable(index, default_block, table) => { + let index = self.value(&mut fb, index); + let default_block = default_block.as_ref().map(|b| self.block(b)); - match loc { - ir::DataLocationKind::Memory => builder.memory_store(addr, val), - ir::DataLocationKind::Storage => builder.storage_store(addr, val), + let table = table + .iter() + .map(|(val, block)| { + let block = self.block(block); + (self.value(&mut fb, val), block) + }) + .collect::>(); + fb.br_table(index, default_block, &table); + } + ast::StmtKind::Call(ast::Call(name, args)) => { + let func_ref = self.func_ref(&mut fb.module_builder, name); + + let args = args + .iter() + .map(|val| self.value(&mut fb, val)) + .collect::>(); + fb.call(func_ref, &args).unwrap(); } } - ast::StmtKind::Return(val) => { - let val = val.as_ref().map(|v| build_value(builder, v)); - builder.ret(val); - } - ast::StmtKind::Jump(block_id) => { - let block_id = ir::Block(block_id.0.unwrap()); - builder.jump(block_id); - } - ast::StmtKind::Branch(cond, true_block, false_block) => { - let cond = build_value(builder, cond); - let true_block = ir::Block(true_block.0.unwrap()); - let false_block = ir::Block(false_block.0.unwrap()); - builder.br(cond, true_block, false_block); - } - ast::StmtKind::BranchTable(index, default_block, table) => { - let index = build_value(builder, index); - let default_block = default_block.as_ref().map(|b| ir::Block(b.0.unwrap())); - let table = table - .iter() - .map(|(val, block)| { - (build_value(builder, val), ir::Block(block.0.unwrap())) - }) - .collect::>(); - builder.br_table(index, default_block, &table); - } - ast::StmtKind::Call(ast::Call(name, args)) => { - let func_ref = builder.module_builder.get_func_ref(&name.0).unwrap(); - let args = args - .iter() - .map(|val| build_value(builder, val)) - .collect::>(); - builder.call(func_ref, &args).unwrap(); - } } } + + for (val, span) in self.undefined.drain() { + let name = self.func_value_names.get_by_left(&val).unwrap(); + self.errors + .push(Error::Undefined(UndefinedKind::Value(name.clone()), span)); + } + let names = std::mem::take(&mut self.func_value_names); + self.value_names.insert(func_ref, names); + fb.seal_all(); + fb.finish() + } + + fn func_ref(&mut self, mb: &mut ModuleBuilder, name: &Spanned) -> FuncRef { + mb.get_func_ref(&name.inner.0).unwrap_or_else(|| { + self.errors.push(Error::Undefined( + UndefinedKind::Func(name.inner.0.clone()), + name.span, + )); + FuncRef::from_u32(0) + }) + } + + fn block(&mut self, b: &ast::BlockId) -> ir::Block { + let block = ir::Block(b.id.unwrap()); + if !self.blocks.contains(&block) { + self.errors + .push(Error::Undefined(UndefinedKind::Block(block), b.span)); + } + block + } + + pub fn name_value(&mut self, func: &mut ir::Function, value: ir::Value, name: &ast::ValueName) { + if let Some(v) = self.func_value_names.get_by_right(&name.string) { + if self.undefined.remove(v).is_some() { + func.dfg.change_to_alias(*v, value); + } else { + self.errors + .push(Error::DuplicateValueName(name.string.clone(), name.span)); + } + } + self.func_value_names.insert(value, name.string.clone()); } -} -fn build_value(builder: &mut FunctionBuilder, val: &ast::Value) -> ir::Value { - match val { - ast::Value::Immediate(imm) => builder.make_imm_value(*imm), - ast::Value::Named(v) => builder.get_named_value(&v.0), - ast::Value::Error => unreachable!(), + pub fn get_named_value(&mut self, func: &mut ir::Function, name: &ast::ValueName) -> ir::Value { + if let Some(v) = self.func_value_names.get_by_right(&name.string).copied() { + v + } else { + let v = func.dfg.make_value(ir::ValueData::Immediate { + imm: ir::Immediate::I128(424242), + ty: ir::Type::I128, + }); + + self.undefined.insert(v, name.span); + self.name_value(func, v, name); + v + } } -} -fn build_type(builder: &mut ModuleBuilder, t: &ast::Type) -> ir::Type { - match t { - ast::Type::Int(i) => (*i).into(), - ast::Type::Ptr(t) => { - let t = build_type(builder, t); - builder.ptr_type(t) + fn value(&mut self, fb: &mut FunctionBuilder, val: &ast::Value) -> ir::Value { + match &val.kind { + ast::ValueKind::Immediate(imm) => fb.make_imm_value(*imm), + ast::ValueKind::Named(v) => self.get_named_value(&mut fb.func, v), + ast::ValueKind::Error => unreachable!(), } - ast::Type::Array(t, n) => { - let elem = build_type(builder, t); - builder.declare_array_type(elem, *n) + } + + fn type_(&mut self, mb: &mut ModuleBuilder, t: &ast::Type) -> ir::Type { + match &t.kind { + ast::TypeKind::Int(i) => (*i).into(), + ast::TypeKind::Ptr(t) => { + let t = self.type_(mb, t); + mb.ptr_type(t) + } + ast::TypeKind::Array(t, n) => { + let elem = self.type_(mb, t); + mb.declare_array_type(elem, *n) + } + ast::TypeKind::Void => ir::Type::Void, + ast::TypeKind::Struct(name) => mb.get_struct_type(name).unwrap_or_else(|| { + self.errors + .push(Error::Undefined(UndefinedKind::Type(name.clone()), t.span)); + ir::Type::Void + }), + ast::TypeKind::Error => unreachable!(), } - ast::Type::Void => ir::Type::Void, - ast::Type::Struct(name) => builder.get_struct_type(name).unwrap_or_else(|| { - // xxx error on undeclared struct - eprintln!("struct type not found: {name}"); - ir::Type::Void - }), - ast::Type::Error => todo!(), } } diff --git a/crates/parser/src/syntax.rs b/crates/parser/src/syntax.rs index 8b61c98f..6b64515f 100644 --- a/crates/parser/src/syntax.rs +++ b/crates/parser/src/syntax.rs @@ -7,6 +7,50 @@ use pest::iterators::Pair; #[grammar = "sonatina.pest"] pub struct Parser; +#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)] +pub struct Span(pub u32, pub u32); + +impl Span { + pub fn from_range(r: Range) -> Self { + Self(r.start as u32, r.end as u32) + } + + pub fn as_range(&self) -> Range { + self.0 as usize..self.1 as usize + } +} + +#[derive(Debug, Clone)] +pub struct Spanned { + pub span: Span, + pub inner: T, +} + +impl AsRef for Spanned { + fn as_ref(&self) -> &T { + &self.inner + } +} + +impl AsMut for Spanned { + fn as_mut(&mut self) -> &mut T { + &mut self.inner + } +} + +impl FromSyntax for Spanned +where + T: FromSyntax, +{ + fn from_syntax(node: &mut Node) -> Self { + let inner = T::from_syntax(node); + Self { + span: node.span, + inner, + } + } +} + pub trait FromSyntax { fn from_syntax(node: &mut Node) -> Self; } @@ -14,7 +58,7 @@ pub trait FromSyntax { pub struct Node<'i, E> { pub rule: Rule, pub txt: &'i str, - pub span: Range, + pub span: Span, pairs: Vec>>, pub errors: Vec, child: Option>, @@ -31,7 +75,7 @@ impl<'i, E> Node<'i, E> { self.rule = pair.as_rule(); self.txt = pair.as_str(); let s = pair.as_span(); - self.span = s.start()..s.end(); + self.span = Span::from_range(s.start()..s.end()); self.pairs.clear(); self.pairs.extend(pair.into_inner().map(Some)); debug_assert!(self.errors.is_empty()); @@ -201,50 +245,3 @@ impl<'i, E> std::default::Default for Node<'i, E> { } } } - -// #[cfg(test)] -// mod tests { -// use super::*; - -// #[test] -// fn test_with_module_comment() { -// let input = " -// #! Module comment 1 -// #! Module comment 2 - -// target = \"evm-ethereum-london\" - -// # f1 start 1 -// # f1 start 2 -// func private %f1() -> i32 { -// block0: -// return 311.i32; -// } - -// # f2 start 1 -// # f2 start 2 -// func public %f2() -> i32 { -// block0: -// return 311.i32; -// }"; - -// let parsed_module = parse_module2(input).unwrap(); -// let module_comments = parsed_module.module_comments; -// assert_eq!(module_comments[0], "#! Module comment 1"); -// assert_eq!(module_comments[1], "#! Module comment 2"); - -// let module = parsed_module.module; -// let mut funcs = module.iter_functions(); -// let func1 = funcs.next().unwrap(); -// let func1_comment = &parsed_module.func_comments[func1]; -// assert_eq!(func1_comment.len(), 2); -// assert_eq!(func1_comment[0], "# f1 start 1"); -// assert_eq!(func1_comment[1], "# f1 start 2"); - -// let func2 = funcs.next().unwrap(); -// let func2_comment = &parsed_module.func_comments[func2]; -// assert_eq!(func2_comment.len(), 2); -// assert_eq!(func2_comment[0], "# f2 start 1"); -// assert_eq!(func2_comment[1], "# f2 start 2"); -// } -// } diff --git a/crates/parser/test_files/errors/bad_target.snap b/crates/parser/test_files/errors/bad_target.snap new file mode 100644 index 00000000..183cc190 --- /dev/null +++ b/crates/parser/test_files/errors/bad_target.snap @@ -0,0 +1,11 @@ +--- +source: crates/parser/tests/errors.rs +expression: s +input_file: crates/parser/test_files/errors/bad_target.sntn +--- +error: parse error +--> /Users/sean/code/sonatina-salsa/crates/parser/test_files/errors/bad_target.sntn:0:11 + | +0 | target = "ewasm-ethereum-foo" + | ^^^^^^^^^^^^^^^^^^ given architecture is not supported + | diff --git a/crates/parser/test_files/errors/bad_target.sntn b/crates/parser/test_files/errors/bad_target.sntn new file mode 100644 index 00000000..33a5de25 --- /dev/null +++ b/crates/parser/test_files/errors/bad_target.sntn @@ -0,0 +1 @@ +target = "ewasm-ethereum-foo" diff --git a/crates/parser/test_files/errors/duplicate_val.snap b/crates/parser/test_files/errors/duplicate_val.snap new file mode 100644 index 00000000..3c591d44 --- /dev/null +++ b/crates/parser/test_files/errors/duplicate_val.snap @@ -0,0 +1,11 @@ +--- +source: crates/parser/tests/errors.rs +expression: s +input_file: crates/parser/test_files/errors/duplicate_val.sntn +--- +error: parse error + --> /Users/sean/code/sonatina-salsa/crates/parser/test_files/errors/duplicate_val.sntn:5:9 + | +5 | v0.i8 = add 2.i8 3.i8; + | ^^ value name `v0` is already defined + | diff --git a/crates/parser/test_files/errors/duplicate_val.sntn b/crates/parser/test_files/errors/duplicate_val.sntn new file mode 100644 index 00000000..d89d0029 --- /dev/null +++ b/crates/parser/test_files/errors/duplicate_val.sntn @@ -0,0 +1,8 @@ +target = "evm-ethereum-london" + +func public %main() { + block0: + v0.i8 = add 0.i8 1.i8; + v0.i8 = add 2.i8 3.i8; + return v0; +} diff --git a/crates/parser/test_files/errors/numbers.snap b/crates/parser/test_files/errors/numbers.snap new file mode 100644 index 00000000..a6ffd4b6 --- /dev/null +++ b/crates/parser/test_files/errors/numbers.snap @@ -0,0 +1,21 @@ +--- +source: crates/parser/tests/errors.rs +expression: s +input_file: crates/parser/test_files/errors/numbers.sntn +--- +error: parse error + --> /Users/sean/code/sonatina-salsa/crates/parser/test_files/errors/numbers.sntn:4:27 + | +4 | v0.i8 = call %foo 1000.i8; + | ^^^^ number out of bounds + |error: parse error + --> /Users/sean/code/sonatina-salsa/crates/parser/test_files/errors/numbers.sntn:6:28 + | +6 | v2.i16 = add 1.i16 -50000.i16; + | ^^^^^^ number out of bounds + |error: parse error + --> /Users/sean/code/sonatina-salsa/crates/parser/test_files/errors/numbers.sntn:7:19 + | +7 | jump block203948029830482; + | ^^^^^^^^^^^^^^^ number out of bounds + | diff --git a/crates/parser/test_files/errors/numbers.sntn b/crates/parser/test_files/errors/numbers.sntn new file mode 100644 index 00000000..a825efa8 --- /dev/null +++ b/crates/parser/test_files/errors/numbers.sntn @@ -0,0 +1,9 @@ +target = "evm-ethereum-london" + +func public %main() { + block0: + v0.i8 = call %foo 1000.i8; + v129830918203.i8 = add v0 v0; + v2.i16 = add 1.i16 -50000.i16; + jump block203948029830482; +} diff --git a/crates/parser/test_files/errors/parse_error.snap b/crates/parser/test_files/errors/parse_error.snap new file mode 100644 index 00000000..ade69f9f --- /dev/null +++ b/crates/parser/test_files/errors/parse_error.snap @@ -0,0 +1,16 @@ +--- +source: crates/parser/tests/errors.rs +expression: s +input_file: crates/parser/test_files/errors/parse_error.sntn +--- +error: parse error + --> /Users/sean/code/sonatina-salsa/crates/parser/test_files/errors/parse_error.sntn:3:5 + | +3 | v0.i8 = call %foo 100.i8; + | ^ --> 4:5 + | +4 | v0.i8 = call %foo 100.i8; + | ^--- + | + = expected COMMENT or block_ident + | diff --git a/crates/parser/test_files/errors/parse_error.sntn b/crates/parser/test_files/errors/parse_error.sntn new file mode 100644 index 00000000..a7d0991a --- /dev/null +++ b/crates/parser/test_files/errors/parse_error.sntn @@ -0,0 +1,6 @@ +target = "evm-ethereum-london" + +func %f() { + v0.i8 = call %foo 100.i8; + v1 +} diff --git a/crates/parser/test_files/errors/undefined.snap b/crates/parser/test_files/errors/undefined.snap new file mode 100644 index 00000000..9e51187c --- /dev/null +++ b/crates/parser/test_files/errors/undefined.snap @@ -0,0 +1,26 @@ +--- +source: crates/parser/tests/errors.rs +expression: s +input_file: crates/parser/test_files/errors/undefined.sntn +--- +error: parse error + --> /Users/sean/code/sonatina-salsa/crates/parser/test_files/errors/undefined.sntn:2:25 + | +2 | type %foo = { i8, i16, *%s1 }; + | ^^^ undefined type: `%s1` + |error: parse error + --> /Users/sean/code/sonatina-salsa/crates/parser/test_files/errors/undefined.sntn:6:22 + | +6 | v0.i8 = call %foo 100.i8; + | ^^^^ undefined function: `%foo` + |error: parse error + --> /Users/sean/code/sonatina-salsa/crates/parser/test_files/errors/undefined.sntn:9:14 + | +9 | jump block2; + | ^^^^^^ undefined block: `block2` + |error: parse error + --> /Users/sean/code/sonatina-salsa/crates/parser/test_files/errors/undefined.sntn:7:24 + | +7 | v2.i8 = add v0 v1; + | ^^ undefined value: `v1` + | diff --git a/crates/parser/test_files/errors/undefined.sntn b/crates/parser/test_files/errors/undefined.sntn new file mode 100644 index 00000000..1a0769ec --- /dev/null +++ b/crates/parser/test_files/errors/undefined.sntn @@ -0,0 +1,13 @@ +target = "evm-ethereum-london" + +type %foo = { i8, i16, *%s1 }; + +func public %main() { + block0: + v0.i8 = call %foo 100.i8; + v2.i8 = add v0 v1; + v3.i8 = add v1 v1; + jump block2; + block1: + return; +} diff --git a/crates/parser/test_files/syntax/module/simple.ast.snap b/crates/parser/test_files/syntax/module/simple.ast.snap index 38f631a5..59b6c67d 100644 --- a/crates/parser/test_files/syntax/module/simple.ast.snap +++ b/crates/parser/test_files/syntax/module/simple.ast.snap @@ -1,7 +1,7 @@ --- -source: crates/parser2/tests/syntax.rs +source: crates/parser/tests/syntax.rs expression: "format!(\"{:#?}\", module)" -input_file: crates/parser2/test_files/syntax/module/simple.sntn +input_file: crates/parser/test_files/syntax/module/simple.sntn --- Module { target: Some( @@ -20,17 +20,35 @@ Module { "add_i8", ), params: [ - Int( - I8, - ), - Int( - I8, - ), + Type { + kind: Int( + I8, + ), + span: Span( + 100, + 102, + ), + }, + Type { + kind: Int( + I8, + ), + span: Span( + 104, + 106, + ), + }, ], ret_type: Some( - Int( - I8, - ), + Type { + kind: Int( + I8, + ), + span: Span( + 111, + 113, + ), + }, ), }, ], @@ -40,17 +58,41 @@ Module { "foo", ), fields: [ - Int( - I8, - ), - Int( - I16, - ), - Ptr( - Int( - I64, + Type { + kind: Int( + I8, ), - ), + span: Span( + 130, + 132, + ), + }, + Type { + kind: Int( + I16, + ), + span: Span( + 134, + 137, + ), + }, + Type { + kind: Ptr( + Type { + kind: Int( + I64, + ), + span: Span( + 140, + 143, + ), + }, + ), + span: Span( + 139, + 143, + ), + }, ], packed: false, }, @@ -59,15 +101,33 @@ Module { "bar", ), fields: [ - Int( - I8, - ), - Array( - Int( + Type { + kind: Int( I8, ), - 31, - ), + span: Span( + 162, + 164, + ), + }, + Type { + kind: Array( + Type { + kind: Int( + I8, + ), + span: Span( + 167, + 169, + ), + }, + 31, + ), + span: Span( + 166, + 174, + ), + }, ], packed: true, }, @@ -84,33 +144,59 @@ Module { }, blocks: [ Block { - id: BlockId( - Some( + id: BlockId { + id: Some( 0, ), - ), + span: Span( + 206, + 212, + ), + }, stmts: [ Stmt { kind: Define( ValueDeclaration( - ValueName( - "v0", - ), - Int( - I8, - ), + ValueName { + string: "v0", + span: Span( + 222, + 224, + ), + }, + Type { + kind: Int( + I8, + ), + span: Span( + 225, + 227, + ), + }, ), Call( Call( - FunctionName( - "foo", - ), + Spanned { + span: Span( + 235, + 239, + ), + inner: FunctionName( + "foo", + ), + }, [ - Immediate( - I8( - 100, + Value { + kind: Immediate( + I8( + 100, + ), ), - ), + span: Span( + 240, + 243, + ), + }, ], ), ), @@ -134,79 +220,161 @@ Module { ), params: [ ValueDeclaration( - ValueName( - "v0", - ), - Int( - I8, - ), + ValueName { + string: "v0", + span: Span( + 307, + 309, + ), + }, + Type { + kind: Int( + I8, + ), + span: Span( + 310, + 312, + ), + }, ), ], ret_type: Some( - Int( - I8, - ), + Type { + kind: Int( + I8, + ), + span: Span( + 317, + 319, + ), + }, ), }, blocks: [ Block { - id: BlockId( - Some( + id: BlockId { + id: Some( 0, ), - ), + span: Span( + 322, + 328, + ), + }, stmts: [ Stmt { kind: Define( ValueDeclaration( - ValueName( - "v1", - ), - Int( - I8, - ), + ValueName { + string: "v1", + span: Span( + 330, + 332, + ), + }, + Type { + kind: Int( + I8, + ), + span: Span( + 333, + 335, + ), + }, ), Binary( Mul, - Named( - ValueName( - "v0", + Value { + kind: Named( + ValueName { + string: "v0", + span: Span( + 342, + 344, + ), + }, ), - ), - Immediate( - I8( - 2, + span: Span( + 342, + 344, ), - ), + }, + Value { + kind: Immediate( + I8( + 2, + ), + ), + span: Span( + 345, + 346, + ), + }, ), ), }, Stmt { kind: Define( ValueDeclaration( - ValueName( - "v2", - ), - Int( - I8, - ), + ValueName { + string: "v2", + span: Span( + 351, + 353, + ), + }, + Type { + kind: Int( + I8, + ), + span: Span( + 354, + 356, + ), + }, ), Call( Call( - FunctionName( - "add_i8", - ), + Spanned { + span: Span( + 364, + 371, + ), + inner: FunctionName( + "add_i8", + ), + }, [ - Named( - ValueName( - "v0", + Value { + kind: Named( + ValueName { + string: "v0", + span: Span( + 372, + 374, + ), + }, ), - ), - Named( - ValueName( - "v1", + span: Span( + 372, + 374, ), - ), + }, + Value { + kind: Named( + ValueName { + string: "v1", + span: Span( + 375, + 377, + ), + }, + ), + span: Span( + 375, + 377, + ), + }, ], ), ), @@ -215,11 +383,21 @@ Module { Stmt { kind: Return( Some( - Named( - ValueName( - "v2", + Value { + kind: Named( + ValueName { + string: "v2", + span: Span( + 386, + 388, + ), + }, ), - ), + span: Span( + 386, + 388, + ), + }, ), ), }, @@ -238,81 +416,187 @@ Module { ), params: [ ValueDeclaration( - ValueName( - "v0", - ), - Ptr( - Int( - I8, + ValueName { + string: "v0", + span: Span( + 405, + 407, ), - ), + }, + Type { + kind: Ptr( + Type { + kind: Int( + I8, + ), + span: Span( + 409, + 411, + ), + }, + ), + span: Span( + 408, + 411, + ), + }, ), ValueDeclaration( - ValueName( - "v1", - ), - Array( - Int( - I8, + ValueName { + string: "v1", + span: Span( + 413, + 415, ), - 2, - ), + }, + Type { + kind: Array( + Type { + kind: Int( + I8, + ), + span: Span( + 417, + 419, + ), + }, + 2, + ), + span: Span( + 416, + 423, + ), + }, ), ValueDeclaration( - ValueName( - "v2", - ), - Array( - Ptr( - Int( - I8, - ), + ValueName { + string: "v2", + span: Span( + 425, + 427, ), - 2, - ), + }, + Type { + kind: Array( + Type { + kind: Ptr( + Type { + kind: Int( + I8, + ), + span: Span( + 430, + 432, + ), + }, + ), + span: Span( + 429, + 432, + ), + }, + 2, + ), + span: Span( + 428, + 436, + ), + }, ), ValueDeclaration( - ValueName( - "v3", - ), - Array( - Array( - Int( - I8, - ), + ValueName { + string: "v3", + span: Span( + 438, + 440, + ), + }, + Type { + kind: Array( + Type { + kind: Array( + Type { + kind: Int( + I8, + ), + span: Span( + 443, + 445, + ), + }, + 2, + ), + span: Span( + 442, + 449, + ), + }, 2, ), - 2, - ), + span: Span( + 441, + 453, + ), + }, ), ValueDeclaration( - ValueName( - "v4", - ), - Struct( - "foo", - ), + ValueName { + string: "v4", + span: Span( + 455, + 457, + ), + }, + Type { + kind: Struct( + "foo", + ), + span: Span( + 458, + 462, + ), + }, ), ValueDeclaration( - ValueName( - "v5", - ), - Ptr( - Struct( - "foo", + ValueName { + string: "v5", + span: Span( + 464, + 466, ), - ), + }, + Type { + kind: Ptr( + Type { + kind: Struct( + "foo", + ), + span: Span( + 468, + 472, + ), + }, + ), + span: Span( + 467, + 472, + ), + }, ), ], ret_type: None, }, blocks: [ Block { - id: BlockId( - Some( + id: BlockId { + id: Some( 0, ), - ), + span: Span( + 480, + 486, + ), + }, stmts: [ Stmt { kind: Return( @@ -332,62 +616,110 @@ Module { ), params: [ ValueDeclaration( - ValueName( - "v0", - ), - Int( - I8, - ), + ValueName { + string: "v0", + span: Span( + 519, + 521, + ), + }, + Type { + kind: Int( + I8, + ), + span: Span( + 522, + 524, + ), + }, ), ], ret_type: None, }, blocks: [ Block { - id: BlockId( - Some( + id: BlockId { + id: Some( 0, ), - ), + span: Span( + 532, + 538, + ), + }, stmts: [ Stmt { kind: BranchTable( - Named( - ValueName( - "v0", + Value { + kind: Named( + ValueName { + string: "v0", + span: Span( + 557, + 559, + ), + }, ), - ), + span: Span( + 557, + 559, + ), + }, Some( - BlockId( - Some( + BlockId { + id: Some( 0, ), - ), + span: Span( + 560, + 566, + ), + }, ), [ ( - Immediate( - I8( - 1, + Value { + kind: Immediate( + I8( + 1, + ), ), - ), - BlockId( - Some( + span: Span( + 568, + 569, + ), + }, + BlockId { + id: Some( 1, ), - ), + span: Span( + 573, + 579, + ), + }, ), ( - Immediate( - I8( - 2, + Value { + kind: Immediate( + I8( + 2, + ), ), - ), - BlockId( - Some( + span: Span( + 582, + 583, + ), + }, + BlockId { + id: Some( 2, ), - ), + span: Span( + 587, + 593, + ), + }, ), ], ), @@ -395,40 +727,60 @@ Module { ], }, Block { - id: BlockId( - Some( + id: BlockId { + id: Some( 1, ), - ), + span: Span( + 600, + 606, + ), + }, stmts: [ Stmt { kind: Return( Some( - Immediate( - I8( - 1, + Value { + kind: Immediate( + I8( + 1, + ), ), - ), + span: Span( + 623, + 624, + ), + }, ), ), }, ], }, Block { - id: BlockId( - Some( + id: BlockId { + id: Some( 2, ), - ), + span: Span( + 633, + 639, + ), + }, stmts: [ Stmt { kind: Return( Some( - Immediate( - I8( - 2, + Value { + kind: Immediate( + I8( + 2, + ), ), - ), + span: Span( + 656, + 657, + ), + }, ), ), }, @@ -445,81 +797,143 @@ Module { ), params: [ ValueDeclaration( - ValueName( - "v0", - ), - Int( - I64, - ), + ValueName { + string: "v0", + span: Span( + 676, + 678, + ), + }, + Type { + kind: Int( + I64, + ), + span: Span( + 679, + 682, + ), + }, ), ], ret_type: Some( - Int( - I64, - ), + Type { + kind: Int( + I64, + ), + span: Span( + 687, + 690, + ), + }, ), }, blocks: [ Block { - id: BlockId( - Some( + id: BlockId { + id: Some( 0, ), - ), + span: Span( + 697, + 703, + ), + }, stmts: [ Stmt { kind: Jump( - BlockId( - Some( + BlockId { + id: Some( 1, ), - ), + span: Span( + 718, + 724, + ), + }, ), }, ], }, Block { - id: BlockId( - Some( + id: BlockId { + id: Some( 1, ), - ), + span: Span( + 730, + 736, + ), + }, stmts: [ Stmt { kind: Define( ValueDeclaration( - ValueName( - "v1", - ), - Int( - I64, - ), + ValueName { + string: "v1", + span: Span( + 746, + 748, + ), + }, + Type { + kind: Int( + I64, + ), + span: Span( + 749, + 752, + ), + }, ), Phi( [ ( - Named( - ValueName( - "v0", + Value { + kind: Named( + ValueName { + string: "v0", + span: Span( + 760, + 762, + ), + }, ), - ), - BlockId( - Some( + span: Span( + 760, + 762, + ), + }, + BlockId { + id: Some( 0, ), - ), + span: Span( + 763, + 769, + ), + }, ), ( - Immediate( - I64( - 100, + Value { + kind: Immediate( + I64( + 100, + ), ), - ), - BlockId( - Some( + span: Span( + 772, + 775, + ), + }, + BlockId { + id: Some( 2, ), - ), + span: Span( + 780, + 786, + ), + }, ), ], ), @@ -528,82 +942,148 @@ Module { Stmt { kind: Define( ValueDeclaration( - ValueName( - "v2", - ), - Int( - I1, - ), + ValueName { + string: "v2", + span: Span( + 797, + 799, + ), + }, + Type { + kind: Int( + I1, + ), + span: Span( + 800, + 802, + ), + }, ), Binary( Gt, - Named( - ValueName( - "v1", + Value { + kind: Named( + ValueName { + string: "v1", + span: Span( + 808, + 810, + ), + }, ), - ), - Immediate( - I64( - 10, + span: Span( + 808, + 810, ), - ), + }, + Value { + kind: Immediate( + I64( + 10, + ), + ), + span: Span( + 811, + 813, + ), + }, ), ), }, Stmt { kind: Branch( - Named( - ValueName( - "v2", + Value { + kind: Named( + ValueName { + string: "v2", + span: Span( + 830, + 832, + ), + }, ), - ), - BlockId( - Some( + span: Span( + 830, + 832, + ), + }, + BlockId { + id: Some( 2, ), - ), - BlockId( - Some( + span: Span( + 833, + 839, + ), + }, + BlockId { + id: Some( 3, ), - ), + span: Span( + 840, + 846, + ), + }, ), }, ], }, Block { - id: BlockId( - Some( + id: BlockId { + id: Some( 2, ), - ), + span: Span( + 852, + 858, + ), + }, stmts: [ Stmt { kind: Jump( - BlockId( - Some( + BlockId { + id: Some( 1, ), - ), + span: Span( + 873, + 879, + ), + }, ), }, ], }, Block { - id: BlockId( - Some( + id: BlockId { + id: Some( 3, ), - ), + span: Span( + 885, + 891, + ), + }, stmts: [ Stmt { kind: Return( Some( - Named( - ValueName( - "v1", + Value { + kind: Named( + ValueName { + string: "v1", + span: Span( + 908, + 910, + ), + }, ), - ), + span: Span( + 908, + 910, + ), + }, ), ), }, diff --git a/crates/parser/tests/common/mod.rs b/crates/parser/tests/common/mod.rs new file mode 100644 index 00000000..71f72834 --- /dev/null +++ b/crates/parser/tests/common/mod.rs @@ -0,0 +1,40 @@ +// copied from fe test-utils +/// A macro to assert that a value matches a snapshot. +/// If the snapshot does not exist, it will be created in the same directory as +/// the test file. +#[macro_export] +macro_rules! snap_test { + ($value:expr, $fixture_path: expr) => { + snap_test!($value, $fixture_path, None) + }; + + ($value:expr, $fixture_path: expr, $suffix: expr) => { + let mut settings = insta::Settings::new(); + let fixture_path = ::std::path::Path::new($fixture_path); + let fixture_dir = fixture_path.parent().unwrap(); + let fixture_name = fixture_path.file_stem().unwrap().to_str().unwrap(); + + settings.set_snapshot_path(fixture_dir); + settings.set_input_file($fixture_path); + settings.set_prepend_module_to_snapshot(false); + let suffix: Option<&str> = $suffix; + let name = if let Some(suffix) = suffix { + format!("{fixture_name}.{suffix}") + } else { + fixture_name.into() + }; + settings.bind(|| { + insta::_macro_support::assert_snapshot( + name.into(), + &$value, + env!("CARGO_MANIFEST_DIR"), + fixture_name, + module_path!(), + file!(), + line!(), + stringify!($value), + ) + .unwrap() + }) + }; +} diff --git a/crates/parser/tests/errors.rs b/crates/parser/tests/errors.rs new file mode 100644 index 00000000..74481553 --- /dev/null +++ b/crates/parser/tests/errors.rs @@ -0,0 +1,20 @@ +use dir_test::{dir_test, Fixture}; +use sonatina_parser::parse_module; +mod common; + +#[dir_test( + dir: "$CARGO_MANIFEST_DIR/test_files/errors/", + glob: "*.sntn" +)] +fn test_errors(fixture: Fixture<&str>) { + let Err(errs) = parse_module(fixture.content()) else { + panic!("expected parse_module to fail with errors"); + }; + let mut v = vec![]; + for err in errs { + err.print(&mut v, fixture.path(), fixture.content(), false) + .unwrap(); + } + let s = String::from_utf8(v).unwrap(); + snap_test!(s, fixture.path()); +} diff --git a/crates/parser/tests/syntax.rs b/crates/parser/tests/syntax.rs index d1d6f3b9..332b1f75 100644 --- a/crates/parser/tests/syntax.rs +++ b/crates/parser/tests/syntax.rs @@ -8,6 +8,7 @@ use sonatina_parser::{ Error, }; use std::fmt::{self, Write}; +mod common; #[dir_test( dir: "$CARGO_MANIFEST_DIR/test_files/syntax/stmts", @@ -48,7 +49,7 @@ fn test_module_ast(fixture: Fixture<&str>) { )] fn test_module_ir(fixture: Fixture<&str>) { let module = parse_module(fixture.content()).unwrap(); - let mut w = ModuleWriter::new(&module.module); + let mut w = ModuleWriter::with_debug_provider(&module.module, &module.debug); snap_test!(w.dump_string().unwrap(), fixture.path(), Some("ir")); } @@ -66,7 +67,7 @@ fn test_rule(rule: Rule, fixture: Fixture<&str>) { } fn report_error(err: pest::error::Error, fixture: &Fixture<&str>) { - let s = Error::SyntaxError(err).print_to_string(fixture.path(), fixture.content()); + let s = Error::SyntaxError(err).print_to_string(fixture.path(), fixture.content(), true); eprintln!("{s}"); } @@ -85,46 +86,3 @@ impl<'i> fmt::Debug for PairsWrapper<'i> { Ok(()) } } - -// xxx copied from fe test-utils -#[doc(hidden)] -pub use insta as _insta; -/// A macro to assert that a value matches a snapshot. -/// If the snapshot does not exist, it will be created in the same directory as -/// the test file. -#[macro_export] -macro_rules! snap_test { - ($value:expr, $fixture_path: expr) => { - snap_test!($value, $fixture_path, None) - }; - - ($value:expr, $fixture_path: expr, $suffix: expr) => { - let mut settings = insta::Settings::new(); - let fixture_path = ::std::path::Path::new($fixture_path); - let fixture_dir = fixture_path.parent().unwrap(); - let fixture_name = fixture_path.file_stem().unwrap().to_str().unwrap(); - - settings.set_snapshot_path(fixture_dir); - settings.set_input_file($fixture_path); - settings.set_prepend_module_to_snapshot(false); - let suffix: Option<&str> = $suffix; - let name = if let Some(suffix) = suffix { - format!("{fixture_name}.{suffix}") - } else { - fixture_name.into() - }; - settings.bind(|| { - insta::_macro_support::assert_snapshot( - name.into(), - &$value, - env!("CARGO_MANIFEST_DIR"), - fixture_name, - module_path!(), - file!(), - line!(), - stringify!($value), - ) - .unwrap() - }) - }; -}