diff --git a/lib/shiika_ast/src/location.rs b/lib/shiika_ast/src/location.rs index 73d8eb7f..0eb96f58 100644 --- a/lib/shiika_ast/src/location.rs +++ b/lib/shiika_ast/src/location.rs @@ -16,39 +16,82 @@ impl Location { /// Range in a source file (end-exclusive) #[derive(Debug, PartialEq, Clone)] -pub struct LocationSpan { - pub filepath: Rc, - pub begin: Location, - pub end: Location, +pub enum LocationSpan { + Empty, + Just { + filepath: Rc, + begin: Location, + end: Location, + }, } impl LocationSpan { pub fn new(filepath: &Rc, begin: Location, end: Location) -> LocationSpan { - LocationSpan { + if begin.pos > end.pos { + println!( + "[BUG] invalid LocationSpan pos (begin: {}, end: {})", + begin.pos, end.pos + ); + return LocationSpan::internal(); + } + LocationSpan::Just { filepath: filepath.clone(), begin, end, } } - pub fn begin_end(&self) -> (Location, Location) { - (self.begin.clone(), self.end.clone()) + pub fn merge(begin: &LocationSpan, end: &LocationSpan) -> LocationSpan { + match (begin, end) { + (LocationSpan::Empty, LocationSpan::Empty) => LocationSpan::Empty, + (LocationSpan::Just { .. }, LocationSpan::Empty) => begin.clone(), + (LocationSpan::Empty, LocationSpan::Just { .. }) => end.clone(), + ( + LocationSpan::Just { + filepath, begin, .. + }, + LocationSpan::Just { + filepath: filepath2, + end, + .. + }, + ) if filepath == filepath2 => Self::new(&filepath, begin.clone(), end.clone()), + _ => { + println!( + "[BUG] invalid LocationSpan (begin: {:?}, end: {:?})", + begin, end + ); + LocationSpan::Empty + } + } + } + + pub fn get_begin(&self) -> Location { + match self { + LocationSpan::Just { begin, .. } => begin.clone(), + _ => panic!("get_end called on Empty"), + } + } + + pub fn get_end(&self) -> Location { + match self { + LocationSpan::Just { end, .. } => end.clone(), + _ => panic!("get_end called on Empty"), + } + } + + // fn begin_end(&self) -> (Location, Location) { + // (self.begin.clone(), self.end.clone()) + // } + + /// Denotes that this ast or hir does not correspond to any source text. + pub fn internal() -> LocationSpan { + LocationSpan::Empty } + // Used as placeholder. // TODO: remove this pub fn todo() -> LocationSpan { - LocationSpan { - filepath: Rc::new(PathBuf::new()), - begin: Location { - line: 0, - col: 0, - pos: 0, - }, - end: Location { - line: 0, - col: 0, - pos: 0, - }, - } + LocationSpan::Empty } } diff --git a/lib/shiika_parser/src/ast_builder.rs b/lib/shiika_parser/src/ast_builder.rs index f1bcf5bb..93374c2e 100644 --- a/lib/shiika_parser/src/ast_builder.rs +++ b/lib/shiika_parser/src/ast_builder.rs @@ -50,9 +50,9 @@ impl AstBuilder { } pub fn logical_and(&self, left: AstExpression, right: AstExpression) -> AstExpression { - self.primary_expression( - left.locs.begin.clone(), - right.locs.end.clone(), + self.primary_expression_( + &left.locs.clone(), + &right.locs.clone(), AstExpressionBody::LogicalAnd { left: Box::new(left), right: Box::new(right), @@ -61,9 +61,9 @@ impl AstBuilder { } pub fn logical_or(&self, left: AstExpression, right: AstExpression) -> AstExpression { - self.primary_expression( - left.locs.begin.clone(), - right.locs.end.clone(), + self.primary_expression_( + &left.locs.clone(), + &right.locs.clone(), AstExpressionBody::LogicalOr { left: Box::new(left), right: Box::new(right), @@ -328,6 +328,19 @@ impl AstBuilder { } } + fn primary_expression_( + &self, + begin: &LocationSpan, + end: &LocationSpan, + body: AstExpressionBody, + ) -> AstExpression { + AstExpression { + primary: true, + body, + locs: LocationSpan::merge(begin, end), + } + } + fn non_primary_expression( &self, begin: Location, @@ -341,6 +354,19 @@ impl AstBuilder { } } + fn non_primary_expression_( + &self, + begin: &LocationSpan, + end: &LocationSpan, + body: AstExpressionBody, + ) -> AstExpression { + AstExpression { + primary: false, + body, + locs: LocationSpan::merge(begin, end), + } + } + /// Create an expression of the form `left right` pub fn bin_op_expr( &self, @@ -348,11 +374,9 @@ impl AstBuilder { op: &str, right: AstExpression, ) -> AstExpression { - let begin = left.locs.begin.clone(); - let end = right.locs.end.clone(); - self.non_primary_expression( - begin, - end, + self.non_primary_expression_( + &left.locs.clone(), + &right.locs.clone(), AstExpressionBody::MethodCall(AstMethodCall { receiver_expr: Some(Box::new(left)), method_name: method_firstname(op), @@ -366,8 +390,8 @@ impl AstBuilder { /// Create an expression of the form `lhs = rhs` pub fn assignment(&self, lhs: AstExpression, rhs: AstExpression) -> AstExpression { - let begin = lhs.locs.begin.clone(); - let end = rhs.locs.end.clone(); + let begin = &lhs.locs.clone(); + let end = &rhs.locs.clone(); let body = match lhs.body { AstExpressionBody::BareName(s) => AstExpressionBody::LVarAssign { name: s, @@ -396,7 +420,7 @@ impl AstBuilder { } _ => panic!("[BUG] unexpectd lhs: {:?}", lhs.body), }; - self.non_primary_expression(begin, end, body) + self.non_primary_expression_(begin, end, body) } /// Extend `foo.bar` to `foo.bar args`, or @@ -408,8 +432,8 @@ impl AstBuilder { args: Vec, has_block: bool, ) -> AstExpression { - let begin = expr.locs.begin.clone(); - let end = args.last().unwrap().locs.end.clone(); + let begin = &expr.locs; + let end = &args.last().unwrap().locs.clone(); match expr.body { AstExpressionBody::MethodCall(x) => { if !x.arg_exprs.is_empty() { @@ -418,7 +442,7 @@ impl AstBuilder { x.arg_exprs ); } - self.non_primary_expression( + self.non_primary_expression_( begin, end, AstExpressionBody::MethodCall(AstMethodCall { @@ -428,7 +452,7 @@ impl AstBuilder { }), ) } - AstExpressionBody::BareName(s) => self.non_primary_expression( + AstExpressionBody::BareName(s) => self.non_primary_expression_( begin, end, AstExpressionBody::MethodCall(AstMethodCall { diff --git a/lib/shiika_parser/src/definition_parser.rs b/lib/shiika_parser/src/definition_parser.rs index 41fc57d0..111b2cff 100644 --- a/lib/shiika_parser/src/definition_parser.rs +++ b/lib/shiika_parser/src/definition_parser.rs @@ -362,13 +362,16 @@ impl<'a> Parser<'a> { .iter() .filter(|param| param.is_iparam) .map(|param| { - let span = LocationSpan::todo(); + let loc = Location { + pos: 0, + col: 0, + line: 0, + }; self.ast.ivar_assign( param.name.clone(), - self.ast - .bare_name(¶m.name, span.begin.clone(), span.end.clone()), - span.begin, - span.end, + self.ast.bare_name(¶m.name, loc.clone(), loc.clone()), + loc.clone(), + loc.clone(), ) }) .collect() diff --git a/lib/shiika_parser/src/expression_parser.rs b/lib/shiika_parser/src/expression_parser.rs index 5a63269a..310d246b 100644 --- a/lib/shiika_parser/src/expression_parser.rs +++ b/lib/shiika_parser/src/expression_parser.rs @@ -587,7 +587,7 @@ impl<'a> Parser<'a> { self.parse_exprs(vec![Token::KwEnd, Token::KwElse, Token::KwElsif])?; self.skip_wsn()?; let cont = self._parse_if_expr(cond_expr2, then_exprs2, begin.clone())?; - let end = cont.locs.end.clone(); + let end = cont.locs.get_end(); Ok(self .ast .if_expr(cond_expr, then_exprs, Some(vec![cont]), begin, end)) @@ -749,6 +749,7 @@ impl<'a> Parser<'a> { fn parse_method_chain(&mut self, expr: AstExpression) -> Result { self.lv += 1; self.debug_log("parse_method_chain"); + let begin = self.lexer.location(); // . self.set_lexer_state(LexerState::MethodName); assert!(self.consume(Token::Dot)?); @@ -786,7 +787,6 @@ impl<'a> Parser<'a> { }; self.lv -= 1; - let begin = expr.locs.begin.clone(); let end = self.lexer.location(); Ok(self.ast.method_call( true, diff --git a/lib/skc_ast2hir/src/convert_exprs.rs b/lib/skc_ast2hir/src/convert_exprs.rs index ffc5db71..8049449c 100644 --- a/lib/skc_ast2hir/src/convert_exprs.rs +++ b/lib/skc_ast2hir/src/convert_exprs.rs @@ -19,6 +19,8 @@ use skc_hir::*; struct LVarInfo { ty: TermTy, detail: LVarDetail, + /// The position of this lvar in the source text + locs: LocationSpan, } #[derive(Debug)] enum LVarDetail { @@ -36,30 +38,22 @@ enum LVarDetail { impl LVarInfo { /// Returns HirExpression to refer this lvar - fn ref_expr(&self) -> HirExpression { - match &self.detail { - LVarDetail::CurrentScope { name } => { - Hir::lvar_ref(self.ty.clone(), name.clone(), LocationSpan::todo()) - } - LVarDetail::Argument { idx } => { - Hir::arg_ref(self.ty.clone(), *idx, LocationSpan::todo()) - } + fn ref_expr(self) -> HirExpression { + match self.detail { + LVarDetail::CurrentScope { name } => Hir::lvar_ref(self.ty, name, self.locs), + LVarDetail::Argument { idx } => Hir::arg_ref(self.ty, idx, self.locs), LVarDetail::OuterScope { cidx, readonly } => { - Hir::lambda_capture_ref(self.ty.clone(), *cidx, *readonly, LocationSpan::todo()) + Hir::lambda_capture_ref(self.ty, cidx, readonly, self.locs) } } } /// Returns HirExpression to update this lvar - fn assign_expr(&self, expr: HirExpression) -> HirExpression { - match &self.detail { - LVarDetail::CurrentScope { name, .. } => { - Hir::lvar_assign(name, expr, LocationSpan::todo()) - } + fn assign_expr(self, expr: HirExpression) -> HirExpression { + match self.detail { + LVarDetail::CurrentScope { name, .. } => Hir::lvar_assign(name, expr, self.locs), LVarDetail::Argument { .. } => panic!("[BUG] Cannot reassign argument"), - LVarDetail::OuterScope { cidx, .. } => { - Hir::lambda_capture_write(*cidx, expr, LocationSpan::todo()) - } + LVarDetail::OuterScope { cidx, .. } => Hir::lambda_capture_write(cidx, expr, self.locs), } } } @@ -147,7 +141,7 @@ impl<'hir_maker> HirMaker<'hir_maker> { } AstExpressionBody::SpecializeExpression { base_name, args } => { - self.convert_specialize_expr(base_name, args) + self.convert_specialize_expr(base_name, args, &expr.locs) } AstExpressionBody::PseudoVariable(token) => { @@ -366,13 +360,13 @@ impl<'hir_maker> HirMaker<'hir_maker> { ) -> Result { let expr = self.convert_expr(rhs)?; // For `var x`, `x` should not be exist - if *is_var && self._lookup_var(name).is_some() { + if *is_var && self._lookup_var(name, locs.clone()).is_some() { return Err(error::program_error(&format!( "variable `{}' already exists", name ))); } - if let Some(mut lvar_info) = self._find_var(name, true)? { + if let Some(mut lvar_info) = self._find_var(name, locs.clone(), true)? { // Reassigning if lvar_info.ty != expr.ty { if let Some(t) = self @@ -393,7 +387,7 @@ impl<'hir_maker> HirMaker<'hir_maker> { } else { // Create new lvar self.ctx_stack.declare_lvar(name, expr.ty.clone(), !is_var); - Ok(Hir::lvar_assign(name, expr, locs.clone())) + Ok(Hir::lvar_assign(name.to_string(), expr, locs.clone())) } } @@ -509,7 +503,7 @@ impl<'hir_maker> HirMaker<'hir_maker> { ) -> Result { // Check if this is a lambda invocation if receiver_expr.is_none() { - if let Some(lvar) = self._lookup_var(&method_name.0) { + if let Some(lvar) = self._lookup_var(&method_name.0, locs.clone()) { if let Some(tys) = lvar.ty.fn_x_info() { let arg_hirs = method_call::convert_method_args( self, @@ -650,9 +644,9 @@ impl<'hir_maker> HirMaker<'hir_maker> { } /// Generate local variable reference or method call with implicit receiver(self) - fn convert_bare_name(&mut self, name: &str, _locs: &LocationSpan) -> Result { + fn convert_bare_name(&mut self, name: &str, locs: &LocationSpan) -> Result { // Found a local variable - if let Some(lvar_info) = self._find_var(name, false)? { + if let Some(lvar_info) = self._find_var(name, locs.clone(), false)? { return Ok(lvar_info.ref_expr()); } @@ -672,19 +666,24 @@ impl<'hir_maker> HirMaker<'hir_maker> { } /// Return the variable of the given name, if any - fn _lookup_var(&mut self, name: &str) -> Option { - self._find_var(name, false).unwrap() + fn _lookup_var(&mut self, name: &str, locs: LocationSpan) -> Option { + self._find_var(name, locs, false).unwrap() } /// Find the variable of the given name. /// If it is a free variable, lambda_ctx.captures will be modified - fn _find_var(&mut self, name: &str, updating: bool) -> Result> { + fn _find_var( + &mut self, + name: &str, + locs: LocationSpan, + updating: bool, + ) -> Result> { let (in_lambda, cidx) = if let Some(lambda_ctx) = self.ctx_stack.lambda_ctx() { (true, lambda_ctx.captures.len()) } else { (false, 0) }; - let (found, opt_cap) = self.__find_var(in_lambda, cidx, name, updating)?; + let (found, opt_cap) = self.__find_var(in_lambda, cidx, name, locs, updating)?; if let Some(cap) = opt_cap { self.ctx_stack.push_lambda_capture(cap); } @@ -696,6 +695,7 @@ impl<'hir_maker> HirMaker<'hir_maker> { in_lambda: bool, cidx: usize, name: &str, + locs: LocationSpan, updating: bool, ) -> Result<(Option, Option)> { let mut lambda_seen = false; @@ -722,6 +722,7 @@ impl<'hir_maker> HirMaker<'hir_maker> { cidx, readonly: false, }, + locs, }; return Ok((Some(lvar_info), Some(cap))); } else { @@ -730,6 +731,7 @@ impl<'hir_maker> HirMaker<'hir_maker> { detail: LVarDetail::CurrentScope { name: name.to_string(), }, + locs, }; return Ok((Some(lvar_info), None)); } @@ -753,12 +755,14 @@ impl<'hir_maker> HirMaker<'hir_maker> { cidx, readonly: true, }, + locs, }; return Ok((Some(lvar_info), Some(cap))); } else { let lvar_info = LVarInfo { ty: param.ty.clone(), detail: LVarDetail::Argument { idx }, + locs, }; return Ok((Some(lvar_info), None)); } @@ -843,9 +847,10 @@ impl<'hir_maker> HirMaker<'hir_maker> { &mut self, base_name: &UnresolvedConstName, args: &[AstExpression], + locs: &LocationSpan, ) -> Result { debug_assert!(!args.is_empty()); - let base_expr = self.resolve_class_expr(base_name, &LocationSpan::todo())?; + let base_expr = self.resolve_class_expr(base_name, locs)?; let mut arg_exprs = vec![]; let mut type_args = vec![]; for arg in args { @@ -854,7 +859,7 @@ impl<'hir_maker> HirMaker<'hir_maker> { AstExpressionBody::SpecializeExpression { base_name: n, args: a, - } => self.convert_specialize_expr(n, a)?, + } => self.convert_specialize_expr(n, a, locs)?, _ => panic!("[BUG] unexpected arg in SpecializeExpression"), }; type_args.push(cls_expr.ty.as_type_argument()); @@ -961,7 +966,7 @@ impl<'hir_maker> HirMaker<'hir_maker> { method_fullname_raw("Array", "new"), vec![], ); - exprs.push(Hir::lvar_assign(&tmp_name, call_new, locs.clone())); + exprs.push(Hir::lvar_assign(tmp_name.clone(), call_new, locs.clone())); // `tmp.push(item)` for item_expr in item_exprs { diff --git a/lib/skc_ast2hir/src/pattern_match.rs b/lib/skc_ast2hir/src/pattern_match.rs index 95f654b5..7212c03a 100644 --- a/lib/skc_ast2hir/src/pattern_match.rs +++ b/lib/skc_ast2hir/src/pattern_match.rs @@ -39,7 +39,7 @@ pub fn convert_match_expr( }); let lvars = vec![(tmp_name.clone(), cond_expr.ty.clone())]; - let tmp_assign = Hir::lvar_assign(&tmp_name, cond_expr, LocationSpan::todo()); + let tmp_assign = Hir::lvar_assign(tmp_name, cond_expr, LocationSpan::todo()); Ok(( Hir::match_expression(result_ty, tmp_assign, clauses, LocationSpan::todo()), lvars, diff --git a/lib/skc_ast2hir/src/type_system/type_checking.rs b/lib/skc_ast2hir/src/type_system/type_checking.rs index 0f5dbfee..6ada8466 100644 --- a/lib/skc_ast2hir/src/type_system/type_checking.rs +++ b/lib/skc_ast2hir/src/type_system/type_checking.rs @@ -165,8 +165,7 @@ fn check_arg_type( ); let locs = &arg_hir.locs; let report = build_report(msg.clone(), locs, |r, locs_span| { - r.with_message(msg) - .with_label(Label::new(locs_span).with_message(&arg_hir.ty)) + r.with_label(Label::new(locs_span).with_message(&arg_hir.ty)) }); Err(report) } @@ -199,30 +198,46 @@ pub fn check_block_arity( type AriadneSpan<'a> = (&'a String, Range); -fn build_report(fallback_msg: String, locs: &LocationSpan, f: F) -> Error +/// Helper for building report with ariadne crate. +fn build_report(main_msg: String, locs: &LocationSpan, f: F) -> Error where F: for<'b> FnOnce( ReportBuilder>, AriadneSpan<'b>, ) -> ReportBuilder>, { - // ariadne::Id for the file `locs.filepath` - // ariadne 0.1.5 needs Id: Display (zesterer/ariadne#12) - let id = format!("{}", locs.filepath.display()); - // ariadne::Span equivalent to `locs` - let locs_span = (&id, locs.begin.pos..locs.end.pos); - - if id.is_empty() { - // This would never happen once LocationSpan::todo() is removed. - return type_error(fallback_msg); + if let LocationSpan::Just { + filepath, + begin, + end, + } = locs + { + // ariadne::Id for the file `locs.filepath` + // ariadne 0.1.5 needs Id: Display (zesterer/ariadne#12) + let id = format!("{}", filepath.display()); + // ariadne::Span equivalent to `locs` + let locs_span = (&id, begin.pos..end.pos); + + if id.is_empty() {} + let src = Source::from(fs::read_to_string(&**filepath).unwrap_or_default()); + let report = f(Report::build(ReportKind::Error, &id, begin.pos), locs_span) + .with_message(main_msg.clone()) + .finish(); + + match std::panic::catch_unwind(|| { + let mut rendered = vec![]; + report.write((&id, src), &mut rendered).unwrap(); + String::from_utf8_lossy(&rendered).to_string() + }) { + Ok(u8str) => type_error(u8str), + Err(e) => { + println!("[BUG] ariadne crate crashed!"); + dbg!(&e); + type_error(main_msg) + } + } + } else { + // No location information available + type_error(main_msg) } - let src = Source::from(fs::read_to_string(&*locs.filepath).unwrap_or_default()); - let r = f( - Report::build(ReportKind::Error, &id, locs.begin.pos), - locs_span, - ); - let mut rendered = vec![]; - r.finish().write((&id, src), &mut rendered).unwrap(); - let u8str = String::from_utf8_lossy(&rendered).to_string(); - type_error(u8str) } diff --git a/lib/skc_corelib/src/lib.rs b/lib/skc_corelib/src/lib.rs index 158a2df4..7373696c 100644 --- a/lib/skc_corelib/src/lib.rs +++ b/lib/skc_corelib/src/lib.rs @@ -1,7 +1,6 @@ pub mod class; mod fn_x; pub mod rustlib_methods; -use shiika_ast::LocationSpan; use shiika_core::names::*; use shiika_core::ty::{self, Erasure}; use skc_hir::*; @@ -206,11 +205,7 @@ fn object_initialize() -> SkMethod { SkMethod { signature: sig, body: SkMethodBody::Normal { - exprs: Hir::expressions(vec![Hir::const_ref( - ty::raw("Void"), - toplevel_const("Void"), - LocationSpan::todo(), - )]), + exprs: Hir::expressions(vec![]), }, lvars: vec![], } diff --git a/lib/skc_hir/src/lib.rs b/lib/skc_hir/src/lib.rs index 2fa22be5..c1e263f9 100644 --- a/lib/skc_hir/src/lib.rs +++ b/lib/skc_hir/src/lib.rs @@ -101,11 +101,11 @@ impl HirExpressions { } /// Make a HirExpression to refer `::Void` -fn void_const_ref() -> HirExpression { +pub fn void_const_ref() -> HirExpression { Hir::const_ref( ty::raw("Void"), toplevel_const("Void"), - LocationSpan::todo(), + LocationSpan::internal(), ) } @@ -403,11 +403,11 @@ impl Hir { } } - pub fn lvar_assign(name: &str, rhs: HirExpression, locs: LocationSpan) -> HirExpression { + pub fn lvar_assign(name: String, rhs: HirExpression, locs: LocationSpan) -> HirExpression { HirExpression { ty: rhs.ty.clone(), node: HirExpressionBase::HirLVarAssign { - name: name.to_string(), + name, rhs: Box::new(rhs), }, locs, @@ -456,15 +456,14 @@ impl Hir { method_fullname: MethodFullname, arg_hirs: Vec, ) -> HirExpression { - let locs = LocationSpan { - filepath: receiver_hir.locs.filepath.clone(), - begin: receiver_hir.locs.begin.clone(), - end: if let Some(e) = arg_hirs.last() { - e.locs.end.clone() + let locs = LocationSpan::merge( + &receiver_hir.locs, + if let Some(e) = arg_hirs.last() { + &e.locs } else { - receiver_hir.locs.end.clone() + &receiver_hir.locs }, - }; + ); HirExpression { ty: result_ty, node: HirExpressionBase::HirMethodCall { @@ -484,15 +483,14 @@ impl Hir { method_idx: usize, arg_hirs: Vec, ) -> HirExpression { - let locs = LocationSpan { - filepath: receiver_hir.locs.filepath.clone(), - begin: receiver_hir.locs.begin.clone(), - end: if let Some(e) = arg_hirs.last() { - e.locs.end.clone() + let locs = LocationSpan::merge( + &receiver_hir.locs, + if let Some(e) = arg_hirs.last() { + &e.locs } else { - receiver_hir.locs.end.clone() + &receiver_hir.locs }, - }; + ); HirExpression { ty: result_ty, node: HirExpressionBase::HirModuleMethodCall { @@ -671,7 +669,7 @@ impl Hir { initialize_name, init_cls_name, }, - locs: LocationSpan::todo(), + locs: LocationSpan::internal(), } }