mirror of
https://github.com/HigherOrderCO/Bend.git
synced 2024-09-11 03:45:29 +03:00
Merge pull request #625 from HigherOrderCO/multi-line-comments
Add multi line comment syntax
This commit is contained in:
commit
6bbc223048
14
CHANGELOG.md
14
CHANGELOG.md
@ -11,10 +11,12 @@ and this project does not currently adhere to a particular versioning scheme.
|
|||||||
|
|
||||||
- Fix variable binding in pattern matching when the irrefutable pattern optimization occurs. ([#618][gh-618])
|
- Fix variable binding in pattern matching when the irrefutable pattern optimization occurs. ([#618][gh-618])
|
||||||
- Don't warn on unused generated definitions. ([#514][gh-514])
|
- Don't warn on unused generated definitions. ([#514][gh-514])
|
||||||
|
- Fix local definitions not being desugared properly. ([#623][gh-623])
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Add import system ([#544][gh-544])
|
- Add import system. ([#544][gh-544])
|
||||||
|
- Add multi line comment `#{ ... #}` syntax. ([#595][gh-595])
|
||||||
|
|
||||||
## [0.2.36] - 2024-07-04
|
## [0.2.36] - 2024-07-04
|
||||||
|
|
||||||
@ -370,9 +372,11 @@ and this project does not currently adhere to a particular versioning scheme.
|
|||||||
[gh-494]: https://github.com/HigherOrderCO/Bend/issues/494
|
[gh-494]: https://github.com/HigherOrderCO/Bend/issues/494
|
||||||
[gh-502]: https://github.com/HigherOrderCO/Bend/issues/502
|
[gh-502]: https://github.com/HigherOrderCO/Bend/issues/502
|
||||||
[gh-512]: https://github.com/HigherOrderCO/Bend/issues/512
|
[gh-512]: https://github.com/HigherOrderCO/Bend/issues/512
|
||||||
|
[gh-514]: https://github.com/HigherOrderCO/Bend/issues/514
|
||||||
[gh-516]: https://github.com/HigherOrderCO/Bend/issues/516
|
[gh-516]: https://github.com/HigherOrderCO/Bend/issues/516
|
||||||
[gh-526]: https://github.com/HigherOrderCO/Bend/issues/526
|
[gh-526]: https://github.com/HigherOrderCO/Bend/issues/526
|
||||||
[gh-528]: https://github.com/HigherOrderCO/Bend/issues/528
|
[gh-528]: https://github.com/HigherOrderCO/Bend/issues/528
|
||||||
|
[gh-544]: https://github.com/HigherOrderCO/Bend/pull/544
|
||||||
[gh-562]: https://github.com/HigherOrderCO/Bend/issues/562
|
[gh-562]: https://github.com/HigherOrderCO/Bend/issues/562
|
||||||
[gh-570]: https://github.com/HigherOrderCO/Bend/issues/570
|
[gh-570]: https://github.com/HigherOrderCO/Bend/issues/570
|
||||||
[gh-573]: https://github.com/HigherOrderCO/Bend/issues/573
|
[gh-573]: https://github.com/HigherOrderCO/Bend/issues/573
|
||||||
@ -383,9 +387,9 @@ and this project does not currently adhere to a particular versioning scheme.
|
|||||||
[gh-582]: https://github.com/HigherOrderCO/Bend/issues/582
|
[gh-582]: https://github.com/HigherOrderCO/Bend/issues/582
|
||||||
[gh-583]: https://github.com/HigherOrderCO/Bend/issues/583
|
[gh-583]: https://github.com/HigherOrderCO/Bend/issues/583
|
||||||
[gh-586]: https://github.com/HigherOrderCO/Bend/issues/586
|
[gh-586]: https://github.com/HigherOrderCO/Bend/issues/586
|
||||||
|
[gh-595]: https://github.com/HigherOrderCO/Bend/issues/595
|
||||||
[gh-596]: https://github.com/HigherOrderCO/Bend/issues/596
|
[gh-596]: https://github.com/HigherOrderCO/Bend/issues/596
|
||||||
[gh-618]: https://github.com/HigherOrderCO/Bend/issues/618
|
|
||||||
[gh-514]: https://github.com/HigherOrderCO/Bend/issues/514
|
|
||||||
[gh-544]: https://github.com/HigherOrderCO/Bend/pull/544
|
|
||||||
[Unreleased]: https://github.com/HigherOrderCO/Bend/compare/0.2.36...HEAD
|
|
||||||
[gh-598]: https://github.com/HigherOrderCO/Bend/issues/598
|
[gh-598]: https://github.com/HigherOrderCO/Bend/issues/598
|
||||||
|
[gh-618]: https://github.com/HigherOrderCO/Bend/issues/618
|
||||||
|
[gh-623]: https://github.com/HigherOrderCO/Bend/issues/623
|
||||||
|
[Unreleased]: https://github.com/HigherOrderCO/Bend/compare/0.2.36...HEAD
|
||||||
|
@ -70,6 +70,13 @@ impl Term {
|
|||||||
Term::List { els } => *self = Term::encode_list(std::mem::take(els)),
|
Term::List { els } => *self = Term::encode_list(std::mem::take(els)),
|
||||||
Term::Str { val } => *self = Term::encode_str(val),
|
Term::Str { val } => *self = Term::encode_str(val),
|
||||||
Term::Nat { val } => *self = Term::encode_nat(*val),
|
Term::Nat { val } => *self = Term::encode_nat(*val),
|
||||||
|
Term::Def { def, nxt } => {
|
||||||
|
for rule in def.rules.iter_mut() {
|
||||||
|
rule.pats.iter_mut().for_each(Pattern::encode_builtins);
|
||||||
|
rule.body.encode_builtins();
|
||||||
|
}
|
||||||
|
nxt.encode_builtins();
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
for child in self.children_mut() {
|
for child in self.children_mut() {
|
||||||
child.encode_builtins();
|
child.encode_builtins();
|
||||||
|
@ -167,10 +167,10 @@ impl fmt::Display for Term {
|
|||||||
}
|
}
|
||||||
Term::List { els } => write!(f, "[{}]", DisplayJoin(|| els.iter(), ", "),),
|
Term::List { els } => write!(f, "[{}]", DisplayJoin(|| els.iter(), ", "),),
|
||||||
Term::Open { typ, var, bod } => write!(f, "open {typ} {var}; {bod}"),
|
Term::Open { typ, var, bod } => write!(f, "open {typ} {var}; {bod}"),
|
||||||
Term::Def { nam, rules, nxt } => {
|
Term::Def { def, nxt } => {
|
||||||
write!(f, "def ")?;
|
write!(f, "def ")?;
|
||||||
for rule in rules.iter() {
|
for rule in def.rules.iter() {
|
||||||
write!(f, "{}", rule.display(nam))?;
|
write!(f, "{}", rule.display(&def.name))?;
|
||||||
}
|
}
|
||||||
write!(f, "{nxt}")
|
write!(f, "{nxt}")
|
||||||
}
|
}
|
||||||
@ -500,13 +500,13 @@ impl Term {
|
|||||||
Term::Num { val: Num::F24(val) } => write!(f, "{val:.3}"),
|
Term::Num { val: Num::F24(val) } => write!(f, "{val:.3}"),
|
||||||
Term::Str { val } => write!(f, "{val:?}"),
|
Term::Str { val } => write!(f, "{val:?}"),
|
||||||
Term::Ref { nam } => write!(f, "{nam}"),
|
Term::Ref { nam } => write!(f, "{nam}"),
|
||||||
Term::Def { nam, rules, nxt } => {
|
Term::Def { def, nxt } => {
|
||||||
write!(f, "def ")?;
|
write!(f, "def ")?;
|
||||||
for (i, rule) in rules.iter().enumerate() {
|
for (i, rule) in def.rules.iter().enumerate() {
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
writeln!(f, "{}", rule.display_def_aux(nam, tab + 4))?;
|
writeln!(f, "{}", rule.display_def_aux(&def.name, tab + 4))?;
|
||||||
} else {
|
} else {
|
||||||
writeln!(f, "{:tab$}{}", "", rule.display_def_aux(nam, tab + 4), tab = tab + 4)?;
|
writeln!(f, "{:tab$}{}", "", rule.display_def_aux(&def.name, tab + 4), tab = tab + 4)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
write!(f, "{:tab$}{}", "", nxt.display_pretty(tab))
|
write!(f, "{:tab$}{}", "", nxt.display_pretty(tab))
|
||||||
|
@ -65,14 +65,14 @@ pub type Adts = IndexMap<Name, Adt>;
|
|||||||
pub type Constructors = IndexMap<Name, Name>;
|
pub type Constructors = IndexMap<Name, Name>;
|
||||||
|
|
||||||
/// A pattern matching function definition.
|
/// A pattern matching function definition.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
pub struct Definition {
|
pub struct Definition {
|
||||||
pub name: Name,
|
pub name: Name,
|
||||||
pub rules: Vec<Rule>,
|
pub rules: Vec<Rule>,
|
||||||
pub source: Source,
|
pub source: Source,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
pub enum Source {
|
pub enum Source {
|
||||||
Builtin,
|
Builtin,
|
||||||
/// Was generated by the compiler.
|
/// Was generated by the compiler.
|
||||||
@ -199,8 +199,7 @@ pub enum Term {
|
|||||||
nam: Name,
|
nam: Name,
|
||||||
},
|
},
|
||||||
Def {
|
Def {
|
||||||
nam: Name,
|
def: Definition,
|
||||||
rules: Vec<Rule>,
|
|
||||||
nxt: Box<Term>,
|
nxt: Box<Term>,
|
||||||
},
|
},
|
||||||
Era,
|
Era,
|
||||||
@ -402,7 +401,7 @@ impl Clone for Term {
|
|||||||
Self::Open { typ: typ.clone(), var: var.clone(), bod: nxt.clone() }
|
Self::Open { typ: typ.clone(), var: var.clone(), bod: nxt.clone() }
|
||||||
}
|
}
|
||||||
Self::Ref { nam } => Self::Ref { nam: nam.clone() },
|
Self::Ref { nam } => Self::Ref { nam: nam.clone() },
|
||||||
Self::Def { nam, rules, nxt } => Self::Def { nam: nam.clone(), rules: rules.clone(), nxt: nxt.clone() },
|
Self::Def { def, nxt } => Self::Def { def: def.clone(), nxt: nxt.clone() },
|
||||||
Self::Era => Self::Era,
|
Self::Era => Self::Era,
|
||||||
Self::Err => Self::Err,
|
Self::Err => Self::Err,
|
||||||
})
|
})
|
||||||
|
@ -631,7 +631,8 @@ impl<'a> TermParser<'a> {
|
|||||||
if name == "def" {
|
if name == "def" {
|
||||||
// parse the nxt def term.
|
// parse the nxt def term.
|
||||||
self.index = nxt_def;
|
self.index = nxt_def;
|
||||||
return Ok(Term::Def { nam: cur_name, rules, nxt: Box::new(self.parse_term()?) });
|
let def = FunDefinition::new(name, rules, Source::Local(nxt_def..*self.index()));
|
||||||
|
return Ok(Term::Def { def, nxt: Box::new(self.parse_term()?) });
|
||||||
}
|
}
|
||||||
if name == cur_name {
|
if name == cur_name {
|
||||||
rules.push(rule);
|
rules.push(rule);
|
||||||
@ -648,7 +649,8 @@ impl<'a> TermParser<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
let nxt = self.parse_term()?;
|
let nxt = self.parse_term()?;
|
||||||
return Ok(Term::Def { nam: cur_name, rules, nxt: Box::new(nxt) });
|
let def = FunDefinition::new(cur_name, rules, Source::Local(nxt_term..*self.index()));
|
||||||
|
return Ok(Term::Def { def, nxt: Box::new(nxt) });
|
||||||
}
|
}
|
||||||
|
|
||||||
// If
|
// If
|
||||||
@ -1093,14 +1095,31 @@ impl<'a> Parser<'a> for TermParser<'a> {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if c == '#' {
|
if c == '#' {
|
||||||
while let Some(c) = self.peek_one() {
|
self.advance_one();
|
||||||
if c != '\n' {
|
if let Some(c) = self.peek_one() {
|
||||||
|
if c == '{' {
|
||||||
self.advance_one();
|
self.advance_one();
|
||||||
|
while let Some(c) = self.peek_one() {
|
||||||
|
self.advance_one();
|
||||||
|
if c == '#' {
|
||||||
|
if let Some('}') = self.peek_one() {
|
||||||
|
self.advance_one();
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
self.advance_one();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
break;
|
while let Some(c) = self.peek_one() {
|
||||||
|
if c != '\n' {
|
||||||
|
self.advance_one();
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.advance_one(); // Skip the newline character as well
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -1243,12 +1262,35 @@ pub trait ParserCommons<'a>: Parser<'a> {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if c == '#' {
|
if c == '#' {
|
||||||
while let Some(c) = self.peek_one() {
|
self.advance_one();
|
||||||
if c != '\n' {
|
char_count += 1;
|
||||||
|
if let Some(c) = self.peek_one() {
|
||||||
|
if c == '{' {
|
||||||
self.advance_one();
|
self.advance_one();
|
||||||
char_count += 1;
|
char_count += 1;
|
||||||
|
while let Some(c) = self.peek_one() {
|
||||||
|
self.advance_one();
|
||||||
|
char_count += 1;
|
||||||
|
if c == '#' {
|
||||||
|
if let Some('}') = self.peek_one() {
|
||||||
|
self.advance_one();
|
||||||
|
char_count += 1;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
self.advance_one();
|
||||||
|
char_count += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
break;
|
while let Some(c) = self.peek_one() {
|
||||||
|
if c != '\n' {
|
||||||
|
self.advance_one();
|
||||||
|
char_count += 1;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
|
@ -38,7 +38,9 @@ impl Ctx<'_> {
|
|||||||
impl Definition {
|
impl Definition {
|
||||||
pub fn desugar_match_def(&mut self, ctrs: &Constructors, adts: &Adts) -> Vec<DesugarMatchDefErr> {
|
pub fn desugar_match_def(&mut self, ctrs: &Constructors, adts: &Adts) -> Vec<DesugarMatchDefErr> {
|
||||||
let mut errs = vec![];
|
let mut errs = vec![];
|
||||||
|
for rule in self.rules.iter_mut() {
|
||||||
|
desugar_inner_match_defs(&mut rule.body, ctrs, adts, &mut errs);
|
||||||
|
}
|
||||||
let repeated_bind_errs = fix_repeated_binds(&mut self.rules);
|
let repeated_bind_errs = fix_repeated_binds(&mut self.rules);
|
||||||
errs.extend(repeated_bind_errs);
|
errs.extend(repeated_bind_errs);
|
||||||
|
|
||||||
@ -55,6 +57,25 @@ impl Definition {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn desugar_inner_match_defs(
|
||||||
|
term: &mut Term,
|
||||||
|
ctrs: &Constructors,
|
||||||
|
adts: &Adts,
|
||||||
|
errs: &mut Vec<DesugarMatchDefErr>,
|
||||||
|
) {
|
||||||
|
maybe_grow(|| match term {
|
||||||
|
Term::Def { def, nxt } => {
|
||||||
|
errs.extend(def.desugar_match_def(ctrs, adts));
|
||||||
|
desugar_inner_match_defs(nxt, ctrs, adts, errs);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
for child in term.children_mut() {
|
||||||
|
desugar_inner_match_defs(child, ctrs, adts, errs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// When a rule has repeated bind, the only one that is actually useful is the last one.
|
/// When a rule has repeated bind, the only one that is actually useful is the last one.
|
||||||
///
|
///
|
||||||
/// Example: In `(Foo x x x x) = x`, the function should return the fourth argument.
|
/// Example: In `(Foo x x x x) = x`, the function should return the fourth argument.
|
||||||
|
@ -23,26 +23,37 @@ impl Ctx<'_> {
|
|||||||
impl Term {
|
impl Term {
|
||||||
fn desugar_open(&mut self, adts: &Adts) -> Result<(), String> {
|
fn desugar_open(&mut self, adts: &Adts) -> Result<(), String> {
|
||||||
maybe_grow(|| {
|
maybe_grow(|| {
|
||||||
if let Term::Open { typ, var, bod } = self {
|
match self {
|
||||||
if let Some(adt) = adts.get(&*typ) {
|
Term::Open { typ, var, bod } => {
|
||||||
if adt.ctrs.len() == 1 {
|
bod.desugar_open(adts)?;
|
||||||
let ctr = adt.ctrs.keys().next().unwrap();
|
if let Some(adt) = adts.get(&*typ) {
|
||||||
*self = Term::Mat {
|
if adt.ctrs.len() == 1 {
|
||||||
arg: Box::new(Term::Var { nam: var.clone() }),
|
let ctr = adt.ctrs.keys().next().unwrap();
|
||||||
bnd: Some(std::mem::take(var)),
|
*self = Term::Mat {
|
||||||
with_bnd: vec![],
|
arg: Box::new(Term::Var { nam: var.clone() }),
|
||||||
with_arg: vec![],
|
bnd: Some(std::mem::take(var)),
|
||||||
arms: vec![(Some(ctr.clone()), vec![], std::mem::take(bod))],
|
with_bnd: vec![],
|
||||||
|
with_arg: vec![],
|
||||||
|
arms: vec![(Some(ctr.clone()), vec![], std::mem::take(bod))],
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Err(format!("Type '{typ}' of an 'open' has more than one constructor"));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return Err(format!("Type '{typ}' of an 'open' has more than one constructor"));
|
return Err(format!("Type '{typ}' of an 'open' is not defined"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Term::Def { def, nxt } => {
|
||||||
|
for rule in def.rules.iter_mut() {
|
||||||
|
rule.body.desugar_open(adts)?;
|
||||||
|
}
|
||||||
|
nxt.desugar_open(adts)?;
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
for child in self.children_mut() {
|
||||||
|
child.desugar_open(adts)?;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
return Err(format!("Type '{typ}' of an 'open' is not defined"));
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
for child in self.children_mut() {
|
|
||||||
child.desugar_open(adts)?;
|
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
diagnostics::Diagnostics,
|
diagnostics::Diagnostics,
|
||||||
fun::{Adts, Constructors, Ctx, Pattern},
|
fun::{Adts, Constructors, Ctx, Pattern, Rule, Term},
|
||||||
};
|
};
|
||||||
|
|
||||||
impl Ctx<'_> {
|
impl Ctx<'_> {
|
||||||
@ -15,18 +15,7 @@ impl Ctx<'_> {
|
|||||||
|
|
||||||
let def_arity = def.arity();
|
let def_arity = def.arity();
|
||||||
for rule in &mut def.rules {
|
for rule in &mut def.rules {
|
||||||
if rule.arity() != def_arity {
|
rule.fix_match_defs(def_arity, &self.book.ctrs, &self.book.adts, &mut errs);
|
||||||
errs.push(format!(
|
|
||||||
"Incorrect pattern matching rule arity. Expected {} args, found {}.",
|
|
||||||
def_arity,
|
|
||||||
rule.arity()
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
for pat in &mut rule.pats {
|
|
||||||
pat.resolve_pat(&self.book.ctrs);
|
|
||||||
pat.check_good_ctr(&self.book.ctrs, &self.book.adts, &mut errs);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for err in errs {
|
for err in errs {
|
||||||
@ -38,6 +27,44 @@ impl Ctx<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Rule {
|
||||||
|
fn fix_match_defs(&mut self, def_arity: usize, ctrs: &Constructors, adts: &Adts, errs: &mut Vec<String>) {
|
||||||
|
if self.arity() != def_arity {
|
||||||
|
errs.push(format!(
|
||||||
|
"Incorrect pattern matching rule arity. Expected {} args, found {}.",
|
||||||
|
def_arity,
|
||||||
|
self.arity()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
for pat in &mut self.pats {
|
||||||
|
pat.resolve_pat(ctrs);
|
||||||
|
pat.check_good_ctr(ctrs, adts, errs);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.body.fix_match_defs(ctrs, adts, errs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Term {
|
||||||
|
fn fix_match_defs(&mut self, ctrs: &Constructors, adts: &Adts, errs: &mut Vec<String>) {
|
||||||
|
match self {
|
||||||
|
Term::Def { def, nxt } => {
|
||||||
|
let def_arity = def.arity();
|
||||||
|
for rule in &mut def.rules {
|
||||||
|
rule.fix_match_defs(def_arity, ctrs, adts, errs);
|
||||||
|
}
|
||||||
|
nxt.fix_match_defs(ctrs, adts, errs);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
for children in self.children_mut() {
|
||||||
|
children.fix_match_defs(ctrs, adts, errs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Pattern {
|
impl Pattern {
|
||||||
/// If a var pattern actually refers to an ADT constructor, convert it into a constructor pattern.
|
/// If a var pattern actually refers to an ADT constructor, convert it into a constructor pattern.
|
||||||
fn resolve_pat(&mut self, ctrs: &Constructors) {
|
fn resolve_pat(&mut self, ctrs: &Constructors) {
|
||||||
|
@ -84,8 +84,14 @@ impl Term {
|
|||||||
if matches!(self, Term::Mat { .. } | Term::Fold { .. }) {
|
if matches!(self, Term::Mat { .. } | Term::Fold { .. }) {
|
||||||
self.fix_match(&mut errs, ctrs, adts);
|
self.fix_match(&mut errs, ctrs, adts);
|
||||||
}
|
}
|
||||||
// Add a use term to each arm rebuilding the matched variable
|
|
||||||
match self {
|
match self {
|
||||||
|
Term::Def { def, nxt } => {
|
||||||
|
for rule in def.rules.iter_mut() {
|
||||||
|
errs.extend(rule.body.fix_match_terms(ctrs, adts));
|
||||||
|
}
|
||||||
|
errs.extend(nxt.fix_match_terms(ctrs, adts));
|
||||||
|
}
|
||||||
|
// Add a use term to each arm rebuilding the matched variable
|
||||||
Term::Mat { arg: _, bnd, with_bnd: _, with_arg: _, arms }
|
Term::Mat { arg: _, bnd, with_bnd: _, with_arg: _, arms }
|
||||||
| Term::Fold { bnd, arg: _, with_bnd: _, with_arg: _, arms } => {
|
| Term::Fold { bnd, arg: _, with_bnd: _, with_arg: _, arms } => {
|
||||||
for (ctr, fields, body) in arms {
|
for (ctr, fields, body) in arms {
|
||||||
|
@ -2,7 +2,10 @@ use std::collections::BTreeSet;
|
|||||||
|
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
|
|
||||||
use crate::fun::{Book, Definition, Name, Pattern, Rule, Term};
|
use crate::{
|
||||||
|
fun::{Book, Definition, Name, Pattern, Rule, Term},
|
||||||
|
maybe_grow,
|
||||||
|
};
|
||||||
|
|
||||||
impl Book {
|
impl Book {
|
||||||
pub fn lift_local_defs(&mut self) {
|
pub fn lift_local_defs(&mut self) {
|
||||||
@ -25,10 +28,10 @@ impl Rule {
|
|||||||
|
|
||||||
impl Term {
|
impl Term {
|
||||||
pub fn lift_local_defs(&mut self, parent: &Name, defs: &mut IndexMap<Name, Definition>, gen: &mut usize) {
|
pub fn lift_local_defs(&mut self, parent: &Name, defs: &mut IndexMap<Name, Definition>, gen: &mut usize) {
|
||||||
match self {
|
maybe_grow(|| match self {
|
||||||
Term::Def { nam, rules, nxt } => {
|
Term::Def { def, nxt } => {
|
||||||
let local_name = Name::new(format!("{}__local_{}_{}", parent, gen, nam));
|
let local_name = Name::new(format!("{}__local_{}_{}", parent, gen, def.name));
|
||||||
for rule in rules.iter_mut() {
|
for rule in def.rules.iter_mut() {
|
||||||
rule.body.lift_local_defs(&local_name, defs, gen);
|
rule.body.lift_local_defs(&local_name, defs, gen);
|
||||||
}
|
}
|
||||||
nxt.lift_local_defs(parent, defs, gen);
|
nxt.lift_local_defs(parent, defs, gen);
|
||||||
@ -36,7 +39,8 @@ impl Term {
|
|||||||
|
|
||||||
let inner_defs =
|
let inner_defs =
|
||||||
defs.keys().filter(|name| name.starts_with(local_name.as_ref())).cloned().collect::<BTreeSet<_>>();
|
defs.keys().filter(|name| name.starts_with(local_name.as_ref())).cloned().collect::<BTreeSet<_>>();
|
||||||
let (r#use, fvs, mut rules) = gen_use(inner_defs, &local_name, nam, nxt, std::mem::take(rules));
|
let (r#use, fvs, mut rules) =
|
||||||
|
gen_use(inner_defs, &local_name, &def.name, nxt, std::mem::take(&mut def.rules));
|
||||||
*self = r#use;
|
*self = r#use;
|
||||||
|
|
||||||
apply_closure(&mut rules, &fvs);
|
apply_closure(&mut rules, &fvs);
|
||||||
@ -49,7 +53,7 @@ impl Term {
|
|||||||
child.lift_local_defs(parent, defs, gen);
|
child.lift_local_defs(parent, defs, gen);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,9 +93,7 @@ fn gen_use(
|
|||||||
|
|
||||||
fn apply_closure(rules: &mut [Rule], fvs: &BTreeSet<Name>) {
|
fn apply_closure(rules: &mut [Rule], fvs: &BTreeSet<Name>) {
|
||||||
for rule in rules.iter_mut() {
|
for rule in rules.iter_mut() {
|
||||||
let pats = std::mem::take(&mut rule.pats);
|
let captured = fvs.iter().cloned().map(Some).collect::<Vec<_>>();
|
||||||
let mut captured = fvs.iter().cloned().map(|nam| Pattern::Var(Some(nam))).collect::<Vec<_>>();
|
rule.body = Term::rfold_lams(std::mem::take(&mut rule.body), captured.into_iter());
|
||||||
captured.extend(pats);
|
|
||||||
rule.pats = captured;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,8 @@ impl Ctx<'_> {
|
|||||||
push_scope(name.as_ref(), &mut scope);
|
push_scope(name.as_ref(), &mut scope);
|
||||||
}
|
}
|
||||||
|
|
||||||
let res = rule.body.resolve_refs(&def_names, self.book.entrypoint.as_ref(), &mut scope);
|
let res =
|
||||||
|
rule.body.resolve_refs(&def_names, self.book.entrypoint.as_ref(), &mut scope, &mut self.info);
|
||||||
self.info.take_rule_err(res, def_name.clone());
|
self.info.take_rule_err(res, def_name.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -45,31 +46,48 @@ impl Term {
|
|||||||
def_names: &HashSet<Name>,
|
def_names: &HashSet<Name>,
|
||||||
main: Option<&Name>,
|
main: Option<&Name>,
|
||||||
scope: &mut HashMap<&'a Name, usize>,
|
scope: &mut HashMap<&'a Name, usize>,
|
||||||
|
info: &mut Diagnostics,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
maybe_grow(move || {
|
maybe_grow(move || {
|
||||||
if let Term::Var { nam } = self {
|
match self {
|
||||||
if is_var_in_scope(nam, scope) {
|
Term::Var { nam } => {
|
||||||
// If the variable is actually a reference to main, don't swap and return an error.
|
if is_var_in_scope(nam, scope) {
|
||||||
if let Some(main) = main {
|
// If the variable is actually a reference to main, don't swap and return an error.
|
||||||
if nam == main {
|
if let Some(main) = main {
|
||||||
return Err("Main definition can't be referenced inside the program.".to_string());
|
if nam == main {
|
||||||
|
return Err("Main definition can't be referenced inside the program.".to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the variable is actually a reference to a function, swap the term.
|
||||||
|
if def_names.contains(nam) {
|
||||||
|
*self = Term::r#ref(nam);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
Term::Def { def, nxt } => {
|
||||||
|
for rule in def.rules.iter_mut() {
|
||||||
|
let mut scope = HashMap::new();
|
||||||
|
|
||||||
// If the variable is actually a reference to a function, swap the term.
|
for name in rule.pats.iter().flat_map(Pattern::binds) {
|
||||||
if def_names.contains(nam) {
|
push_scope(name.as_ref(), &mut scope);
|
||||||
*self = Term::r#ref(nam);
|
}
|
||||||
|
|
||||||
|
let res = rule.body.resolve_refs(def_names, main, &mut scope, info);
|
||||||
|
info.take_rule_err(res, def.name.clone());
|
||||||
}
|
}
|
||||||
|
nxt.resolve_refs(def_names, main, scope, info)?;
|
||||||
}
|
}
|
||||||
}
|
_ => {
|
||||||
|
for (child, binds) in self.children_mut_with_binds() {
|
||||||
for (child, binds) in self.children_mut_with_binds() {
|
for bind in binds.clone() {
|
||||||
for bind in binds.clone() {
|
push_scope(bind.as_ref(), scope);
|
||||||
push_scope(bind.as_ref(), scope);
|
}
|
||||||
}
|
child.resolve_refs(def_names, main, scope, info)?;
|
||||||
child.resolve_refs(def_names, main, scope)?;
|
for bind in binds.rev() {
|
||||||
for bind in binds.rev() {
|
pop_scope(bind.as_ref(), scope);
|
||||||
pop_scope(bind.as_ref(), scope);
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -391,7 +391,7 @@ impl Stmt {
|
|||||||
StmtToFun::Assign(pat, term) => (Some(pat), term),
|
StmtToFun::Assign(pat, term) => (Some(pat), term),
|
||||||
};
|
};
|
||||||
let def = def.to_fun()?;
|
let def = def.to_fun()?;
|
||||||
let term = fun::Term::Def { nam: def.name, rules: def.rules, nxt: Box::new(nxt) };
|
let term = fun::Term::Def { def, nxt: Box::new(nxt) };
|
||||||
if let Some(pat) = nxt_pat {
|
if let Some(pat) = nxt_pat {
|
||||||
StmtToFun::Assign(pat, term)
|
StmtToFun::Assign(pat, term)
|
||||||
} else {
|
} else {
|
||||||
|
@ -91,8 +91,6 @@ pub fn desugar_book(
|
|||||||
|
|
||||||
ctx.set_entrypoint();
|
ctx.set_entrypoint();
|
||||||
|
|
||||||
ctx.book.lift_local_defs();
|
|
||||||
|
|
||||||
ctx.book.encode_adts(opts.adt_encoding);
|
ctx.book.encode_adts(opts.adt_encoding);
|
||||||
|
|
||||||
ctx.fix_match_defs()?;
|
ctx.fix_match_defs()?;
|
||||||
@ -109,6 +107,8 @@ pub fn desugar_book(
|
|||||||
|
|
||||||
ctx.fix_match_terms()?;
|
ctx.fix_match_terms()?;
|
||||||
|
|
||||||
|
ctx.book.lift_local_defs();
|
||||||
|
|
||||||
ctx.desugar_bend()?;
|
ctx.desugar_bend()?;
|
||||||
ctx.desugar_fold()?;
|
ctx.desugar_fold()?;
|
||||||
ctx.desugar_with_blocks()?;
|
ctx.desugar_with_blocks()?;
|
||||||
|
55
tests/golden_tests/parse_file/multi_line_comment.bend
Normal file
55
tests/golden_tests/parse_file/multi_line_comment.bend
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
#{
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#}
|
||||||
|
|
||||||
|
def main:
|
||||||
|
#{
|
||||||
|
ok
|
||||||
|
#}#
|
||||||
|
#
|
||||||
|
return 0
|
||||||
|
|
||||||
|
#{foo type#}
|
||||||
|
type Foo:
|
||||||
|
#{foo constructor#}
|
||||||
|
Foo { foo }
|
||||||
|
|
||||||
|
#{bar type#}
|
||||||
|
object Bar { bar }
|
||||||
|
#{
|
||||||
|
# ignore
|
||||||
|
#}#
|
||||||
|
|
||||||
|
#{X x x x#}
|
||||||
|
(X x x x) = #{ x #} x #{ x #}
|
||||||
|
|
||||||
|
#{V#}
|
||||||
|
type V
|
||||||
|
= #{V constructor#} V
|
||||||
|
|
||||||
|
(String/is_empty s) =
|
||||||
|
#{test if string is nil#}
|
||||||
|
match s #{
|
||||||
|
|
||||||
|
#}{
|
||||||
|
String/Nil: 1
|
||||||
|
String/Cons: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
def String/not_empty(s):
|
||||||
|
#{test if string if not nil#}
|
||||||
|
match s:
|
||||||
|
#{
|
||||||
|
its
|
||||||
|
not
|
||||||
|
#}
|
||||||
|
case String/Nil:
|
||||||
|
return 0
|
||||||
|
case String/Cons:
|
||||||
|
#{
|
||||||
|
it
|
||||||
|
is#}
|
||||||
|
return 1
|
@ -2,4 +2,4 @@
|
|||||||
source: tests/golden_tests.rs
|
source: tests/golden_tests.rs
|
||||||
input_file: tests/golden_tests/parse_file/fun_def.bend
|
input_file: tests/golden_tests/parse_file/fun_def.bend
|
||||||
---
|
---
|
||||||
(main) = let base = 0; def (aux []) = base(aux (List/Cons head tail)) = (+ head (aux tail))(aux [1, 2, 3])
|
(main) = let base = 0; def (aux) = λ%arg0 match %arg0 = %arg0 { List/Nil: base; List/Cons %arg0.head %arg0.tail: use tail = %arg0.tail; use head = %arg0.head; (+ head (aux tail)); }(aux (List/Cons 1 (List/Cons 2 (List/Cons 3 List/Nil))))
|
||||||
|
23
tests/snapshots/parse_file__multi_line_comment.bend.snap
Normal file
23
tests/snapshots/parse_file__multi_line_comment.bend.snap
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
---
|
||||||
|
source: tests/golden_tests.rs
|
||||||
|
input_file: tests/golden_tests/parse_file/multi_line_comment.bend
|
||||||
|
---
|
||||||
|
(X) = λ%arg0 λ%arg1 λ%arg2 use x = %arg2; x
|
||||||
|
|
||||||
|
(String/is_empty) = λ%arg0 use s = %arg0; match s = s { String/Nil: 1; String/Cons: 0; }
|
||||||
|
|
||||||
|
(main) = 0
|
||||||
|
|
||||||
|
(String/not_empty) = λ%arg0 use s = %arg0; match s = s { String/Nil: 0; String/Cons: 1; }
|
||||||
|
|
||||||
|
(Foo/Foo) = λfoo λ%x (%x Foo/Foo/tag foo)
|
||||||
|
|
||||||
|
(Bar) = λbar λ%x (%x Bar/tag bar)
|
||||||
|
|
||||||
|
(V/V) = λ%x (%x V/V/tag)
|
||||||
|
|
||||||
|
(Foo/Foo/tag) = 0
|
||||||
|
|
||||||
|
(Bar/tag) = 0
|
||||||
|
|
||||||
|
(V/V/tag) = 0
|
Loading…
Reference in New Issue
Block a user