From 986fc10b9aa3c9e160ff89ee0fa821e719ef109c Mon Sep 17 00:00:00 2001 From: gvozdvmozgu Date: Sun, 23 Mar 2025 07:51:11 -0700 Subject: [PATCH] add tuple parsing for types and expressions --- components/dada-ir-ast/src/ast/types.rs | 3 +++ components/dada-ir-ast/src/ast/util.rs | 8 +++++++- components/dada-ir-ast/src/diagnostic.rs | 4 ++++ components/dada-ir-sym/src/check/types.rs | 12 ++++++++++++ components/dada-ir-sym/src/ir/module.rs | 2 +- components/dada-ir-sym/src/ir/populate.rs | 3 +++ components/dada-parser/src/expr.rs | 9 +++++++++ components/dada-parser/src/lib.rs | 5 ++++- components/dada-parser/src/types.rs | 23 ++++++++++++++++++++--- tests/parser/tuple.dada | 8 ++++++++ 10 files changed, 71 insertions(+), 6 deletions(-) create mode 100644 tests/parser/tuple.dada diff --git a/components/dada-ir-ast/src/ast/types.rs b/components/dada-ir-ast/src/ast/types.rs index 63dd48a4..78f4a467 100644 --- a/components/dada-ir-ast/src/ast/types.rs +++ b/components/dada-ir-ast/src/ast/types.rs @@ -29,6 +29,9 @@ pub enum AstTyKind<'db> { /// `type T` GenericDecl(AstGenericDecl<'db>), + + /// `(A, B, C)` + Tuple(SpanVec<'db, AstTy<'db>>), } #[derive(SalsaSerialize)] diff --git a/components/dada-ir-ast/src/ast/util.rs b/components/dada-ir-ast/src/ast/util.rs index 261cca11..9f899461 100644 --- a/components/dada-ir-ast/src/ast/util.rs +++ b/components/dada-ir-ast/src/ast/util.rs @@ -1,4 +1,4 @@ -use std::ops::Deref; +use std::ops::{Deref, DerefMut}; use salsa::Update; use serde::Serialize; @@ -20,6 +20,12 @@ impl Deref for SpanVec<'_, T> { } } +impl DerefMut for SpanVec<'_, T> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.values + } +} + impl<'db, T> IntoIterator for &'db SpanVec<'db, T> where T: Update, diff --git a/components/dada-ir-ast/src/diagnostic.rs b/components/dada-ir-ast/src/diagnostic.rs index d9174e86..fa02a654 100644 --- a/components/dada-ir-ast/src/diagnostic.rs +++ b/components/dada-ir-ast/src/diagnostic.rs @@ -87,6 +87,10 @@ impl Diagnostic { Self::new(db, Level::Info, span, message) } + pub fn warning<'db>(db: &'db dyn crate::Db, span: Span<'db>, message: impl Display) -> Self { + Self::new(db, Level::Warning, span, message) + } + pub fn new<'db>( db: &'db dyn crate::Db, level: Level, diff --git a/components/dada-ir-sym/src/check/types.rs b/components/dada-ir-sym/src/check/types.rs index a66ff484..2558a334 100644 --- a/components/dada-ir-sym/src/check/types.rs +++ b/components/dada-ir-sym/src/check/types.rs @@ -54,6 +54,18 @@ impl<'db> CheckInEnv<'db> for AstTy<'db> { let symbol = decl.symbol(db); SymTy::var(db, symbol) } + + AstTyKind::Tuple(ast_elts) => { + let mut elts: Vec> = Vec::with_capacity(ast_elts.len()); + for elt in &ast_elts { + elts.push(elt.check_in_env(env).await.into()); + } + SymTy::named( + db, + crate::ir::types::SymTyName::Tuple { arity: elts.len() }, + elts, + ) + } }) .await } diff --git a/components/dada-ir-sym/src/ir/module.rs b/components/dada-ir-sym/src/ir/module.rs index 0cbc37c3..407302b7 100644 --- a/components/dada-ir-sym/src/ir/module.rs +++ b/components/dada-ir-sym/src/ir/module.rs @@ -209,7 +209,7 @@ fn report_duplicate<'db>( db, Level::Info, canonical_span, - format!("we will map `{id:?}` to this other definition"), + format!("we will map `{id}` to this other definition"), ) .report(db); } diff --git a/components/dada-ir-sym/src/ir/populate.rs b/components/dada-ir-sym/src/ir/populate.rs index 641b3b2d..4d95750e 100644 --- a/components/dada-ir-sym/src/ir/populate.rs +++ b/components/dada-ir-sym/src/ir/populate.rs @@ -41,6 +41,9 @@ impl<'db> PopulateSignatureSymbols<'db> for AstTy<'db> { AstTyKind::GenericDecl(ast_generic_decl) => { ast_generic_decl.populate_signature_symbols(db, symbols) } + AstTyKind::Tuple(elts) => elts + .iter() + .for_each(|e| e.populate_signature_symbols(db, symbols)), } } } diff --git a/components/dada-parser/src/expr.rs b/components/dada-parser/src/expr.rs index 48ca3374..6224891b 100644 --- a/components/dada-parser/src/expr.rs +++ b/components/dada-parser/src/expr.rs @@ -212,6 +212,15 @@ fn base_expr_precedence<'db, const SELECT: u32>( return Ok(Some(AstExprKind::Literal(literal))); } + if let Some(elts) = AstExpr::opt_parse_delimited( + db, + parser, + crate::tokenizer::Delimiter::Parentheses, + AstExpr::eat_comma, + )? { + return Ok(Some(AstExprKind::Tuple(elts))); + } + if let Ok(if_span) = parser.eat_keyword(Keyword::If) { return Ok(Some(if_chain(db, parser, if_span)?)); } diff --git a/components/dada-parser/src/lib.rs b/components/dada-parser/src/lib.rs index 0178dfc8..b1be48ec 100644 --- a/components/dada-parser/src/lib.rs +++ b/components/dada-parser/src/lib.rs @@ -547,6 +547,7 @@ trait Parse<'db>: Sized { let input_offset = text_span.start + 1; // account for the opening delimiter let tokenized = tokenize(db, text_span.anchor, input_offset, text); let mut parser1 = Parser::new(db, text_span.anchor, &tokenized); + parser1.last_span = parser.last_span(); let opt_list_err = eat_method(db, &mut parser1); parser.take_diagnostics(parser1); Ok(Some(opt_list_err?)) @@ -557,6 +558,8 @@ trait Parse<'db>: Sized { db: &'db dyn crate::Db, parser: &mut Parser<'_, 'db>, ) -> Result>, ParseFail<'db>> { + let start_span = parser.peek_span(); + match Self::opt_parse(db, parser) { Ok(Some(item)) => { let mut values = vec![item]; @@ -573,7 +576,7 @@ trait Parse<'db>: Sized { } Ok(Some(SpanVec { - span: parser.last_span(), + span: start_span.to(db, parser.last_span()), values, })) } diff --git a/components/dada-parser/src/types.rs b/components/dada-parser/src/types.rs index 7df14814..5d0bc39c 100644 --- a/components/dada-parser/src/types.rs +++ b/components/dada-parser/src/types.rs @@ -26,8 +26,11 @@ enum TyOrPerm<'db> { /// Perm that starts with a keyword, like `my` PermKeyword(AstPerm<'db>), - /// P1 P2 + /// `P1 P2` Apply(AstPerm<'db>, AstTy<'db>), + + /// `(A, B, C)` + Tuple(SpanVec<'db, AstTy<'db>>), } impl<'db> Parse<'db> for TyOrPerm<'db> { @@ -56,6 +59,14 @@ impl<'db> Parse<'db> for TyOrPerm<'db> { return TyOrPerm::PermKeyword(p).maybe_apply(db, parser); } + let start_span = parser.peek_span(); + if let Some(mut elts) = + AstTy::opt_parse_delimited(db, parser, Delimiter::Parentheses, AstTy::eat_comma)? + { + elts.span = start_span.to(db, parser.last_span()); + return Ok(Some(TyOrPerm::Tuple(elts))); + } + Ok(None) } @@ -74,6 +85,7 @@ impl<'db> Spanned<'db> for TyOrPerm<'db> { TyOrPerm::Generic(decl) => decl.span(db), TyOrPerm::PermKeyword(p) => p.span(db), TyOrPerm::Apply(p, ty) => p.span(db).to(db, ty.span(db)), + TyOrPerm::Tuple(elts) => elts.span, } } } @@ -103,6 +115,7 @@ impl<'db> TyOrPerm<'db> { TyOrPerm::Generic(decl) => matches!(decl.kind(db), AstGenericKind::Perm(_)), TyOrPerm::PermKeyword(_) => true, TyOrPerm::Apply(_, _) => false, + TyOrPerm::Tuple(_) => false, } } @@ -123,16 +136,18 @@ impl<'db> TyOrPerm<'db> { }, TyOrPerm::PermKeyword(p) => Some(p), TyOrPerm::Apply(_, _) => None, + TyOrPerm::Tuple(_) => None, } } - /// True if this could syntactically be a permission. + /// True if this could syntactically be a type. fn can_be_ty(&self, db: &'db dyn crate::Db) -> bool { match self { TyOrPerm::Path(..) => true, TyOrPerm::Generic(decl) => matches!(decl.kind(db), AstGenericKind::Type(_)), TyOrPerm::PermKeyword(_) => false, TyOrPerm::Apply(_, _) => true, + TyOrPerm::Tuple(_) => true, } } @@ -146,6 +161,7 @@ impl<'db> TyOrPerm<'db> { }, TyOrPerm::PermKeyword(_) => None, TyOrPerm::Apply(p, t) => Some(AstTy::new(db, span, AstTyKind::Perm(p, t))), + TyOrPerm::Tuple(elts) => Some(AstTy::new(db, span, AstTyKind::Tuple(elts))), } } } @@ -272,7 +288,8 @@ impl<'db> Parse<'db> for AstGenericTerm<'db> { TyOrPerm::Generic(_) | TyOrPerm::PermKeyword(_) | TyOrPerm::Path(..) - | TyOrPerm::Apply(_, _) => { + | TyOrPerm::Apply(_, _) + | TyOrPerm::Tuple(_) => { let can_be_perm = ty_or_perm.can_be_perm(db); let can_be_ty = ty_or_perm.can_be_ty(db); diff --git a/tests/parser/tuple.dada b/tests/parser/tuple.dada new file mode 100644 index 00000000..bfe353e4 --- /dev/null +++ b/tests/parser/tuple.dada @@ -0,0 +1,8 @@ +#:fn_asts + +fn main() { + let empty: () = () + let single: (i32) = (42) + let single: (i32,) = (42,) + let nested: ((i32, i32), i32) = ((1, 2), 3) +} \ No newline at end of file