mirror of
https://github.com/HigherOrderCO/Kind1.git
synced 2024-10-26 13:31:31 +03:00
feat: added attributes in order to derive match and open
This commit is contained in:
parent
f22d92da95
commit
b7bc9e9f1f
@ -9,3 +9,4 @@ edition = "2021"
|
||||
kind-span = { path = "../kind-span" }
|
||||
kind-tree = { path = "../kind-tree" }
|
||||
kind-report = { path = "../kind-report" }
|
||||
fxhash = "0.2.1"
|
@ -2,3 +2,4 @@
|
||||
|
||||
pub mod matching;
|
||||
pub mod open;
|
||||
pub mod subst;
|
||||
|
@ -1,6 +1,7 @@
|
||||
//! Module to derive a dependent
|
||||
//! eliminator out of a sum type declaration.
|
||||
|
||||
use fxhash::FxHashMap;
|
||||
use kind_span::Range;
|
||||
|
||||
use kind_tree::concrete::expr::Expr;
|
||||
@ -9,6 +10,8 @@ use kind_tree::concrete::*;
|
||||
use kind_tree::concrete::{self};
|
||||
use kind_tree::symbol::{Ident, QualifiedIdent};
|
||||
|
||||
use crate::subst::{substitute_in_expr};
|
||||
|
||||
/// Derives an eliminator from a sum type declaration.
|
||||
pub fn derive_match(range: Range, sum: &SumTypeDecl) -> concrete::Entry {
|
||||
let mk_var = |name: Ident| -> Box<Expr> {
|
||||
@ -202,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()
|
||||
@ -218,7 +221,22 @@ pub fn derive_match(range: Range, sum: &SumTypeDecl) -> concrete::Entry {
|
||||
Binding::Named(_, _, _) => unreachable!(),
|
||||
})
|
||||
.collect::<Vec<AppBinding>>();
|
||||
args_indices = args_indices[sum.parameters.len()..].into();
|
||||
args_indices = {
|
||||
let mut indices = args_indices[sum.parameters.len()..].to_vec();
|
||||
|
||||
let renames = FxHashMap::from_iter(
|
||||
sum.parameters
|
||||
.extend(&cons.args)
|
||||
.map(|x| (x.name.to_string(), format!("_{}_", x.name.to_string())))
|
||||
.iter()
|
||||
.cloned(),
|
||||
);
|
||||
|
||||
for indice in &mut indices {
|
||||
substitute_in_expr(&mut indice.data, &renames)
|
||||
}
|
||||
indices
|
||||
};
|
||||
}
|
||||
_ => unreachable!(),
|
||||
},
|
||||
@ -228,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
|
||||
|
21
src/kind-derive/src/subst.rs
Normal file
21
src/kind-derive/src/subst.rs
Normal file
@ -0,0 +1,21 @@
|
||||
use fxhash::FxHashMap;
|
||||
use kind_tree::{concrete::{visitor::Visitor, expr::Expr}, symbol::Symbol};
|
||||
|
||||
pub struct Subst<'a> {
|
||||
pub names: &'a FxHashMap<String, String>,
|
||||
}
|
||||
|
||||
impl<'a> Visitor for Subst<'a> {
|
||||
fn visit_ident(&mut self, ident: &mut kind_tree::symbol::Ident) {
|
||||
if let Some(res) = self.names.get(ident.to_str()) {
|
||||
ident.data = Symbol::new(res.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn substitute_in_expr(expr: &mut Expr, names: &FxHashMap<String, String>) {
|
||||
let mut session = Subst {
|
||||
names,
|
||||
};
|
||||
session.visit_expr(expr)
|
||||
}
|
@ -123,6 +123,13 @@ impl<'a> Parser<'a> {
|
||||
Ok(ident)
|
||||
}
|
||||
|
||||
pub fn parse_any_id(&mut self) -> Result<Ident, SyntaxError> {
|
||||
let range = self.range();
|
||||
let id = eat_single!(self, Token::LowerId(x) | Token::UpperId(x, None) => x.clone())?;
|
||||
let ident = Ident::new_static(&id, range);
|
||||
Ok(ident)
|
||||
}
|
||||
|
||||
pub fn parse_upper_id(&mut self) -> Result<QualifiedIdent, SyntaxError> {
|
||||
let range = self.range();
|
||||
let (start, end) =
|
||||
@ -554,11 +561,11 @@ impl<'a> Parser<'a> {
|
||||
|
||||
pub fn parse_sttm(&mut self) -> Result<Box<Sttm>, SyntaxError> {
|
||||
let start = self.range();
|
||||
if self.check_actual(Token::Ask) {
|
||||
if self.check_actual_id("ask") {
|
||||
self.parse_ask()
|
||||
} else if self.check_actual(Token::Return) {
|
||||
} else if self.check_actual_id("return") {
|
||||
self.parse_return()
|
||||
} else if self.check_actual(Token::Let) {
|
||||
} else if self.check_actual_id("let") {
|
||||
self.parse_monadic_let()
|
||||
} else {
|
||||
let expr = self.parse_expr(false)?;
|
||||
@ -643,7 +650,7 @@ impl<'a> Parser<'a> {
|
||||
let mut cases = Vec::new();
|
||||
|
||||
while !self.get().same_variant(&Token::RBrace) {
|
||||
let constructor = self.parse_id()?;
|
||||
let constructor = self.parse_any_id()?;
|
||||
let (_range, bindings, ignore_rest) = self.parse_pat_destruct_bindings()?;
|
||||
self.eat_variant(Token::FatArrow)?;
|
||||
let value = self.parse_expr(false)?;
|
||||
@ -722,7 +729,7 @@ impl<'a> Parser<'a> {
|
||||
self.eat_variant(Token::LBrace)?;
|
||||
let if_ = self.parse_expr(false)?;
|
||||
self.eat_variant(Token::RBrace)?;
|
||||
self.eat_variant(Token::Else)?;
|
||||
self.eat_id("else")?;
|
||||
self.eat_variant(Token::LBrace)?;
|
||||
let els_ = self.parse_expr(false)?;
|
||||
let end = self.eat_variant(Token::RBrace)?.1;
|
||||
@ -738,13 +745,13 @@ impl<'a> Parser<'a> {
|
||||
/// some looakhead tokens.
|
||||
pub fn parse_expr(&mut self, multiline: bool) -> Result<Box<Expr>, SyntaxError> {
|
||||
self.ignore_docs();
|
||||
if self.check_actual(Token::Do) {
|
||||
if self.check_actual_id("do") {
|
||||
self.parse_do()
|
||||
} else if self.check_actual(Token::Match) {
|
||||
} else if self.check_actual_id("match") {
|
||||
self.parse_match()
|
||||
} else if self.check_actual(Token::Let) {
|
||||
} else if self.check_actual_id("let") {
|
||||
self.parse_let()
|
||||
} else if self.check_actual(Token::If) {
|
||||
} else if self.check_actual_id("if") {
|
||||
self.parse_if()
|
||||
} else if self.check_actual(Token::Dollar) {
|
||||
self.parse_sigma_pair()
|
||||
|
@ -52,18 +52,6 @@ impl<'a> Lexer<'a> {
|
||||
pub fn to_keyword(data: &str) -> Token {
|
||||
match data {
|
||||
"_" => Token::Hole,
|
||||
"ask" => Token::Ask,
|
||||
"do" => Token::Do,
|
||||
"if" => Token::If,
|
||||
"else" => Token::Else,
|
||||
"match" => Token::Match,
|
||||
"let" => Token::Let,
|
||||
"use" => Token::Use,
|
||||
"as" => Token::As,
|
||||
"return" => Token::Return,
|
||||
"type" => Token::Type,
|
||||
"record" => Token::Record,
|
||||
"constructor" => Token::Constructor,
|
||||
_ => Token::LowerId(data.to_string()),
|
||||
}
|
||||
}
|
||||
|
@ -30,18 +30,18 @@ pub enum Token {
|
||||
UpperId(String, Option<String>),
|
||||
|
||||
// Keywords
|
||||
Do,
|
||||
If,
|
||||
Else,
|
||||
Match,
|
||||
Ask,
|
||||
Return,
|
||||
Let,
|
||||
Type,
|
||||
Record,
|
||||
Constructor,
|
||||
Use,
|
||||
As,
|
||||
// Do,
|
||||
// If,
|
||||
// Else,
|
||||
// Match,
|
||||
// Ask,
|
||||
// Return,
|
||||
// Let,
|
||||
// Type,
|
||||
// Record,
|
||||
// Constructor,
|
||||
// Use,
|
||||
// As,
|
||||
|
||||
// Literals
|
||||
Char(char),
|
||||
@ -139,18 +139,6 @@ impl fmt::Display for Token {
|
||||
Token::LowerId(id) => write!(f, "{}", id),
|
||||
Token::UpperId(main, Some(aux)) => write!(f, "{}/{}", main, aux),
|
||||
Token::UpperId(main, None) => write!(f, "{}", main),
|
||||
Token::Do => write!(f, "do"),
|
||||
Token::If => write!(f, "if"),
|
||||
Token::Else => write!(f, "else"),
|
||||
Token::Match => write!(f, "match"),
|
||||
Token::Ask => write!(f, "ask"),
|
||||
Token::Return => write!(f, "return"),
|
||||
Token::Let => write!(f, "let"),
|
||||
Token::Type => write!(f, "type"),
|
||||
Token::Record => write!(f, "record"),
|
||||
Token::Constructor => write!(f, "constructor"),
|
||||
Token::Use => write!(f, "use"),
|
||||
Token::As => write!(f, "as"),
|
||||
Token::Char(c) => write!(f, "'{}'", c),
|
||||
Token::Str(s) => write!(f, "\"{}\"", s),
|
||||
Token::Num60(n) => write!(f, "{}", n),
|
||||
|
@ -105,6 +105,13 @@ impl<'a> Parser<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eat_id(&mut self, expect: &str) -> Result<(Token, Range), SyntaxError> {
|
||||
match self.get() {
|
||||
Token::LowerId(x) if x == expect => Ok(self.advance()),
|
||||
_ => self.fail(vec![Token::LowerId(expect.to_string())])
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eat<T>(&mut self, expect: fn(&Token) -> Option<T>) -> Result<T, SyntaxError> {
|
||||
match expect(self.get()) {
|
||||
None => self.fail(vec![]),
|
||||
@ -128,6 +135,10 @@ impl<'a> Parser<'a> {
|
||||
self.get().same_variant(&expect)
|
||||
}
|
||||
|
||||
pub fn check_actual_id(&self, expect: &str) -> bool {
|
||||
matches!(self.get(), Token::LowerId(x) if x == expect)
|
||||
}
|
||||
|
||||
pub fn try_single<T>(
|
||||
&mut self,
|
||||
fun: &dyn Fn(&mut Parser<'a>) -> Result<T, SyntaxError>,
|
||||
|
@ -6,11 +6,30 @@ use crate::lexer::tokens::Token;
|
||||
use crate::state::Parser;
|
||||
|
||||
impl<'a> Parser<'a> {
|
||||
pub fn parse_attr_args(&mut self) -> Result<Vec<AttributeStyle>, SyntaxError> {
|
||||
let mut attrs = Vec::new();
|
||||
|
||||
let range = self.range();
|
||||
|
||||
if self.check_and_eat(Token::LBracket) {
|
||||
while let Some(res) = self.try_single(&|fun| fun.parse_attr_style())? {
|
||||
attrs.push(res);
|
||||
if !self.check_and_eat(Token::Comma) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
self.eat_closing_keyword(Token::RBracket, range)?;
|
||||
}
|
||||
|
||||
Ok(attrs)
|
||||
}
|
||||
|
||||
pub fn parse_attr_style(&mut self) -> Result<AttributeStyle, SyntaxError> {
|
||||
match self.get().clone() {
|
||||
Token::LowerId(_) => {
|
||||
Token::LowerId(_) | Token::UpperId(_, None) => {
|
||||
let range = self.range();
|
||||
let ident = self.parse_id()?;
|
||||
let ident = self.parse_any_id()?;
|
||||
Ok(AttributeStyle::Ident(range, ident))
|
||||
}
|
||||
Token::Num60(num) => {
|
||||
@ -46,7 +65,11 @@ impl<'a> Parser<'a> {
|
||||
pub fn parse_attr(&mut self) -> Result<Attribute, SyntaxError> {
|
||||
let start = self.range();
|
||||
self.eat_variant(Token::Hash)?;
|
||||
|
||||
let name = self.parse_id()?;
|
||||
|
||||
let args = self.parse_attr_args()?;
|
||||
|
||||
let style = if self.check_and_eat(Token::Eq) {
|
||||
Some(self.parse_attr_style()?)
|
||||
} else {
|
||||
@ -55,6 +78,7 @@ impl<'a> Parser<'a> {
|
||||
Ok(Attribute {
|
||||
range: start.mix(style.clone().map(|x| x.locate()).unwrap_or(name.range)),
|
||||
value: style,
|
||||
args,
|
||||
name,
|
||||
})
|
||||
}
|
||||
|
@ -32,8 +32,8 @@ impl<'a> Parser<'a> {
|
||||
|
||||
pub fn is_top_level_start(&self) -> bool {
|
||||
self.is_top_level_entry()
|
||||
|| self.get().same_variant(&Token::Type)
|
||||
|| self.get().same_variant(&Token::Record)
|
||||
|| self.check_actual_id("type")
|
||||
|| self.check_actual_id("record")
|
||||
|| self.get().same_variant(&Token::Hash)
|
||||
|| self.get().is_doc()
|
||||
}
|
||||
@ -231,13 +231,13 @@ impl<'a> Parser<'a> {
|
||||
let docs = self.parse_docs()?;
|
||||
let attrs = self.parse_attrs()?;
|
||||
|
||||
if self.check_actual(Token::Type) {
|
||||
if self.check_actual_id("type") {
|
||||
Ok(TopLevel::SumType(self.parse_sum_type_def(docs, attrs)?))
|
||||
} else if self.check_actual(Token::Record) {
|
||||
} else if self.check_actual_id("record") {
|
||||
Ok(TopLevel::RecordType(self.parse_record_def(docs, attrs)?))
|
||||
} else if self.is_top_level_entry_continuation() {
|
||||
Ok(TopLevel::Entry(self.parse_entry(docs, attrs)?))
|
||||
} else if self.check_actual(Token::Use) {
|
||||
} else if self.check_actual_id("use") {
|
||||
Err(SyntaxError::CannotUseUse(self.range()))
|
||||
} else {
|
||||
self.fail(vec![])
|
||||
@ -245,9 +245,9 @@ impl<'a> Parser<'a> {
|
||||
}
|
||||
|
||||
pub fn parse_use(&mut self) -> Result<(String, String), SyntaxError> {
|
||||
self.eat_variant(Token::Use)?;
|
||||
self.eat_id("use")?;
|
||||
let origin = self.parse_upper_id()?;
|
||||
self.eat_variant(Token::As)?;
|
||||
self.eat_id("as")?;
|
||||
let alias = self.parse_upper_id()?;
|
||||
|
||||
match (origin, alias) {
|
||||
@ -275,7 +275,7 @@ impl<'a> Parser<'a> {
|
||||
let mut entries: Vec<TopLevel> = Vec::new();
|
||||
let mut uses: FxHashMap<String, String> = Default::default();
|
||||
|
||||
while self.get().same_variant(&Token::Use) {
|
||||
while self.check_actual_id("use") {
|
||||
match self.parse_use() {
|
||||
Ok((origin, alias)) => {
|
||||
uses.insert(alias, origin);
|
||||
|
@ -7,7 +7,7 @@ use crate::state::Parser;
|
||||
impl<'a> Parser<'a> {
|
||||
pub fn parse_constructor(&mut self) -> Result<Constructor, SyntaxError> {
|
||||
let docs = self.parse_docs()?;
|
||||
let name = self.parse_id()?;
|
||||
let name = self.parse_any_id()?;
|
||||
let args = self.parse_arguments()?;
|
||||
|
||||
let typ = if self.check_and_eat(Token::Colon) {
|
||||
@ -29,7 +29,7 @@ impl<'a> Parser<'a> {
|
||||
docs: Vec<String>,
|
||||
attrs: Vec<Attribute>,
|
||||
) -> Result<SumTypeDecl, SyntaxError> {
|
||||
self.eat_variant(Token::Type)?;
|
||||
self.eat_id("type")?;
|
||||
let name = self.parse_upper_id()?;
|
||||
|
||||
let parameters = self.parse_arguments()?;
|
||||
@ -66,7 +66,7 @@ impl<'a> Parser<'a> {
|
||||
docs: Vec<String>,
|
||||
attrs: Vec<Attribute>,
|
||||
) -> Result<RecordDecl, SyntaxError> {
|
||||
self.eat_variant(Token::Record)?;
|
||||
self.eat_id("record")?;
|
||||
let name = self.parse_upper_id()?;
|
||||
|
||||
let parameters = self.parse_arguments()?;
|
||||
@ -74,7 +74,7 @@ impl<'a> Parser<'a> {
|
||||
let range = self.range();
|
||||
|
||||
self.eat_variant(Token::LBrace)?;
|
||||
self.eat_variant(Token::Constructor)?;
|
||||
self.eat_id("constructor")?;
|
||||
|
||||
let constructor = self.parse_id()?;
|
||||
|
||||
|
@ -4,7 +4,7 @@ use kind_tree::concrete::{expr, CaseBinding, Destruct, TopLevel};
|
||||
use kind_tree::desugared;
|
||||
use kind_tree::symbol::Ident;
|
||||
|
||||
use crate::errors::PassError;
|
||||
use crate::errors::{PassError, Sugar};
|
||||
|
||||
use super::DesugarState;
|
||||
|
||||
@ -68,6 +68,7 @@ impl<'a> DesugarState<'a> {
|
||||
match binding {
|
||||
Destruct::Destruct(_, typ, case, jump_rest) => {
|
||||
let count = self.old_book.count.get(&typ.to_string()).unwrap();
|
||||
let open_id = typ.add_segment("open");
|
||||
|
||||
let rec = count
|
||||
.is_record_cons_of
|
||||
@ -81,6 +82,11 @@ impl<'a> DesugarState<'a> {
|
||||
return desugared::Expr::err(typ.range);
|
||||
};
|
||||
|
||||
if self.old_book.count.get(&open_id.to_string()).is_none() {
|
||||
self.send_err(PassError::NeedToImplementMethods(binding.locate(), Sugar::Open(typ.to_string())));
|
||||
return desugared::Expr::err(range);
|
||||
}
|
||||
|
||||
let ordered_fields = self.order_case_arguments(
|
||||
(&typ.range, typ.to_string()),
|
||||
&record
|
||||
@ -102,8 +108,6 @@ impl<'a> DesugarState<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
let open_id = typ.add_segment("open");
|
||||
|
||||
let irrelev = count.arguments.map(|x| x.erased).to_vec();
|
||||
|
||||
let spine = vec![
|
||||
@ -148,6 +152,13 @@ impl<'a> DesugarState<'a> {
|
||||
) -> Box<desugared::Expr> {
|
||||
let entry = self.old_book.entries.get(&match_.typ.to_string()).unwrap();
|
||||
|
||||
let match_id = match_.typ.add_segment("match");
|
||||
|
||||
if self.old_book.entries.get(&match_id.to_string()).is_none() {
|
||||
self.send_err(PassError::NeedToImplementMethods(range, Sugar::Match(match_.typ.to_string())));
|
||||
return desugared::Expr::err(range);
|
||||
}
|
||||
|
||||
let sum = if let TopLevel::SumType(sum) = entry {
|
||||
sum
|
||||
} else {
|
||||
@ -226,7 +237,6 @@ impl<'a> DesugarState<'a> {
|
||||
self.send_err(PassError::NoCoverage(range, unbound))
|
||||
}
|
||||
|
||||
let match_id = match_.typ.add_segment("match");
|
||||
|
||||
let motive = if let Some(res) = &match_.motive {
|
||||
self.desugar_expr(res)
|
||||
|
@ -7,6 +7,8 @@ pub enum Sugar {
|
||||
Sigma,
|
||||
Pair,
|
||||
BoolIf,
|
||||
Match(String),
|
||||
Open(String),
|
||||
}
|
||||
|
||||
/// Describes all of the possible errors inside each
|
||||
@ -30,6 +32,11 @@ pub enum PassError {
|
||||
ShouldBeAParameter(Span, Range),
|
||||
NoFieldCoverage(Range, Vec<String>),
|
||||
CannotPatternMatchOnErased(Range),
|
||||
|
||||
AttributeDoesNotAcceptEqual(Range),
|
||||
InvalidAttributeArgument(Range),
|
||||
DuplicatedAttributeArgument(Range, Range),
|
||||
CannotDerive(String, Range),
|
||||
}
|
||||
|
||||
// TODO: A way to build an error message with methods
|
||||
@ -150,11 +157,13 @@ impl Diagnostic for PassError {
|
||||
Sugar::Sigma => "You must implement 'Sigma' in order to use the sigma notation.".to_string(),
|
||||
Sugar::Pair => "You must implement 'Sigma' and 'Sigma.new' in order to use the sigma notation.".to_string(),
|
||||
Sugar::BoolIf => "You must implement 'Bool.if' in order to use the if notation.".to_string(),
|
||||
Sugar::Match(name) => format!("You must implement '{}.match' in order to use the match notation (or derive match with #derive[match]).", name),
|
||||
Sugar::Open(name) => format!("You must implement '{}.open' in order to use the open notation (or derive open with #derive[open]).", name),
|
||||
}],
|
||||
positions: vec![Marker {
|
||||
position: *expr_place,
|
||||
color: Color::Fst,
|
||||
text: "Here!".to_string(),
|
||||
text: "You cannot use this expression!".to_string(),
|
||||
no_code: false,
|
||||
main: true,
|
||||
}],
|
||||
@ -422,6 +431,68 @@ impl Diagnostic for PassError {
|
||||
main: true,
|
||||
}],
|
||||
},
|
||||
PassError::AttributeDoesNotAcceptEqual(place) => DiagnosticFrame {
|
||||
code: 209,
|
||||
severity: Severity::Error,
|
||||
title: "This attribute does not support values!".to_string(),
|
||||
subtitles: vec![],
|
||||
hints: vec![],
|
||||
positions: vec![Marker {
|
||||
position: *place,
|
||||
color: Color::Fst,
|
||||
text: "Try to remove everything after the equal".to_string(),
|
||||
no_code: false,
|
||||
main: true,
|
||||
}],
|
||||
},
|
||||
PassError::InvalidAttributeArgument(place) => DiagnosticFrame {
|
||||
code: 209,
|
||||
severity: Severity::Error,
|
||||
title: "Invalid attribute argument".to_string(),
|
||||
subtitles: vec![],
|
||||
hints: vec![],
|
||||
positions: vec![Marker {
|
||||
position: *place,
|
||||
color: Color::Fst,
|
||||
text: "Remove it or replace".to_string(),
|
||||
no_code: false,
|
||||
main: true,
|
||||
}],
|
||||
},
|
||||
PassError::CannotDerive(name, place) => DiagnosticFrame {
|
||||
code: 209,
|
||||
severity: Severity::Error,
|
||||
title: format!("Cannot derive '{}' for this definition", name),
|
||||
subtitles: vec![],
|
||||
hints: vec![],
|
||||
positions: vec![Marker {
|
||||
position: *place,
|
||||
color: Color::Fst,
|
||||
text: "Here!".to_string(),
|
||||
no_code: false,
|
||||
main: true,
|
||||
}],
|
||||
},
|
||||
PassError::DuplicatedAttributeArgument(first, sec) => DiagnosticFrame {
|
||||
code: 209,
|
||||
severity: Severity::Warning,
|
||||
title: "Duplicated attribute argument".to_string(),
|
||||
subtitles: vec![],
|
||||
hints: vec![],
|
||||
positions: vec![Marker {
|
||||
position: *sec,
|
||||
color: Color::For,
|
||||
text: "Second declaration".to_string(),
|
||||
no_code: false,
|
||||
main: true,
|
||||
},Marker {
|
||||
position: *first,
|
||||
color: Color::For,
|
||||
text: "First declaration!".to_string(),
|
||||
no_code: false,
|
||||
main: true,
|
||||
}],
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,38 +2,182 @@
|
||||
//! Currently it just derives `match` and `open` for sum type
|
||||
//! and record types respectively.
|
||||
|
||||
use std::fmt::Display;
|
||||
use std::sync::mpsc::Sender;
|
||||
|
||||
use fxhash::FxHashMap;
|
||||
use kind_derive::matching::derive_match;
|
||||
use kind_derive::open::derive_open;
|
||||
use kind_report::data::Diagnostic;
|
||||
use kind_tree::concrete::{Book, TopLevel};
|
||||
use kind_span::Locatable;
|
||||
use kind_span::Range;
|
||||
use kind_tree::concrete::{Attribute, Book, TopLevel};
|
||||
|
||||
use crate::errors::PassError;
|
||||
/// Expands sum type and record definitions to a lot of
|
||||
/// helper definitions like eliminators and replace qualified identifiers
|
||||
/// by their module names.
|
||||
pub mod uses;
|
||||
|
||||
pub fn expand_book(error_channel: Sender<Box<dyn Diagnostic>>, book: &mut Book) {
|
||||
#[derive(Debug, Hash, PartialEq, Eq)]
|
||||
pub enum Derive {
|
||||
Match,
|
||||
Open
|
||||
}
|
||||
|
||||
impl Display for Derive {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Derive::Match => write!(f, "match"),
|
||||
Derive::Open => write!(f, "open"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert_or_report(
|
||||
channel: Sender<Box<dyn Diagnostic>>,
|
||||
hashmap: &mut FxHashMap<Derive, Range>,
|
||||
key: Derive,
|
||||
range: Range,
|
||||
) {
|
||||
match hashmap.get(&key) {
|
||||
Some(last_range) => {
|
||||
channel
|
||||
.send(Box::new(PassError::DuplicatedAttributeArgument(last_range.clone(), range)))
|
||||
.unwrap();
|
||||
},
|
||||
None => {
|
||||
hashmap.insert(key, range);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn string_to_derive(name: &str) -> Option<Derive> {
|
||||
match name {
|
||||
"match" => Some(Derive::Match),
|
||||
"open" => Some(Derive::Open),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn expand_derive(
|
||||
error_channel: Sender<Box<dyn Diagnostic>>,
|
||||
attrs: &[Attribute],
|
||||
) -> Option<FxHashMap<Derive, Range>> {
|
||||
let mut failed = false;
|
||||
let mut def = FxHashMap::default();
|
||||
|
||||
for attr in attrs {
|
||||
if attr.name.to_str() != "derive" {
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Some(attr) = &attr.value {
|
||||
error_channel
|
||||
.send(Box::new(PassError::AttributeDoesNotAcceptEqual(
|
||||
attr.locate(),
|
||||
)))
|
||||
.unwrap();
|
||||
failed = true;
|
||||
}
|
||||
|
||||
use kind_tree::concrete::AttributeStyle::*;
|
||||
for arg in &attr.args {
|
||||
match arg {
|
||||
Ident(range, ident) => match string_to_derive(ident.to_str()) {
|
||||
Some(key) => insert_or_report(error_channel.clone(), &mut def, key, range.clone()),
|
||||
_ => {
|
||||
error_channel
|
||||
.send(Box::new(PassError::InvalidAttributeArgument(
|
||||
ident.locate(),
|
||||
)))
|
||||
.unwrap();
|
||||
failed = true;
|
||||
}
|
||||
},
|
||||
other => {
|
||||
error_channel
|
||||
.send(Box::new(PassError::InvalidAttributeArgument(
|
||||
other.locate(),
|
||||
)))
|
||||
.unwrap();
|
||||
failed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if failed {
|
||||
None
|
||||
} else {
|
||||
Some(def)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pub fn expand_book(error_channel: Sender<Box<dyn Diagnostic>>, book: &mut Book) -> bool {
|
||||
let mut failed = false;
|
||||
|
||||
let mut entries = FxHashMap::default();
|
||||
|
||||
for entry in book.entries.values() {
|
||||
match entry {
|
||||
TopLevel::SumType(sum) => {
|
||||
if let Some(derive) = expand_derive(error_channel.clone(), &sum.attrs) {
|
||||
for (key, val) in derive {
|
||||
match key {
|
||||
Derive::Match => {
|
||||
let res = derive_match(sum.name.range, sum);
|
||||
let info = res.extract_book_info();
|
||||
entries.insert(res.name.to_string(), (res, info));
|
||||
}
|
||||
other => {
|
||||
error_channel
|
||||
.send(Box::new(PassError::CannotDerive(
|
||||
other.to_string(), val,
|
||||
)))
|
||||
.unwrap();
|
||||
failed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
failed = true;
|
||||
}
|
||||
}
|
||||
TopLevel::RecordType(rec) => {
|
||||
if let Some(derive) = expand_derive(error_channel.clone(), &rec.attrs) {
|
||||
for (key, val) in derive {
|
||||
match key {
|
||||
Derive::Open => {
|
||||
let res = derive_open(rec.name.range, rec);
|
||||
let info = res.extract_book_info();
|
||||
entries.insert(res.name.to_string(), (res, info));
|
||||
}
|
||||
other => {
|
||||
error_channel
|
||||
.send(Box::new(PassError::CannotDerive(
|
||||
other.to_string(), val,
|
||||
)))
|
||||
.unwrap();
|
||||
failed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
failed = true;
|
||||
}
|
||||
}
|
||||
TopLevel::Entry(_) => (),
|
||||
}
|
||||
}
|
||||
|
||||
for (name, (tl, count)) in entries {
|
||||
book.count.insert(name.clone(), count);
|
||||
book.names.insert(name.clone().to_string(), tl.name.clone());
|
||||
book.entries.insert(name.clone(), TopLevel::Entry(tl));
|
||||
}
|
||||
|
||||
failed
|
||||
}
|
||||
|
@ -6,6 +6,6 @@
|
||||
|
||||
pub mod desugar;
|
||||
pub mod erasure;
|
||||
mod errors;
|
||||
pub mod expand;
|
||||
pub mod unbound;
|
||||
mod errors;
|
||||
|
@ -15,8 +15,7 @@ pub struct Resource<T> {
|
||||
path: PathBuf,
|
||||
|
||||
concrete_tree: concrete::Module,
|
||||
/// Accumulated diagnostics while
|
||||
diagnostics: Vec<Box<dyn Diagnostic>>,
|
||||
|
||||
/// Useful for LSP URIs
|
||||
ext: T,
|
||||
}
|
||||
|
@ -67,6 +67,7 @@ pub enum AttributeStyle {
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct Attribute {
|
||||
pub name: Ident,
|
||||
pub args: Vec<AttributeStyle>,
|
||||
pub value: Option<AttributeStyle>,
|
||||
pub range: Range,
|
||||
}
|
||||
@ -362,6 +363,14 @@ impl Display for AttributeStyle {
|
||||
impl Display for Attribute {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
|
||||
write!(f, "#{}", self.name)?;
|
||||
if !self.args.is_empty() {
|
||||
write!(f, "[")?;
|
||||
write!(f, "{}", self.args[0])?;
|
||||
for arg in self.args[1..].iter() {
|
||||
write!(f, ", {}", arg)?;
|
||||
}
|
||||
write!(f, "]")?;
|
||||
}
|
||||
if let Some(res) = &self.value {
|
||||
write!(f, " = {}", res)?;
|
||||
}
|
||||
|
@ -11,6 +11,12 @@ use kind_span::{Range, SyntaxCtxIndex};
|
||||
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub struct Symbol(String);
|
||||
|
||||
impl Symbol {
|
||||
pub fn new(str: String) -> Symbol {
|
||||
Symbol(str)
|
||||
}
|
||||
}
|
||||
|
||||
/// Identifier inside a syntax context.
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct Ident {
|
||||
|
Loading…
Reference in New Issue
Block a user