mirror of
https://github.com/HigherOrderCO/Bend.git
synced 2024-09-11 03:45:29 +03:00
[sc-686] Add record types and record type destructuring
This commit is contained in:
parent
3e9785b707
commit
a763052c11
@ -56,6 +56,22 @@ The exact function they become depends on the encoding.
|
||||
|
||||
Read [defining data types](./defining-data-types.md) to know more.
|
||||
|
||||
### Object
|
||||
|
||||
Defines a type with a single constructor (like a struct, a record or a class).
|
||||
|
||||
```python
|
||||
object Pair { fst, snd }
|
||||
|
||||
object Function { name, args, body }
|
||||
|
||||
object Vec { len, data }
|
||||
```
|
||||
|
||||
The constructor created from this definition has the same name as the type.
|
||||
|
||||
Since it only has one constructor, `fold`ing a recursive `object` requires some additional stop condition apart from pattern matching on the value itself (like an `if` statement).
|
||||
|
||||
## Statements
|
||||
|
||||
### Assignment
|
||||
@ -251,6 +267,28 @@ def bend(x, y, ...):
|
||||
return ...
|
||||
```
|
||||
|
||||
### Open
|
||||
|
||||
```python
|
||||
p = Point { x: 1, y: 2 }
|
||||
open Point: p
|
||||
return Point { x: p.x * p.x, y: p.y * p.y }
|
||||
```
|
||||
|
||||
Brings the inner fields of an object into scope. The original variable can still be accessed, but doing so will cause any used fields to be duplicated.
|
||||
|
||||
It's equivalent to pattern matching on the object, with the restriction that its type must have only one constructor.
|
||||
|
||||
```python
|
||||
open Point: p
|
||||
...
|
||||
|
||||
// Equivalent to:
|
||||
match p:
|
||||
Point:
|
||||
...
|
||||
```
|
||||
|
||||
### Do
|
||||
|
||||
```python
|
||||
@ -721,6 +759,25 @@ It is desugared according to the chosen encoding. Read [pattern matching](./patt
|
||||
|
||||
Using `;` is optional.
|
||||
|
||||
### Open
|
||||
|
||||
```rust
|
||||
let x = (Pair 1 2);
|
||||
open Pair x;
|
||||
(+ x.fst x.snd)
|
||||
```
|
||||
|
||||
Brings the inner fields of an object into scope. The original variable can still be accessed, but doing so will cause any used fields to be duplicated.
|
||||
|
||||
It's equivalent to pattern matching on the value, with the restriction that its type must have only one constructor.
|
||||
|
||||
```rust
|
||||
let x = (Pair 1 2)
|
||||
match x {
|
||||
Pair: (+ x.fst x.snd)
|
||||
}
|
||||
```
|
||||
|
||||
### Monadic bind blocks
|
||||
|
||||
```rust
|
||||
|
@ -56,4 +56,6 @@ def sort(d, s, t):
|
||||
return flow(d, s, sort(d-1, 0, t.a), sort(d-1, 1, t.b))
|
||||
|
||||
def main:
|
||||
return sum(18, sort(18, 0, gen(18, 0)))
|
||||
// Sorts the numbers from 0 to 2^depth.
|
||||
depth = 14
|
||||
return sum(depth, sort(depth, 0, gen(depth, 0)))
|
||||
|
@ -146,6 +146,7 @@ impl fmt::Display for Term {
|
||||
write!(f, "({} {} {})", opr, fst, snd)
|
||||
}
|
||||
Term::List { els } => write!(f, "[{}]", DisplayJoin(|| els.iter(), ", "),),
|
||||
Term::Open { typ, var, bod } => write!(f, "open {typ} {var}; {bod}"),
|
||||
Term::Err => write!(f, "<Invalid>"),
|
||||
})
|
||||
}
|
||||
@ -415,6 +416,9 @@ impl Term {
|
||||
writeln!(f, "{:tab$}{}", "", base.display_pretty(tab + 2), tab = tab + 2)?;
|
||||
write!(f, "{:tab$}}}", "")
|
||||
}
|
||||
Term::Open { typ, var, bod } => {
|
||||
write!(f, "open {typ} {var};\n{:tab$}{}", "", bod.display_pretty(tab))
|
||||
}
|
||||
Term::Nat { val } => write!(f, "#{val}"),
|
||||
Term::Num { val: Num::U24(val) } => write!(f, "{val}"),
|
||||
Term::Num { val: Num::I24(val) } => write!(f, "{}{}", if *val < 0 { "-" } else { "+" }, val.abs()),
|
||||
|
@ -156,6 +156,11 @@ pub enum Term {
|
||||
step: Box<Term>,
|
||||
base: Box<Term>,
|
||||
},
|
||||
Open {
|
||||
typ: Name,
|
||||
var: Name,
|
||||
bod: Box<Term>,
|
||||
},
|
||||
Ref {
|
||||
nam: Name,
|
||||
},
|
||||
@ -370,6 +375,9 @@ impl Clone for Term {
|
||||
step: step.clone(),
|
||||
base: base.clone(),
|
||||
},
|
||||
Self::Open { typ, var, bod: nxt } => {
|
||||
Self::Open { typ: typ.clone(), var: var.clone(), bod: nxt.clone() }
|
||||
}
|
||||
Self::Ref { nam } => Self::Ref { nam: nam.clone() },
|
||||
Self::Era => Self::Era,
|
||||
Self::Err => Self::Err,
|
||||
@ -515,7 +523,9 @@ impl Term {
|
||||
| Term::Use { val: fst, nxt: snd, .. }
|
||||
| Term::App { fun: fst, arg: snd, .. }
|
||||
| Term::Oper { fst, snd, .. } => ChildrenIter::Two([fst.as_ref(), snd.as_ref()]),
|
||||
Term::Lam { bod, .. } | Term::Do { bod, .. } => ChildrenIter::One([bod.as_ref()]),
|
||||
Term::Lam { bod, .. } | Term::Do { bod, .. } | Term::Open { bod, .. } => {
|
||||
ChildrenIter::One([bod.as_ref()])
|
||||
}
|
||||
Term::Var { .. }
|
||||
| Term::Link { .. }
|
||||
| Term::Num { .. }
|
||||
@ -548,7 +558,9 @@ impl Term {
|
||||
| Term::Use { val: fst, nxt: snd, .. }
|
||||
| Term::App { fun: fst, arg: snd, .. }
|
||||
| Term::Oper { fst, snd, .. } => ChildrenIter::Two([fst.as_mut(), snd.as_mut()]),
|
||||
Term::Lam { bod, .. } | Term::Do { bod, .. } => ChildrenIter::One([bod.as_mut()]),
|
||||
Term::Lam { bod, .. } | Term::Do { bod, .. } | Term::Open { bod, .. } => {
|
||||
ChildrenIter::One([bod.as_mut()])
|
||||
}
|
||||
Term::Var { .. }
|
||||
| Term::Link { .. }
|
||||
| Term::Num { .. }
|
||||
@ -623,6 +635,7 @@ impl Term {
|
||||
| Term::Ref { .. }
|
||||
| Term::Era
|
||||
| Term::Err => ChildrenIter::Zero([]),
|
||||
Term::Open { .. } => unreachable!("Open should be removed in earlier pass"),
|
||||
}
|
||||
}
|
||||
|
||||
@ -682,6 +695,7 @@ impl Term {
|
||||
| Term::Ref { .. }
|
||||
| Term::Era
|
||||
| Term::Err => ChildrenIter::Zero([]),
|
||||
Term::Open { .. } => unreachable!("Open should be removed in earlier pass"),
|
||||
}
|
||||
}
|
||||
|
||||
@ -737,6 +751,7 @@ impl Term {
|
||||
| Term::Ref { .. }
|
||||
| Term::Era
|
||||
| Term::Err => ChildrenIter::Zero([]),
|
||||
Term::Open { .. } => unreachable!("Open should be removed in earlier pass"),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -64,35 +64,49 @@ impl<'a> TermParser<'a> {
|
||||
let mut indent = self.advance_newlines();
|
||||
while !self.is_eof() {
|
||||
let ini_idx = *self.index();
|
||||
if self.try_parse_keyword("type") {
|
||||
// Imp type definition
|
||||
if self.try_parse_keyword("type") {
|
||||
let mut prs = PyParser { input: self.input, index: *self.index() };
|
||||
let (enum_, nxt_indent) = prs.parse_data_type(indent)?;
|
||||
let (enum_, nxt_indent) = prs.parse_type(indent)?;
|
||||
self.index = prs.index;
|
||||
let end_idx = *self.index();
|
||||
prs.add_type(enum_, &mut book, ini_idx, end_idx, builtin)?;
|
||||
indent = nxt_indent;
|
||||
} else if self.try_parse_keyword("def") {
|
||||
continue;
|
||||
}
|
||||
// Imp record type definition
|
||||
if self.try_parse_keyword("object") {
|
||||
let mut prs = PyParser { input: self.input, index: *self.index() };
|
||||
let (obj, nxt_indent) = prs.parse_object(indent)?;
|
||||
self.index = prs.index;
|
||||
let end_idx = *self.index();
|
||||
prs.add_object(obj, &mut book, ini_idx, end_idx, builtin)?;
|
||||
indent = nxt_indent;
|
||||
continue;
|
||||
}
|
||||
// Imp function definition
|
||||
if self.try_parse_keyword("def") {
|
||||
let mut prs = PyParser { input: self.input, index: *self.index() };
|
||||
let (def, nxt_indent) = prs.parse_def(indent)?;
|
||||
self.index = prs.index;
|
||||
let end_idx = *self.index();
|
||||
prs.add_def(def, &mut book, ini_idx, end_idx)?;
|
||||
indent = nxt_indent;
|
||||
} else if self.try_parse_keyword("data") {
|
||||
continue;
|
||||
}
|
||||
// Fun type definition
|
||||
if self.try_parse_keyword("data") {
|
||||
let (nam, adt) = self.parse_datatype(builtin)?;
|
||||
let end_idx = *self.index();
|
||||
self.with_ctx(book.add_adt(nam, adt), ini_idx, end_idx)?;
|
||||
indent = self.advance_newlines();
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
// Fun function definition
|
||||
let (name, rule) = self.parse_rule()?;
|
||||
book.add_rule(name, rule, builtin);
|
||||
indent = self.advance_newlines();
|
||||
}
|
||||
}
|
||||
|
||||
Ok(book)
|
||||
}
|
||||
@ -494,6 +508,18 @@ impl<'a> TermParser<'a> {
|
||||
});
|
||||
}
|
||||
|
||||
// Open
|
||||
if self.try_parse_keyword("open") {
|
||||
unexpected_tag(self)?;
|
||||
self.skip_trivia();
|
||||
let typ = self.parse_top_level_name()?;
|
||||
self.skip_trivia();
|
||||
let var = self.parse_bend_name()?;
|
||||
self.try_consume(";");
|
||||
let bod = self.parse_term()?;
|
||||
return Ok(Term::Open { typ, var, bod: Box::new(bod) });
|
||||
}
|
||||
|
||||
// Var
|
||||
unexpected_tag(self)?;
|
||||
let nam = self.labelled(|p| p.parse_bend_name(), "term")?;
|
||||
|
@ -210,6 +210,7 @@ impl<'t, 'l> EncodeTermState<'t, 'l> {
|
||||
| Term::Mat { .. } // Removed in earlier pass
|
||||
| Term::Bend { .. } // Removed in desugar_bend
|
||||
| Term::Fold { .. } // Removed in desugar_fold
|
||||
| Term::Open { .. } // Removed in desugar_open
|
||||
| Term::Nat { .. } // Removed in encode_nat
|
||||
| Term::Str { .. } // Removed in encode_str
|
||||
| Term::List { .. } // Removed in encode_list
|
||||
|
49
src/fun/transform/desugar_open.rs
Normal file
49
src/fun/transform/desugar_open.rs
Normal file
@ -0,0 +1,49 @@
|
||||
use crate::{
|
||||
diagnostics::Diagnostics,
|
||||
fun::{Adts, Ctx, Term},
|
||||
maybe_grow,
|
||||
};
|
||||
|
||||
impl Ctx<'_> {
|
||||
pub fn desugar_open(&mut self) -> Result<(), Diagnostics> {
|
||||
self.info.start_pass();
|
||||
|
||||
for def in self.book.defs.values_mut() {
|
||||
for rule in def.rules.iter_mut() {
|
||||
if let Err(err) = rule.body.desugar_open(&self.book.adts) {
|
||||
self.info.add_rule_error(err, def.name.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.info.fatal(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Term {
|
||||
fn desugar_open(&mut self, adts: &Adts) -> Result<(), String> {
|
||||
maybe_grow(|| {
|
||||
if let Term::Open { typ, var, bod } = self {
|
||||
if let Some(adt) = adts.get(&*typ) {
|
||||
if adt.ctrs.len() == 1 {
|
||||
let ctr = adt.ctrs.keys().next().unwrap();
|
||||
*self = Term::Mat {
|
||||
arg: Box::new(Term::Var { nam: var.clone() }),
|
||||
bnd: Some(std::mem::take(var)),
|
||||
with: 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 {
|
||||
return Err(format!("Type '{typ}' of an 'open' is not defined"));
|
||||
}
|
||||
}
|
||||
for child in self.children_mut() {
|
||||
child.desugar_open(adts)?;
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
@ -209,6 +209,7 @@ impl Term {
|
||||
| Term::List { .. }
|
||||
| Term::Do { .. }
|
||||
| Term::Ask { .. }
|
||||
| Term::Open { .. }
|
||||
| Term::Err => unreachable!(),
|
||||
}
|
||||
}
|
||||
@ -254,7 +255,9 @@ impl Term {
|
||||
| Term::Ref { .. }
|
||||
| Term::Era
|
||||
| Term::Err => FloatIter::Zero([]),
|
||||
Term::Do { .. } | Term::Ask { .. } | Term::Bend { .. } | Term::Fold { .. } => unreachable!(),
|
||||
Term::Do { .. } | Term::Ask { .. } | Term::Bend { .. } | Term::Fold { .. } | Term::Open { .. } => {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ pub mod desugar_bend;
|
||||
pub mod desugar_do_blocks;
|
||||
pub mod desugar_fold;
|
||||
pub mod desugar_match_defs;
|
||||
pub mod desugar_open;
|
||||
pub mod encode_adts;
|
||||
pub mod encode_match_terms;
|
||||
pub mod expand_generated;
|
||||
|
@ -101,6 +101,9 @@ impl Stmt {
|
||||
*self = gen_get(self, substitutions);
|
||||
}
|
||||
}
|
||||
Stmt::Open { typ: _, var: _, nxt } => {
|
||||
nxt.gen_map_get(id);
|
||||
}
|
||||
Stmt::Err => {}
|
||||
}
|
||||
}
|
||||
|
@ -160,6 +160,12 @@ pub enum Stmt {
|
||||
Return {
|
||||
term: Box<Expr>,
|
||||
},
|
||||
// "open" {typ} ":" {var} ";"? {nxt}
|
||||
Open {
|
||||
typ: Name,
|
||||
var: Name,
|
||||
nxt: Box<Stmt>,
|
||||
},
|
||||
#[default]
|
||||
Err,
|
||||
}
|
||||
|
@ -81,6 +81,9 @@ impl Stmt {
|
||||
nxt.order_kwargs(book)?;
|
||||
}
|
||||
}
|
||||
Stmt::Open { typ: _, var: _, nxt } => {
|
||||
nxt.order_kwargs(book)?;
|
||||
}
|
||||
Stmt::Return { term } => term.order_kwargs(book)?,
|
||||
Stmt::Err => {}
|
||||
}
|
||||
|
@ -245,6 +245,7 @@ impl<'a> PyParser<'a> {
|
||||
}
|
||||
|
||||
/// "λ" (<name> ","?)+ ":" <expr>
|
||||
/// | "open" <type> ":" <var>
|
||||
/// | <infix>
|
||||
fn parse_expr(&mut self, inline: bool) -> ParseResult<Expr> {
|
||||
fn parse_lam_var(p: &mut PyParser) -> ParseResult<(Name, bool)> {
|
||||
@ -257,13 +258,15 @@ impl<'a> PyParser<'a> {
|
||||
}
|
||||
|
||||
self.skip_trivia_maybe_inline(inline);
|
||||
|
||||
// lambda
|
||||
if self.try_parse_keyword("lam") | self.try_consume_exactly("λ") {
|
||||
let names = self.list_like(|p| parse_lam_var(p), "", ":", ",", false, 1)?;
|
||||
let bod = self.parse_expr(inline)?;
|
||||
Ok(Expr::Lam { names, bod: Box::new(bod) })
|
||||
} else {
|
||||
self.parse_infix_expr(0, inline)
|
||||
return Ok(Expr::Lam { names, bod: Box::new(bod) });
|
||||
}
|
||||
|
||||
self.parse_infix_expr(0, inline)
|
||||
}
|
||||
|
||||
/// Named argument of a function call.
|
||||
@ -329,16 +332,6 @@ impl<'a> PyParser<'a> {
|
||||
/// Parses a statement and returns the indentation of the next statement.
|
||||
fn parse_statement(&mut self, indent: &mut Indent) -> ParseResult<(Stmt, Indent)> {
|
||||
maybe_grow(|| {
|
||||
/* let got_indent = self.advance_indent(*indent)?;
|
||||
match got_indent {
|
||||
Indent::Eof => {
|
||||
let Indent::Val(expected) = *indent else { unreachable!() };
|
||||
let msg = format!("Indentation error. Expected {} spaces, got end-of-input.", expected);
|
||||
let idx = *self.index();
|
||||
self.with_ctx(Err(msg), idx, idx + 1)
|
||||
}
|
||||
Indent::Val(_) => {}
|
||||
} */
|
||||
if self.try_parse_keyword("return") {
|
||||
self.parse_return()
|
||||
} else if self.try_parse_keyword("if") {
|
||||
@ -353,6 +346,8 @@ impl<'a> PyParser<'a> {
|
||||
self.parse_bend(indent)
|
||||
} else if self.try_parse_keyword("do") {
|
||||
self.parse_do(indent)
|
||||
} else if self.try_parse_keyword("open") {
|
||||
self.parse_open(indent)
|
||||
} else {
|
||||
self.parse_assign(indent)
|
||||
}
|
||||
@ -799,6 +794,23 @@ impl<'a> PyParser<'a> {
|
||||
Ok(AssignPattern::Var(var))
|
||||
}
|
||||
|
||||
/// "open" {typ} ":" {var} ";"? {nxt}
|
||||
fn parse_open(&mut self, indent: &mut Indent) -> ParseResult<(Stmt, Indent)> {
|
||||
self.skip_trivia_inline();
|
||||
let typ = self.labelled(|p| p.parse_bend_name(), "type name")?;
|
||||
self.skip_trivia_inline();
|
||||
self.consume_exactly(":")?;
|
||||
self.skip_trivia_inline();
|
||||
let var = self.labelled(|p| p.parse_bend_name(), "variable name")?;
|
||||
self.skip_trivia_inline();
|
||||
self.try_consume_exactly(";");
|
||||
self.consume_new_line()?;
|
||||
self.consume_indent_exactly(*indent)?;
|
||||
let (nxt, nxt_indent) = self.parse_statement(indent)?;
|
||||
let stmt = Stmt::Open { typ, var, nxt: Box::new(nxt) };
|
||||
Ok((stmt, nxt_indent))
|
||||
}
|
||||
|
||||
pub fn parse_def(&mut self, mut indent: Indent) -> ParseResult<(Definition, Indent)> {
|
||||
if indent != Indent::Val(0) {
|
||||
let msg = "Indentation error. Functions defined with 'def' must be at the start of the line.";
|
||||
@ -827,14 +839,7 @@ impl<'a> PyParser<'a> {
|
||||
Ok((def, nxt_indent))
|
||||
}
|
||||
|
||||
pub fn parse_data_type(&mut self, mut indent: Indent) -> ParseResult<(Enum, Indent)> {
|
||||
fn parse_variant_field(p: &mut PyParser) -> ParseResult<CtrField> {
|
||||
let rec = p.try_consume_exactly("~");
|
||||
p.skip_trivia();
|
||||
let nam = p.parse_bend_name()?;
|
||||
Ok(CtrField { nam, rec })
|
||||
}
|
||||
|
||||
pub fn parse_type(&mut self, mut indent: Indent) -> ParseResult<(Enum, Indent)> {
|
||||
if indent != Indent::Val(0) {
|
||||
let msg = "Indentation error. Types defined with 'type' must be at the start of the line.";
|
||||
let idx = *self.index();
|
||||
@ -842,7 +847,7 @@ impl<'a> PyParser<'a> {
|
||||
}
|
||||
|
||||
self.skip_trivia_inline();
|
||||
let typ_name = self.parse_bend_name()?;
|
||||
let typ_name = self.parse_top_level_name()?;
|
||||
self.skip_trivia_inline();
|
||||
self.consume_exactly(":")?;
|
||||
self.consume_new_line()?;
|
||||
@ -852,14 +857,17 @@ impl<'a> PyParser<'a> {
|
||||
let mut variants = Vec::new();
|
||||
let mut nxt_indent = indent;
|
||||
while nxt_indent == indent {
|
||||
let ctr_name = self.parse_bend_name()?;
|
||||
let ctr_name = self.parse_top_level_name()?;
|
||||
let ctr_name = Name::new(format!("{typ_name}/{ctr_name}"));
|
||||
let mut fields = Vec::new();
|
||||
self.skip_trivia_inline();
|
||||
if self.starts_with("{") {
|
||||
fields = self.list_like(|p| parse_variant_field(p), "{", "}", ",", false, 0)?;
|
||||
fields = self.list_like(|p| p.parse_variant_field(), "{", "}", ",", false, 0)?;
|
||||
}
|
||||
variants.push(Variant { name: ctr_name, fields });
|
||||
if !self.is_eof() {
|
||||
self.consume_new_line()?;
|
||||
}
|
||||
nxt_indent = self.consume_indent_at_most(indent)?;
|
||||
}
|
||||
indent.exit_level();
|
||||
@ -868,6 +876,35 @@ impl<'a> PyParser<'a> {
|
||||
Ok((enum_, nxt_indent))
|
||||
}
|
||||
|
||||
pub fn parse_object(&mut self, indent: Indent) -> ParseResult<(Variant, Indent)> {
|
||||
if indent != Indent::Val(0) {
|
||||
let msg = "Indentation error. Types defined with 'object' must be at the start of the line.";
|
||||
let idx = *self.index();
|
||||
return self.with_ctx(Err(msg), idx, idx + 1);
|
||||
}
|
||||
|
||||
self.skip_trivia_inline();
|
||||
let name = self.parse_top_level_name()?;
|
||||
self.skip_trivia_inline();
|
||||
let fields = if self.starts_with("{") {
|
||||
self.list_like(|p| p.parse_variant_field(), "{", "}", ",", false, 0)?
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
if !self.is_eof() {
|
||||
self.consume_new_line()?;
|
||||
}
|
||||
let nxt_indent = self.advance_newlines();
|
||||
Ok((Variant { name, fields }, nxt_indent))
|
||||
}
|
||||
|
||||
fn parse_variant_field(&mut self) -> ParseResult<CtrField> {
|
||||
let rec = self.try_consume_exactly("~");
|
||||
self.skip_trivia();
|
||||
let nam = self.parse_bend_name()?;
|
||||
Ok(CtrField { nam, rec })
|
||||
}
|
||||
|
||||
pub fn add_def(
|
||||
&mut self,
|
||||
mut def: Definition,
|
||||
@ -919,6 +956,33 @@ impl<'a> PyParser<'a> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn add_object(
|
||||
&mut self,
|
||||
obj: Variant,
|
||||
book: &mut Book,
|
||||
ini_idx: usize,
|
||||
end_idx: usize,
|
||||
builtin: bool,
|
||||
) -> ParseResult<()> {
|
||||
if book.adts.contains_key(&obj.name) {
|
||||
let msg = format!("Redefinition of type '{}'.", obj.name);
|
||||
return self.with_ctx(Err(msg), ini_idx, end_idx);
|
||||
}
|
||||
let mut adt = Adt { ctrs: Default::default(), builtin };
|
||||
if book.defs.contains_key(&obj.name) {
|
||||
let msg = format!("Redefinition of function '{}'.", obj.name);
|
||||
return self.with_ctx(Err(msg), ini_idx, end_idx);
|
||||
}
|
||||
if book.ctrs.contains_key(&obj.name) {
|
||||
let msg = format!("Redefinition of constructor '{}'.", obj.name);
|
||||
return self.with_ctx(Err(msg), ini_idx, end_idx);
|
||||
}
|
||||
book.ctrs.insert(obj.name.clone(), obj.name.clone());
|
||||
adt.ctrs.insert(obj.name.clone(), obj.fields);
|
||||
book.adts.insert(obj.name, adt);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn expected_indent<T>(&mut self, expected: Indent, got: Indent) -> ParseResult<T> {
|
||||
match (expected, got) {
|
||||
(Indent::Eof, Indent::Eof) => unreachable!(),
|
||||
|
@ -255,7 +255,7 @@ impl Stmt {
|
||||
let term = fun::Term::Do { typ, bod: Box::new(bod) };
|
||||
wrap_nxt_assign_stmt(term, nxt, pat)?
|
||||
}
|
||||
Self::Ask { pat, val, nxt } => {
|
||||
Stmt::Ask { pat, val, nxt } => {
|
||||
let (nxt_pat, nxt) = match nxt.into_fun()? {
|
||||
StmtToFun::Return(term) => (None, term),
|
||||
StmtToFun::Assign(pat, term) => (Some(pat), term),
|
||||
@ -264,6 +264,14 @@ impl Stmt {
|
||||
fun::Term::Ask { pat: Box::new(pat.into_fun()), val: Box::new(val.to_fun()), nxt: Box::new(nxt) };
|
||||
if let Some(pat) = nxt_pat { StmtToFun::Assign(pat, term) } else { StmtToFun::Return(term) }
|
||||
}
|
||||
Stmt::Open { typ, var, nxt } => {
|
||||
let (nxt_pat, nxt) = match nxt.into_fun()? {
|
||||
StmtToFun::Return(term) => (None, term),
|
||||
StmtToFun::Assign(pat, term) => (Some(pat), term),
|
||||
};
|
||||
let term = fun::Term::Open { typ, var, bod: Box::new(nxt) };
|
||||
if let Some(pat) = nxt_pat { StmtToFun::Assign(pat, term) } else { StmtToFun::Return(term) }
|
||||
}
|
||||
Stmt::Return { term } => StmtToFun::Return(term.to_fun()),
|
||||
Stmt::Err => unreachable!(),
|
||||
};
|
||||
|
@ -90,6 +90,8 @@ pub fn desugar_book(
|
||||
|
||||
ctx.apply_args(args)?;
|
||||
|
||||
ctx.desugar_open()?;
|
||||
|
||||
ctx.book.encode_builtins();
|
||||
|
||||
ctx.resolve_refs()?;
|
||||
|
13
tests/golden_tests/run_file/open.bend
Normal file
13
tests/golden_tests/run_file/open.bend
Normal file
@ -0,0 +1,13 @@
|
||||
data Pair = (new a b)
|
||||
|
||||
type State:
|
||||
new { a, b }
|
||||
|
||||
def with_state(x, s):
|
||||
open State: s
|
||||
return Pair/new(s, x(s.a))
|
||||
|
||||
main =
|
||||
let x = (with_state @x x (State/new 1 2))
|
||||
open Pair x;
|
||||
{x.a x.b}
|
6
tests/golden_tests/run_file/open_object.bend
Normal file
6
tests/golden_tests/run_file/open_object.bend
Normal file
@ -0,0 +1,6 @@
|
||||
object Pair { fst, snd }
|
||||
|
||||
def main:
|
||||
x = Pair(1, 2)
|
||||
open Pair: x
|
||||
return x.fst
|
8
tests/golden_tests/run_file/open_too_many_ctrs.bend
Normal file
8
tests/golden_tests/run_file/open_too_many_ctrs.bend
Normal file
@ -0,0 +1,8 @@
|
||||
type MyTree:
|
||||
node { a, b }
|
||||
leaf { a }
|
||||
|
||||
def main:
|
||||
x = MyTree/node(1, 2)
|
||||
open MyTree: x
|
||||
return x.a
|
4
tests/golden_tests/run_file/open_undefined_type.bend
Normal file
4
tests/golden_tests/run_file/open_undefined_type.bend
Normal file
@ -0,0 +1,4 @@
|
||||
def main:
|
||||
x = 1
|
||||
open MyType: x
|
||||
return x.a
|
@ -2,4 +2,4 @@
|
||||
source: tests/golden_tests.rs
|
||||
input_file: examples/bitonic_sort.bend
|
||||
---
|
||||
16515072
|
||||
16760832
|
||||
|
@ -2,8 +2,6 @@
|
||||
source: tests/golden_tests.rs
|
||||
input_file: tests/golden_tests/run_file/exp.bend
|
||||
---
|
||||
[4m[1m[33mWarnings:[0m
|
||||
[1mDuring readback:[0m
|
||||
Reached Root.
|
||||
|
||||
λa λb (<Invalid> λ$c $c $c)
|
||||
[4m[1m[31mErrors:[0m
|
||||
Error reading result from hvm. Output :
|
||||
ERROR: attempt to clone a non-affine global reference.
|
||||
|
5
tests/snapshots/run_file__open.bend.snap
Normal file
5
tests/snapshots/run_file__open.bend.snap
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
source: tests/golden_tests.rs
|
||||
input_file: tests/golden_tests/run_file/open.bend
|
||||
---
|
||||
{λa (a 1 2) 1}
|
5
tests/snapshots/run_file__open_object.bend.snap
Normal file
5
tests/snapshots/run_file__open_object.bend.snap
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
source: tests/golden_tests.rs
|
||||
input_file: tests/golden_tests/run_file/open_object.bend
|
||||
---
|
||||
1
|
7
tests/snapshots/run_file__open_too_many_ctrs.bend.snap
Normal file
7
tests/snapshots/run_file__open_too_many_ctrs.bend.snap
Normal file
@ -0,0 +1,7 @@
|
||||
---
|
||||
source: tests/golden_tests.rs
|
||||
input_file: tests/golden_tests/run_file/open_too_many_ctrs.bend
|
||||
---
|
||||
[4m[1m[31mErrors:[0m
|
||||
[1mIn definition '[4mmain[0m[1m':[0m
|
||||
Type 'MyTree' of an 'open' has more than one constructor
|
7
tests/snapshots/run_file__open_undefined_type.bend.snap
Normal file
7
tests/snapshots/run_file__open_undefined_type.bend.snap
Normal file
@ -0,0 +1,7 @@
|
||||
---
|
||||
source: tests/golden_tests.rs
|
||||
input_file: tests/golden_tests/run_file/open_undefined_type.bend
|
||||
---
|
||||
[4m[1m[31mErrors:[0m
|
||||
[1mIn definition '[4mmain[0m[1m':[0m
|
||||
Type 'MyType' of an 'open' is not defined
|
Loading…
Reference in New Issue
Block a user