Expand description

An experimental implementation of the Marlowe language for Cardano smart contracts.

Its primary use-case is for creating smart contracts in Rust rather than directly using Marlowe, and instead be able to export the contracts into Marlowe programatically.

Parser tests are done against all example contracts available on the Marlowe Playground as seen on 2022-06-06.
( Deserialize -> Re-serialize -> Compare )

Stability

At the time of writing this, the language specification of Marlowe is not yet finalized and so this crate may not exactly reflect the current syntax as seen in official implementations such as the Marlowe playground.

When the Marlowe v3 specs are finalized, this crate will (hopefully) be updated to reflect those.

Main entry-points:

This crate uses Pest.rs!

[grammars.rs] Click to expand/collapse
WHITESPACE = _{ " " | "\t" | "\r" | "\n" | "\r\n" }
comma  = _{ "," }
lpar   = _{ "(" }
rpar   = _{ ")" }
lbra   = _{ "[" }
rbra   = _{ "]" }
Number = ${ "-"{0,1} ~ ASCII_DIGIT+ }
quoted_string = _{ "\"" ~ string ~ "\""  }
string = { str_char* }
str_char = _{
    !("\"" | "\\") ~ ANY
    | "\\" ~ ("\"" | "\\" )
}

Bound = ${  "Bound" ~ WHITESPACE+ ~ Number ~ WHITESPACE+ ~ Number}
xBound = _{ 
    BoundHole | Bound | lpar ~ Bound ~ rpar
}

Timeout = _{ TimeoutHole | TimeConstant | Number | TimeParam }
TimeConstant = { ASCII_DIGIT+ }
TimeInterval = ${ lpar ~ WHITESPACE* ~ "TimeInterval" ~ WHITESPACE+ ~ Timeout ~ WHITESPACE+ ~ Timeout ~ WHITESPACE* ~ rpar }
TimeParam = ${ lpar ~ WHITESPACE* ~ "TimeParam" ~ WHITESPACE+ ~ quoted_string ~ WHITESPACE* ~ rpar }

Token = _{TokenHole|ADA|Currency}
ADA = { "Token \"\" \"\"" }
Currency = ${ lpar ~ WHITESPACE* ~ "Token" ~ WHITESPACE+ ~ quoted_string ~ WHITESPACE+ ~ quoted_string ~ WHITESPACE* ~ rpar }
ChoiceId = ${ lpar ~ WHITESPACE* ~"ChoiceId" ~ WHITESPACE+ ~ quoted_string ~ WHITESPACE+ ~ Party ~ WHITESPACE* ~ rpar}
Value = _{   
    Constant
    | ConstantParam
    | AvailableMoney
    | Cond
    | ChoiceValue
    | MulValue
    | DivValue
    | SubValue
    | AddValue
    | NegValue
    | UseValue
    | TimeIntervalStart
    | TimeIntervalEnd
    | ValueHole
 }
    TimeIntervalStart = { "TimeIntervalStart" }
    TimeIntervalEnd = { "TimeIntervalEnd" }
    Cond = ${ lpar ~ WHITESPACE* ~ "Cond" ~ WHITESPACE+ ~ Observation ~ WHITESPACE+ ~ Value ~ WHITESPACE+ ~ Value ~ WHITESPACE* ~ rpar}
    ChoiceValue = ${ lpar ~ WHITESPACE* ~ "ChoiceValue" ~ WHITESPACE+ ~ ChoiceId ~ WHITESPACE* ~ rpar}
    MulValue = ${ lpar ~ WHITESPACE* ~ "MulValue" ~ WHITESPACE+ ~ Value ~ WHITESPACE+ ~ Value ~ WHITESPACE* ~ rpar }
    DivValue = ${ lpar ~ WHITESPACE* ~ "DivValue" ~ WHITESPACE+ ~ Value ~ WHITESPACE+ ~ Value ~ WHITESPACE* ~ rpar}
    SubValue = ${ lpar ~ WHITESPACE* ~ "SubValue" ~ WHITESPACE+ ~ Value ~ WHITESPACE+ ~ Value ~ WHITESPACE* ~ rpar}
    AddValue = ${ lpar ~ WHITESPACE* ~ "AddValue" ~ WHITESPACE+ ~ Value ~ WHITESPACE+ ~ Value ~ WHITESPACE* ~ rpar}
    NegValue = ${ lpar ~ WHITESPACE* ~ "NegValue" ~ WHITESPACE+ ~ Value ~ WHITESPACE* ~ rpar}
    UseValue = ${ lpar ~ WHITESPACE* ~ "UseValue" ~ WHITESPACE+ ~ quoted_string ~ WHITESPACE* ~ rpar}
    Constant = ${ lpar ~ WHITESPACE* ~ "Constant" ~ WHITESPACE+ ~ Number ~ WHITESPACE* ~ rpar }
    ConstantParam = ${ lpar ~ WHITESPACE* ~ "ConstantParam" ~ WHITESPACE+ ~ quoted_string ~ WHITESPACE* ~ rpar }
    AvailableMoney = ${ lpar ~ WHITESPACE* ~ "AvailableMoney" ~ WHITESPACE+ ~ Party ~ WHITESPACE+ ~ Token ~ WHITESPACE* ~ rpar}

Observation = _{ ObservationHole | TrueObs | FalseObs | ValueEQ | ValueLE | 
                 ValueLT | ValueGT | ValueGE | OrObs | 
                 NotObs | AndObs | ChoseSomething }
    ValueEQ = ${ lpar ~ WHITESPACE* ~ "ValueE"  ~ WHITESPACE+  ~ Value  ~ WHITESPACE+ ~ Value ~WHITESPACE* ~ rpar }
    ValueLE = ${ lpar ~ WHITESPACE* ~ "ValueLE" ~ WHITESPACE+  ~ Value ~ WHITESPACE+  ~ Value ~WHITESPACE*  ~ rpar }
    ValueLT = ${ lpar ~ WHITESPACE* ~ "ValueLT" ~ WHITESPACE+  ~ Value ~ WHITESPACE+  ~ Value ~WHITESPACE*  ~ rpar }
    ValueGT = ${ lpar ~ WHITESPACE* ~ "ValueGT" ~ WHITESPACE+  ~ Value ~ WHITESPACE+  ~ Value ~WHITESPACE*  ~ rpar }
    ValueGE = ${ lpar ~ WHITESPACE* ~ "ValueGE" ~ WHITESPACE+  ~ Value ~ WHITESPACE+  ~ Value ~WHITESPACE*  ~ rpar }
    TrueObs = { "TrueObs" }
    FalseObs = { "FalseObs" }
    ChoseSomething = @{ lpar ~ WHITESPACE* ~ "ChoseSomething" ~ WHITESPACE+ ~ ChoiceId ~WHITESPACE*~ rpar }
    NotObs = ${ lpar ~ WHITESPACE* ~ "NotObs" ~ WHITESPACE+ ~ Observation ~ WHITESPACE* ~ rpar }
    OrObs =  ${ lpar ~ WHITESPACE* ~ "OrObs"  ~ WHITESPACE+ ~ Observation ~ WHITESPACE+ ~ Observation ~ WHITESPACE* ~ rpar }
    AndObs = ${ lpar ~ WHITESPACE* ~ "AndObs" ~ WHITESPACE+ ~ Observation ~ WHITESPACE+ ~ Observation ~ WHITESPACE* ~ rpar }
   
Action = _{ ActionHole | Notify | Choice | Deposit }
    Deposit = ${ lpar ~ WHITESPACE* ~"Deposit" ~ WHITESPACE+ ~ Party ~ WHITESPACE+ ~ FromParty ~ WHITESPACE+ ~ Token ~ WHITESPACE+ ~ Value ~ WHITESPACE* ~ rpar }
    Choice =  ${ lpar ~ WHITESPACE* ~"Choice"  ~ WHITESPACE+ ~ ChoiceId ~ WHITESPACE+ ~ ArrayOfBounds ~ WHITESPACE* ~ rpar }
    Notify =  ${ lpar ~ WHITESPACE* ~"Notify"  ~ WHITESPACE+ ~ Observation ~ WHITESPACE* ~ rpar }

Case = ${ 
    lpar ~ WHITESPACE* ~ "Case" ~ WHITESPACE+ ~ Action ~ WHITESPACE+ ~ WrappedContract ~ WHITESPACE* ~ rpar
    | "Case" ~ WHITESPACE+ ~ Action ~ WHITESPACE+ ~ WrappedContract
}

Payee = _{ PayeeHole | PayeeAccount | PayeeParty }
PayeeAccount = ${ lpar ~ "Account" ~ WHITESPACE+ ~ Party ~ rpar }
PayeeParty = ${ lpar ~ "Party" ~ WHITESPACE+ ~ Party ~ rpar }
Party = _{ PartyHole | Role | PK }
FromParty = _{ FromPartyHole | Role | PK }
Role = ${ lpar ~ "Role" ~ WHITESPACE+ ~ quoted_string ~ rpar }
PubKey = ${ "\"" ~ ("0"|"1"|"2"|"3"|"4"|"5"|"6"|"7"|"8"|"9"|"A"|"B"|"C"|"D"|"E"|"F"){64,64} ~ "\"" }
PK = ${ lpar ~ "PK" ~ WHITESPACE+ ~ PubKey ~ rpar }
Account = ${ "Account" ~ WHITESPACE+ ~ Party }

MainContract = _{ Contract ~ EOI }

Contract = { Close | When | If | Let | Assert | Pay }
    When   = ${ "When" ~ WHITESPACE+ ~ ArrayOfCases ~ WHITESPACE+ ~ Timeout ~ WHITESPACE+ ~ WrappedContract }
    Pay    = ${ "Pay"  ~ WHITESPACE+ ~ Party ~ WHITESPACE+ ~ Payee ~ WHITESPACE+ ~ Token ~ WHITESPACE+ ~ Value  ~ WHITESPACE+ ~ WrappedContract }
    If     = ${ "If"   ~ WHITESPACE+ ~ Observation ~ WHITESPACE+ ~ WrappedContract ~ WHITESPACE+ ~ WrappedContract }
    Let    = ${ "Let"  ~ WHITESPACE+ ~ quoted_string ~ WHITESPACE+ ~ Value ~ WHITESPACE+ ~ WrappedContract }
    Assert = ${ "Assert" ~ WHITESPACE+ ~ Observation ~ WHITESPACE+ ~ WrappedContract }
    Close  = { "Close" }

WrappedContract = _{
    ContractHole | Close | "(" ~ WHITESPACE* ~ (Assert|Let|If|Pay|When) ~ WHITESPACE* ~  ")"
}

ArrayOfCases = ${ 
     lbra ~ WHITESPACE* ~ (Case|CaseHole) ~ (WHITESPACE* ~ "," ~ WHITESPACE* ~ (Case|CaseHole))* ~ WHITESPACE* ~ rbra 
     | lbra ~ WHITESPACE* ~ rbra
}

ArrayOfBounds = ${ 
     lbra ~ WHITESPACE* ~ xBound ~ (WHITESPACE* ~ "," ~ WHITESPACE* ~ xBound)* ~ WHITESPACE* ~ rbra 
     | lbra ~ WHITESPACE* ~ rbra
}

Hole = _ { "?"~("-"|"_"|ASCII_DIGIT|ASCII_ALPHA)* }
PartyHole = { Hole }
FromPartyHole = { Hole }
ContractHole = { Hole }
PayeeHole = { Hole }
ValueHole = { Hole }
ObservationHole = { Hole }
TimeoutHole = { Hole }
TokenHole = { Hole }
BoundHole = { Hole }
RoleHole = { Hole }
PubkeyHole = { Hole }
CaseHole = { Hole }
ActionHole = { Hole }
AccountHole = { Hole }

Example usage

use marlowe_lang::types::marlowe::*;
use marlowe_lang::parsing::{
 deserialization::deserialize,
 serialization::serialize,
};
 
let my_contract = Contract::When {
    when: vec![
        Some(Case { 
            case: Some(Action::Notify { 
                notify_if: Some(Observation::True)
            }), 
            then: Some(Contract::Close.boxed()) })
    ],
    timeout: Some(Timeout::TimeParam("test".into())),
    timeout_continuation: Some(Contract::Close.boxed()),
};
 
let serialized = serialize(my_contract);
let deserialized : Contract = deserialize(&serialized).unwrap();
println!("{serialized}");
Where ‘println!(“{serialized}”)’ would output this:
When [ Case (Notify (True)) Close ] (TimeParam "test") Close

Modules

Where the parsing happens

Top level types module