refactor: split project into multiple crates

This commit is contained in:
Romain Paquet 2025-11-05 20:23:17 +01:00
parent 486af67fc2
commit 857f747524
27 changed files with 308 additions and 222 deletions

10
lila-ast/Cargo.toml Normal file
View file

@ -0,0 +1,10 @@
[package]
name = "lila-ast"
version = "0.0.1"
edition = "2021"
[features]
ariadne = [ "dep:ariadne" ]
[dependencies]
ariadne = { version = "0.4.1", optional = true }

78
lila-ast/src/expr.rs Normal file
View file

@ -0,0 +1,78 @@
use crate::typing::Type;
use crate::*;
#[derive(Debug, PartialEq)]
pub struct SExpr {
pub expr: Expr,
pub span: Span,
}
#[derive(Debug, PartialEq)]
pub struct BinaryExpression {
pub lhs: Box<SExpr>,
pub op: BinaryOperator,
pub op_span: Span,
pub rhs: Box<SExpr>,
pub typ: Type,
}
#[derive(Debug, PartialEq)]
pub enum Expr {
BinaryExpression(BinaryExpression),
UnaryExpression {
op: UnaryOperator,
inner: Box<SExpr>,
},
Identifier {
name: String,
typ: Type,
},
Call(Box<Call>),
Block(Box<Block>),
/// Last field is either Expr::Block or Expr::IfExpr
IfExpr {
cond: Box<SExpr>,
then_body: Box<Block>,
else_body: Box<SExpr>,
typ: Type,
},
// Literals
UnitLiteral,
BooleanLiteral(bool),
IntegerLiteral(i64),
FloatLiteral(f64),
StringLiteral(String),
}
impl Block {
#[inline]
pub fn ty(&self) -> Type {
// XXX: Cloning may be expensive -> TypeId?
self.typ.clone()
}
}
impl Expr {
pub fn ty(&self) -> Type {
match self {
Expr::BinaryExpression(BinaryExpression { typ, .. }) => typ.clone(),
Expr::UnaryExpression { inner, .. } => inner.ty(), // XXX: problems will arise here
Expr::Identifier { typ, .. } => typ.clone(),
Expr::Call(call) => call.typ.clone(),
Expr::Block(block) => block.typ.clone(),
Expr::IfExpr { typ, .. } => typ.clone(),
Expr::UnitLiteral => Type::Unit,
Expr::BooleanLiteral(_) => Type::Bool,
Expr::IntegerLiteral(_) => Type::Int,
Expr::FloatLiteral(_) => Type::Float,
Expr::StringLiteral(_) => Type::Str,
}
}
}
impl SExpr {
#[inline]
pub fn ty(&self) -> Type {
self.expr.ty()
}
}

241
lila-ast/src/lib.rs Normal file
View file

@ -0,0 +1,241 @@
pub mod expr;
pub mod typing;
pub use expr::{BinaryExpression, Expr, SExpr};
use crate::typing::Type;
use std::{fmt::Display, path::Path};
#[derive(Debug, PartialEq, Clone)]
pub enum BinaryOperator {
// Logic
And,
Or,
// Arithmetic
Add,
Sub,
Mul,
Div,
Modulo,
Equal,
NotEqual,
}
impl Display for BinaryOperator {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(match self {
BinaryOperator::And => "&&",
BinaryOperator::Or => "||",
BinaryOperator::Add => "+",
BinaryOperator::Sub => "-",
BinaryOperator::Mul => "*",
BinaryOperator::Div => "/",
BinaryOperator::Modulo => "%",
BinaryOperator::Equal => "==",
BinaryOperator::NotEqual => "!=",
})
}
}
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum UnaryOperator {
Not,
}
pub type Identifier = String;
pub type SourceId = u32;
#[derive(Debug, PartialEq, Clone, Copy)]
pub struct Span {
pub source: SourceId,
pub start: usize,
pub end: usize,
}
#[cfg(feature = "ariadne")]
impl ariadne::Span for Span {
type SourceId = SourceId;
fn source(&self) -> &Self::SourceId {
&self.source
}
fn start(&self) -> usize {
self.start
}
fn end(&self) -> usize {
self.end
}
}
#[derive(Debug, PartialEq, Clone, Default, Eq, Hash)]
pub struct ModulePath {
components: Vec<String>,
}
impl ModulePath {
pub fn concat(lhs: &ModulePath, rhs: &ModulePath) -> ModulePath {
ModulePath {
components: Vec::from_iter(
lhs.components
.iter()
.chain(rhs.components.iter())
.map(Clone::clone),
),
}
}
}
impl std::fmt::Display for ModulePath {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!("{}", self.components.join("::")))
}
}
impl From<&Path> for ModulePath {
fn from(path: &Path) -> Self {
let meta = std::fs::metadata(path).unwrap();
ModulePath {
components: path
.components()
.map(|component| match component {
std::path::Component::Normal(n) => {
if meta.is_file() {
n.to_str().unwrap().split('.').nth(0).unwrap().to_string()
} else if meta.is_dir() {
n.to_str().unwrap().to_string()
} else {
// XXX: symlinks?
unreachable!()
}
}
_ => unreachable!(),
})
.collect(),
}
}
}
impl From<&str> for ModulePath {
fn from(string: &str) -> Self {
ModulePath {
components: string.split("::").map(|c| c.to_string()).collect(),
}
}
}
#[derive(Eq, PartialEq, Debug)]
pub struct Import(pub String);
#[derive(Debug, PartialEq)]
pub struct ReturnStatement {
pub expr: Option<SExpr>,
pub span: Span,
}
#[derive(Debug, PartialEq)]
pub enum Statement {
DeclareStatement {
lhs: Identifier,
rhs: Box<SExpr>,
span: Span,
},
AssignStatement {
lhs: Identifier,
rhs: Box<SExpr>,
span: Span,
},
ReturnStatement(ReturnStatement),
CallStatement {
call: Box<Call>,
span: Span,
},
UseStatement {
import: Box<Import>,
span: Span,
},
IfStatement {
condition: Box<SExpr>,
then_block: Box<Block>,
span: Span,
},
WhileStatement {
condition: Box<SExpr>,
loop_block: Box<Block>,
span: Span,
},
}
#[derive(Debug, PartialEq)]
pub struct Block {
pub statements: Vec<Statement>,
pub value: Option<SExpr>,
pub typ: Type,
pub span: Option<Span>,
}
impl Block {
pub fn empty() -> Block {
Block {
typ: Type::Unit,
statements: Vec::with_capacity(0),
value: None,
span: None,
}
}
}
#[derive(Debug, PartialEq)]
pub enum Definition {
FunctionDefinition(FunctionDefinition),
}
#[derive(Debug, PartialEq)]
pub struct FunctionDefinition {
pub name: Identifier,
pub parameters: Vec<Parameter>,
pub return_type: Option<Type>,
pub return_type_span: Option<Span>,
pub body: Box<Block>,
pub span: Span,
}
#[derive(Debug, PartialEq, Default)]
pub struct Module {
pub file: Option<std::path::PathBuf>,
pub path: ModulePath,
pub functions: Vec<FunctionDefinition>,
pub imports: Vec<Import>,
}
impl Module {
pub fn new(path: ModulePath) -> Self {
Self {
path,
..Default::default()
}
}
pub fn full_func_path(&self, func: usize) -> ModulePath {
ModulePath::concat(
&self.path,
&ModulePath::from(self.functions[func].name.as_str()),
)
}
}
#[derive(Debug, PartialEq)]
pub struct Call {
pub callee: Box<SExpr>,
pub args: Vec<SExpr>,
pub typ: Type,
}
#[derive(Debug, PartialEq)]
pub struct Parameter {
pub name: Identifier,
pub typ: Type,
}

View file

@ -0,0 +1,56 @@
use std::fmt::Debug;
use crate::{BinaryOperator, Identifier, ModulePath, Span, Type, UnaryOperator};
#[derive(Debug, PartialEq)]
pub struct TypeError {
pub file: Option<std::path::PathBuf>,
pub module: ModulePath,
pub function: Option<String>,
pub kind: TypeErrorKind,
}
#[derive(PartialEq, Debug)]
pub struct TypeAndSpan {
pub ty: Type,
pub span: Span,
}
#[derive(PartialEq, Debug)]
pub struct BinOpAndSpan {
pub op: BinaryOperator,
pub span: Span,
}
#[derive(Debug, PartialEq)]
pub enum TypeErrorKind {
InvalidBinaryOperator {
operator: BinOpAndSpan,
lhs: TypeAndSpan,
rhs: TypeAndSpan,
},
BlockTypeDoesNotMatchFunctionType {
block_type: Type,
},
ReturnTypeDoesNotMatchFunctionType {
return_expr: Option<TypeAndSpan>,
return_stmt: TypeAndSpan,
},
UnknownIdentifier {
identifier: String,
},
AssignmentMismatch {
lht: Type,
rht: Type,
},
AssignUndeclared,
VariableRedeclaration,
UnknownFunctionCalled(Identifier),
WrongFunctionArguments,
ConditionIsNotBool,
IfElseMismatch,
InvalidUnaryOperator {
operator: UnaryOperator,
inner: Type,
},
}

View file

@ -0,0 +1,73 @@
pub mod error;
use crate::{FunctionDefinition, Identifier};
use std::fmt::Display;
#[derive(Debug, PartialEq, Clone)]
pub enum Type {
/// Not a real type, used for parsing pass
Undefined,
Bool,
Int,
Float,
Unit,
Str,
Function {
params: Vec<Type>,
returns: Box<Type>,
},
Custom(Identifier),
}
impl Display for Type {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Type::Undefined => f.write_str("UNDEFINED"),
Type::Bool => f.write_str("Bool"),
Type::Int => f.write_str("Int"),
Type::Float => f.write_str("Float"),
Type::Unit => f.write_str("Unit"),
Type::Str => f.write_str("Str"),
Type::Custom(identifier) => f.write_str(identifier),
Type::Function { params, returns } => {
f.write_str("Fn(")?;
for param in params {
f.write_fmt(format_args!("{param}, "))?;
}
f.write_str(") -> ")?;
f.write_fmt(format_args!("{returns}"))
}
}
}
}
impl From<&str> for Type {
fn from(value: &str) -> Self {
match value {
"int" => Type::Int,
"float" => Type::Float,
"bool" => Type::Bool,
_ => Type::Custom(Identifier::from(value)),
}
}
}
#[derive(Debug, PartialEq, Clone)]
pub struct Signature(pub Vec<Type>, pub Type);
impl From<Signature> for Type {
fn from(val: Signature) -> Self {
Type::Function {
params: val.0,
returns: Box::new(val.1),
}
}
}
impl FunctionDefinition {
pub fn signature(&self) -> Signature {
let return_type = self.return_type.clone().unwrap_or(Type::Unit);
let params_types = self.parameters.iter().map(|p| p.typ.clone()).collect();
Signature(params_types, return_type)
}
}