Add UPLC support for 'ProtoList' & 'ProtoPair' constants
Supersedes #35. The syntax for these elements isn't "set in stone"; in the sense that it is unspecified in [input-output-hk/plutus](https://github.com/input-output-hk/plutus). There's no visible plan from IOG to extend the Haskell parser to support this syntax, though there are samples of imagined syntax in the code. Thus, we can lead the way and simply choose a suitable syntax and let the Haskell implementation align to it later. This syntax is thus inspired from input-output-hk/plutus' samples, with only a small change: we use `<` and `>` for encapsulating type declaration instead of `(`, `)`. There are already enough parentheses in the UPLC syntax, adding more reduces visibility. Doing this, I've also added a lot more test cases for the UPLC parser. There could be more, but this is a good start. Here are some example programs (taken from test cases) utilizing this syntax: ``` (program 0.0.0 (con list<bytestring> [#00, #01])) ``` ``` (program 0.0.0 (con pair <integer, integer> [14, 42] ) ) ``` ``` (program 0.0.0 (con pair<string, list<integer>> ["foo", [14, 42]]) ) ``` _(Note that this was mainly done as an exercise to get more familiar with Rust and parts of Aiken.)_
This commit is contained in:
parent
5ec93a8692
commit
375499930a
|
@ -14,8 +14,6 @@
|
|||
| `equalsInteger` | \- | (integer, integer) | bool |
|
||||
| `lessThanInteger` | \- | (integer, integer) | bool |
|
||||
| `lessThanEqualsInteger` | \- | (integer, integer) | bool |
|
||||
| `greaterThanInteger` | \- | (integer, integer) | bool |
|
||||
| `greaterThanEqualsInteger` | \- | (integer, integer) | bool |
|
||||
| --- | --- | --- | --- |
|
||||
| `appendString` | \- | (string, string) | string |
|
||||
| `emptyString` | \- | (string) | bool |
|
||||
|
@ -24,15 +22,12 @@
|
|||
| --- | --- | --- | --- |
|
||||
| `appendByteString` | \- | (bytestring, bytestring) | bytestring |
|
||||
| `consByteString` | \- | (integer, bytestring) | bytestring |
|
||||
| `indexByteString` | \- | (integer, bytestring) | integer |
|
||||
| `indexByteString` | \- | (bytestring, integer) | integer |
|
||||
| `sliceByteString` | \- | (integer, integer, bytestring) | bytestring |
|
||||
| `lengthOfByteString` | \- | (bytestring) | integer |
|
||||
| `emptyByteString` | \- | (bytestring) | bool |
|
||||
| `equalsByteString` | \- | (bytestring, bytestring) | bool |
|
||||
| `lessThanByteString` | \- | (bytestring, bytestring) | bool |
|
||||
| `lessThanEqualsByteString` | \- | (bytestring, bytestring) | bool |
|
||||
| `greaterThanByteString` | \- | (bytestring, bytestring) | bool |
|
||||
| `greaterThanEqualsByteString` | \- | (bytestring, bytestring) | bool |
|
||||
| `decodeUtf8` | \- | (bytestring) | string |
|
||||
| --- | --- | --- | --- |
|
||||
| `chooseData`[^2] | \\(α\\) | (data, \\(α\\), \\(α\\), \\(α\\), \\(α\\), \\(α\\)) | \\(α\\) |
|
||||
|
|
|
@ -4,22 +4,26 @@ Let's start with a little reminder about the syntax. The complete syntax for Unt
|
|||
|
||||
## Primitive Types
|
||||
|
||||
Plutus Core has 5 primitive types: `unit`, `bool`, `integer`, `string` and
|
||||
`bytestring`. One can construct constants using the `con` keyword, followed by
|
||||
the name of the primitive type and its value.
|
||||
Plutus Core has 7 primitive types (a.k.a. constants): `unit`, `bool`, `integer`, `bytestring`, `string`, `pair` and `list`.
|
||||
One can construct constants using the `con` keyword, followed by the name of the primitive type and its value.
|
||||
|
||||
- Unit is denoted `()`;
|
||||
- Bool are `True` or `False`;
|
||||
- Bytestrings are denoted with a leading `#` followed by an hexadecimal sequence;
|
||||
- Strings are UTF-8 text strings, between double quotes `"`.
|
||||
- Strings are UTF-8 text strings, between double quotes `"` `"`;
|
||||
- Pair and lists are encapsulated between brackets `[` and `]`.
|
||||
|
||||
| Primitive Type | Example |
|
||||
| --- | --- |
|
||||
| `unit` | `con unit ()` |
|
||||
| `bool` | `con bool True` |
|
||||
| `integer` | `con integer 42` |
|
||||
| `bytestring` | `con bytestring #41696b656e` |
|
||||
| `string` | `con string "Aiken"` |
|
||||
Note that each constant is named after its type. For pairs and lists -- which are compound types --, the type of their elements is specified between chevrons `<` and `>`.
|
||||
|
||||
| Primitive Type | Example |
|
||||
| --- | --- |
|
||||
| `unit` | `con unit ()` |
|
||||
| `bool` | `con bool True` |
|
||||
| `integer` | `con integer 42` |
|
||||
| `bytestring` | `con bytestring #41696b656e` |
|
||||
| `string` | `con string "Aiken"` |
|
||||
| `pair` | `con pair<bool, integer> [True, 42]` |
|
||||
| `list` | `con list<bytestring> [#00, #aa]` |
|
||||
|
||||
## Functions
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use std::{rc::Rc, str::FromStr};
|
||||
|
||||
use crate::{
|
||||
ast::{Constant, Name, Program, Term},
|
||||
ast::{Constant, Name, Program, Term, Type},
|
||||
builtins::DefaultFunction,
|
||||
};
|
||||
|
||||
|
@ -38,6 +38,22 @@ pub fn term(src: &str) -> Result<Term<Name>, ParseError<LineCol>> {
|
|||
Ok(term)
|
||||
}
|
||||
|
||||
// Returns the inner type of a list, provided that the given type is a list.
|
||||
fn list_sub_type(type_info: Option<&Type>) -> Option<&Type> {
|
||||
match type_info {
|
||||
Some(Type::List(t)) => Some(t),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the left and right types of a pair, provided that the given type is a pair.
|
||||
fn pair_sub_type(type_info: Option<&Type>) -> Option<(&Type, &Type)> {
|
||||
match type_info {
|
||||
Some(Type::Pair(l, r)) => Some((l, r)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
peg::parser! {
|
||||
grammar uplc() for str {
|
||||
pub rule program() -> Program<Name>
|
||||
|
@ -68,6 +84,8 @@ peg::parser! {
|
|||
/ constant_unit()
|
||||
/ constant_bool()
|
||||
/ constant_data()
|
||||
/ constant_list()
|
||||
/ constant_pair()
|
||||
) _* ")" {
|
||||
Term::Constant(con)
|
||||
}
|
||||
|
@ -109,33 +127,121 @@ peg::parser! {
|
|||
= "integer" _+ i:big_number() { Constant::Integer(i as i128) }
|
||||
|
||||
rule constant_bytestring() -> Constant
|
||||
= "bytestring" _+ "#" i:ident()* {
|
||||
Constant::ByteString(hex::decode(String::from_iter(i)).unwrap())
|
||||
}
|
||||
= "bytestring" _+ bs:bytestring() { Constant::ByteString(bs) }
|
||||
|
||||
rule constant_string() -> Constant
|
||||
= "string" _+ "\"" s:[^ '"']* "\"" { Constant::String(String::from_iter(s)) }
|
||||
= "string" _+ s:string() { Constant::String(s) }
|
||||
|
||||
rule constant_bool() -> Constant
|
||||
= "bool" _+ b:$("True" / "False") { Constant::Bool(b == "True") }
|
||||
= "bool" _+ b:boolean() { Constant::Bool(b) }
|
||||
|
||||
rule constant_unit() -> Constant
|
||||
= "unit" _+ "()" { Constant::Unit }
|
||||
|
||||
rule constant_data() -> Constant
|
||||
= "data" _+ d:data() { Constant::Data(d) }
|
||||
|
||||
rule constant_list() -> Constant
|
||||
= "list" _* "<" _* t:type_info() _* ">" _+ ls:list(Some(&t)) {
|
||||
Constant::ProtoList(t, ls)
|
||||
}
|
||||
|
||||
rule constant_pair() -> Constant
|
||||
= "pair" _* "<" _* l:type_info() _* "," r:type_info() _* ">" _+ p:pair(Some((&l, &r))) {
|
||||
Constant::ProtoPair(l, r, Box::new(p.0), Box::new(p.1))
|
||||
}
|
||||
|
||||
rule pair(type_info: Option<(&Type, &Type)>) -> (Constant, Constant)
|
||||
= "[" _* x:typed_constant(type_info.map(|t| t.0)) _* "," _* y:typed_constant(type_info.map(|t| t.1)) _* "]" { (x, y) }
|
||||
|
||||
rule number() -> isize
|
||||
= n:$("-"* ['0'..='9']+) {? n.parse().or(Err("isize")) }
|
||||
|
||||
rule big_number() -> i128
|
||||
= n:$("-"* ['0'..='9']+) {? n.parse().or(Err("i128")) }
|
||||
|
||||
rule constant_data() -> Constant
|
||||
= "data" _+ "#" i:ident()* {
|
||||
Constant::Data(
|
||||
rule boolean() -> bool
|
||||
= b:$("True" / "False") { b == "True" }
|
||||
|
||||
rule bytestring() -> Vec<u8>
|
||||
= "#" i:ident()* { hex::decode(String::from_iter(i)).unwrap() }
|
||||
|
||||
rule string() -> String
|
||||
= "\"" s:[^ '"']* "\"" { String::from_iter(s) }
|
||||
|
||||
rule data() -> PlutusData
|
||||
= "#" i:ident()* {
|
||||
PlutusData::decode_fragment(
|
||||
hex::decode(String::from_iter(i)).unwrap().as_slice()
|
||||
).unwrap()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
rule list(type_info: Option<&Type>) -> Vec<Constant>
|
||||
= "[" _* xs:(typed_constant(type_info) ** (_* "," _*)) _* "]" { xs }
|
||||
|
||||
rule typed_constant(type_info : Option<&Type>) -> Constant
|
||||
= "()" {?
|
||||
match type_info {
|
||||
Some(Type::Unit) => Ok(Constant::Unit),
|
||||
_ => Err("found 'Unit' instead of expected type")
|
||||
}
|
||||
}
|
||||
/ b:boolean() {?
|
||||
match type_info {
|
||||
Some(Type::Bool) => Ok(Constant::Bool(b)),
|
||||
_ => Err("found 'Bool' instead of expected type")
|
||||
}
|
||||
}
|
||||
/ n:big_number() {?
|
||||
match type_info {
|
||||
Some(Type::Integer) => Ok(Constant::Integer(n)),
|
||||
_ => Err("found 'Integer' instead of expected type")
|
||||
}
|
||||
}
|
||||
/ bs:bytestring() {?
|
||||
match type_info {
|
||||
Some(Type::ByteString) => Ok(Constant::ByteString(bs)),
|
||||
_ => Err("found 'ByteString' instead of expected type")
|
||||
}
|
||||
}
|
||||
/ s:string() {?
|
||||
match type_info {
|
||||
Some(Type::String) => Ok(Constant::String(s)),
|
||||
_ => Err("found 'String' instead of expected type")
|
||||
}
|
||||
}
|
||||
/ s:data() {?
|
||||
match type_info {
|
||||
Some(Type::Data) => Ok(Constant::Data(s)),
|
||||
_ => Err("found 'Data' instead of expected type")
|
||||
}
|
||||
}
|
||||
/ ls:list(list_sub_type(type_info)) {?
|
||||
match type_info {
|
||||
Some(Type::List(t)) => Ok(Constant::ProtoList(*t.clone(), ls)),
|
||||
_ => Err("found 'List' instead of expected type")
|
||||
}
|
||||
}
|
||||
/ p:pair(pair_sub_type(type_info)) {?
|
||||
match type_info {
|
||||
Some(Type::Pair(l, r)) => Ok(Constant::ProtoPair(*l.clone(), *r.clone(), Box::new(p.0), Box::new(p.1))),
|
||||
_ => Err("found 'Pair' instead of expected type")
|
||||
}
|
||||
}
|
||||
|
||||
rule type_info() -> Type
|
||||
= _* "unit" { Type::Unit }
|
||||
/ _* "bool" { Type::Bool }
|
||||
/ _* "integer" { Type::Integer }
|
||||
/ _* "bytestring" { Type::ByteString }
|
||||
/ _* "string" { Type::String }
|
||||
/ _* "data" { Type::Data }
|
||||
/ _* "list" _* "<" _* t:type_info() _* ">" {
|
||||
Type::List(Box::new(t))
|
||||
}
|
||||
/ _* "pair" _* "<" l:type_info() "," r:type_info() ">" {
|
||||
Type::Pair(Box::new(l), Box::new(r))
|
||||
}
|
||||
|
||||
rule name() -> Name
|
||||
= text:ident() { Name { text, unique: 0.into() } }
|
||||
|
@ -151,23 +257,569 @@ peg::parser! {
|
|||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::ast::{Constant, Name, Program, Term};
|
||||
use crate::ast::{Constant, Name, Program, Term, Type, Unique};
|
||||
use crate::builtins::DefaultFunction;
|
||||
use std::rc::Rc;
|
||||
|
||||
#[test]
|
||||
fn parse_program() {
|
||||
let code = r#"
|
||||
fn parse_apply() {
|
||||
let uplc = "(program 1.0.0 [(lam x x) (con integer 0)])";
|
||||
let x = Name {
|
||||
text: "x".to_string(),
|
||||
unique: Unique::new(0),
|
||||
};
|
||||
assert_eq!(
|
||||
super::program(uplc).unwrap(),
|
||||
Program::<Name> {
|
||||
version: (1, 0, 0),
|
||||
term: Term::Apply {
|
||||
function: Rc::new(Term::Lambda {
|
||||
parameter_name: x.clone(),
|
||||
body: Rc::new(Term::Var(x)),
|
||||
}),
|
||||
argument: Rc::new(Term::Constant(Constant::Integer(0)))
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_lambda() {
|
||||
let uplc = "(program 1.0.0 (lam x x))";
|
||||
let x = Name {
|
||||
text: "x".to_string(),
|
||||
unique: Unique::new(0),
|
||||
};
|
||||
assert_eq!(
|
||||
super::program(uplc).unwrap(),
|
||||
Program::<Name> {
|
||||
version: (1, 0, 0),
|
||||
term: Term::Lambda {
|
||||
parameter_name: x.clone(),
|
||||
body: Rc::new(Term::Var(x)),
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_delay_lambda() {
|
||||
let uplc = "(program 1.0.0 (lam x (delay x)))";
|
||||
let x = Name {
|
||||
text: "x".to_string(),
|
||||
unique: Unique::new(0),
|
||||
};
|
||||
assert_eq!(
|
||||
super::program(uplc).unwrap(),
|
||||
Program::<Name> {
|
||||
version: (1, 0, 0),
|
||||
term: Term::Lambda {
|
||||
parameter_name: x.clone(),
|
||||
body: Rc::new(Term::Delay(Rc::new(Term::Var(x)))),
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_error() {
|
||||
let uplc = "(program 1.0.0 (error))";
|
||||
assert_eq!(
|
||||
super::program(uplc).unwrap(),
|
||||
Program::<Name> {
|
||||
version: (1, 0, 0),
|
||||
term: Term::Error
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_formatted() {
|
||||
let uplc = r#"
|
||||
(program 11.22.33
|
||||
(con integer 11)
|
||||
)
|
||||
"#;
|
||||
let program = super::program(code).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
program,
|
||||
super::program(uplc).unwrap(),
|
||||
Program::<Name> {
|
||||
version: (11, 22, 33),
|
||||
term: Term::Constant(Constant::Integer(11)),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_builtin_add_integer_curried() {
|
||||
parse_builtin_integer(
|
||||
"(program 1.0.0 [ [ (builtin addInteger) (con integer 1)] (con integer 1) ])",
|
||||
DefaultFunction::AddInteger,
|
||||
1,
|
||||
1,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_builtin_add_integer() {
|
||||
parse_builtin_integer(
|
||||
"(program 1.0.0 [ (builtin addInteger) (con integer 1) (con integer 2) ])",
|
||||
DefaultFunction::AddInteger,
|
||||
1,
|
||||
2,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_builtin_subtract_integer() {
|
||||
parse_builtin_integer(
|
||||
"(program 1.0.0 [ (builtin subtractInteger) (con integer 42) (con integer 14) ])",
|
||||
DefaultFunction::SubtractInteger,
|
||||
42,
|
||||
14,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_builtin_multiply_integer() {
|
||||
parse_builtin_integer(
|
||||
"(program 1.0.0 [ (builtin multiplyInteger) (con integer 1) (con integer -1) ])",
|
||||
DefaultFunction::MultiplyInteger,
|
||||
1,
|
||||
-1,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_builtin_divide_integer() {
|
||||
parse_builtin_integer(
|
||||
"(program 1.0.0 [ (builtin divideInteger) (con integer 1) (con integer 0) ])",
|
||||
DefaultFunction::DivideInteger,
|
||||
1,
|
||||
0,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_builtin_quotient_integer() {
|
||||
parse_builtin_integer(
|
||||
"(program 1.0.0 [ (builtin quotientInteger) (con integer 1) (con integer 0) ])",
|
||||
DefaultFunction::QuotientInteger,
|
||||
1,
|
||||
0,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_builtin_remainder_integer() {
|
||||
parse_builtin_integer(
|
||||
"(program 1.0.0 [ (builtin remainderInteger) (con integer 1) (con integer 0) ])",
|
||||
DefaultFunction::RemainderInteger,
|
||||
1,
|
||||
0,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_builtin_mod_integer() {
|
||||
parse_builtin_integer(
|
||||
"(program 1.0.0 [ [ (builtin modInteger) (con integer 2) ] (con integer 3) ])",
|
||||
DefaultFunction::ModInteger,
|
||||
2,
|
||||
3,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_builtin_equals_integer() {
|
||||
parse_builtin_integer(
|
||||
"(program 1.0.0 [ [ (builtin equalsInteger) (con integer 1) ] (con integer 2) ])",
|
||||
DefaultFunction::EqualsInteger,
|
||||
1,
|
||||
2,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_builtin_less_than_integer() {
|
||||
parse_builtin_integer(
|
||||
"(program 1.0.0 [ [ (builtin lessThanInteger) (con integer 1) ] (con integer 2) ])",
|
||||
DefaultFunction::LessThanInteger,
|
||||
1,
|
||||
2,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_builtin_less_than_equals_integer() {
|
||||
parse_builtin_integer(
|
||||
"(program 1.0.0 [ [ (builtin lessThanEqualsInteger) (con integer 1) ] (con integer 2) ])",
|
||||
DefaultFunction::LessThanEqualsInteger,
|
||||
1,
|
||||
2,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_builtin_append_bytestring() {
|
||||
let uplc = "(program 1.0.0 [ [(builtin appendByteString) (con bytestring #00FF)] (con bytestring #FF00) ])";
|
||||
assert_eq!(
|
||||
super::program(uplc).unwrap(),
|
||||
Program::<Name> {
|
||||
version: (1, 0, 0),
|
||||
term: Term::Apply {
|
||||
function: Rc::new(Term::Apply {
|
||||
function: Rc::new(Term::Builtin(DefaultFunction::AppendByteString)),
|
||||
argument: Rc::new(Term::Constant(Constant::ByteString(vec![0x00, 0xFF]))),
|
||||
}),
|
||||
argument: Rc::new(Term::Constant(Constant::ByteString(vec![0xFF, 0x00])))
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_builtin_cons_bytestring() {
|
||||
let uplc =
|
||||
"(program 1.0.0 [(builtin consByteString) (con integer 256) (con bytestring #)])";
|
||||
assert_eq!(
|
||||
super::program(uplc).unwrap(),
|
||||
Program::<Name> {
|
||||
version: (1, 0, 0),
|
||||
term: Term::Apply {
|
||||
function: Rc::new(Term::Apply {
|
||||
function: Rc::new(Term::Builtin(DefaultFunction::ConsByteString)),
|
||||
argument: Rc::new(Term::Constant(Constant::Integer(256))),
|
||||
}),
|
||||
argument: Rc::new(Term::Constant(Constant::ByteString(vec![])))
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_builtin_slice_bytestring() {
|
||||
let uplc = "(program 0.0.0 [ [ [ (builtin sliceByteString) (con integer 1)] (con integer 2) ] (con bytestring #00ffaa) ])";
|
||||
assert_eq!(
|
||||
super::program(uplc).unwrap(),
|
||||
Program::<Name> {
|
||||
version: (0, 0, 0),
|
||||
term: Term::Apply {
|
||||
function: Rc::new(Term::Apply {
|
||||
function: Rc::new(Term::Apply {
|
||||
function: Rc::new(Term::Builtin(DefaultFunction::SliceByteString)),
|
||||
argument: Rc::new(Term::Constant(Constant::Integer(1))),
|
||||
}),
|
||||
argument: Rc::new(Term::Constant(Constant::Integer(2))),
|
||||
}),
|
||||
argument: Rc::new(Term::Constant(Constant::ByteString(vec![0x00, 0xFF, 0xAA])))
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_builtin_length_of_bytestring() {
|
||||
let uplc = "(program 0.0.0 [ (builtin lengthOfByteString) (con bytestring #00ffaa) ])";
|
||||
assert_eq!(
|
||||
super::program(uplc).unwrap(),
|
||||
Program::<Name> {
|
||||
version: (0, 0, 0),
|
||||
term: Term::Apply {
|
||||
function: Rc::new(Term::Builtin(DefaultFunction::LengthOfByteString)),
|
||||
argument: Rc::new(Term::Constant(Constant::ByteString(vec![0x00, 0xFF, 0xAA])))
|
||||
},
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_builtin_index_bytestring() {
|
||||
let uplc = "(program 1.0.0 [(builtin indexByteString) (con bytestring #00) (con integer 9223372036854775808)])";
|
||||
assert_eq!(
|
||||
super::program(uplc).unwrap(),
|
||||
Program::<Name> {
|
||||
version: (1, 0, 0),
|
||||
term: Term::Apply {
|
||||
function: Rc::new(Term::Apply {
|
||||
function: Rc::new(Term::Builtin(DefaultFunction::IndexByteString)),
|
||||
argument: Rc::new(Term::Constant(Constant::ByteString(vec![0x00])))
|
||||
}),
|
||||
argument: Rc::new(Term::Constant(Constant::Integer(9223372036854775808))),
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_builtin_equals_bytestring() {
|
||||
let uplc = "(program 0.0.0 [ [ (builtin equalsByteString) (con bytestring #00ffaa) ] (con bytestring #00ffaa) ])";
|
||||
assert_eq!(
|
||||
super::program(uplc).unwrap(),
|
||||
Program::<Name> {
|
||||
version: (0, 0, 0),
|
||||
term: Term::Apply {
|
||||
function: Rc::new(Term::Apply {
|
||||
function: Rc::new(Term::Builtin(DefaultFunction::EqualsByteString)),
|
||||
argument: Rc::new(Term::Constant(Constant::ByteString(vec![
|
||||
0x00, 0xff, 0xaa
|
||||
])))
|
||||
}),
|
||||
argument: Rc::new(Term::Constant(Constant::ByteString(vec![0x00, 0xff, 0xaa]))),
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_builtin_less_than_bytestring() {
|
||||
let uplc = "(program 0.0.0 [ [(builtin lessThanByteString) (con bytestring #00ff)] (con bytestring #00ffaa) ])";
|
||||
assert_eq!(
|
||||
super::program(uplc).unwrap(),
|
||||
Program::<Name> {
|
||||
version: (0, 0, 0),
|
||||
term: Term::Apply {
|
||||
function: Rc::new(Term::Apply {
|
||||
function: Rc::new(Term::Builtin(DefaultFunction::LessThanByteString)),
|
||||
argument: Rc::new(Term::Constant(Constant::ByteString(vec![0x00, 0xff])))
|
||||
}),
|
||||
argument: Rc::new(Term::Constant(Constant::ByteString(vec![0x00, 0xff, 0xaa]))),
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_builtin_less_than_equals_bytestring() {
|
||||
let uplc = "(program 0.0.0 [ [(builtin lessThanEqualsByteString) (con bytestring #00ff)] (con bytestring #00) ])";
|
||||
assert_eq!(
|
||||
super::program(uplc).unwrap(),
|
||||
Program::<Name> {
|
||||
version: (0, 0, 0),
|
||||
term: Term::Apply {
|
||||
function: Rc::new(Term::Apply {
|
||||
function: Rc::new(Term::Builtin(DefaultFunction::LessThanEqualsByteString)),
|
||||
argument: Rc::new(Term::Constant(Constant::ByteString(vec![0x00, 0xff])))
|
||||
}),
|
||||
argument: Rc::new(Term::Constant(Constant::ByteString(vec![0x00]))),
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_list_empty() {
|
||||
let uplc = "(program 0.0.0 (con list<unit> []))";
|
||||
assert_eq!(
|
||||
super::program(uplc).unwrap(),
|
||||
Program::<Name> {
|
||||
version: (0, 0, 0),
|
||||
term: Term::Constant(Constant::ProtoList(Type::Unit, vec![]))
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_list_singleton_unit() {
|
||||
let uplc = "(program 0.0.0 (con list<unit> [ () ]))";
|
||||
assert_eq!(
|
||||
super::program(uplc).unwrap(),
|
||||
Program::<Name> {
|
||||
version: (0, 0, 0),
|
||||
term: Term::Constant(Constant::ProtoList(Type::Unit, vec![Constant::Unit]))
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_list_bools() {
|
||||
let uplc = "(program 0.0.0 (con list<bool> [True, False, True]))";
|
||||
assert_eq!(
|
||||
super::program(uplc).unwrap(),
|
||||
Program::<Name> {
|
||||
version: (0, 0, 0),
|
||||
term: Term::Constant(Constant::ProtoList(
|
||||
Type::Bool,
|
||||
vec![
|
||||
Constant::Bool(true),
|
||||
Constant::Bool(false),
|
||||
Constant::Bool(true)
|
||||
]
|
||||
))
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_list_bytestrings() {
|
||||
let uplc = "(program 0.0.0 (con list<bytestring> [#00, #01]))";
|
||||
assert_eq!(
|
||||
super::program(uplc).unwrap(),
|
||||
Program::<Name> {
|
||||
version: (0, 0, 0),
|
||||
term: Term::Constant(Constant::ProtoList(
|
||||
Type::ByteString,
|
||||
vec![
|
||||
Constant::ByteString(vec![0x00]),
|
||||
Constant::ByteString(vec![0x01]),
|
||||
]
|
||||
))
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_list_list_integers() {
|
||||
let uplc = "(program 0.0.0 (con list<list<integer>> [[14,42], [1337]]))";
|
||||
assert_eq!(
|
||||
super::program(uplc).unwrap(),
|
||||
Program::<Name> {
|
||||
version: (0, 0, 0),
|
||||
term: Term::Constant(Constant::ProtoList(
|
||||
Type::List(Box::new(Type::Integer)),
|
||||
vec![
|
||||
Constant::ProtoList(
|
||||
Type::Integer,
|
||||
vec![Constant::Integer(14), Constant::Integer(42)]
|
||||
),
|
||||
Constant::ProtoList(Type::Integer, vec![Constant::Integer(1337)])
|
||||
]
|
||||
))
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_list_multiline() {
|
||||
let uplc = r#"
|
||||
(program 0.0.0
|
||||
(con list
|
||||
<integer>
|
||||
[ 14
|
||||
, 42
|
||||
]
|
||||
)
|
||||
)"#;
|
||||
assert_eq!(
|
||||
super::program(uplc).unwrap(),
|
||||
Program::<Name> {
|
||||
version: (0, 0, 0),
|
||||
term: Term::Constant(Constant::ProtoList(
|
||||
Type::Integer,
|
||||
vec![Constant::Integer(14), Constant::Integer(42)],
|
||||
))
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_pair_unit_unit() {
|
||||
let uplc = "(program 0.0.0 (con pair <unit,unit> [(),()]))";
|
||||
assert_eq!(
|
||||
super::program(uplc).unwrap(),
|
||||
Program::<Name> {
|
||||
version: (0, 0, 0),
|
||||
term: Term::Constant(Constant::ProtoPair(
|
||||
Type::Unit,
|
||||
Type::Unit,
|
||||
Box::new(Constant::Unit),
|
||||
Box::new(Constant::Unit)
|
||||
))
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_pair_bool_pair_integer_bytestring() {
|
||||
let uplc = "(program 0.0.0 (con pair<bool, pair<integer, bytestring>> [True, [14, #42]]))";
|
||||
assert_eq!(
|
||||
super::program(uplc).unwrap(),
|
||||
Program::<Name> {
|
||||
version: (0, 0, 0),
|
||||
term: Term::Constant(Constant::ProtoPair(
|
||||
Type::Bool,
|
||||
Type::Pair(Box::new(Type::Integer), Box::new(Type::ByteString)),
|
||||
Box::new(Constant::Bool(true)),
|
||||
Box::new(Constant::ProtoPair(
|
||||
Type::Integer,
|
||||
Type::ByteString,
|
||||
Box::new(Constant::Integer(14)),
|
||||
Box::new(Constant::ByteString(vec![0x42])),
|
||||
))
|
||||
))
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_pair_string_list_integer() {
|
||||
let uplc = "(program 0.0.0 (con pair<string, list<integer>> [\"foo\", [14, 42]]))";
|
||||
assert_eq!(
|
||||
super::program(uplc).unwrap(),
|
||||
Program::<Name> {
|
||||
version: (0, 0, 0),
|
||||
term: Term::Constant(Constant::ProtoPair(
|
||||
Type::String,
|
||||
Type::List(Box::new(Type::Integer)),
|
||||
Box::new(Constant::String(String::from("foo"))),
|
||||
Box::new(Constant::ProtoList(
|
||||
Type::Integer,
|
||||
vec![Constant::Integer(14), Constant::Integer(42)],
|
||||
))
|
||||
))
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_pair_multiline() {
|
||||
let uplc = r#"
|
||||
(program 0.0.0
|
||||
(con pair
|
||||
<integer, integer>
|
||||
[14, 42]
|
||||
)
|
||||
)"#;
|
||||
assert_eq!(
|
||||
super::program(uplc).unwrap(),
|
||||
Program::<Name> {
|
||||
version: (0, 0, 0),
|
||||
term: Term::Constant(Constant::ProtoPair(
|
||||
Type::Integer,
|
||||
Type::Integer,
|
||||
Box::new(Constant::Integer(14)),
|
||||
Box::new(Constant::Integer(42))
|
||||
))
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_list_type_mismatch() {
|
||||
let uplc = "(program 0.0.0 (con list<integer> [True, False]))";
|
||||
assert!(super::program(uplc).is_err())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_list_mixed_types() {
|
||||
let uplc = "(program 0.0.0 (con list<integer> [14, False]))";
|
||||
assert!(super::program(uplc).is_err())
|
||||
}
|
||||
|
||||
// Helper function for all simple programs that involve only a direct application of a builtin
|
||||
// function operating on two integers.
|
||||
fn parse_builtin_integer(uplc: &str, default_function: DefaultFunction, x: i128, y: i128) {
|
||||
assert_eq!(
|
||||
super::program(uplc).unwrap(),
|
||||
Program::<Name> {
|
||||
version: (1, 0, 0),
|
||||
term: Term::Apply {
|
||||
function: Rc::new(Term::Apply {
|
||||
function: Rc::new(Term::Builtin(default_function)),
|
||||
argument: Rc::new(Term::Constant(Constant::Integer(x))),
|
||||
}),
|
||||
argument: Rc::new(Term::Constant(Constant::Integer(y)))
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -193,14 +193,9 @@ impl Constant {
|
|||
Constant::Bool(b) => RcDoc::text("bool")
|
||||
.append(RcDoc::line())
|
||||
.append(RcDoc::text(if *b { "True" } else { "False" })),
|
||||
Constant::ProtoList(r#type, items) => RcDoc::text("(")
|
||||
.append(
|
||||
RcDoc::text("list")
|
||||
.append(RcDoc::line())
|
||||
.append(r#type.to_doc()),
|
||||
)
|
||||
Constant::ProtoList(r#type, items) => RcDoc::text("list")
|
||||
.append(RcDoc::line_())
|
||||
.append(RcDoc::text(")"))
|
||||
.append(r#type.to_doc())
|
||||
.append(RcDoc::line())
|
||||
.append(RcDoc::text("["))
|
||||
.append(RcDoc::intersperse(
|
||||
|
@ -208,22 +203,19 @@ impl Constant {
|
|||
RcDoc::text(","),
|
||||
))
|
||||
.append(RcDoc::text("]")),
|
||||
Constant::ProtoPair(type1, type2, left, right) => RcDoc::text("(")
|
||||
.append(
|
||||
RcDoc::text("pair")
|
||||
.append(RcDoc::line())
|
||||
.append(type1.to_doc())
|
||||
.append(RcDoc::line())
|
||||
.append(type2.to_doc()),
|
||||
)
|
||||
Constant::ProtoPair(type_left, type_right, left, right) => RcDoc::text("pair")
|
||||
.append(RcDoc::line_())
|
||||
.append(RcDoc::text(")"))
|
||||
.append(RcDoc::text("<"))
|
||||
.append(type_left.to_doc())
|
||||
.append(RcDoc::text(", "))
|
||||
.append(type_right.to_doc())
|
||||
.append(RcDoc::text(">"))
|
||||
.append(RcDoc::line())
|
||||
.append(RcDoc::text("("))
|
||||
.append(RcDoc::text("["))
|
||||
.append(left.to_doc_list())
|
||||
.append(RcDoc::text(","))
|
||||
.append(right.to_doc_list())
|
||||
.append(RcDoc::text(")")),
|
||||
.append(RcDoc::text("]")),
|
||||
d @ Constant::Data(_) => RcDoc::text("data ").append(d.to_doc_list()),
|
||||
}
|
||||
}
|
||||
|
@ -264,24 +256,16 @@ impl Type {
|
|||
Type::String => RcDoc::text("string"),
|
||||
Type::ByteString => RcDoc::text("bytestring"),
|
||||
Type::Unit => RcDoc::text("unit"),
|
||||
Type::List(r#type) => RcDoc::text("(")
|
||||
.append(
|
||||
RcDoc::text("list")
|
||||
.append(RcDoc::line())
|
||||
.append(r#type.to_doc()),
|
||||
)
|
||||
.append(RcDoc::line_())
|
||||
.append(RcDoc::text(")")),
|
||||
Type::Pair(type1, type2) => RcDoc::text("(")
|
||||
.append(
|
||||
RcDoc::text("list")
|
||||
.append(RcDoc::line())
|
||||
.append(type1.to_doc())
|
||||
.append(RcDoc::line())
|
||||
.append(type2.to_doc()),
|
||||
)
|
||||
.append(RcDoc::line_())
|
||||
.append(RcDoc::text(")")),
|
||||
Type::List(r#type) => RcDoc::text("list")
|
||||
.append(RcDoc::text("<"))
|
||||
.append(r#type.to_doc())
|
||||
.append(RcDoc::text(">")),
|
||||
Type::Pair(l, r) => RcDoc::text("pair")
|
||||
.append(RcDoc::text("<"))
|
||||
.append(l.to_doc())
|
||||
.append(RcDoc::text(", "))
|
||||
.append(r.to_doc())
|
||||
.append(RcDoc::text(">")),
|
||||
Type::Data => RcDoc::text("data"),
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue