From 94578c90bad3337a7b7d58645ab8c8117d7ed7e0 Mon Sep 17 00:00:00 2001 From: Marceline Cramer Date: Mon, 4 May 2026 18:53:46 -0600 Subject: [PATCH] Parse names with paths --- crates/frontend/src/ast.rs | 46 +++++++++++++++++++++++++++++++++----- 1 file changed, 41 insertions(+), 5 deletions(-) diff --git a/crates/frontend/src/ast.rs b/crates/frontend/src/ast.rs index 47d0bf7..b913463 100644 --- a/crates/frontend/src/ast.rs +++ b/crates/frontend/src/ast.rs @@ -251,7 +251,7 @@ pub fn expr(db: &dyn Database, ast: AstNode) -> Expr { } else if ast.get_field(db, "false").is_some() { Value(Boolean(false)) } else if let Some(sym) = ast.get_field(db, "symbol") { - todo!() + Extra(Arc::new(ExprExtra::SymbolName(name(db, sym)))) } else if let Some(int) = ast.get_field(db, "integer") { Value(match int.contents(db).to_string().parse() { Ok(val) => Integer(val), @@ -304,7 +304,7 @@ pub fn aggregate(db: &dyn Database, ast: AstNode) -> Aggregate { /// Parses an application operation. pub fn apply(db: &dyn Database, ast: AstNode) -> Apply { - let head = ast.expect_field(db, "head").with_contents(db); + let head = name(db, ast.expect_field(db, "head")); let body = expr(db, ast.expect_field(db, "body")); Apply { head, body } } @@ -336,7 +336,13 @@ pub enum ExprExtra { /// A structured tuple of sub-expressions. Tuple(Vec), + /// An unresolved symbol name. + SymbolName(Name), + /// An unresolved variable name. + /// + /// Variables are only locally-scoped, so this is represented as a plain + /// string, not a [Name] with a full path. VariableName(String), /// An abstract application expression. @@ -373,12 +379,42 @@ pub enum AggregateBody { #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct Apply { /// The name of the relation to query or function to invoke. - pub head: WithAst, + pub head: Name, /// The parameters to the function or query. pub body: Expr, } +/// Parses a name. +#[salsa::tracked] +pub fn name(db: &dyn Database, ast: AstNode) -> Name { + Name { + ast, + path: ast + .get_fields(db, "path") + .map(|ast| ast.with_contents(db)) + .collect(), + } +} + +/// A named reference to some object. +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct Name { + /// The AST node of the name. + pub ast: AstNode, + + /// The segments of the path to the object. + pub path: Vec>, +} + +impl Display for Name { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let segments = self.path.iter().cloned().map(|ast| ast.inner); + let path = segments.collect::>().join("."); + write!(f, "{path}") + } +} + /// Extends [ir::BinaryOpKind] with the omitted operators to preserve syntax. #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum BinaryOpKind { @@ -465,7 +501,7 @@ impl ir::EnumRepr for RedundantBinaryOpKind { /// Parses a type. pub fn ty(db: &dyn Database, ast: AstNode) -> WithAst { ast.with(if let Some(ast) = ast.get_field(db, "named") { - Type::Named(ast.contents(db).to_string()) + Type::Named(name(db, ast)) } else { Type::Tuple(ast.get_fields(db, "tuple").map(|ast| ty(db, ast)).collect()) }) @@ -477,7 +513,7 @@ pub fn ty(db: &dyn Database, ast: AstNode) -> WithAst { /// dereferencing any aliases or checking semantic validity. #[derive(Clone, PartialEq, Eq, Hash)] pub enum Type { - Named(String), + Named(Name), Tuple(Vec>), }