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 )

The create can be compiled to WASM.

For a list of features, see https://github.com/OlofBlomqvist/marlowe_lang.

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.//!

Grammars

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+ ~ ValueId ~ 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* ~ "ValueEQ"  ~ 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 | Address }
FromParty = _{ FromPartyHole | Role | Address }
Role = ${ lpar ~ "Role" ~ WHITESPACE+ ~ quoted_string ~ rpar }
Address = ${ lpar ~ "Address" ~ WHITESPACE+ ~ quoted_string ~ rpar }
Account = ${ "Account" ~ WHITESPACE+ ~ Party }

MainContract = _{ Contract ~ EOI }

ValueId = { quoted_string }

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+ ~ ValueId ~ 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 }
CaseHole = { Hole }
ActionHole = { Hole }
AccountHole = { Hole }

Example usage

use marlowe_lang::types::marlowe::*;
use marlowe_lang::parsing::{
 deserialization::deserialize,
 serialization::marlowe::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

Extra features such as plutus encoding/decoding, cli tool etc.
Where the parsing happens
Top level types module