Experimenting with the LSP
This commit is contained in:
@@ -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,
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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,
|
||||
);
|
||||
)*
|
||||
};
|
||||
|
||||
@@ -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:?}");
|
||||
}
|
||||
|
||||
@@ -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
@@ -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,
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user