Experimenting with the LSP

This commit is contained in:
Mia
2026-03-08 14:34:12 +01:00
parent 2aababbbe1
commit 45fd421e19
16 changed files with 1682 additions and 228 deletions
@@ -2,10 +2,17 @@ use derive_more::Debug;
use leaf_parser::{LineCol, ParseError};
use std::ops::Range;
use crate::SourceFile;
use crate::{SourceFile, metadata::CodePosition};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Kind {
Info,
Warning,
Error,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Code {
Unknown = 0x0000,
Parsing = 0x0100,
@@ -36,30 +43,40 @@ pub enum Location {
file: SourceFile,
range: Range<usize>,
},
Position {
#[debug("{:?}", file.file)]
file: SourceFile,
position: LineCol,
},
}
#[derive(Debug)]
pub struct CompilationError {
impl Location {
pub fn file(&self) -> Option<&SourceFile> {
match self {
Location::None => None,
Location::Range { file, .. } => Some(file),
}
}
pub fn range(&self) -> Range<usize> {
match self {
Location::None => 0..0,
Location::Range { range, .. } => range.clone(),
}
}
}
#[derive(Debug, Clone)]
pub struct Diagnostic {
pub kind: Kind,
pub code: Code,
pub message: String,
pub location: Location,
pub cause: Option<Box<CompilationError>>,
pub position: CodePosition,
pub cause: Option<Box<Diagnostic>>,
}
impl CompilationError {
pub fn parsing(file: SourceFile, err: ParseError<LineCol>) -> Self {
impl Diagnostic {
pub fn parsing_err(file: SourceFile, err: ParseError<LineCol>) -> Self {
Self {
kind: Kind::Parsing,
kind: Kind::Error,
code: Code::Parsing,
message: err.to_string(),
location: Location::Position {
file,
position: err.location,
},
position: CodePosition::new(file, err.location.offset..err.location.offset + 1),
cause: None,
}
}
+15
View File
@@ -0,0 +1,15 @@
use crate::{diagnostics::Diagnostic, metadata::CodePosition};
use leaf_assembly::values::AnyValue;
#[derive(Debug, Clone)]
pub enum Event<'l> {
Symbol {
value: AnyValue<'l>,
position: CodePosition,
},
Definition {
value: AnyValue<'l>,
position: CodePosition,
},
Diagnostic(Diagnostic),
}
+50 -9
View File
@@ -1,4 +1,4 @@
use crate::{error::CompilationError, scope::Scope};
use crate::{diagnostics::Diagnostic, events::Event, metadata::Metadata, scope::Scope};
use arcstr::literal_substr;
use leaf_allocators::SyncAllocator;
use leaf_assembly::{
@@ -8,9 +8,16 @@ use leaf_assembly::{
values::{AnyConst, AnyValue},
};
use leaf_parser::{SourceCode, ast};
use std::{collections::VecDeque, ops::Deref, sync::Arc};
use std::{
any::TypeId,
collections::{HashMap, VecDeque},
ops::Deref,
sync::{Arc, Mutex},
};
mod error;
pub mod diagnostics;
pub mod events;
pub mod metadata;
mod scope;
pub type SourceFile = Arc<SourceCode>;
@@ -19,7 +26,9 @@ type FuncQueue<'l> = VecDeque<(&'l Function<'l>, Arc<ast::Block>, Scope<'l>)>;
pub struct CompilationContext<'l> {
ctx: &'l Context<'l>,
alloc: &'l dyn SyncAllocator,
_alloc: &'l dyn SyncAllocator,
event_callbacks: Vec<Box<dyn Fn(&Event<'_>) + Send + Sync + 'static>>,
metadata: Mutex<HashMap<(AnyValue<'l>, TypeId), Arc<dyn Metadata>>>,
}
impl<'l> Deref for CompilationContext<'l> {
@@ -33,8 +42,20 @@ impl<'l> Deref for CompilationContext<'l> {
impl<'l> CompilationContext<'l> {
pub fn new(alloc: &'l dyn SyncAllocator) -> Self {
Self {
alloc,
_alloc: alloc,
ctx: Context::new(alloc),
metadata: Default::default(),
event_callbacks: vec![],
}
}
pub fn add_event_callback(&mut self, callback: impl Fn(&Event<'_>) + Send + Sync + 'static) {
self.event_callbacks.push(Box::new(callback));
}
fn emit_event(&self, event: Event) {
for callback in &self.event_callbacks {
callback(&event);
}
}
@@ -42,32 +63,52 @@ impl<'l> CompilationContext<'l> {
self.ctx.get_assembly(ident)
}
#[allow(private_bounds)]
pub fn get_metadata<T: Metadata>(&self, v: AnyValue<'l>) -> Option<Arc<T>> {
let arc = {
let metadata = self.metadata.lock().unwrap();
metadata.get(&(v, TypeId::of::<T>()))?.clone()
};
let ptr = Arc::into_raw(arc);
let ptr = ptr as *const T;
unsafe { Some(Arc::from_raw(ptr)) }
}
#[allow(private_bounds)]
pub fn set_metadata<T: Metadata>(&self, v: AnyValue<'l>, meta: T) -> Arc<T> {
let meta = Arc::new(meta);
self.metadata
.lock()
.unwrap()
.insert((v, TypeId::of::<T>()), meta.clone());
meta
}
pub fn extend(
&'l self,
ident: AssemblyIdentifier,
sources: &[Arc<SourceCode>],
) -> Result<(), CompilationError> {
) -> Result<(), Diagnostic> {
let assembly = self.ctx.get_or_create_assembly(ident.clone());
let mut units = Vec::with_capacity(sources.len());
for src in sources {
match leaf_parser::parse(src) {
Ok(unit) => units.push(unit),
Err(err) => return Err(CompilationError::parsing(src.clone(), err)),
Err(err) => return Err(Diagnostic::parsing_err(src.clone(), err)),
}
}
let mut scopes: Vec<_> = sources
.iter()
.map(|src| {
let mut scope = Scope::new(assembly, src.clone());
let mut scope = Scope::new(self, assembly, src.clone());
macro_rules! insert_types {
($($id:literal : $ty:expr,)*) => {
$(
scope.insert(
literal_substr!($id),
AnyValue::Constant(AnyConst::Type($ty.into())),
false,
);
)*
};
+5
View File
@@ -13,9 +13,11 @@ use leaf_parser::SourceCode;
use std::{
path::{Path, PathBuf},
sync::Arc,
time::Instant,
};
fn main() {
let start = Instant::now();
let alloc = SyncArenaAllocator::default();
let context = CompilationContext::new(&alloc);
let ident = AssemblyIdentifier {
@@ -32,6 +34,7 @@ fn main() {
println!("{:#?}", err);
return;
}
let time = start.elapsed();
let assembly = context.get_assembly(&ident).unwrap();
println!("{:#?}\n", assembly);
@@ -60,4 +63,6 @@ fn main() {
target_machine
.write_to_file(&module, FileType::Assembly, Path::new("out.asm"))
.unwrap();
println!("Codegen time: {time:?}");
}
+66
View File
@@ -0,0 +1,66 @@
use leaf_parser::{LineCol, SourceCode};
use std::{
any::Any,
ops::Range,
sync::{Arc, OnceLock},
};
pub trait Metadata: Any + Send + Sync {}
impl<T: Any + Send + Sync> Metadata for T {}
#[derive(Debug, Clone)]
pub struct CodePosition {
pub file: Arc<SourceCode>,
pub range: Range<usize>,
line_col: OnceLock<Range<LineCol>>,
}
impl CodePosition {
pub fn new(file: Arc<SourceCode>, range: Range<usize>) -> Self {
Self {
file,
range,
line_col: OnceLock::new(),
}
}
pub fn line_col(&self) -> &Range<LineCol> {
self.line_col.get_or_init(|| {
let mut line_col_range = LineCol {
line: 0,
column: 0,
offset: self.range.start,
}..LineCol {
line: 0,
column: 0,
offset: self.range.end,
};
let mut line = 0;
let mut col = 0;
for (byte_idx, ch) in self.file.text.char_indices() {
if byte_idx == self.range.start {
line_col_range.start.line = line;
line_col_range.start.column = col;
}
if byte_idx == self.range.end {
line_col_range.end.line = line;
line_col_range.end.column = col;
}
if ch == '\n' {
line += 1;
col = 0;
} else {
col += 1;
}
}
if self.range.end == self.file.text.len() {
line_col_range.end.line = line;
line_col_range.end.column = col;
}
line_col_range
})
}
}
+176 -175
View File
@@ -1,4 +1,4 @@
use crate::{FuncQueue, error::*};
use crate::{CompilationContext, FuncQueue, diagnostics::*, events::Event, metadata::CodePosition};
use arcstr::{Substr, literal_substr};
use leaf_assembly::{
assembly::Assembly,
@@ -46,31 +46,35 @@ impl<'l, 'r, 'a> ExpressionContext<'l, 'r> {
#[derive(Clone)]
struct Variable<'l> {
value: Arc<OnceLock<AnyValue<'l>>>,
mutable: bool,
}
#[derive(Clone)]
pub struct Scope<'l> {
ctx: &'l CompilationContext<'l>,
assembly: &'l Assembly<'l>,
source: Arc<SourceCode>,
values: HashMap<Substr, Variable<'l>>,
}
impl<'l> Scope<'l> {
pub fn new(assembly: &'l Assembly<'l>, source: Arc<SourceCode>) -> Self {
pub fn new(
ctx: &'l CompilationContext<'l>,
assembly: &'l Assembly<'l>,
source: Arc<SourceCode>,
) -> Self {
Self {
ctx,
assembly,
source,
values: HashMap::default(),
}
}
pub fn insert(&mut self, name: Substr, value: AnyValue<'l>, mutable: bool) {
pub fn insert(&mut self, name: Substr, value: AnyValue<'l>) {
self.values.insert(
name,
Variable {
value: Arc::new(OnceLock::from(value)),
mutable,
},
);
}
@@ -82,7 +86,6 @@ impl<'l> Scope<'l> {
name.clone(),
Variable {
value: Arc::default(),
mutable: false,
},
);
}
@@ -93,7 +96,7 @@ impl<'l> Scope<'l> {
&mut self,
decl: &[ConstDecl],
fn_queue: &mut FuncQueue<'l>,
) -> Result<(), CompilationError> {
) -> Result<(), Diagnostic> {
for val in decl {
let expr = self.compile_expression(
&val.value,
@@ -105,13 +108,19 @@ impl<'l> Scope<'l> {
},
)?;
match &val.names {
NamePattern::Single(ident) => self
.values
.get_mut(&ident.0)
.unwrap()
.value
.set(expr)
.unwrap(),
NamePattern::Single(ident) => {
self.values
.get_mut(&ident.0)
.unwrap()
.value
.set(expr)
.unwrap();
self.ctx.emit_event(Event::Definition {
value: expr,
position: CodePosition::new(self.source.clone(), ident.range()),
});
}
NamePattern::Tuple(_) => todo!(),
NamePattern::List(_) => todo!(),
}
@@ -124,7 +133,7 @@ impl<'l> Scope<'l> {
func: &'l Function<'l>,
block: &Arc<ast::Block>,
fn_queue: &mut FuncQueue<'l>,
) -> Result<(), CompilationError> {
) -> Result<(), Diagnostic> {
let mut builder = func.create_body().unwrap();
let mut ret = self.compile_block(
block,
@@ -159,42 +168,42 @@ impl<'l> Scope<'l> {
&mut self,
expr: &Expr,
ctx: &mut ExpressionContext<'l, '_>,
) -> Result<AnyValue<'l>, CompilationError> {
) -> Result<AnyValue<'l>, Diagnostic> {
match expr {
Expr::Ident(Ident(name)) => match self.values.get(name) {
None => Err(CompilationError {
kind: Kind::SymbolNotFound,
None => Err(Diagnostic {
kind: Kind::Error,
code: Code::SymbolNotFound,
message: format!("Symbol `{name}` does not exist in the current scope."),
location: Location::Range {
file: self.source.clone(),
range: name.range(),
},
position: CodePosition::new(self.source.clone(), name.range()),
cause: None,
}),
Some(Variable { value, .. }) => match value.get() {
None => Err(CompilationError {
kind: Kind::UninitializedSymbol,
None => Err(Diagnostic {
kind: Kind::Error,
code: Code::UninitializedSymbol,
message: format!("Symbol `{name}` is not initialized at this time."),
location: Location::Range {
file: self.source.clone(),
range: name.range(),
},
position: CodePosition::new(self.source.clone(), name.range()),
cause: None,
}),
Some(value) => Ok(*value),
Some(value) => {
self.ctx.emit_event(Event::Symbol {
value: *value,
position: CodePosition::new(self.source.clone(), name.range()),
});
Ok(*value)
}
},
},
Expr::Func(func) => self
.make_function(func, ctx)
.map(|f| AnyValue::Constant(AnyConst::Function(f)))
.map_err(|err| CompilationError {
kind: Kind::FunctionCompilationFailed,
.map_err(|err| Diagnostic {
kind: Kind::Error,
code: Code::FunctionCompilationFailed,
message: "Could not compile function.".to_string(),
location: Location::Range {
file: self.source.clone(),
range: func.text.range(),
},
position: CodePosition::new(self.source.clone(), func.text.range()),
cause: Some(Box::new(err)),
}),
@@ -210,18 +219,16 @@ impl<'l> Scope<'l> {
_ => n.text.parse::<$ty>(),
}
.map(|v| AnyValue::Constant(AnyConst::Int(Int::$id(v))))
.map_err(|_| CompilationError {
kind: Kind::InvalidInteger,
.map_err(|_| Diagnostic {
kind: Kind::Error,
code: Code::InvalidInteger,
message: format!("`{}` is not a valid integer.", n.text),
location: Location::Range {
file: self.source.clone(),
range: n.text.range(),
},
position: CodePosition::new(self.source.clone(), n.text.range()),
cause: None,
})
};
}
match n.r#type.as_ref().map(|v| v.as_str()) {
let value = match n.r#type.as_ref().map(|v| v.as_str()) {
None if ctx.type_hint == Some(Type::I8) => parse_number!(i8, I8),
None if ctx.type_hint == Some(Type::I16) => parse_number!(i16, I16),
None if ctx.type_hint == Some(Type::I32) => parse_number!(i32, I32),
@@ -248,16 +255,19 @@ impl<'l> Scope<'l> {
Some("u64") => parse_number!(u64, U64),
Some("u128") => parse_number!(u128, U128),
Some("usize") => parse_number!(u64, USize),
Some(ty) => Err(CompilationError {
kind: Kind::InvalidIntegerType,
Some(ty) => Err(Diagnostic {
kind: Kind::Error,
code: Code::InvalidIntegerType,
message: format!("`{ty}` is not a valid integer type."),
location: Location::Range {
file: self.source.clone(),
range: n.text.range(),
},
position: CodePosition::new(self.source.clone(), n.text.range()),
cause: None,
}),
}
}?;
self.ctx.emit_event(Event::Symbol {
value: value,
position: CodePosition::new(self.source.clone(), n.text.range()),
});
Ok(value)
}
Expr::Access(expr) => {
@@ -269,49 +279,63 @@ impl<'l> Scope<'l> {
if let Some(value) = value.get_associated_value(&field.0) {
return Ok(value);
}
match value.ty() {
Type::Struct(StructT { fields, .. }) => {
if let Some(fields) = fields.get() {
if let Some(field) = fields.get(field.0.as_str()) {
let builder = ctx.builder.as_mut().unwrap();
return Ok(builder
.get_element_value(value, field.name.as_any_value())
.unwrap()
.as_any_value());
}
}
}
Type::Ptr(PtrT {
base: Type::Struct(StructT { fields, .. }),
..
}) => {
if let Some(fields) = fields.get() {
if let Some(field) = fields.get(field.0.as_str()) {
let builder = ctx.builder.as_mut().unwrap();
let inst = builder
.get_element_ptr(value, field.name.as_any_value())
.unwrap()
.as_any_value();
unsafe {
if let AnyValue::Instruction(inst) = inst {
inst.edit_flags(|f| f | ValueFlags::LValue);
}
let value = 'value: {
match value.ty() {
Type::Struct(StructT { fields, .. }) => {
if let Some(fields) = fields.get() {
if let Some(field) = fields.get(field.0.as_str()) {
let builder = ctx.builder.as_mut().unwrap();
break 'value Some(
builder
.get_element_value(value, field.name.as_any_value())
.unwrap()
.as_any_value(),
);
}
return Ok(inst);
}
None
}
Type::Ptr(PtrT {
base: Type::Struct(StructT { fields, .. }),
..
}) => {
if let Some(fields) = fields.get() {
if let Some(field) = fields.get(field.0.as_str()) {
let builder = ctx.builder.as_mut().unwrap();
let inst = builder
.get_element_ptr(value, field.name.as_any_value())
.unwrap()
.as_any_value();
unsafe {
if let AnyValue::Instruction(inst) = inst {
inst.edit_flags(|f| f | ValueFlags::LValue);
}
}
break 'value Some(inst);
}
}
None
}
_ => None,
}
_ => {}
};
return Err(CompilationError {
kind: Kind::FieldNotFound,
if let Some(value) = value {
self.ctx.emit_event(Event::Symbol {
value: value,
position: CodePosition::new(self.source.clone(), field.range()),
});
return Ok(value);
}
return Err(Diagnostic {
kind: Kind::Error,
code: Code::FieldNotFound,
message: format!("Value does not contain field `{}`.", field.0),
location: Location::Range {
file: self.source.clone(),
range: value_expr.range(),
},
position: CodePosition::new(self.source.clone(), field.range()),
cause: None,
});
}
@@ -453,30 +477,24 @@ impl<'l> Scope<'l> {
}
(Type::Ptr(PtrT { base, .. }), ty, BinaryOp::Assign(_)) => match *base == ty {
true => Ok(builder.store(lhs, rhs).unwrap()),
false => Err(CompilationError {
kind: Kind::InvalidType,
false => Err(Diagnostic {
kind: Kind::Error,
code: Code::InvalidType,
message: format!(
"Cannot assign a value of type `{ty}` to a value of type `{base}`."
),
location: Location::Range {
file: self.source.clone(),
range: rhs_expr.range(),
},
position: CodePosition::new(self.source.clone(), expr.range()),
cause: None,
}),
},
(src_ty, Type::Type, BinaryOp::Cast(_)) => {
let dst_ty =
self.assert_ty(rhs, rhs_expr)
.map_err(|err| CompilationError {
kind: Kind::InvalidCast,
message: "Cannot perform cast.".to_string(),
location: Location::Range {
file: self.source.clone(),
range: expr.range(),
},
cause: Some(Box::new(err)),
})?;
let dst_ty = self.assert_ty(rhs, rhs_expr).map_err(|err| Diagnostic {
kind: Kind::Error,
code: Code::InvalidCast,
message: "Cannot perform cast.".to_string(),
position: CodePosition::new(self.source.clone(), expr.range()),
cause: Some(Box::new(err)),
})?;
if src_ty == dst_ty {
return Ok(lhs);
}
@@ -533,13 +551,11 @@ impl<'l> Scope<'l> {
let func = match self.compile_expression(func, ctx)? {
AnyValue::Constant(AnyConst::Function(func)) => func,
_ => {
return Err(CompilationError {
kind: Kind::NotAFunction,
return Err(Diagnostic {
kind: Kind::Error,
code: Code::NotAFunction,
message: "Value is not a function".into(),
location: Location::Range {
file: self.source.clone(),
range: func.range(),
},
position: CodePosition::new(self.source.clone(), func.range()),
cause: None,
});
}
@@ -574,7 +590,7 @@ impl<'l> Scope<'l> {
};
let ctx = self.assembly.ctx();
scope.insert(literal_substr!("Self"), struct_ty.as_any_value(), false);
scope.insert(literal_substr!("Self"), struct_ty.as_any_value());
let mut field_map = FieldMap::default();
for ast::Field {
name,
@@ -612,14 +628,12 @@ impl<'l> Scope<'l> {
unreachable!()
};
if mutable.is_some() && !*is_mut {
return Err(CompilationError {
kind: Kind::NotAFunction,
return Err(Diagnostic {
kind: Kind::Error,
code: Code::NotAFunction,
message: "Cannot obtain a mutable pointer to an immutable value."
.into(),
location: Location::Range {
file: self.source.clone(),
range: expr.range(),
},
position: CodePosition::new(self.source.clone(), expr.range()),
cause: None,
});
}
@@ -677,13 +691,11 @@ impl<'l> Scope<'l> {
match value.is_lvalue() {
false => unsafe {
if !matches!(ty, Type::Ptr(_)) {
return Err(CompilationError {
kind: Kind::CannotDereference,
return Err(Diagnostic {
kind: Kind::Error,
code: Code::CannotDereference,
message: format!("Cannot dereference a value of type `{ty}`."),
location: Location::Range {
file: self.source.clone(),
range: expr.range(),
},
position: CodePosition::new(self.source.clone(), expr.range()),
cause: None,
});
}
@@ -703,13 +715,11 @@ impl<'l> Scope<'l> {
let Type::Ptr(PtrT { base, .. }) = ty else {
unreachable!()
};
return Err(CompilationError {
kind: Kind::CannotDereference,
return Err(Diagnostic {
kind: Kind::Error,
code: Code::CannotDereference,
message: format!("Cannot dereference a value of type `{base}`."),
location: Location::Range {
file: self.source.clone(),
range: expr.range(),
},
position: CodePosition::new(self.source.clone(), expr.range()),
cause: None,
});
}
@@ -735,13 +745,11 @@ impl<'l> Scope<'l> {
// TODO Add support for custom indexing operations
if index.ty() != Type::USIZE {
return Err(CompilationError {
kind: Kind::InvalidType,
return Err(Diagnostic {
kind: Kind::Error,
code: Code::InvalidType,
message: "Value is not of type `usize`".into(),
location: Location::Range {
file: self.source.clone(),
range: expr.index.range(),
},
position: CodePosition::new(self.source.clone(), expr.index.range()),
cause: None,
});
}
@@ -775,13 +783,11 @@ impl<'l> Scope<'l> {
None => match ctx.type_hint {
Some(ty) => AnyValue::Constant(AnyConst::Type(ty)),
None => {
return Err(CompilationError {
kind: Kind::CannotInferType,
return Err(Diagnostic {
kind: Kind::Error,
code: Code::CannotInferType,
message: "Type cannot be inferred.".into(),
location: Location::Range {
file: self.source.clone(),
range: ctor.range(),
},
position: CodePosition::new(self.source.clone(), ctor.range()),
cause: None,
});
}
@@ -791,16 +797,17 @@ impl<'l> Scope<'l> {
struct_ty @ StructT { fields, .. },
))) = ty
else {
return Err(CompilationError {
kind: Kind::NotAStruct,
return Err(Diagnostic {
kind: Kind::Error,
code: Code::NotAStruct,
message: format!("Expected struct type, got value of type `{}`.", ty.ty()),
location: Location::Range {
file: self.source.clone(),
range: match &ctor.r#type {
position: CodePosition::new(
self.source.clone(),
match &ctor.r#type {
None => ctor.range(),
Some(ty) => ty.range(),
},
},
),
cause: None,
});
};
@@ -813,13 +820,11 @@ impl<'l> Scope<'l> {
} in fields.values()
{
let Some(name_value_pair) = ctor.values.get(*fld_name) else {
return Err(CompilationError {
kind: Kind::UninitializedField,
return Err(Diagnostic {
kind: Kind::Error,
code: Code::UninitializedField,
message: format!("Uninitialized field `{fld_name}`."),
location: Location::Range {
file: self.source.clone(),
range: expr.range(),
},
position: CodePosition::new(self.source.clone(), expr.range()),
cause: None,
});
};
@@ -859,7 +864,7 @@ impl<'l> Scope<'l> {
&mut self,
ast: &Arc<ast::Function>,
ctx: &mut ExpressionContext<'l, '_>,
) -> Result<&'l Function<'l>, CompilationError> {
) -> Result<&'l Function<'l>, Diagnostic> {
let ret_ty = match ast.ret.as_ref() {
None => AnyValue::Constant(AnyConst::Type(Type::Void)),
Some(ty) => self.compile_expression(ty, ctx)?,
@@ -885,7 +890,7 @@ impl<'l> Scope<'l> {
let mut scope = self.clone();
for (i, arg) in ast.args.iter().enumerate() {
scope.insert(arg.name.0.clone(), AnyValue::Parameter(i, func), false);
scope.insert(arg.name.0.clone(), AnyValue::Parameter(i, func));
}
ctx.fn_queue.push_back((func, block.clone(), scope));
@@ -899,7 +904,7 @@ impl<'l> Scope<'l> {
value: &Expr,
mutable: bool,
ctx: &mut ExpressionContext<'l, '_>,
) -> Result<AnyValue<'l>, CompilationError> {
) -> Result<AnyValue<'l>, Diagnostic> {
let mut sub_ctx = ExpressionContext {
decl_names: Some(names),
builder: ctx.builder.as_deref_mut(),
@@ -922,9 +927,13 @@ impl<'l> Scope<'l> {
ident.0.clone(),
Variable {
value: Arc::new(OnceLock::from(value)),
mutable,
},
);
self.ctx.emit_event(Event::Definition {
value: value,
position: CodePosition::new(self.source.clone(), ident.range()),
});
}
NamePattern::Tuple(_) => todo!(),
NamePattern::List(_) => todo!(),
@@ -936,7 +945,7 @@ impl<'l> Scope<'l> {
&mut self,
block: &Block,
ctx: &mut ExpressionContext<'l, '_>,
) -> Result<AnyValue<'l>, CompilationError> {
) -> Result<AnyValue<'l>, Diagnostic> {
let mut scope = self.clone();
let builder = ctx.builder.as_mut().unwrap();
@@ -960,7 +969,7 @@ impl<'l> Scope<'l> {
&mut self,
expr: &If,
ctx: &mut ExpressionContext<'l, '_>,
) -> Result<AnyValue<'l>, CompilationError> {
) -> Result<AnyValue<'l>, Diagnostic> {
let If { cond, block, else_ } = expr;
let condition = self.compile_expression(cond, ctx)?;
self.assert_ty_eq(&condition, cond, &Type::Bool)?;
@@ -1018,20 +1027,14 @@ impl<'l> Scope<'l> {
.into())
}
fn assert_ty(
&self,
val: AnyValue<'l>,
value_expr: &Expr,
) -> Result<Type<'l>, CompilationError> {
fn assert_ty(&self, val: AnyValue<'l>, value_expr: &Expr) -> Result<Type<'l>, Diagnostic> {
match val {
AnyValue::Constant(AnyConst::Type(ty)) => Ok(ty),
_ => Err(CompilationError {
kind: Kind::NotAType,
_ => Err(Diagnostic {
kind: Kind::Error,
code: Code::NotAType,
message: "Value is not a type.".to_string(),
location: Location::Range {
file: self.source.clone(),
range: value_expr.range(),
},
position: CodePosition::new(self.source.clone(), value_expr.range()),
cause: None,
}),
}
@@ -1042,18 +1045,16 @@ impl<'l> Scope<'l> {
value: &AnyValue<'l>,
value_expr: &Expr,
expected: &Type<'l>,
) -> Result<Type<'l>, CompilationError> {
) -> Result<Type<'l>, Diagnostic> {
let value_ty = value.ty();
match value_ty == *expected {
true => Ok(value_ty),
false => {
return Err(CompilationError {
kind: Kind::InvalidType,
return Err(Diagnostic {
kind: Kind::Error,
code: Code::InvalidType,
message: format!("Expected value of type `{expected}`, found `{value_ty}`."),
location: Location::Range {
file: self.source.clone(),
range: value_expr.range(),
},
position: CodePosition::new(self.source.clone(), value_expr.range()),
cause: None,
});
}