AST API compiles

This commit is contained in:
2026-04-29 16:54:52 -06:00
parent 0250e066a8
commit a0f3f4e5aa

View File

@@ -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<ItemMeta<'db>> {
todo!()
}
/// Parses an [ItemMeta] into its corresponding [Item].
pub fn item<'db>(db: &'db dyn Database, meta: ItemMeta<'db>) -> WithAst<Item<'db>> {
todo!()
}
/// Common information on top-level items.
#[salsa::tracked]
pub struct ItemMeta<'db> {
/// Documentation comments on this item.
pub docs: Vec<AstNode>,
/// 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<AstProgramInfo>;
@@ -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<ExprExtra>;
}
/// Extra [ExprKind] variants in the parse stage.