refactor: split project into multiple crates
This commit is contained in:
parent
486af67fc2
commit
857f747524
27 changed files with 308 additions and 222 deletions
10
lila-ast/Cargo.toml
Normal file
10
lila-ast/Cargo.toml
Normal 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
78
lila-ast/src/expr.rs
Normal 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
241
lila-ast/src/lib.rs
Normal 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,
|
||||
}
|
||||
56
lila-ast/src/typing/error.rs
Normal file
56
lila-ast/src/typing/error.rs
Normal 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,
|
||||
},
|
||||
}
|
||||
73
lila-ast/src/typing/mod.rs
Normal file
73
lila-ast/src/typing/mod.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue