diff --git a/crates/frontend/src/ast.rs b/crates/frontend/src/ast.rs index 4066d1e..b677117 100644 --- a/crates/frontend/src/ast.rs +++ b/crates/frontend/src/ast.rs @@ -17,13 +17,50 @@ use std::{convert::Infallible, fmt::Display, str::FromStr, sync::Arc}; use ir::EnumParse; -use strum::EnumIter; +use strum::{EnumDiscriminants, EnumIter}; use crate::prelude::*; // TODO: migrate AST logic (after redesigning grammar) // TODO: AST unit tests? executing on examples may be sufficient +/// Retrieves, but does not parse, all top-level items in a file. +// TODO: is `Vec` the most efficient to incrementally update here? +#[salsa::tracked] +pub fn items<'db>(db: &'db dyn Database, file: workspace::File) -> Vec> { + todo!() +} + +/// Parses an [ItemMeta] into its corresponding [Item]. +pub fn item<'db>(db: &'db dyn Database, meta: ItemMeta<'db>) -> WithAst> { + todo!() +} + +/// Common information on top-level items. +#[salsa::tracked] +pub struct ItemMeta<'db> { + /// Documentation comments on this item. + pub docs: Vec, + + /// The AST node of the item itself. + pub ast: AstNode, + + /// The kind of item this is. + pub kind: ItemKind, +} + +/// The contents of a top-level item. +#[derive(EnumDiscriminants)] +#[strum_discriminants(name(ItemKind))] +#[strum_discriminants(derive(Hash))] +pub enum Item<'db> { + TypeAlias(TypeAlias<'db>), + Import, + Relation(Relation<'db>), + Rule, + Assumption, +} + /// Parse an expression from the AST. #[salsa::tracked] pub fn expr(db: &dyn Database, ast: AstNode) -> Expr { @@ -32,12 +69,12 @@ pub fn expr(db: &dyn Database, ast: AstNode) -> Expr { // parse the kind depending on which field is selected let kind = if let Some(tuple) = ast.get_field(db, "tuple") { let els = tuple.get_fields(db, "el").map(|el| expr(db, el)).collect(); - ExprKind::Extra(ExprExtra::Tuple(els)) + ExprKind::Extra(Arc::new(ExprExtra::Tuple(els))) } else if let Some(val) = ast.get_field(db, "value") { - ExprKind::Value(parse_value(db, val)) + ExprKind::Value(value(db, val)) } else if let Some(var) = ast.get_field(db, "variable") { let name = var.contents(db).to_string(); - ExprKind::Extra(ExprExtra::VariableName(name)) + ExprKind::Extra(Arc::new(ExprExtra::VariableName(name))) } else if let Some(binary) = ast.get_field(db, "binary") { let op = binary.expect_field(db, "op").expect_parse(db); let lhs = Arc::new(expr(db, binary.expect_field(db, "lhs"))); @@ -51,7 +88,7 @@ pub fn expr(db: &dyn Database, ast: AstNode) -> Expr { let head = atom.expect_field(db, "head"); let head = WithAst::new(head, head.contents(db).to_string()); let body = expr(db, atom.expect_field(db, "body")); - ExprKind::Extra(ExprExtra::Atom { head, body }) + ExprKind::Extra(Arc::new(ExprExtra::Atom { head, body })) } else if let Some(parens) = ast.get_field(db, "parens") { return expr(db, parens); } else { @@ -62,6 +99,27 @@ pub fn expr(db: &dyn Database, ast: AstNode) -> Expr { Expr { meta: ast, kind } } +/// Parses a value from an AST. +fn value(db: &dyn Database, ast: AstNode) -> ir::Value { + use ir::Value::*; + if ast.get_field(db, "true").is_some() { + Boolean(true) + } else if ast.get_field(db, "false").is_some() { + Boolean(false) + } else if let Some(sym) = ast.get_field(db, "symbol") { + todo!() + } else { + let int = ast.expect_field(db, "integer"); + match int.contents(db).to_string().parse() { + Ok(val) => Integer(val), + Err(_) => { + diagnostic::SimpleError::new(int, "failed to parse integer literal").accumulate(db); + Integer(0) + } + } + } +} + /// A type alias for parse stage [Expr]. pub type Expr = ir::Expr; @@ -77,7 +135,7 @@ impl ir::ProgramInfo for AstProgramInfo { type ExprMeta = AstNode; type BinaryOp = BinaryOpKind; type UnaryOp = ir::UnaryOpKind; - type ExprExtra = ExprExtra; + type ExprExtra = Arc; } /// Extra [ExprKind] variants in the parse stage.