#![warn(missing_docs)]
use dashmap::DashMap;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::sync::Arc;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Span {
pub start: u32,
pub end: u32,
}
impl Span {
pub fn new(start: u32, end: u32) -> Self {
Self { start, end }
}
pub fn unknown() -> Self {
Self { start: 0, end: 0 }
}
}
impl Default for Span {
fn default() -> Self {
Self::unknown()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Loc {
pub source_id: u32,
pub span: Span,
}
impl Loc {
pub fn new(source_id: u32, start: u32, end: u32) -> Self {
Self {
source_id,
span: Span::new(start, end),
}
}
pub fn unknown() -> Self {
Self {
source_id: 0,
span: Span::unknown(),
}
}
pub fn is_unknown(&self) -> bool {
self.source_id == 0
}
}
impl Default for Loc {
fn default() -> Self {
Self::unknown()
}
}
pub struct SourceEntry {
pub path: String,
pub content: Arc<String>,
pub line_map: LineMap,
}
pub struct LineMap {
line_starts: Vec<u32>,
}
impl LineMap {
pub fn new(content: &str) -> Self {
let mut line_starts = vec![0];
for (i, c) in content.char_indices() {
if c == '\n' {
line_starts.push((i + 1) as u32);
}
}
Self { line_starts }
}
pub fn lookup(&self, offset: u32) -> (u32, u32) {
let line = match self.line_starts.binary_search(&offset) {
Ok(line) => line,
Err(line) => line - 1,
};
let col = offset - self.line_starts[line];
(line as u32 + 1, col + 1)
}
}
pub struct SourceManager {
sources: DashMap<u32, SourceEntry>,
next_id: std::sync::atomic::AtomicUsize,
}
impl SourceManager {
pub fn new() -> Self {
Self {
sources: DashMap::new(),
next_id: std::sync::atomic::AtomicUsize::new(1),
}
}
pub fn register(&self, path: &str, content: String) -> u32 {
let id = self
.next_id
.fetch_add(1, std::sync::atomic::Ordering::SeqCst) as u32;
let line_map = LineMap::new(&content);
let entry = SourceEntry {
path: path.to_string(),
content: Arc::new(content),
line_map,
};
self.sources.insert(id, entry);
id
}
pub fn get(&self, id: u32) -> Option<dashmap::mapref::one::Ref<'_, u32, SourceEntry>> {
self.sources.get(&id)
}
}
impl Default for SourceManager {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum Intent {
Map,
Reduce,
StateUpdate,
Branch,
Loop,
LifeCycle,
Meta,
Trap,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum Context {
Linear,
RefCounting,
Async,
Comptime,
GPU,
SIMD,
Safe,
General,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct IntentNode {
pub intent: Intent,
pub contexts: Vec<Context>,
pub attributes: HashMap<String, String>,
}
impl IntentNode {
pub fn new(intent: Intent) -> Self {
Self {
intent,
contexts: vec![Context::General],
attributes: HashMap::new(),
}
}
pub fn with_context(mut self, context: Context) -> Self {
self.contexts.push(context);
self
}
pub fn with_attribute(mut self, key: &str, value: &str) -> Self {
self.attributes.insert(key.to_string(), value.to_string());
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ChomskyError {
pub kind: Box<ChomskyErrorKind>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ChomskyErrorKind {
UirError {
kind: String, details: String,
},
FrontendError {
stage: String, file: Option<String>,
line: Option<u32>,
message: String,
},
BackendError {
target: String, stage: String, message: String,
},
IoError {
path: Option<String>,
operation: String,
message: String,
},
Unknown {
code: i32,
message: String,
},
}
impl ChomskyErrorKind {
pub fn key(&self) -> &str {
match self {
Self::UirError { .. } => "uir_error",
Self::FrontendError { .. } => "frontend_error",
Self::BackendError { .. } => "backend_error",
Self::IoError { .. } => "io_error",
Self::Unknown { .. } => "unknown",
}
}
}
impl std::fmt::Display for ChomskyError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match &*self.kind {
ChomskyErrorKind::UirError { kind, details } => {
write!(f, "UIR Error [{}]: {}", kind, details)
}
ChomskyErrorKind::FrontendError {
stage,
file,
line,
message,
} => {
let loc = match (file, line) {
(Some(file), Some(line)) => format!("{}:{}", file, line),
(Some(file), None) => file.clone(),
_ => "unknown location".to_string(),
};
write!(f, "Frontend Error [{} at {}]: {}", stage, loc, message)
}
ChomskyErrorKind::BackendError {
target,
stage,
message,
} => write!(f, "Backend Error [{} - {}]: {}", target, stage, message),
ChomskyErrorKind::IoError {
path,
operation,
message,
} => {
let p = path.as_deref().unwrap_or("unknown path");
write!(f, "IO Error [{} on {}]: {}", operation, p, message)
}
ChomskyErrorKind::Unknown { code, message } => {
write!(f, "Unknown Error ({}): {}", code, message)
}
}
}
}
impl std::error::Error for ChomskyError {}
impl ChomskyError {
pub fn new(kind: ChomskyErrorKind) -> Self {
Self {
kind: Box::new(kind),
}
}
pub fn uir_error(msg: impl Into<String>) -> Self {
Self::new(ChomskyErrorKind::UirError {
kind: "General".to_string(),
details: msg.into(),
})
}
pub fn frontend_error(msg: impl Into<String>) -> Self {
Self::new(ChomskyErrorKind::FrontendError {
stage: "General".to_string(),
file: None,
line: None,
message: msg.into(),
})
}
pub fn backend_error(msg: impl Into<String>) -> Self {
Self::new(ChomskyErrorKind::BackendError {
target: "Unknown".to_string(),
stage: "General".to_string(),
message: msg.into(),
})
}
pub fn io_error(msg: impl Into<String>) -> Self {
Self::new(ChomskyErrorKind::IoError {
path: None,
operation: "Unknown".to_string(),
message: msg.into(),
})
}
pub fn unknown(msg: impl Into<String>) -> Self {
Self::new(ChomskyErrorKind::Unknown {
code: -1,
message: msg.into(),
})
}
}
impl From<std::io::Error> for ChomskyError {
fn from(err: std::io::Error) -> Self {
Self::io_error(err.to_string())
}
}
pub type ChomskyResult<T> = Result<T, ChomskyError>;