refactor: split project into multiple crates
This commit is contained in:
45
Cargo.toml
45
Cargo.toml
@@ -1,34 +1,13 @@
|
||||
[package]
|
||||
name = "lilac"
|
||||
[workspace]
|
||||
resolver = "3"
|
||||
members = [
|
||||
"lila",
|
||||
"lila-ast",
|
||||
"lila-checking",
|
||||
"lila-cli",
|
||||
"lila-jit",
|
||||
"lila-parsing",
|
||||
]
|
||||
|
||||
[workspace.package]
|
||||
version = "0.0.1"
|
||||
edition = "2021"
|
||||
|
||||
[features]
|
||||
default = ["pest"]
|
||||
pest = ["dep:pest", "dep:pest_derive"]
|
||||
tree-sitter = ["dep:tree-sitter", "dep:tree-sitter-lila"]
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "4.5.7", features = ["derive"] }
|
||||
cranelift = "0.109.0"
|
||||
cranelift-jit = "0.109.0"
|
||||
cranelift-module = "0.109.0"
|
||||
cranelift-native = "0.109.0"
|
||||
lazy_static = "1.4.0"
|
||||
pest = { version = "2.7.4", optional = true }
|
||||
pest_derive = { version = "2.7.4", optional = true }
|
||||
tree-sitter = { version = "0.22", optional = true }
|
||||
ariadne = "0.4.1"
|
||||
anyhow = "1.0.86"
|
||||
|
||||
[dependencies.tree-sitter-lila]
|
||||
version = "0.0.1"
|
||||
optional = true
|
||||
git = "https://git.sr.ht/~rpqt/tree-sitter-lila"
|
||||
branch = "main"
|
||||
|
||||
[dev-dependencies]
|
||||
pretty_assertions = "1.4.0"
|
||||
|
||||
[build-dependencies]
|
||||
cc = "*"
|
||||
|
||||
@@ -26,10 +26,15 @@
|
||||
let
|
||||
craneLib = crane.mkLib pkgs;
|
||||
|
||||
pestFilter = path: _type: (builtins.match ".*\.pest$" path) != null;
|
||||
sourceFilter = path: type: (craneLib.filterCargoSources path type) || (pestFilter path type);
|
||||
sourceFilter =
|
||||
path: type:
|
||||
builtins.any (suffix: lib.hasSuffix suffix path) [
|
||||
".pest"
|
||||
]
|
||||
|| (craneLib.filterCargoSources path type);
|
||||
|
||||
lilac-crate = craneLib.buildPackage ({
|
||||
pname = "lilac";
|
||||
src = lib.cleanSourceWith {
|
||||
src = ./.;
|
||||
filter = sourceFilter;
|
||||
|
||||
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 }
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::ast::*;
|
||||
use crate::typing::Type;
|
||||
use crate::*;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct SExpr {
|
||||
@@ -1,10 +1,10 @@
|
||||
pub mod expr;
|
||||
pub mod typing;
|
||||
|
||||
pub use expr::{BinaryExpression, Expr, SExpr};
|
||||
|
||||
use crate::typing::Type;
|
||||
|
||||
use ariadne;
|
||||
use std::{fmt::Display, path::Path};
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
@@ -54,6 +54,7 @@ pub struct Span {
|
||||
pub end: usize,
|
||||
}
|
||||
|
||||
#[cfg(feature = "ariadne")]
|
||||
impl ariadne::Span for Span {
|
||||
type SourceId = SourceId;
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
11
lila-checking/Cargo.toml
Normal file
11
lila-checking/Cargo.toml
Normal file
@@ -0,0 +1,11 @@
|
||||
[package]
|
||||
name = "lila-checking"
|
||||
version = "0.0.1"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
lila-ast = { path = "../lila-ast" }
|
||||
|
||||
[dev-dependencies]
|
||||
lila-parsing = { path = "../lila-parsing" }
|
||||
pretty_assertions = "1.4.0"
|
||||
@@ -1,87 +1,21 @@
|
||||
mod error;
|
||||
|
||||
use crate::ast::*;
|
||||
use crate::typing::error::{TypeAndSpan, TypeError, TypeErrorKind};
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::Display;
|
||||
|
||||
use lila_ast::typing::error::{BinOpAndSpan, TypeAndSpan, TypeError, TypeErrorKind};
|
||||
use lila_ast::typing::{Signature, Type};
|
||||
use lila_ast::*;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
#[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(Vec<Type>, Type);
|
||||
|
||||
impl From<Signature> for Type {
|
||||
fn from(val: Signature) -> Self {
|
||||
Type::Function {
|
||||
params: val.0,
|
||||
returns: Box::new(val.1),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FunctionDefinition {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct CheckedModule(pub Module);
|
||||
|
||||
impl Module {
|
||||
pub fn type_check(&mut self) -> Result<(), Vec<TypeError>> {
|
||||
pub trait TypeCheckModule {
|
||||
fn type_check(&mut self) -> Result<(), Vec<TypeError>>;
|
||||
}
|
||||
|
||||
impl TypeCheckModule for Module {
|
||||
fn type_check(&mut self) -> Result<(), Vec<TypeError>> {
|
||||
let mut ctx = TypingContext::new(self.path.clone());
|
||||
ctx.file.clone_from(&self.file);
|
||||
|
||||
@@ -325,7 +259,7 @@ impl TypeCheck for Expr {
|
||||
typ,
|
||||
op_span,
|
||||
}) => {
|
||||
let operator = error::BinOpAndSpan {
|
||||
let operator = BinOpAndSpan {
|
||||
op: op.clone(),
|
||||
span: *op_span,
|
||||
};
|
||||
@@ -1,11 +1,7 @@
|
||||
use crate::{
|
||||
ast::ModulePath,
|
||||
parsing::{DefaultParser, Parser},
|
||||
typing::error::*,
|
||||
typing::*,
|
||||
};
|
||||
|
||||
#[cfg(test)]
|
||||
use crate::TypeCheckModule;
|
||||
use lila_ast::typing::{error::*, Type};
|
||||
use lila_ast::ModulePath;
|
||||
use lila_parsing::{DefaultParser, Parser};
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
#[test]
|
||||
22
lila-cli/Cargo.toml
Normal file
22
lila-cli/Cargo.toml
Normal file
@@ -0,0 +1,22 @@
|
||||
[package]
|
||||
name = "lila-cli"
|
||||
version = "0.0.1"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.86"
|
||||
ariadne = "0.4.1"
|
||||
clap = { version = "4.5.7", features = ["derive"] }
|
||||
lila = { path = "../lila" }
|
||||
lila-ast = { path = "../lila-ast", features = ["ariadne"] }
|
||||
lila-checking = { path = "../lila-checking" }
|
||||
lila-jit = { path = "../lila-jit" }
|
||||
lila-parsing = { path = "../lila-parsing" }
|
||||
|
||||
[dev-dependencies]
|
||||
pretty_assertions = "1.4.0"
|
||||
|
||||
[[bin]]
|
||||
name = "lilac"
|
||||
path = "src/main.rs"
|
||||
|
||||
@@ -1,18 +1,15 @@
|
||||
pub mod ast;
|
||||
pub mod jit;
|
||||
pub mod parsing;
|
||||
pub mod source;
|
||||
pub mod typing;
|
||||
|
||||
use std::default::Default;
|
||||
use std::io::Write as _;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use clap::{Parser as ClapParser, Subcommand};
|
||||
|
||||
use crate::ast::Module;
|
||||
use crate::parsing::Parser;
|
||||
use crate::source::SourceCache;
|
||||
use lila::report::ToReport;
|
||||
use lila::source::SourceCache;
|
||||
use lila_ast::Module;
|
||||
use lila_checking::TypeCheckModule;
|
||||
use lila_jit as jit;
|
||||
use lila_parsing::{DefaultParser as LilaParser, Parser as _};
|
||||
|
||||
/// Experimental compiler for lila
|
||||
#[derive(ClapParser, Debug)]
|
||||
@@ -59,13 +56,13 @@ enum Commands {
|
||||
}
|
||||
|
||||
fn parse(files: &[String]) -> Vec<Module> {
|
||||
let mut parser = parsing::DefaultParser::default();
|
||||
let mut parser = LilaParser::default();
|
||||
let paths = files.iter().map(std::path::Path::new);
|
||||
paths
|
||||
.enumerate()
|
||||
.map(|(i, path)| match parser.parse_file(path, i as u32) {
|
||||
Ok(module) => module,
|
||||
Err(e) => panic!("Parsing error: {:#?}", e),
|
||||
Err(e) => panic!("Parsing error: {e:#?}"),
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
15
lila-jit/Cargo.toml
Normal file
15
lila-jit/Cargo.toml
Normal file
@@ -0,0 +1,15 @@
|
||||
[package]
|
||||
name = "lila-jit"
|
||||
version = "0.0.1"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
ariadne = "0.4.1" # TODO: use ariadne only in CLI
|
||||
cranelift = "0.109.0"
|
||||
cranelift-jit = "0.109.0"
|
||||
cranelift-module = "0.109.0"
|
||||
cranelift-native = "0.109.0"
|
||||
lila = { path = "../lila" }
|
||||
lila-ast = { path = "../lila-ast", features = ["ariadne"] } # TODO: don't include ariadne feature
|
||||
lila-checking = { path = "../lila-checking" }
|
||||
lila-parsing = { path = "../lila-parsing" }
|
||||
@@ -1,16 +1,17 @@
|
||||
use crate::{
|
||||
ast::{
|
||||
self, expr::BinaryExpression, BinaryOperator, Expr, FunctionDefinition, ModulePath,
|
||||
ReturnStatement, SourceId, Statement, UnaryOperator,
|
||||
},
|
||||
parsing::{DefaultParser, Parser},
|
||||
typing::Type,
|
||||
SourceCache,
|
||||
};
|
||||
use ariadne::Cache as _;
|
||||
use cranelift::{codegen::ir::UserFuncName, prelude::*};
|
||||
use cranelift_jit::{JITBuilder, JITModule};
|
||||
use cranelift_module::{DataDescription, FuncId, FuncOrDataId, Linkage, Module};
|
||||
use lila::report::ToReport as _;
|
||||
use lila::source::SourceCache;
|
||||
use lila_ast as ast;
|
||||
use lila_ast::typing::{self, Type};
|
||||
use lila_ast::{
|
||||
expr::BinaryExpression, BinaryOperator, Expr, FunctionDefinition, ModulePath, ReturnStatement,
|
||||
SourceId, Statement, UnaryOperator,
|
||||
};
|
||||
use lila_checking::TypeCheckModule as _;
|
||||
use lila_parsing::{DefaultParser, Parser};
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// The basic JIT class.
|
||||
@@ -44,7 +45,7 @@ impl Default for JIT {
|
||||
flag_builder.set("is_pic", "false").unwrap();
|
||||
|
||||
let isa_builder = cranelift_native::builder().unwrap_or_else(|msg| {
|
||||
panic!("host machine is not supported: {}", msg);
|
||||
panic!("host machine is not supported: {msg}");
|
||||
});
|
||||
|
||||
let isa = isa_builder
|
||||
@@ -137,12 +138,12 @@ impl JIT {
|
||||
|
||||
for param in &func.parameters {
|
||||
assert_ne!(param.typ, Type::Unit);
|
||||
sig.params.append(&mut Vec::from(¶m.typ));
|
||||
sig.params.append(&mut to_abi_params(¶m.typ));
|
||||
}
|
||||
|
||||
if let Some(return_type) = &func.return_type {
|
||||
if *return_type != Type::Unit {
|
||||
sig.returns = return_type.into();
|
||||
sig.returns = to_abi_params(return_type);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -235,14 +236,12 @@ impl JIT {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Type> for Vec<AbiParam> {
|
||||
fn from(value: &Type) -> Self {
|
||||
match value {
|
||||
Type::Bool => vec![AbiParam::new(types::I8)],
|
||||
Type::Int => vec![AbiParam::new(types::I32)],
|
||||
Type::Float => vec![AbiParam::new(types::F32)],
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
fn to_abi_params(value: &Type) -> Vec<AbiParam> {
|
||||
match value {
|
||||
Type::Bool => vec![AbiParam::new(types::I8)],
|
||||
Type::Int => vec![AbiParam::new(types::I32)],
|
||||
Type::Float => vec![AbiParam::new(types::F32)],
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
29
lila-parsing/Cargo.toml
Normal file
29
lila-parsing/Cargo.toml
Normal file
@@ -0,0 +1,29 @@
|
||||
[package]
|
||||
name = "lila-parsing"
|
||||
version = "0.0.1"
|
||||
edition = "2021"
|
||||
|
||||
[features]
|
||||
default = ["pest"]
|
||||
pest = ["dep:pest", "dep:pest_derive", "dep:lazy_static"]
|
||||
tree-sitter = ["dep:tree-sitter", "dep:tree-sitter-lila"]
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.86"
|
||||
lazy_static = { version = "1.4.0", optional = true }
|
||||
lila-ast = { path = "../lila-ast" }
|
||||
pest = { version = "2.7.4", optional = true }
|
||||
pest_derive = { version = "2.7.4", optional = true }
|
||||
tree-sitter = { version = "0.22", optional = true }
|
||||
|
||||
[dependencies.tree-sitter-lila]
|
||||
version = "0.0.1"
|
||||
optional = true
|
||||
git = "https://git.sr.ht/~rpqt/tree-sitter-lila"
|
||||
branch = "main"
|
||||
|
||||
[build-dependencies]
|
||||
cc = "*"
|
||||
|
||||
[dev-dependencies]
|
||||
pretty_assertions = "1.4.0"
|
||||
@@ -1,14 +1,12 @@
|
||||
use expr::BinaryExpression;
|
||||
use lila_ast::typing::Type;
|
||||
use pest::iterators::Pair;
|
||||
use pest::pratt_parser::PrattParser;
|
||||
use pest::Parser as PestParser;
|
||||
use ReturnStatement;
|
||||
|
||||
use crate::ast::*;
|
||||
use crate::typing::Type;
|
||||
use lila_ast::*;
|
||||
|
||||
#[derive(pest_derive::Parser)]
|
||||
#[grammar = "parsing/backend/pest/grammar.pest"]
|
||||
#[grammar = "src/backend/pest/grammar.pest"]
|
||||
struct LilaParser;
|
||||
|
||||
use lazy_static;
|
||||
@@ -34,7 +32,7 @@ pub struct Parser {
|
||||
source: SourceId,
|
||||
}
|
||||
|
||||
impl crate::parsing::Parser for Parser {
|
||||
impl crate::Parser for Parser {
|
||||
fn parse_as_module(
|
||||
&mut self,
|
||||
source: &str,
|
||||
@@ -1,7 +1,7 @@
|
||||
mod backend;
|
||||
mod tests;
|
||||
|
||||
use crate::ast::{Module, ModulePath, SourceId};
|
||||
use lila_ast::{Module, ModulePath, SourceId};
|
||||
|
||||
pub trait Parser: Default {
|
||||
fn parse_file(&mut self, path: &std::path::Path, id: SourceId) -> anyhow::Result<Module> {
|
||||
@@ -3,9 +3,9 @@ use pretty_assertions::assert_eq;
|
||||
|
||||
#[test]
|
||||
fn test_addition_function() {
|
||||
use crate::ast::*;
|
||||
use crate::parsing::*;
|
||||
use crate::typing::*;
|
||||
use crate::*;
|
||||
use lila_ast::typing::*;
|
||||
use lila_ast::*;
|
||||
|
||||
let source = "fn add(a: int, b: int) int { a + b }";
|
||||
let path = ModulePath::from("test");
|
||||
8
lila/Cargo.toml
Normal file
8
lila/Cargo.toml
Normal file
@@ -0,0 +1,8 @@
|
||||
[package]
|
||||
name = "lila"
|
||||
version = "0.0.1"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
ariadne = "0.4.1" # TODO: only CLI should use ariadne
|
||||
lila-ast = { path = "../lila-ast" }
|
||||
2
lila/src/lib.rs
Normal file
2
lila/src/lib.rs
Normal file
@@ -0,0 +1,2 @@
|
||||
pub mod report;
|
||||
pub mod source;
|
||||
@@ -1,64 +1,14 @@
|
||||
use ariadne::{ColorGenerator, Fmt, Label, Report, ReportKind, Span as _};
|
||||
use std::fmt::Debug;
|
||||
use lila_ast::typing::error::{TypeError, TypeErrorKind};
|
||||
use lila_ast::typing::Type;
|
||||
use lila_ast::{Module, Span};
|
||||
|
||||
use super::{Span, UnaryOperator};
|
||||
use crate::typing::{BinaryOperator, Identifier, ModulePath, Type};
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct TypeError {
|
||||
pub file: Option<std::path::PathBuf>,
|
||||
pub module: ModulePath,
|
||||
pub function: Option<String>,
|
||||
pub kind: TypeErrorKind,
|
||||
pub trait ToReport {
|
||||
fn to_report(&self, ast: &Module) -> Report<Span>;
|
||||
}
|
||||
|
||||
#[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,
|
||||
},
|
||||
}
|
||||
|
||||
impl TypeError {
|
||||
pub fn to_report(&self, ast: &crate::ast::Module) -> Report<Span> {
|
||||
impl ToReport for TypeError {
|
||||
fn to_report(&self, ast: &Module) -> Report<Span> {
|
||||
let mut colors = ColorGenerator::new();
|
||||
let c0 = colors.next();
|
||||
let c1 = colors.next();
|
||||
@@ -85,6 +35,7 @@ impl TypeError {
|
||||
.with_color(c2)
|
||||
.with_order(1),
|
||||
])
|
||||
// TODO: add hint for conversion
|
||||
.finish()
|
||||
}
|
||||
|
||||
@@ -129,7 +80,7 @@ impl TypeError {
|
||||
if let Some(span) = function.return_type_span {
|
||||
report.add_label(
|
||||
Label::new(span)
|
||||
.with_message(format!("The signature shows {}", func_return_type_text))
|
||||
.with_message(format!("The signature shows {func_return_type_text}"))
|
||||
.with_color(signature_color),
|
||||
);
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::ast::SourceId;
|
||||
use ariadne::FileCache;
|
||||
use lila_ast::SourceId;
|
||||
|
||||
pub struct SourceCache {
|
||||
pub paths: Vec<std::path::PathBuf>,
|
||||
Reference in New Issue
Block a user