mirror of
https://github.com/HigherOrderCO/Kind1.git
synced 2024-10-26 13:38:47 +03:00
feat: started to make kind-query
This commit is contained in:
parent
58684b874b
commit
2c69eb3341
@ -83,6 +83,11 @@ fn mk_ctr_name(ident: &QualifiedIdent) -> Box<Term> {
|
||||
mk_single_ctr(format!("{}.", ident))
|
||||
}
|
||||
|
||||
fn mk_ctr_name_from_str(ident: &str) -> Box<Term> {
|
||||
// Adds an empty segment (so it just appends a dot in the end)
|
||||
mk_single_ctr(format!("{}.", ident))
|
||||
}
|
||||
|
||||
fn span_to_num(span: Span) -> Box<Term> {
|
||||
Box::new(Term::Num {
|
||||
numb: span.encode().0,
|
||||
@ -506,12 +511,12 @@ fn codegen_entry(file: &mut lang::File, entry: &desugared::Entry) {
|
||||
|
||||
/// Compiles a book into an format that is executed by the
|
||||
/// type checker in HVM.
|
||||
pub fn codegen_book(book: &Book) -> lang::File {
|
||||
pub fn codegen_book(book: &Book, functions_to_check: Vec<String>) -> lang::File {
|
||||
let mut file = lang::File { rules: vec![] };
|
||||
|
||||
let functions_entry = lang::Rule {
|
||||
lhs: mk_ctr("Functions".to_owned(), vec![]),
|
||||
rhs: codegen_vec(book.entrs.values().map(|x| mk_ctr_name(&x.name))),
|
||||
rhs: codegen_vec(functions_to_check.iter().map(|x| mk_ctr_name_from_str(&x))),
|
||||
};
|
||||
|
||||
for entry in book.entrs.values() {
|
||||
|
@ -58,6 +58,19 @@ fn context_to_subtitles(ctx: &Context, subtitles: &mut Vec<Subtitle>) {
|
||||
}
|
||||
|
||||
impl Diagnostic for TypeError {
|
||||
fn get_syntax_ctx(&self) -> Option<kind_span::SyntaxCtxIndex> {
|
||||
match self {
|
||||
TypeError::UnboundVariable(_, range) => Some(range.ctx),
|
||||
TypeError::CantInferHole(_, range) => Some(range.ctx),
|
||||
TypeError::CantInferLambda(_, range) => Some(range.ctx),
|
||||
TypeError::InvalidCall(_, range) => Some(range.ctx),
|
||||
TypeError::ImpossibleCase(_, range, _, _) => Some(range.ctx),
|
||||
TypeError::Inspection(_, range, _) => Some(range.ctx),
|
||||
TypeError::TooManyArguments(_, range) => Some(range.ctx),
|
||||
TypeError::TypeMismatch(_, range, _, _) => Some(range.ctx),
|
||||
}
|
||||
}
|
||||
|
||||
fn to_diagnostic_frame(&self) -> DiagnosticFrame {
|
||||
match self {
|
||||
TypeError::TypeMismatch(ctx, range, detected, expected) => {
|
||||
|
@ -19,8 +19,8 @@ const CHECKER_HVM: &str = include_str!("checker.hvm");
|
||||
|
||||
/// Generates the checker in a string format that can be
|
||||
/// parsed by HVM.
|
||||
pub fn gen_checker(book: &Book) -> String {
|
||||
let base_check_code = compiler::codegen_book(book);
|
||||
pub fn gen_checker(book: &Book, functions_to_check: Vec<String>) -> String {
|
||||
let base_check_code = compiler::codegen_book(book, functions_to_check);
|
||||
let mut check_code = CHECKER_HVM.to_string();
|
||||
check_code.push_str(&base_check_code.to_string());
|
||||
|
||||
@ -29,8 +29,8 @@ pub fn gen_checker(book: &Book) -> String {
|
||||
|
||||
/// Type checks a dessugared book. It spawns an HVM instance in order
|
||||
/// to run a compiled version of the book
|
||||
pub fn type_check(book: &Book, tx: Sender<Box<dyn Diagnostic>>) -> bool {
|
||||
let check_code = gen_checker(book);
|
||||
pub fn type_check(book: &Book, tx: Sender<Box<dyn Diagnostic>>, functions_to_check: Vec<String>) -> bool {
|
||||
let check_code = gen_checker(book, functions_to_check);
|
||||
|
||||
let mut runtime = hvm::Runtime::from_code(&check_code).unwrap();
|
||||
let main = runtime.alloc_code("Kind.API.check_all").unwrap();
|
||||
@ -39,7 +39,7 @@ pub fn type_check(book: &Book, tx: Sender<Box<dyn Diagnostic>>) -> bool {
|
||||
let term = runtime.readback(main);
|
||||
|
||||
let errs = parse_report(&term)
|
||||
.expect("Internal Error: Cannot parse the report message from the type checker");
|
||||
.expect(&format!("Internal Error: Cannot parse the report message from the type checker: {}", term));
|
||||
let succeeded = errs.is_empty();
|
||||
|
||||
for err in errs {
|
||||
@ -53,7 +53,7 @@ pub fn type_check(book: &Book, tx: Sender<Box<dyn Diagnostic>>) -> bool {
|
||||
/// we run the "eval_main" that runs the generated version that both HVM and
|
||||
/// and the checker can understand.
|
||||
pub fn eval_api(book: &Book) -> Box<Term> {
|
||||
let check_code = gen_checker(book);
|
||||
let check_code = gen_checker(book, book.names.keys().cloned().collect());
|
||||
|
||||
let mut runtime = hvm::Runtime::from_code(&check_code).unwrap();
|
||||
let main = runtime.alloc_code("Kind.API.eval_main").unwrap();
|
||||
|
@ -17,8 +17,11 @@ path = "src/main.rs"
|
||||
kind-driver = { path = "../kind-driver" }
|
||||
kind-report = { path = "../kind-report" }
|
||||
kind-checker = { path = "../kind-checker" }
|
||||
kind-query = { path = "../kind-query" }
|
||||
|
||||
clap = { version = "4.0.10", features = ["derive"] }
|
||||
notify-debouncer-mini = { version = "*", default-features = false }
|
||||
notify = "5.0.0"
|
||||
|
||||
[dev-dependencies]
|
||||
|
||||
|
@ -9,6 +9,9 @@ use kind_report::report::{FileCache, Report};
|
||||
use kind_report::RenderConfig;
|
||||
|
||||
use kind_driver as driver;
|
||||
use watch::watch_session;
|
||||
|
||||
mod watch;
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(author, version, about, long_about = None)]
|
||||
@ -231,7 +234,7 @@ pub fn run_cli(config: Cli) {
|
||||
driver::desugar_book(session, &PathBuf::from(file.clone()))
|
||||
})
|
||||
.map(|res| {
|
||||
print!("Ok");
|
||||
print!("{}", res);
|
||||
res
|
||||
});
|
||||
}
|
||||
@ -253,8 +256,8 @@ pub fn run_cli(config: Cli) {
|
||||
res
|
||||
});
|
||||
}
|
||||
Command::Watch { file: _ } => {
|
||||
todo!()
|
||||
Command::Watch { file } => {
|
||||
watch_session(root, PathBuf::from(file.clone()), render_config)
|
||||
}
|
||||
Command::Repl => {
|
||||
todo!()
|
||||
|
86
src/kind-cli/src/watch.rs
Normal file
86
src/kind-cli/src/watch.rs
Normal file
@ -0,0 +1,86 @@
|
||||
use core::fmt;
|
||||
use std::{io, path::PathBuf, collections::HashMap};
|
||||
|
||||
use kind_query::{SessionHandler, Storage, Session};
|
||||
use kind_report::{report::{FileCache, Report}, RenderConfig, data::Diagnostic};
|
||||
|
||||
struct ToWriteFmt<T>(pub T);
|
||||
|
||||
impl<T> fmt::Write for ToWriteFmt<T>
|
||||
where
|
||||
T: io::Write,
|
||||
{
|
||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
self.0.write_all(s.as_bytes()).map_err(|_| fmt::Error)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render_to_stderr<T, E>(render_config: &RenderConfig, session: &T, err: &E)
|
||||
where
|
||||
T: FileCache,
|
||||
E: Report,
|
||||
{
|
||||
Report::render(
|
||||
err,
|
||||
session,
|
||||
render_config,
|
||||
&mut ToWriteFmt(std::io::stderr()),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
pub struct WatchServer<'a> {
|
||||
config: RenderConfig<'static>,
|
||||
mapper: &'a mut HashMap<PathBuf, usize>,
|
||||
inverse: &'a mut HashMap<usize, PathBuf>,
|
||||
}
|
||||
|
||||
impl<'a> SessionHandler for WatchServer<'a> {
|
||||
fn on_errors(&mut self, storage: &Storage, _uri: usize, errs: Vec<Box<dyn Diagnostic>>) {
|
||||
for err in errs {
|
||||
render_to_stderr(&self.config, storage, &err)
|
||||
}
|
||||
}
|
||||
|
||||
fn on_add_file(&mut self, file: PathBuf, id: usize) {
|
||||
println!("File added {:?} ~ {:?}", file.clone(), id);
|
||||
self.mapper.insert(file.clone(), id);
|
||||
self.inverse.insert(id, file);
|
||||
}
|
||||
|
||||
fn on_rem_file(&mut self, id: usize) {
|
||||
println!("File remove {:?}", id);
|
||||
if let Some(res) = self.inverse.remove(&id) {
|
||||
self.mapper.remove(&res);
|
||||
}
|
||||
}
|
||||
|
||||
fn get_id_by_path(&mut self, path: &PathBuf) -> Option<usize> {
|
||||
self.mapper.get(path).cloned()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn watch_session(root: PathBuf, path: PathBuf, config: RenderConfig<'static>) {
|
||||
|
||||
let mut mapper = Default::default();
|
||||
let mut inverse = Default::default();
|
||||
|
||||
let mut handler = WatchServer { config, mapper: &mut mapper, inverse: &mut inverse };
|
||||
let mut storage = Storage::default();
|
||||
let mut session = Session::new(&mut handler, &mut storage, root);
|
||||
|
||||
let main = session.init_project(path);
|
||||
|
||||
session.check_module(main);
|
||||
|
||||
let id = session.get_id_by_path(&PathBuf::from("./D.kind2")).unwrap();
|
||||
|
||||
session.remove_node(id);
|
||||
|
||||
for (place, node) in &session.storage.graph.nodes {
|
||||
println!("{} = {:?}", place, node)
|
||||
}
|
||||
|
||||
session.check_module(main);
|
||||
|
||||
}
|
@ -205,11 +205,11 @@ pub fn derive_match(range: Range, sum: &SumTypeDecl) -> concrete::Entry {
|
||||
spine_params = sum
|
||||
.parameters
|
||||
.extend(&cons.args)
|
||||
.map(|x| x.name.with_name(|f| format!("_{}_", f)))
|
||||
.map(|x| x.name.with_name(|f| format!("{}_", f)))
|
||||
.to_vec();
|
||||
spine = cons
|
||||
.args
|
||||
.map(|x| x.name.with_name(|f| format!("_{}_", f)))
|
||||
.map(|x| x.name.with_name(|f| format!("{}_", f)))
|
||||
.to_vec();
|
||||
args_indices = sp
|
||||
.iter()
|
||||
@ -227,7 +227,7 @@ pub fn derive_match(range: Range, sum: &SumTypeDecl) -> concrete::Entry {
|
||||
let renames = FxHashMap::from_iter(
|
||||
sum.parameters
|
||||
.extend(&cons.args)
|
||||
.map(|x| (x.name.to_string(), format!("_{}_", x.name.to_string())))
|
||||
.map(|x| (x.name.to_string(), format!("{}_", x.name.to_string())))
|
||||
.iter()
|
||||
.cloned(),
|
||||
);
|
||||
@ -246,12 +246,12 @@ pub fn derive_match(range: Range, sum: &SumTypeDecl) -> concrete::Entry {
|
||||
.parameters
|
||||
.extend(&sum.indices)
|
||||
.extend(&cons.args)
|
||||
.map(|x| x.name.with_name(|f| format!("_{}_", f)))
|
||||
.map(|x| x.name.with_name(|f| format!("{}_", f)))
|
||||
.to_vec();
|
||||
spine = sum
|
||||
.indices
|
||||
.extend(&cons.args)
|
||||
.map(|x| x.name.with_name(|f| format!("_{}_", f)))
|
||||
.map(|x| x.name.with_name(|f| format!("{}_", f)))
|
||||
.to_vec();
|
||||
args_indices = sum
|
||||
.indices
|
||||
|
@ -17,6 +17,16 @@ pub(crate) enum DriverError {
|
||||
}
|
||||
|
||||
impl Diagnostic for DriverError {
|
||||
fn get_syntax_ctx(&self) -> Option<kind_span::SyntaxCtxIndex> {
|
||||
match self {
|
||||
DriverError::CannotFindFile(_) => None,
|
||||
DriverError::ThereIsntAMain => None,
|
||||
DriverError::UnboundVariable(v, _) => Some(v[0].range.ctx),
|
||||
DriverError::MultiplePaths(id, _) => Some(id.range.ctx),
|
||||
DriverError::DefinedMultipleTimes(fst, _) => Some(fst.range.ctx),
|
||||
}
|
||||
}
|
||||
|
||||
fn to_diagnostic_frame(&self) -> DiagnosticFrame {
|
||||
match self {
|
||||
DriverError::UnboundVariable(idents, suggestions) => DiagnosticFrame {
|
||||
|
@ -25,7 +25,9 @@ pub fn type_check_book(session: &mut Session, path: &PathBuf) -> Option<desugare
|
||||
let concrete_book = to_book(session, path)?;
|
||||
let desugared_book = desugar::desugar_book(session.diagnostic_sender.clone(), &concrete_book)?;
|
||||
|
||||
let succeeded = checker::type_check(&desugared_book, session.diagnostic_sender.clone());
|
||||
let all = desugared_book.names.keys().cloned().collect();
|
||||
|
||||
let succeeded = checker::type_check(&desugared_book, session.diagnostic_sender.clone(), all);
|
||||
|
||||
if !succeeded {
|
||||
return None;
|
||||
@ -114,5 +116,5 @@ pub fn eval_in_checker(book: &desugared::Book) -> Box<backend::Term> {
|
||||
}
|
||||
|
||||
pub fn generate_checker(book: &desugared::Book) -> String {
|
||||
checker::gen_checker(book)
|
||||
checker::gen_checker(book, book.names.keys().cloned().collect())
|
||||
}
|
||||
|
@ -80,15 +80,15 @@ fn try_to_insert_new_name<'a>(
|
||||
book: &'a mut Book,
|
||||
) -> bool {
|
||||
if let Some(first_occorence) = book.names.get(ident.to_string().as_str()) {
|
||||
/*session
|
||||
session
|
||||
.diagnostic_sender
|
||||
.send(Box::new(DriverError::DefinedMultipleTimes(
|
||||
first_occorence.clone(),
|
||||
ident,
|
||||
)))
|
||||
.unwrap();
|
||||
*failed = true;*/
|
||||
true
|
||||
*failed = true;
|
||||
false
|
||||
} else {
|
||||
book.names.insert(ident.to_string(), ident);
|
||||
true
|
||||
|
@ -2,7 +2,7 @@
|
||||
//! lexer and the parser.
|
||||
|
||||
use kind_report::data::{Color, Diagnostic, DiagnosticFrame, Marker, Severity};
|
||||
use kind_span::Range;
|
||||
use kind_span::{Range, SyntaxCtxIndex};
|
||||
|
||||
use crate::lexer::tokens::Token;
|
||||
|
||||
@ -45,6 +45,27 @@ fn encode_name(encode: EncodeSequence) -> &'static str {
|
||||
}
|
||||
|
||||
impl Diagnostic for SyntaxError {
|
||||
|
||||
fn get_syntax_ctx(&self) -> Option<SyntaxCtxIndex> {
|
||||
match self {
|
||||
SyntaxError::UnfinishedString(range) => Some(range.ctx),
|
||||
SyntaxError::UnfinishedChar(range) => Some(range.ctx),
|
||||
SyntaxError::UnfinishedComment(range) => Some(range.ctx),
|
||||
SyntaxError::InvalidEscapeSequence(_, range) => Some(range.ctx),
|
||||
SyntaxError::InvalidNumberRepresentation(_, range) => Some(range.ctx),
|
||||
SyntaxError::UnexpectedChar(_, range) => Some(range.ctx),
|
||||
SyntaxError::UnexpectedToken(_,range, _) => Some(range.ctx),
|
||||
SyntaxError::LowerCasedDefinition(_, range) => Some(range.ctx),
|
||||
SyntaxError::NotAClauseOfDef(range, _) => Some(range.ctx),
|
||||
SyntaxError::Unclosed(range) => Some(range.ctx),
|
||||
SyntaxError::IgnoreRestShouldBeOnTheEnd(range) => Some(range.ctx),
|
||||
SyntaxError::UnusedDocString(range) => Some(range.ctx),
|
||||
SyntaxError::CannotUseUse(range) => Some(range.ctx),
|
||||
SyntaxError::ImportsCannotHaveAlias(range) => Some(range.ctx),
|
||||
SyntaxError::InvalidNumberType(_, range) => Some(range.ctx),
|
||||
}
|
||||
}
|
||||
|
||||
fn to_diagnostic_frame(&self) -> DiagnosticFrame {
|
||||
match self {
|
||||
SyntaxError::UnfinishedString(range) => DiagnosticFrame {
|
||||
|
@ -1,5 +1,6 @@
|
||||
use kind_report::data::{Color, Diagnostic, DiagnosticFrame, Marker, Severity};
|
||||
use kind_span::{Range, Span};
|
||||
use kind_span::{Range, Span, SyntaxCtxIndex};
|
||||
use kind_tree::symbol::Ident;
|
||||
|
||||
pub enum Sugar {
|
||||
DoNotation,
|
||||
@ -32,6 +33,7 @@ pub enum PassError {
|
||||
ShouldBeAParameter(Span, Range),
|
||||
NoFieldCoverage(Range, Vec<String>),
|
||||
CannotPatternMatchOnErased(Range),
|
||||
UnboundVariable(Vec<Ident>, Vec<String>),
|
||||
|
||||
AttributeDoesNotExpectEqual(Range),
|
||||
AttributeDoesNotExpectArgs(Range),
|
||||
@ -44,8 +46,63 @@ pub enum PassError {
|
||||
|
||||
// TODO: A way to build an error message with methods
|
||||
impl Diagnostic for PassError {
|
||||
fn get_syntax_ctx(&self) -> Option<SyntaxCtxIndex> {
|
||||
match self {
|
||||
PassError::RepeatedVariable(range, _) => Some(range.ctx),
|
||||
PassError::IncorrectArity(range, _, _, _) => Some(range.ctx),
|
||||
PassError::DuplicatedNamed(range, _) => Some(range.ctx),
|
||||
PassError::LetDestructOnlyForRecord(range) => Some(range.ctx),
|
||||
PassError::LetDestructOnlyForSum(range) => Some(range.ctx),
|
||||
PassError::NoCoverage(range, _) => Some(range.ctx),
|
||||
PassError::CannotFindField(range, _, _) => Some(range.ctx),
|
||||
PassError::CannotFindConstructor(range, _, _) => Some(range.ctx),
|
||||
PassError::NeedToImplementMethods(range, _) => Some(range.ctx),
|
||||
PassError::RuleWithIncorrectArity(range, _, _, _) => Some(range.ctx),
|
||||
PassError::RulesWithInconsistentArity(range) => Some(range[0].0.ctx),
|
||||
PassError::SugarIsBadlyImplemented(range, _, _) => Some(range.ctx),
|
||||
PassError::CannotUseIrrelevant(_, range, _) => Some(range.ctx),
|
||||
PassError::CannotFindAlias(_, range) => Some(range.ctx),
|
||||
PassError::NotATypeConstructor(range, _) => Some(range.ctx),
|
||||
PassError::ShouldBeAParameter(_, range) => Some(range.ctx),
|
||||
PassError::NoFieldCoverage(range, _) => Some(range.ctx),
|
||||
PassError::CannotPatternMatchOnErased(range) => Some(range.ctx),
|
||||
PassError::UnboundVariable(ranges, _) => Some(ranges[0].range.ctx),
|
||||
PassError::AttributeDoesNotExpectEqual(range) => Some(range.ctx),
|
||||
PassError::AttributeDoesNotExpectArgs(range) => Some(range.ctx),
|
||||
PassError::InvalidAttributeArgument(range) => Some(range.ctx),
|
||||
PassError::AttributeExpectsAValue(range) => Some(range.ctx),
|
||||
PassError::DuplicatedAttributeArgument(range, _) => Some(range.ctx),
|
||||
PassError::CannotDerive(_, range) => Some(range.ctx),
|
||||
PassError::AttributeDoesNotExists(range) => Some(range.ctx),
|
||||
}
|
||||
}
|
||||
|
||||
fn to_diagnostic_frame(&self) -> DiagnosticFrame {
|
||||
match self {
|
||||
PassError::UnboundVariable(idents, suggestions) => DiagnosticFrame {
|
||||
code: 100,
|
||||
severity: Severity::Error,
|
||||
title: format!("Cannot find the definition '{}'.", idents[0].to_str()),
|
||||
subtitles: vec![],
|
||||
hints: vec![if !suggestions.is_empty() {
|
||||
format!(
|
||||
"Maybe you're looking for {}",
|
||||
suggestions.iter().map(|x| format!("'{}'", x)).collect::<Vec<String>>().join(", ")
|
||||
)
|
||||
} else {
|
||||
"Take a look at the rules for name searching at https://kind.kindelia.org/hints/name-search".to_string()
|
||||
}],
|
||||
positions: idents
|
||||
.iter()
|
||||
.map(|ident| Marker {
|
||||
position: ident.range,
|
||||
color: Color::Fst,
|
||||
text: "Here!".to_string(),
|
||||
no_code: false,
|
||||
main: true,
|
||||
})
|
||||
.collect(),
|
||||
},
|
||||
PassError::CannotUseIrrelevant(var_decl, place, declarated_place) => {
|
||||
let mut positions = vec![Marker {
|
||||
position: *place,
|
||||
|
@ -1,5 +1,6 @@
|
||||
//! Collects all the unbound variables and
|
||||
//! check if patterns are linear.
|
||||
//! Collects all the unbound variables,
|
||||
//! check if patterns are linear and check
|
||||
//! if the name belongs to the current module.
|
||||
//!
|
||||
//! It also gets all of the identifiers used
|
||||
//! by sugars because it's useful to name resolution
|
||||
@ -31,8 +32,10 @@ pub struct UnboundCollector {
|
||||
// Utils for keeping variables tracking and report duplicated ones.
|
||||
pub context_vars: Vec<(Range, String)>,
|
||||
|
||||
// Keep track of top level definitions.
|
||||
pub top_level_defs: FxHashMap<String, Range>,
|
||||
pub unbound_top_level: FxHashMap<String, FxHashSet<QualifiedIdent>>,
|
||||
|
||||
pub unbound: FxHashMap<String, Vec<Ident>>,
|
||||
pub emit_errs: bool,
|
||||
}
|
||||
@ -53,19 +56,31 @@ impl UnboundCollector {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_module_unbound(
|
||||
/// Collects all of the unbound variables in a module.
|
||||
///
|
||||
/// Invariant: All qualified ident should be expanded.
|
||||
pub fn collect_module_info(
|
||||
diagnostic_sender: Sender<Box<dyn Diagnostic>>,
|
||||
module: &mut Module,
|
||||
emit_errs: bool,
|
||||
) -> (
|
||||
FxHashMap<String, Vec<Ident>>,
|
||||
FxHashMap<String, FxHashSet<QualifiedIdent>>,
|
||||
) {
|
||||
let mut state = UnboundCollector::new(diagnostic_sender, emit_errs);
|
||||
) -> UnboundCollector {
|
||||
let mut state = UnboundCollector::new(diagnostic_sender.clone(), emit_errs);
|
||||
state.visit_module(module);
|
||||
(state.unbound, state.unbound_top_level)
|
||||
|
||||
for idents in state.unbound.values() {
|
||||
diagnostic_sender.send(Box::new(PassError::UnboundVariable(
|
||||
idents.to_vec(),
|
||||
vec![],
|
||||
)))
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
state
|
||||
}
|
||||
|
||||
/// Collects all of the unbound variables in a book.
|
||||
///
|
||||
/// Invariant: All qualified ident should be expanded.
|
||||
pub fn get_book_unbound(
|
||||
diagnostic_sender: Sender<Box<dyn Diagnostic>>,
|
||||
book: &mut Book,
|
||||
@ -83,24 +98,32 @@ impl UnboundCollector {
|
||||
fn visit_top_level_names(&mut self, toplevel: &mut TopLevel) {
|
||||
match toplevel {
|
||||
TopLevel::SumType(sum) => {
|
||||
debug_assert!(sum.name.aux.is_none());
|
||||
self.top_level_defs
|
||||
.insert(sum.name.to_string(), sum.name.range);
|
||||
.insert(sum.name.get_root(), sum.name.range);
|
||||
for cons in &sum.constructors {
|
||||
let name_cons = sum.name.add_segment(cons.name.to_str());
|
||||
debug_assert!(name_cons.aux.is_none());
|
||||
self.top_level_defs
|
||||
.insert(name_cons.to_string(), name_cons.range);
|
||||
.insert(name_cons.get_root(), name_cons.range);
|
||||
}
|
||||
}
|
||||
TopLevel::RecordType(rec) => {
|
||||
self.top_level_defs
|
||||
.insert(rec.name.to_string(), rec.name.range);
|
||||
let name_cons = rec.name.add_segment(rec.constructor.to_str());
|
||||
|
||||
debug_assert!(rec.name.aux.is_none());
|
||||
debug_assert!(name_cons.aux.is_none());
|
||||
|
||||
self.top_level_defs
|
||||
.insert(name_cons.to_string(), name_cons.range);
|
||||
.insert(rec.name.get_root(), rec.name.range);
|
||||
self.top_level_defs
|
||||
.insert(name_cons.get_root(), name_cons.range);
|
||||
}
|
||||
|
||||
TopLevel::Entry(entry) => {
|
||||
debug_assert!(entry.name.aux.is_none());
|
||||
self.top_level_defs
|
||||
.insert(entry.name.to_string(), entry.name.range);
|
||||
.insert(entry.name.get_root(), entry.name.range);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -111,11 +134,7 @@ impl Visitor for UnboundCollector {
|
||||
|
||||
fn visit_ident(&mut self, ident: &mut Ident) {
|
||||
let name = ident.to_str();
|
||||
if self
|
||||
.context_vars
|
||||
.iter()
|
||||
.all(|x| x.1 != name)
|
||||
{
|
||||
if self.context_vars.iter().all(|x| x.1 != name) {
|
||||
let entry = self
|
||||
.unbound
|
||||
.entry(name.to_string())
|
||||
@ -125,19 +144,16 @@ impl Visitor for UnboundCollector {
|
||||
}
|
||||
|
||||
fn visit_qualified_ident(&mut self, ident: &mut QualifiedIdent) {
|
||||
if !self.top_level_defs.contains_key(&ident.to_string()) {
|
||||
let entry = self.unbound_top_level.entry(ident.to_string()).or_default();
|
||||
debug_assert!(ident.aux.is_none());
|
||||
if !self.top_level_defs.contains_key(&ident.get_root()) {
|
||||
let entry = self.unbound_top_level.entry(ident.get_root()).or_default();
|
||||
entry.insert(ident.clone());
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_pat_ident(&mut self, ident: &mut PatIdent) {
|
||||
let name = ident.0.to_str();
|
||||
if let Some(fst) = self
|
||||
.context_vars
|
||||
.iter()
|
||||
.find(|x| x.1 == name)
|
||||
{
|
||||
if let Some(fst) = self.context_vars.iter().find(|x| x.1 == name) {
|
||||
if self.emit_errs {
|
||||
self.errors
|
||||
.send(Box::new(PassError::RepeatedVariable(fst.0, ident.0.range)))
|
||||
|
@ -14,4 +14,5 @@ kind-checker = { path = "../kind-checker" }
|
||||
kind-pass = { path = "../kind-pass" }
|
||||
kind-target-hvm = { path = "../kind-target-hvm" }
|
||||
|
||||
fxhash = "0.2.1"
|
||||
fxhash = "0.2.1"
|
||||
pathdiff = "0.2.1"
|
@ -9,14 +9,20 @@ use kind_tree::symbol::{Ident, QualifiedIdent};
|
||||
/// Describes all of the possible errors inside each
|
||||
/// of the passes inside this crate.
|
||||
pub(crate) enum DriverError {
|
||||
CannotFindFile(String),
|
||||
UnboundVariable(Vec<Ident>, Vec<String>),
|
||||
MultiplePaths(QualifiedIdent, Vec<PathBuf>),
|
||||
DefinedMultipleTimes(QualifiedIdent, QualifiedIdent),
|
||||
ThereIsntAMain,
|
||||
}
|
||||
|
||||
impl Diagnostic for DriverError {
|
||||
fn get_syntax_ctx(&self) -> Option<kind_span::SyntaxCtxIndex> {
|
||||
match self {
|
||||
DriverError::UnboundVariable(v, _) => Some(v[0].range.ctx),
|
||||
DriverError::MultiplePaths(id, _) => Some(id.range.ctx),
|
||||
DriverError::DefinedMultipleTimes(fst, _) => Some(fst.range.ctx),
|
||||
}
|
||||
}
|
||||
|
||||
fn to_diagnostic_frame(&self) -> DiagnosticFrame {
|
||||
match self {
|
||||
DriverError::UnboundVariable(idents, suggestions) => DiagnosticFrame {
|
||||
@ -83,23 +89,7 @@ impl Diagnostic for DriverError {
|
||||
},
|
||||
],
|
||||
},
|
||||
DriverError::CannotFindFile(file) => DiagnosticFrame {
|
||||
code: 103,
|
||||
severity: Severity::Error,
|
||||
title: format!("Cannot find file '{}'", file),
|
||||
subtitles: vec![],
|
||||
hints: vec![],
|
||||
positions: vec![],
|
||||
},
|
||||
|
||||
DriverError::ThereIsntAMain => DiagnosticFrame {
|
||||
code: 103,
|
||||
severity: Severity::Error,
|
||||
title: format!("Cannot find 'Main' function to run the file."),
|
||||
subtitles: vec![],
|
||||
hints: vec![],
|
||||
positions: vec![],
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,18 +1,20 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use fxhash::{FxHashSet, FxHashMap};
|
||||
|
||||
struct Node<T> {
|
||||
data: T,
|
||||
invalidated: bool,
|
||||
children: FxHashSet<usize>,
|
||||
parents: FxHashSet<usize>,
|
||||
root: bool,
|
||||
#[derive(Debug)]
|
||||
pub struct Node<T> {
|
||||
pub data: T,
|
||||
pub invalidated: bool,
|
||||
pub hash: u64,
|
||||
pub children: FxHashSet<usize>,
|
||||
pub parents: FxHashSet<usize>,
|
||||
pub root: bool,
|
||||
pub failed: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Graph<T> {
|
||||
// Using a hashmap to make it easier to add or remove node.s
|
||||
nodes: FxHashMap<usize, Node<T>>,
|
||||
pub nodes: FxHashMap<usize, Node<T>>,
|
||||
count: usize,
|
||||
}
|
||||
|
||||
@ -23,7 +25,15 @@ impl<T> Default for Graph<T> {
|
||||
}
|
||||
|
||||
impl<T> Graph<T> {
|
||||
pub fn add(&mut self, data: T, root: bool) {
|
||||
pub fn get(&self, id: &usize) -> Option<&Node<T>> {
|
||||
self.nodes.get(id)
|
||||
}
|
||||
|
||||
pub fn get_mut(&mut self, id: &usize) -> Option<&mut Node<T>> {
|
||||
self.nodes.get_mut(id)
|
||||
}
|
||||
|
||||
pub fn add(&mut self, data: T, hash: u64, root: bool) {
|
||||
self.nodes.insert(
|
||||
self.count,
|
||||
Node {
|
||||
@ -31,6 +41,8 @@ impl<T> Graph<T> {
|
||||
invalidated: false,
|
||||
children: FxHashSet::default(),
|
||||
parents: FxHashSet::default(),
|
||||
hash,
|
||||
failed: false,
|
||||
root
|
||||
},
|
||||
);
|
||||
@ -76,9 +88,9 @@ impl<T> Graph<T> {
|
||||
fx
|
||||
}
|
||||
|
||||
fn flood_invalidation(&mut self, node: usize) {
|
||||
pub fn flood_invalidation(&mut self, node: usize) {
|
||||
if let Some(node) = self.nodes.get_mut(&node) {
|
||||
if node.invalidated {
|
||||
if !node.invalidated {
|
||||
node.invalidated = true;
|
||||
for parent in node.parents.clone() {
|
||||
self.flood_invalidation(parent)
|
||||
|
@ -2,37 +2,419 @@
|
||||
//! module. It is useful both for LSPs, Watch, Repl
|
||||
//! and many other things.
|
||||
|
||||
mod errors;
|
||||
mod graph;
|
||||
mod names;
|
||||
mod errors;
|
||||
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
use std::rc::Rc;
|
||||
use std::sync::mpsc::Sender;
|
||||
|
||||
use errors::DriverError;
|
||||
use fxhash::FxHashMap;
|
||||
use fxhash::FxHashSet;
|
||||
use graph::Graph;
|
||||
|
||||
use kind_pass::desugar;
|
||||
use kind_pass::erasure;
|
||||
use kind_pass::expand;
|
||||
use kind_pass::unbound;
|
||||
use kind_report::data::Diagnostic;
|
||||
use kind_report::report::FileCache;
|
||||
use kind_span::Range;
|
||||
use kind_tree::concrete;
|
||||
use kind_tree::concrete::Book;
|
||||
use kind_tree::concrete::Module;
|
||||
use kind_tree::concrete::TopLevel;
|
||||
use kind_tree::symbol::QualifiedIdent;
|
||||
|
||||
pub struct Resource<T> {
|
||||
path: PathBuf,
|
||||
hash: usize,
|
||||
pub enum Status {
|
||||
Module,
|
||||
Entry,
|
||||
}
|
||||
|
||||
pub struct Resource {
|
||||
concrete_tree: concrete::Module,
|
||||
exposed_entries: FxHashSet<String>,
|
||||
}
|
||||
|
||||
/// Useful for LSP URIs
|
||||
ext_info: T,
|
||||
pub struct File {
|
||||
path: PathBuf,
|
||||
input: String,
|
||||
hash: u64,
|
||||
}
|
||||
|
||||
pub enum Resolution<T> {
|
||||
Added(T),
|
||||
Reuse(T),
|
||||
Fail,
|
||||
}
|
||||
|
||||
impl<T> Resolution<T> {
|
||||
pub fn is_fail(&self) -> bool {
|
||||
matches!(self, Resolution::Fail)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Session<T> {
|
||||
graph: Graph<usize>,
|
||||
paths: FxHashMap<String, usize>,
|
||||
resources: FxHashMap<usize, Resource<T>>,
|
||||
pub struct Storage {
|
||||
pub graph: Graph<usize>,
|
||||
resources: FxHashMap<usize, Rc<Resource>>,
|
||||
files: FxHashMap<usize, File>,
|
||||
entries: FxHashMap<String, (Range, usize)>,
|
||||
}
|
||||
|
||||
impl<T> Session<T> {
|
||||
pub fn check(&mut self, path: PathBuf) {
|
||||
|
||||
pub struct Session<'a> {
|
||||
pub storage: &'a mut Storage,
|
||||
handler: &'a mut dyn SessionHandler,
|
||||
|
||||
root: PathBuf,
|
||||
count: usize,
|
||||
}
|
||||
|
||||
impl FileCache for Storage {
|
||||
fn fetch(&self, ctx: kind_span::SyntaxCtxIndex) -> Option<(PathBuf, &String)> {
|
||||
let file = self.files.get(&ctx.0).unwrap();
|
||||
Some((file.path.clone(), &file.input))
|
||||
}
|
||||
}
|
||||
|
||||
pub trait SessionHandler {
|
||||
fn on_add_file(&mut self, file: PathBuf, id: usize);
|
||||
|
||||
fn on_rem_file(&mut self, id: usize);
|
||||
|
||||
fn on_errors(&mut self, storage: &Storage, uri: usize, errs: Vec<Box<dyn Diagnostic>>);
|
||||
|
||||
fn get_id_by_path(&mut self, path: &PathBuf) -> Option<usize>;
|
||||
}
|
||||
|
||||
pub fn add_module_to_book(book: &mut Book, module: &Module) {
|
||||
for entry in &module.entries {
|
||||
match entry {
|
||||
TopLevel::SumType(sum) => {
|
||||
for cons in &sum.constructors {
|
||||
let name = sum.name.add_segment(cons.name.to_str()).to_string();
|
||||
book.count.insert(name, cons.extract_book_info(&sum));
|
||||
}
|
||||
|
||||
let name = sum.name.to_string();
|
||||
book.count.insert(name.clone(), sum.extract_book_info());
|
||||
book.entries.insert(name, entry.clone());
|
||||
}
|
||||
TopLevel::RecordType(rec) => {
|
||||
let cons_ident = rec.name.add_segment(rec.constructor.to_str()).to_string();
|
||||
book.count
|
||||
.insert(cons_ident, rec.extract_book_info_of_constructor());
|
||||
|
||||
let name = rec.name.to_string();
|
||||
book.count.insert(name.clone(), rec.extract_book_info());
|
||||
book.entries.insert(name, entry.clone());
|
||||
}
|
||||
TopLevel::Entry(entr) => {
|
||||
let name = entr.name.to_string();
|
||||
book.count.insert(name.clone(), entr.extract_book_info());
|
||||
book.entries.insert(name, entry.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Session<'a> {
|
||||
pub fn new(
|
||||
handler: &'a mut dyn SessionHandler,
|
||||
storage: &'a mut Storage,
|
||||
root: PathBuf,
|
||||
) -> Session<'a> {
|
||||
Session {
|
||||
storage,
|
||||
handler,
|
||||
root,
|
||||
count: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_input(&mut self, module_id: usize, input: String) {
|
||||
if let Some(file) = self.storage.files.get_mut(&module_id) {
|
||||
self.storage.graph.flood_invalidation(module_id);
|
||||
|
||||
file.hash = fxhash::hash64(&input);
|
||||
file.input = input;
|
||||
} else {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remove_node(&mut self, module_id: usize) {
|
||||
self.storage.graph.remove(module_id);
|
||||
self.storage.files.remove(&module_id);
|
||||
if let Some(res) = self.storage.resources.remove(&module_id) {
|
||||
for entry in &res.exposed_entries {
|
||||
self.storage.entries.remove(entry);
|
||||
}
|
||||
};
|
||||
self.handler.on_rem_file(module_id)
|
||||
}
|
||||
|
||||
fn register_module(&mut self, path: PathBuf, _module_name: String, input: String) -> usize {
|
||||
let module_id = self.count;
|
||||
self.count += 1;
|
||||
|
||||
let file = File {
|
||||
path,
|
||||
hash: fxhash::hash64(&input),
|
||||
input,
|
||||
};
|
||||
|
||||
self.storage.graph.add(module_id, 1, false);
|
||||
self.storage.files.insert(module_id, file);
|
||||
|
||||
module_id
|
||||
}
|
||||
|
||||
fn register_new_module(
|
||||
&mut self,
|
||||
errs: Sender<Box<dyn Diagnostic>>,
|
||||
module_name: &QualifiedIdent,
|
||||
) -> Resolution<usize> {
|
||||
let name = module_name.to_string();
|
||||
let node = self.storage.entries.get(&name);
|
||||
if let Some((_, module_id)) = node {
|
||||
Resolution::Reuse(*module_id)
|
||||
} else {
|
||||
let path = match names::ident_to_path(&self.root, module_name) {
|
||||
Ok(Some(res)) => res,
|
||||
Ok(None) => {
|
||||
errs.send(Box::new(DriverError::UnboundVariable(
|
||||
vec![module_name.to_ident()],
|
||||
vec![],
|
||||
)))
|
||||
.unwrap();
|
||||
return Resolution::Fail;
|
||||
}
|
||||
Err(err) => {
|
||||
errs.send(err).unwrap();
|
||||
return Resolution::Fail;
|
||||
}
|
||||
};
|
||||
|
||||
let input = fs::read_to_string(&path).unwrap();
|
||||
|
||||
let id = self.register_module(path.clone(), name, input);
|
||||
|
||||
self.handler.on_add_file(path, id);
|
||||
|
||||
Resolution::Added(id)
|
||||
}
|
||||
}
|
||||
|
||||
fn register_names(
|
||||
&mut self,
|
||||
errs: Sender<Box<dyn Diagnostic>>,
|
||||
names: FxHashMap<String, Range>,
|
||||
module_id: usize,
|
||||
) -> bool {
|
||||
// Pre check to avoid the register of bad inputs.
|
||||
let mut failed = false;
|
||||
for (name, range) in &names {
|
||||
if let Some((first, _)) = self.storage.entries.get(name) {
|
||||
errs.send(Box::new(DriverError::DefinedMultipleTimes(
|
||||
QualifiedIdent::new_static(name, None, first.clone()),
|
||||
QualifiedIdent::new_static(name, None, range.clone()),
|
||||
)))
|
||||
.unwrap();
|
||||
failed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if !failed {
|
||||
for (name, range) in names {
|
||||
self.storage.entries.insert(name, (range, module_id));
|
||||
}
|
||||
}
|
||||
|
||||
failed
|
||||
}
|
||||
|
||||
fn collect_resources(&mut self, root: usize, modules: &mut FxHashMap<usize, Rc<Resource>>) {
|
||||
if !modules.contains_key(&root) {
|
||||
let resource = self.storage.resources.get(&root).unwrap().clone();
|
||||
modules.insert(root, resource);
|
||||
for child in &self.storage.graph.get(&root).unwrap().children.clone() {
|
||||
self.collect_resources(*child, modules);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_id_by_path(&mut self, path: &PathBuf) -> Option<usize> {
|
||||
self.handler.get_id_by_path(path)
|
||||
}
|
||||
|
||||
pub fn check_module(&mut self, module_id: usize) -> Option<()> {
|
||||
let mut added = Vec::new();
|
||||
|
||||
let failed = self.compile_module(module_id, &mut added);
|
||||
|
||||
if !failed {
|
||||
let mut resources = Default::default();
|
||||
self.collect_resources(module_id, &mut resources);
|
||||
|
||||
let mut concrete_book = Book::default();
|
||||
for module in resources.values() {
|
||||
add_module_to_book(&mut concrete_book, &module.concrete_tree)
|
||||
}
|
||||
|
||||
let (rx, tx) = std::sync::mpsc::channel();
|
||||
let desugared_book = desugar::desugar_book(rx.clone(), &concrete_book)?;
|
||||
|
||||
let changed_functions = added
|
||||
.iter()
|
||||
.map(|x| {
|
||||
resources
|
||||
.get(x)
|
||||
.unwrap()
|
||||
.exposed_entries
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect::<Vec<_>>()
|
||||
})
|
||||
.flatten()
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
kind_checker::type_check(&desugared_book, rx.clone(), changed_functions);
|
||||
|
||||
let entrypoints = FxHashSet::from_iter(["Main".to_string()]);
|
||||
|
||||
erasure::erase_book(&desugared_book, rx.clone(), entrypoints)?;
|
||||
|
||||
let errs = tx.try_iter().collect::<Vec<_>>();
|
||||
|
||||
let mut groups = FxHashMap::default();
|
||||
|
||||
for err in errs {
|
||||
if let Some(ctx) = err.get_syntax_ctx() {
|
||||
let res: &mut Vec<_> = groups.entry(ctx).or_default();
|
||||
res.push(err);
|
||||
}
|
||||
}
|
||||
|
||||
for (ctx, errs) in groups {
|
||||
self.handler.on_errors(self.storage, ctx.0, errs)
|
||||
}
|
||||
}
|
||||
|
||||
Some(())
|
||||
}
|
||||
|
||||
pub fn init_project(&mut self, path: PathBuf) -> usize {
|
||||
let input = fs::read_to_string(&path).unwrap();
|
||||
self.register_module(path, "Main".to_string(), input)
|
||||
}
|
||||
|
||||
fn compile_module(&mut self, module_id: usize, added: &mut Vec<usize>) -> bool {
|
||||
let file = self.storage.files.get(&module_id).unwrap();
|
||||
let hash = file.hash;
|
||||
|
||||
if let Some(node) = self.storage.graph.get(&module_id) {
|
||||
if !node.invalidated && node.hash == file.hash {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
let (rx, tx) = std::sync::mpsc::channel();
|
||||
|
||||
// Parses the "module"
|
||||
let (mut module, mut failed) = kind_parser::parse_book(rx.clone(), module_id, &file.input);
|
||||
|
||||
// Expand aliases
|
||||
failed |= expand::uses::expand_uses(&mut module, rx.clone());
|
||||
|
||||
// Collects all of the unbound variables and top level
|
||||
// in order to recursively get all of the unbound files.
|
||||
let state = unbound::collect_module_info(rx.clone(), &mut module, false);
|
||||
|
||||
let module_definitions = state.top_level_defs.clone();
|
||||
|
||||
let last_names = if let Some(res) = self.storage.resources.get(&module_id) {
|
||||
res.exposed_entries.clone()
|
||||
} else {
|
||||
FxHashSet::default()
|
||||
};
|
||||
|
||||
let mut diff = module_definitions.clone();
|
||||
|
||||
for name in last_names {
|
||||
diff.remove(&name);
|
||||
}
|
||||
|
||||
failed |= self.register_names(rx.clone(), diff, module_id);
|
||||
|
||||
if !failed {
|
||||
let mut nodes = FxHashSet::default();
|
||||
|
||||
for (_, idents) in state.unbound_top_level {
|
||||
let first = idents.iter().last().unwrap();
|
||||
let result = self.register_new_module(rx.clone(), &first);
|
||||
failed |= result.is_fail();
|
||||
failed |= match result {
|
||||
Resolution::Reuse(id) => {
|
||||
added.push(id);
|
||||
nodes.insert(id);
|
||||
self.compile_module(id, added)
|
||||
}
|
||||
Resolution::Added(id) => {
|
||||
let file = self.storage.files.get(&id).unwrap();
|
||||
added.push(id);
|
||||
nodes.insert(id);
|
||||
self.compile_module(id, added)
|
||||
}
|
||||
Resolution::Fail => true,
|
||||
};
|
||||
}
|
||||
|
||||
let node = self.storage.graph.get_mut(&module_id).unwrap();
|
||||
node.hash = hash;
|
||||
node.failed = false;
|
||||
|
||||
let removed = node
|
||||
.children
|
||||
.difference(&nodes)
|
||||
.cloned()
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let added = nodes
|
||||
.difference(&node.children)
|
||||
.cloned()
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
node.children.extend(nodes);
|
||||
|
||||
for id in added {
|
||||
self.storage.graph.connect(module_id, id)
|
||||
}
|
||||
|
||||
for id in removed {
|
||||
self.remove_node(id)
|
||||
}
|
||||
}
|
||||
|
||||
let errs = tx.try_iter().collect::<Vec<_>>();
|
||||
|
||||
let node = self.storage.graph.get_mut(&module_id).unwrap();
|
||||
node.failed = failed;
|
||||
|
||||
if errs.is_empty() {
|
||||
self.storage.resources.insert(
|
||||
module_id,
|
||||
Rc::new(Resource {
|
||||
concrete_tree: module,
|
||||
exposed_entries: FxHashSet::from_iter(module_definitions.keys().cloned()),
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
self.handler.on_errors(self.storage, module_id, errs);
|
||||
|
||||
failed
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
use std::path::{PathBuf, Path};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use kind_report::data::Diagnostic;
|
||||
use kind_tree::symbol::QualifiedIdent;
|
||||
@ -10,7 +10,7 @@ const EXT: &str = "kind2";
|
||||
/// Tries to accumulate on a buffer all of the
|
||||
/// paths that exists (so we can just throw an
|
||||
/// error about ambiguous resolution to the user)
|
||||
pub(crate) fn accumulate_neighbour_paths(
|
||||
pub(crate) fn get_module_path(
|
||||
ident: &QualifiedIdent,
|
||||
raw_path: &Path,
|
||||
) -> Result<Option<PathBuf>, Box<dyn Diagnostic>> {
|
||||
@ -34,4 +34,22 @@ pub(crate) fn accumulate_neighbour_paths(
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
/// Transforms an ident into a path
|
||||
pub(crate) fn ident_to_path(
|
||||
root: &Path,
|
||||
ident: &QualifiedIdent,
|
||||
) -> Result<Option<PathBuf>, Box<dyn Diagnostic>> {
|
||||
let name = ident.root.to_string();
|
||||
let segments = name.as_str().split('.').collect::<Vec<&str>>();
|
||||
let mut raw_path = root.to_path_buf();
|
||||
raw_path.push(PathBuf::from(segments.join("/")));
|
||||
match get_module_path(&ident, &raw_path) {
|
||||
Ok(None) => {
|
||||
raw_path.pop();
|
||||
get_module_path(&ident, &raw_path)
|
||||
}
|
||||
rest => rest,
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use kind_span::Range;
|
||||
use kind_span::{Range, SyntaxCtxIndex};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Severity {
|
||||
@ -60,5 +60,6 @@ pub enum Log {
|
||||
}
|
||||
|
||||
pub trait Diagnostic {
|
||||
fn get_syntax_ctx(&self) -> Option<SyntaxCtxIndex>;
|
||||
fn to_diagnostic_frame(&self) -> DiagnosticFrame;
|
||||
}
|
||||
|
@ -459,9 +459,7 @@ impl Report for Box<dyn Diagnostic> {
|
||||
for (ctx, group) in groups {
|
||||
writeln!(fmt)?;
|
||||
let (file, code) = cache.fetch(ctx).unwrap();
|
||||
let diff =
|
||||
pathdiff::diff_paths(&file.clone(), PathBuf::from(".").canonicalize().unwrap())
|
||||
.unwrap();
|
||||
let diff =file.clone();
|
||||
write_code_block(&diff, config, &group, code, fmt)?;
|
||||
}
|
||||
|
||||
|
@ -4,6 +4,7 @@ use std::fmt::Display;
|
||||
|
||||
use kind_span::{Range, SyntaxCtxIndex};
|
||||
|
||||
|
||||
/// Stores the name of a variable or constructor.
|
||||
/// It's simply a string because in the future i plan
|
||||
/// to store all the names and only reference them with
|
||||
@ -51,6 +52,19 @@ impl QualifiedIdent {
|
||||
}
|
||||
}
|
||||
|
||||
/// Most of the times a qualified ident will not have the `aux` field
|
||||
/// because it's removed at the `expand_uses` phase. It returns the root
|
||||
/// and avoid a copy of the string.
|
||||
#[inline]
|
||||
pub fn to_str(&self) -> &str {
|
||||
&self.root.0
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_root(&self) -> String {
|
||||
self.root.0.clone()
|
||||
}
|
||||
|
||||
pub fn change_root(&mut self, str: String) {
|
||||
self.root = Symbol(str);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user