pub mod checks;
pub mod diagnostics;
pub mod eval;
pub mod id;
pub mod name_resolution;
pub mod node_state;
pub mod static_args;
mod types;
use crate::id::IdRef;
use crate::static_args::StaticArgs;
use crate::{
diagnostics::{Diagnostic, Level, Span},
types::check_node_equations,
};
use rustre_parser::ast::{
AstNode, AstToken, Ident, NodeNode, NodeProfileNode, ParamsNode, Root, TypedIdsNode,
};
use std::path::PathBuf;
use std::rc::Rc;
use yeter::Database;
pub fn driver() -> Database {
Database::new()
}
#[derive(Clone, Hash)]
pub struct SourceFile {
pub path: PathBuf,
pub text: String,
}
impl SourceFile {
pub fn new(path: PathBuf, text: String) -> SourceFile {
SourceFile { path, text }
}
}
#[derive(Clone, Debug, Hash)]
pub struct Signature {
pub name: Option<Ident>,
pub params: Vec<TypedIdsNode>,
pub return_params: Vec<TypedIdsNode>,
}
impl Signature {
pub fn from_name(name: Option<Ident>) -> Self {
Self {
name,
params: Default::default(),
return_params: Default::default(),
}
}
#[inline]
pub fn with_params(
mut self,
params: Vec<TypedIdsNode>,
return_params: Vec<TypedIdsNode>,
) -> Self {
self.params = params;
self.return_params = return_params;
self
}
}
#[derive(Clone, Debug, Hash)]
pub struct TypedSignature {
pub name: Option<Ident>,
pub params: Vec<(Ident, types::Type)>,
pub return_params: Vec<(Ident, types::Type)>,
}
impl TypedSignature {
pub fn from_name(name: Option<Ident>) -> Self {
Self {
name,
params: Default::default(),
return_params: Default::default(),
}
}
#[inline]
pub fn with_params(
mut self,
params: Vec<(Ident, types::Type)>,
return_params: Vec<(Ident, types::Type)>,
) -> Self {
self.params = params;
self.return_params = return_params;
self
}
}
#[yeter::query]
pub fn parse_file(db: &Database, file: SourceFile) -> Root {
let source = file.text;
let (root, errors) = rustre_parser::parse(&source);
for error in errors {
let span = Span {
file: file.path.clone(),
start: error.span.start,
end: error.span.end,
};
Diagnostic::new(Level::Error, "parsing error")
.with_attachment(span, error.msg)
.emit(db);
}
root
}
#[yeter::query]
pub fn files(_db: &Database) -> Option<Vec<SourceFile>>;
#[yeter::query]
fn parsed_files(db: &Database) -> Vec<Rc<Root>> {
let files = files(db);
if let Some(files) = files.as_ref() {
files
.iter()
.map(|s| parse_file(db, s.clone()))
.collect::<Vec<_>>()
} else {
vec![]
}
}
#[yeter::query]
pub fn get_signature(db: &Database, node: NodeNode) -> Signature {
let signature = Signature::from_name(node.id_node().and_then(|id| id.ident()));
if node.equal().is_some() {
if let Some(alias) = node.alias() {
if let Some(alias_name) = alias.id_ref_node() {
let id_ref = IdRef::from(alias_name);
let alias_node = name_resolution::find_node(db, &None, id_ref)
.as_ref()
.clone()
.unwrap(); return Signature::clone(&get_signature(db, alias_node));
}
}
return signature;
}
let sig = node.node_profile_node();
let get_params = |f: fn(&NodeProfileNode) -> Option<ParamsNode>| {
sig.clone()
.and_then(|sig| f(&sig))
.and_then(|p| p.all_var_decl_node().next())
.iter()
.flat_map(|v| v.all_typed_ids_node())
.collect::<Vec<_>>()
};
signature.with_params(
get_params(NodeProfileNode::params),
get_params(NodeProfileNode::return_params),
)
}
#[yeter::query]
pub fn get_typed_signature<'a>(
db: &Database,
callee_static_args: &'a StaticArgs,
node: NodeNode,
) -> TypedSignature {
let signature = TypedSignature::from_name(node.id_node().and_then(|id| id.ident()));
if node.equal().is_some() {
if let Some(alias) = node.alias() {
if let Some(alias_name) = alias.id_ref_node() {
let id_ref = IdRef::from(alias_name);
let alias_node = name_resolution::find_node(db, &None, id_ref)
.as_ref()
.clone()
.unwrap(); let static_args = static_args::static_args_of_effective_node(
db,
Some(callee_static_args.clone()),
Some(node),
alias_node.clone(),
alias.static_args_node(),
);
let Some(static_args) = static_args.as_ref().as_ref() else {
Diagnostic::new(Level::Error, "cannot evaluate static arguments")
.with_attachment(
Span::of_node(db, alias.static_args_node().unwrap().syntax()),
"cannot evaluate",
)
.emit(db);
return TypedSignature::clone(&get_typed_signature(
db,
&StaticArgs::default(),
alias_node,
));
};
check_node_equations(db, static_args.clone(), alias_node.clone());
return TypedSignature::clone(&get_typed_signature(db, static_args, alias_node));
}
}
return signature;
}
let sig = get_signature(db, node);
let get_params = |params: &[TypedIdsNode]| {
params
.iter()
.flat_map(|group| {
let ty = group
.type_node()
.map(|t| types::type_of_ast_type(db, Some(callee_static_args.clone()), None, t))
.unwrap_or_default();
group
.all_id_node()
.map(|id| id.ident().unwrap())
.zip(std::iter::repeat(ty.as_ref().clone()))
})
.collect::<Vec<_>>()
};
signature.with_params(get_params(&sig.params), get_params(&sig.return_params))
}
#[yeter::query]
pub fn check(db: &Database) {
let files = parsed_files(db);
for file in files.as_slice() {
for node in file.all_node_node() {
let static_params = static_args::static_params_of_node(db, node.clone());
if static_params.is_empty() {
let _ = get_typed_signature(db, &StaticArgs::default(), node.clone());
let _name = node.id_node().unwrap().ident().unwrap();
let _name = _name.text();
let _ = check_node_equations(db, StaticArgs::default(), node.clone());
node_state::check_node_function_state(db, node.clone(), &StaticArgs::default());
}
checks::check_arity(db, node);
}
}
}
pub fn add_source_file(db: &Database, path: PathBuf) {
let contents = std::fs::read_to_string(&path).unwrap(); let file = SourceFile::new(path, contents);
let files = files(db);
let mut files = Option::clone(&files).unwrap_or_default();
files.push(file);
db.set::<files>((), Some(files));
}
pub fn add_source_contents(db: &mut Database, contents: String) {
let file = SourceFile::new(PathBuf::new(), contents);
let files = files(db);
let mut files = Option::clone(&files).unwrap_or_default();
files.push(file);
db.set::<files>((), Some(files));
}
#[cfg(test)]
mod tests {
use std::path::Path;
#[test]
fn parse_query() {
let driver = super::driver();
super::add_source_file(&driver, Path::new("../tests/stable.lus").to_owned());
let files = super::files(&driver);
let files = files.as_ref().as_deref().unwrap_or_default();
for file in files {
let ast = super::parse_file(&driver, file.clone());
assert_eq!(ast.all_include_statement().count(), 1);
}
}
}