initial commit

This commit is contained in:
2026-04-05 16:08:59 +03:00
commit 75ab9bec9f
1117 changed files with 789034 additions and 0 deletions
+95
View File
@@ -0,0 +1,95 @@
use crate::ast::expr::Span;
/// Debug information that maps bytecode offsets to source locations.
/// Uses a run-length encoded format: each entry covers instructions from
/// its offset until the next entry's offset.
#[derive(Debug, Clone, Default)]
pub struct DebugInfo {
/// Sorted list of (bytecode_offset, span) pairs
entries: Vec<(u32, Span)>,
}
impl DebugInfo {
pub fn new() -> Self {
Self {
entries: Vec::new(),
}
}
/// Add a mapping from a bytecode offset to a source span.
/// Entries must be added in increasing offset order.
pub fn add_entry(&mut self, offset: u32, span: Span) {
// Only add if different from the last entry's span
if let Some((_, last_span)) = self.entries.last() {
if *last_span == span {
return;
}
}
self.entries.push((offset, span));
}
/// Look up the source span for a given bytecode offset.
/// Returns None if no debug info is available.
pub fn get_span(&self, offset: u32) -> Option<Span> {
if self.entries.is_empty() {
return None;
}
// Binary search for the largest offset <= target
match self.entries.binary_search_by_key(&offset, |(off, _)| *off) {
Ok(idx) => Some(self.entries[idx].1),
Err(idx) => {
if idx == 0 {
// Before the first entry
None
} else {
// Use the previous entry
Some(self.entries[idx - 1].1)
}
}
}
}
/// Check if debug info is available
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
/// Get the number of entries
pub fn len(&self) -> usize {
self.entries.len()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_debug_info_lookup() {
let mut info = DebugInfo::new();
info.add_entry(0, Span::new(1, 1));
info.add_entry(10, Span::new(2, 5));
info.add_entry(20, Span::new(3, 10));
// Exact matches
assert_eq!(info.get_span(0), Some(Span::new(1, 1)));
assert_eq!(info.get_span(10), Some(Span::new(2, 5)));
assert_eq!(info.get_span(20), Some(Span::new(3, 10)));
// In-between values use previous entry
assert_eq!(info.get_span(5), Some(Span::new(1, 1)));
assert_eq!(info.get_span(15), Some(Span::new(2, 5)));
assert_eq!(info.get_span(100), Some(Span::new(3, 10)));
}
#[test]
fn test_duplicate_spans_not_added() {
let mut info = DebugInfo::new();
info.add_entry(0, Span::new(1, 1));
info.add_entry(5, Span::new(1, 1)); // Same span, should not add
info.add_entry(10, Span::new(2, 1));
assert_eq!(info.len(), 2);
}
}
+64
View File
@@ -0,0 +1,64 @@
use crate::ast::expr::Span;
use smol_str::SmolStr;
use thiserror::Error;
/// Errors that can occur during VM execution
#[derive(Debug, Error)]
pub enum VMError {
/// Error when types don't match the operation's expectations
#[error("Type error: expected {expected}, got {got}")]
TypeMismatch { expected: String, got: String },
/// Error when a variable is not defined
#[error("Undefined variable: {0}")]
UndefinedVariable(SmolStr),
/// Error when dividing by zero
#[error("Division by zero")]
DivisionByZero,
/// Error in bytecode format or execution
#[error("Bytecode error: {0}")]
BytecodeError(String),
/// Error when a method is not found for a type
#[error("Method '{method}' not found for type '{type_name}'")]
MethodNotFound {
type_name: &'static str,
method: SmolStr,
},
/// Generic runtime error
#[error("Runtime error: {0}")]
RuntimeError(String),
/// Error when an invalid operation is performed
#[error("Type error: cannot {operation} {left_type} and {right_type}")]
InvalidOperation {
operation: &'static str,
left_type: &'static str,
right_type: &'static str,
},
/// Error with source location information
#[error("Error at {span}: {message}")]
WithLocation { span: Span, message: String },
}
impl VMError {
/// Wrap this error with source location information
pub fn with_span(self, span: Span) -> Self {
// Don't double-wrap location errors
if matches!(self, VMError::WithLocation { .. }) {
return self;
}
// Only wrap if we have a valid span (non-zero)
if span.line == 0 && span.column == 0 {
return self;
}
VMError::WithLocation {
span,
message: self.to_string(),
}
}
}
+7
View File
@@ -0,0 +1,7 @@
mod debug_info;
pub mod error;
mod vm;
pub use debug_info::DebugInfo;
pub use error::VMError;
pub use vm::{ExternalFn, ExternalMethod, VM};
+1417
View File
File diff suppressed because it is too large Load Diff