Add dedicated 'Pair' typed and untyped expression
Before this commit, we would parse 'Pair' as a user-defined data-types, and thus piggybacking on that whole record system. While perhaps handy for some things, it's also semantically wrong and induces a lot more complexity in codegen which now needs to systematically distinguish every data-type access between pairs, and others. So it's better to have it as a separate expression, and handle it similar to tuples (since it's fundamentally a 2-tuple with a special serialization).
This commit is contained in:
parent
390bccd406
commit
2cb2c7fa1f
|
@ -970,6 +970,7 @@ pub enum Annotation {
|
|||
location: Span,
|
||||
elems: Vec<Self>,
|
||||
},
|
||||
|
||||
Pair {
|
||||
location: Span,
|
||||
fst: Box<Self>,
|
||||
|
|
|
@ -15,6 +15,9 @@ use std::{cell::RefCell, collections::HashMap, rc::Rc};
|
|||
use strum::IntoEnumIterator;
|
||||
use uplc::builtins::DefaultFunction;
|
||||
|
||||
pub const PRELUDE: &str = "aiken";
|
||||
pub const BUILTIN: &str = "aiken/builtin";
|
||||
|
||||
pub const BYTE_ARRAY: &str = "ByteArray";
|
||||
pub const BOOL: &str = "Bool";
|
||||
pub const INT: &str = "Int";
|
||||
|
@ -37,7 +40,7 @@ pub const FUZZER: &str = "Fuzzer";
|
|||
/// into a compiler pipeline
|
||||
pub fn prelude(id_gen: &IdGenerator) -> TypeInfo {
|
||||
let mut prelude = TypeInfo {
|
||||
name: "aiken".to_string(),
|
||||
name: PRELUDE.to_string(),
|
||||
package: "".to_string(),
|
||||
kind: ModuleKind::Lib,
|
||||
types: HashMap::new(),
|
||||
|
@ -592,7 +595,7 @@ pub fn prelude(id_gen: &IdGenerator) -> TypeInfo {
|
|||
|
||||
pub fn plutus(id_gen: &IdGenerator) -> TypeInfo {
|
||||
let mut plutus = TypeInfo {
|
||||
name: "aiken/builtin".to_string(),
|
||||
name: BUILTIN.to_string(),
|
||||
package: "".to_string(),
|
||||
kind: ModuleKind::Lib,
|
||||
types: HashMap::new(),
|
||||
|
|
|
@ -161,6 +161,13 @@ pub enum TypedExpr {
|
|||
elems: Vec<Self>,
|
||||
},
|
||||
|
||||
Pair {
|
||||
location: Span,
|
||||
tipo: Rc<Type>,
|
||||
fst: Box<Self>,
|
||||
snd: Box<Self>,
|
||||
},
|
||||
|
||||
TupleIndex {
|
||||
location: Span,
|
||||
tipo: Rc<Type>,
|
||||
|
@ -214,6 +221,7 @@ impl TypedExpr {
|
|||
| Self::UnOp { tipo, .. }
|
||||
| Self::BinOp { tipo, .. }
|
||||
| Self::Tuple { tipo, .. }
|
||||
| Self::Pair { tipo, .. }
|
||||
| Self::String { tipo, .. }
|
||||
| Self::ByteArray { tipo, .. }
|
||||
| Self::TupleIndex { tipo, .. }
|
||||
|
@ -256,6 +264,7 @@ impl TypedExpr {
|
|||
| TypedExpr::ErrorTerm { .. }
|
||||
| TypedExpr::BinOp { .. }
|
||||
| TypedExpr::Tuple { .. }
|
||||
| TypedExpr::Pair { .. }
|
||||
| TypedExpr::UnOp { .. }
|
||||
| TypedExpr::String { .. }
|
||||
| TypedExpr::Sequence { .. }
|
||||
|
@ -299,6 +308,7 @@ impl TypedExpr {
|
|||
| Self::List { location, .. }
|
||||
| Self::BinOp { location, .. }
|
||||
| Self::Tuple { location, .. }
|
||||
| Self::Pair { location, .. }
|
||||
| Self::String { location, .. }
|
||||
| Self::UnOp { location, .. }
|
||||
| Self::Pipeline { location, .. }
|
||||
|
@ -337,6 +347,7 @@ impl TypedExpr {
|
|||
| Self::List { location, .. }
|
||||
| Self::BinOp { location, .. }
|
||||
| Self::Tuple { location, .. }
|
||||
| Self::Pair { location, .. }
|
||||
| Self::String { location, .. }
|
||||
| Self::UnOp { location, .. }
|
||||
| Self::Sequence { location, .. }
|
||||
|
@ -392,6 +403,11 @@ impl TypedExpr {
|
|||
.find_map(|e| e.find_node(byte_index))
|
||||
.or(Some(Located::Expression(self))),
|
||||
|
||||
TypedExpr::Pair { fst, snd, .. } => [fst, snd]
|
||||
.iter()
|
||||
.find_map(|e| e.find_node(byte_index))
|
||||
.or(Some(Located::Expression(self))),
|
||||
|
||||
TypedExpr::List { elements, tail, .. } => elements
|
||||
.iter()
|
||||
.find_map(|e| e.find_node(byte_index))
|
||||
|
@ -578,6 +594,12 @@ pub enum UntypedExpr {
|
|||
elems: Vec<Self>,
|
||||
},
|
||||
|
||||
Pair {
|
||||
location: Span,
|
||||
fst: Box<Self>,
|
||||
snd: Box<Self>,
|
||||
},
|
||||
|
||||
TupleIndex {
|
||||
location: Span,
|
||||
index: usize,
|
||||
|
@ -1272,6 +1294,7 @@ impl UntypedExpr {
|
|||
| Self::ByteArray { location, .. }
|
||||
| Self::BinOp { location, .. }
|
||||
| Self::Tuple { location, .. }
|
||||
| Self::Pair { location, .. }
|
||||
| Self::String { location, .. }
|
||||
| Self::Assignment { location, .. }
|
||||
| Self::TupleIndex { location, .. }
|
||||
|
|
|
@ -980,6 +980,14 @@ impl<'comments> Formatter<'comments> {
|
|||
wrap_args(elems.iter().map(|e| (self.wrap_expr(e), false))).group()
|
||||
}
|
||||
|
||||
UntypedExpr::Pair { fst, snd, .. } => "Pair"
|
||||
.to_doc()
|
||||
.append("(")
|
||||
.append(self.expr(fst, false))
|
||||
.append(",")
|
||||
.append(self.expr(snd, false))
|
||||
.append(")"),
|
||||
|
||||
UntypedExpr::TupleIndex { index, tuple, .. } => {
|
||||
let suffix = Ordinal(*index + 1).suffix().to_doc();
|
||||
self.expr(tuple, false)
|
||||
|
|
|
@ -373,7 +373,13 @@ impl<'a> CodeGenerator<'a> {
|
|||
..
|
||||
} => {
|
||||
let data_type = lookup_data_type_by_tipo(&self.data_types, tipo)
|
||||
.expect("Creating a record with no record definition.");
|
||||
.unwrap_or_else(||
|
||||
panic!(
|
||||
"Creating a record of type {:?} with no record definition. Known definitions: {:?}",
|
||||
tipo.to_pretty(0),
|
||||
self.data_types.keys()
|
||||
)
|
||||
);
|
||||
|
||||
let (constr_index, _) = data_type
|
||||
.constructors
|
||||
|
@ -758,6 +764,12 @@ impl<'a> CodeGenerator<'a> {
|
|||
}
|
||||
},
|
||||
|
||||
TypedExpr::Pair { tipo, fst, snd, .. } => AirTree::pair(
|
||||
self.build(fst, module_build_name, &[]),
|
||||
self.build(snd, module_build_name, &[]),
|
||||
tipo.clone(),
|
||||
),
|
||||
|
||||
TypedExpr::Tuple { tipo, elems, .. } => AirTree::tuple(
|
||||
elems
|
||||
.iter()
|
||||
|
|
|
@ -1,20 +1,11 @@
|
|||
use chumsky::prelude::*;
|
||||
|
||||
use super::anonymous_function::parser as anonymous_function;
|
||||
use super::assignment;
|
||||
use super::block::parser as block;
|
||||
use super::bytearray::parser as bytearray;
|
||||
use super::if_else::parser as if_else;
|
||||
use super::int::parser as int;
|
||||
use super::list::parser as list;
|
||||
use super::record::parser as record;
|
||||
use super::record_update::parser as record_update;
|
||||
use super::string::parser as string;
|
||||
use super::tuple::parser as tuple;
|
||||
use super::var::parser as var;
|
||||
use super::when::parser as when;
|
||||
use super::{and_or_chain, anonymous_binop::parser as anonymous_binop};
|
||||
|
||||
use super::{
|
||||
and_or_chain, anonymous_binop::parser as anonymous_binop,
|
||||
anonymous_function::parser as anonymous_function, assignment, block::parser as block,
|
||||
bytearray::parser as bytearray, if_else::parser as if_else, int::parser as int,
|
||||
list::parser as list, pair::parser as pair, record::parser as record,
|
||||
record_update::parser as record_update, string::parser as string, tuple::parser as tuple,
|
||||
var::parser as var, when::parser as when,
|
||||
};
|
||||
use crate::{
|
||||
expr::UntypedExpr,
|
||||
parser::{
|
||||
|
@ -23,6 +14,7 @@ use crate::{
|
|||
token::Token,
|
||||
},
|
||||
};
|
||||
use chumsky::prelude::*;
|
||||
|
||||
pub fn parser<'a>(
|
||||
sequence: Recursive<'a, Token, UntypedExpr, ParseError>,
|
||||
|
@ -58,6 +50,7 @@ pub fn chain_start<'a>(
|
|||
choice((
|
||||
string(),
|
||||
int(),
|
||||
pair(expression.clone()),
|
||||
record_update(expression.clone()),
|
||||
record(expression.clone()),
|
||||
field_access::constructor(),
|
||||
|
|
|
@ -12,6 +12,7 @@ mod fail_todo_trace;
|
|||
mod if_else;
|
||||
mod int;
|
||||
mod list;
|
||||
mod pair;
|
||||
mod record;
|
||||
mod record_update;
|
||||
mod sequence;
|
||||
|
@ -31,6 +32,7 @@ pub use fail_todo_trace::parser as fail_todo_trace;
|
|||
pub use if_else::parser as if_else;
|
||||
pub use int::parser as int;
|
||||
pub use list::parser as list;
|
||||
pub use pair::parser as pair;
|
||||
pub use record::parser as record;
|
||||
pub use record_update::parser as record_update;
|
||||
pub use sequence::parser as sequence;
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
use crate::{
|
||||
builtins::{PAIR, PRELUDE},
|
||||
expr::UntypedExpr,
|
||||
parser::{error::ParseError, token::Token},
|
||||
};
|
||||
use chumsky::prelude::*;
|
||||
|
||||
pub fn parser(
|
||||
r: Recursive<'_, Token, UntypedExpr, ParseError>,
|
||||
) -> impl Parser<Token, UntypedExpr, Error = ParseError> + '_ {
|
||||
select! {Token::Name { name } if &name == PRELUDE => name}
|
||||
.then_ignore(just(Token::Dot))
|
||||
.or_not()
|
||||
.then_ignore(select! {Token::UpName { name } if &name == PAIR => name})
|
||||
.ignore_then(
|
||||
r.clone()
|
||||
.separated_by(just(Token::Comma))
|
||||
.exactly(2)
|
||||
.allow_trailing()
|
||||
.delimited_by(
|
||||
choice((just(Token::LeftParen), just(Token::NewLineLeftParen))),
|
||||
just(Token::RightParen),
|
||||
)
|
||||
.map_with_span(|elems, location| UntypedExpr::Pair {
|
||||
location,
|
||||
fst: elems
|
||||
.first()
|
||||
.expect("Pair should have exactly 2 elements")
|
||||
.to_owned()
|
||||
.into(),
|
||||
snd: elems
|
||||
.last()
|
||||
.expect("Pair should have exactly 2 elements")
|
||||
.to_owned()
|
||||
.into(),
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::assert_expr;
|
||||
|
||||
#[test]
|
||||
fn basic_pair() {
|
||||
assert_expr!(r#"Pair(1, 2)"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pair_from_prelude() {
|
||||
assert_expr!(r#"aiken.Pair(1, 2)"#);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
---
|
||||
source: crates/aiken-lang/src/parser/expr/pair.rs
|
||||
description: "Code:\n\nPair(1, 2)"
|
||||
---
|
||||
Pair {
|
||||
location: 4..10,
|
||||
fst: UInt {
|
||||
location: 5..6,
|
||||
value: "1",
|
||||
base: Decimal {
|
||||
numeric_underscore: false,
|
||||
},
|
||||
},
|
||||
snd: UInt {
|
||||
location: 8..9,
|
||||
value: "2",
|
||||
base: Decimal {
|
||||
numeric_underscore: false,
|
||||
},
|
||||
},
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
---
|
||||
source: crates/aiken-lang/src/parser/expr/pair.rs
|
||||
description: "Code:\n\naiken.Pair(1, 2)"
|
||||
---
|
||||
Pair {
|
||||
location: 10..16,
|
||||
fst: UInt {
|
||||
location: 11..12,
|
||||
value: "1",
|
||||
base: Decimal {
|
||||
numeric_underscore: false,
|
||||
},
|
||||
},
|
||||
snd: UInt {
|
||||
location: 14..15,
|
||||
value: "2",
|
||||
base: Decimal {
|
||||
numeric_underscore: false,
|
||||
},
|
||||
},
|
||||
}
|
|
@ -16,7 +16,7 @@ use crate::{
|
|||
UntypedClauseGuard, UntypedIfBranch, UntypedPattern, UntypedRecordUpdateArg,
|
||||
},
|
||||
builtins::{
|
||||
bool, byte_array, function, g1_element, g2_element, int, list, string, tuple, void,
|
||||
bool, byte_array, function, g1_element, g2_element, int, list, pair, string, tuple, void,
|
||||
},
|
||||
expr::{FnStyle, TypedExpr, UntypedExpr},
|
||||
format,
|
||||
|
@ -226,6 +226,8 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
|
|||
|
||||
UntypedExpr::Tuple { location, elems } => self.infer_tuple(elems, location),
|
||||
|
||||
UntypedExpr::Pair { location, fst, snd } => self.infer_pair(*fst, *snd, location),
|
||||
|
||||
UntypedExpr::String { location, value } => Ok(self.infer_string(value, location)),
|
||||
|
||||
UntypedExpr::LogicalOpChain {
|
||||
|
@ -2020,6 +2022,26 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
|
|||
}
|
||||
}
|
||||
|
||||
fn infer_pair(
|
||||
&mut self,
|
||||
fst: UntypedExpr,
|
||||
snd: UntypedExpr,
|
||||
location: Span,
|
||||
) -> Result<TypedExpr, Error> {
|
||||
let typed_fst = self.infer(fst)?;
|
||||
ensure_serialisable(false, typed_fst.tipo(), location)?;
|
||||
|
||||
let typed_snd = self.infer(snd)?;
|
||||
ensure_serialisable(false, typed_snd.tipo(), location)?;
|
||||
|
||||
Ok(TypedExpr::Pair {
|
||||
location,
|
||||
tipo: pair(typed_fst.tipo(), typed_snd.tipo()),
|
||||
fst: typed_fst.into(),
|
||||
snd: typed_snd.into(),
|
||||
})
|
||||
}
|
||||
|
||||
fn infer_tuple(&mut self, elems: Vec<UntypedExpr>, location: Span) -> Result<TypedExpr, Error> {
|
||||
let mut typed_elems = vec![];
|
||||
|
||||
|
@ -2343,6 +2365,7 @@ fn assert_no_assignment(expr: &UntypedExpr) -> Result<(), Error> {
|
|||
| UntypedExpr::Sequence { .. }
|
||||
| UntypedExpr::String { .. }
|
||||
| UntypedExpr::Tuple { .. }
|
||||
| UntypedExpr::Pair { .. }
|
||||
| UntypedExpr::TupleIndex { .. }
|
||||
| UntypedExpr::UnOp { .. }
|
||||
| UntypedExpr::Var { .. }
|
||||
|
|
|
@ -9,7 +9,7 @@ Schema {
|
|||
Var {
|
||||
tipo: RefCell {
|
||||
value: Generic {
|
||||
id: 33,
|
||||
id: 37,
|
||||
},
|
||||
},
|
||||
alias: None,
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
---
|
||||
source: crates/aiken-project/src/export.rs
|
||||
assertion_line: 154
|
||||
description: "Code:\n\npub type Foo<a> {\n Empty\n Bar(a, Foo<a>)\n}\n\npub fn add(a: Foo<Int>, b: Foo<Int>) -> Int {\n when (a, b) is {\n (Empty, Empty) -> 0\n (Bar(x, y), Bar(c, d)) -> x + c + add(y, d)\n (Empty, Bar(c, d)) -> c + add(Empty, d)\n (Bar(x, y), Empty) -> x + add(y, Empty)\n }\n}\n"
|
||||
---
|
||||
{
|
||||
"name": "test_module.add",
|
||||
"parameters": [
|
||||
{
|
||||
"title": "a",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/test_module~1Foo$Int"
|
||||
}
|
||||
},
|
||||
{
|
||||
"title": "b",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/test_module~1Foo$Int"
|
||||
}
|
||||
}
|
||||
],
|
||||
"compiledCode": "5901d501000032323232323222323232323253330083002300937540062a666010600460126ea801052000001001132323232533300b3004300c375400c2646464a66601c600e601e6ea80284c8cdc019b80003375a60260026600c0046026602800260206ea8028010c044c048008dd6980800098069baa006001132533300b3005300c375400c2a666016600860186ea801c4c8cdc01bad3010001330034c103d879800030103011001300d375400e00200226466e00dd698078009980118079808000a60103d8798000300c375400a600200244464646464a66601e601260206ea800854ccc03cc024c040dd50018a4000002002264a66601e601060206ea80084c8c8c94ccc048c02cc04cdd500309919b80337000066eb4c05c004ccc02c02c008c05cc060004c050dd5003002180a980b0011bad301400130113754004002264a66601e601260206ea800854ccc03cc020c040dd500189919b80375a6028002666010010980103d8798000301430150013011375400600200226466e00dd698098009998038039809980a000a60103d8798000301037540026022004602060220026601c0046601c00297ae0370e90011b8748000c024008c020c024004cc018008cc0180052f5c0ae6955ceaab9e5740ae855d101",
|
||||
"hash": "dca86b6e092019b67ef310ba8360682d7bf8284cc728c6b525fb0b0d",
|
||||
"definitions": {
|
||||
"Int": {
|
||||
"dataType": "integer"
|
||||
},
|
||||
"test_module/Foo$Int": {
|
||||
"title": "Foo",
|
||||
"anyOf": [
|
||||
{
|
||||
"title": "Empty",
|
||||
"dataType": "constructor",
|
||||
"index": 0,
|
||||
"fields": []
|
||||
},
|
||||
{
|
||||
"title": "Bar",
|
||||
"dataType": "constructor",
|
||||
"index": 1,
|
||||
"fields": [
|
||||
{
|
||||
"$ref": "#/definitions/Int"
|
||||
},
|
||||
{
|
||||
"$ref": "#/definitions/test_module~1Foo$Int"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue