#![allow(clippy::needless_borrow)]
use ordered_float::OrderedFloat;
#[salsa::input(debug)]
pub struct SourceProgram {
#[returns(ref)]
pub text: String,
}
#[salsa::interned(debug)]
pub struct VariableId<'db> {
#[returns(ref)]
pub text: String,
}
#[salsa::interned(debug)]
pub struct FunctionId<'db> {
#[returns(ref)]
pub text: String,
}
#[salsa::tracked(debug)]
pub struct Program<'db> {
#[tracked]
#[returns(ref)]
pub statements: Vec<Statement<'db>>,
}
#[derive(Eq, PartialEq, Debug, Hash, salsa::Update)]
pub struct Statement<'db> {
pub span: Span<'db>,
pub data: StatementData<'db>,
}
impl<'db> Statement<'db> {
pub fn new(span: Span<'db>, data: StatementData<'db>) -> Self {
Statement { span, data }
}
}
#[derive(Eq, PartialEq, Debug, Hash, salsa::Update)]
pub enum StatementData<'db> {
Function(Function<'db>),
Print(Expression<'db>),
}
#[derive(Eq, PartialEq, Debug, Hash, salsa::Update)]
pub struct Expression<'db> {
pub span: Span<'db>,
pub data: ExpressionData<'db>,
}
impl<'db> Expression<'db> {
pub fn new(span: Span<'db>, data: ExpressionData<'db>) -> Self {
Expression { span, data }
}
}
#[derive(Eq, PartialEq, Debug, Hash, salsa::Update)]
pub enum ExpressionData<'db> {
Op(Box<Expression<'db>>, Op, Box<Expression<'db>>),
Number(OrderedFloat<f64>),
Variable(VariableId<'db>),
Call(FunctionId<'db>, Vec<Expression<'db>>),
}
#[derive(Eq, PartialEq, Copy, Clone, Hash, Debug)]
pub enum Op {
Add,
Subtract,
Multiply,
Divide,
}
#[salsa::tracked(debug)]
pub struct Function<'db> {
pub name: FunctionId<'db>,
name_span: Span<'db>,
#[tracked]
#[returns(ref)]
pub args: Vec<VariableId<'db>>,
#[tracked]
#[returns(ref)]
pub body: Expression<'db>,
}
#[salsa::tracked(debug)]
pub struct Span<'db> {
#[tracked]
pub start: usize,
#[tracked]
pub end: usize,
}
#[salsa::accumulator]
#[derive(Debug)]
#[allow(dead_code)] pub struct Diagnostic {
pub start: usize,
pub end: usize,
pub message: String,
}
impl Diagnostic {
pub fn new(start: usize, end: usize, message: String) -> Self {
Diagnostic {
start,
end,
message,
}
}
#[cfg(test)]
pub fn render(&self, db: &dyn crate::Db, src: SourceProgram) -> String {
use annotate_snippets::*;
let line_start = src.text(db)[..self.start].lines().count() + 1;
Renderer::plain()
.render(
Level::Error.title(&self.message).snippet(
Snippet::source(src.text(db))
.line_start(line_start)
.origin("input")
.fold(true)
.annotation(Level::Error.span(self.start..self.end).label("here")),
),
)
.to_string()
}
}