use crate::ast::{Expr, Stmt, self};
use crate::util::{concat, parse_str_prefix};
grammar;
// Enable `//` comments
match {
r"\s*" => { },
r"//[^\n\r]*[\n\r]*" => { },
_,
}
pub Program: Expr = <stmts:Stmt*> <ret:Return?> =>
ast::Block { stmts, return_value: ret.map(Into::into) }.into();
Block: Expr = <stmts:Stmt*> <ret:Return> =>
ast::Block { stmts, return_value: Some(ret.into()) }.into();
Stmt: Stmt = {
FnDef,
Assign,
}
Expr: Expr = {
SimpleExpr,
And,
Or,
ChildDerive,
};
SimpleExpr: Expr = {
Number,
Ident,
Call,
Thresh,
BlockExpr,
WithProb,
Array,
ArrayAccess,
Duration,
DateTime,
Hash,
PubKey,
Paren<And>,
Paren<Or>,
Paren<ChildDerive>,
Paren<SimpleExpr>,
};
SExpr: Expr = {
Number,
Ident,
Call,
BlockExpr,
};
Return: Expr = {
Expr, // the preferred form
"return" <Expr> ";"?,
};
// Expressions
Number: Expr = <s:r"\d{1,39}"> => ast::Expr::Number(<>.parse().unwrap()).into();
IdentTerm: ast::Ident = <s:r"[a-zA-Z_$][a-zA-Z0-9_$]{0,38}"> => ast::Ident(<>.into());
Ident: Expr = IdentTerm => <>.into();
Call: Expr = <ident:IdentTerm> "(" <args:List0<Expr, ",">> ")" =>
ast::Call { ident, args }.into();
And: Expr = <List2<SimpleExpr, "&&">> => ast::And(<>).into();
Or: Expr = <List2<SimpleExpr, "||">> => ast::Or(<>).into();
Thresh: Expr = <thresh:SExpr> "of" <policies:SimpleExpr> =>
ast::Thresh { thresh: thresh.into(), policies: policies.into() }.into();
BlockExpr: Expr = "{" <Block> "}" => <>.into();
WithProb: Expr = <prob:SExpr> "@" <expr:SimpleExpr> =>
ast::WithProb { prob: prob.into(), expr: expr.into() }.into();
Array: Expr = "[" <List0<Expr, ",">> "]" =>
ast::Array(<>).into();
ArrayAccess: Expr = <array:ArrayAccessLHS> "." <index:Number> =>
ast::ArrayAccess { array: array.into(), index: index.into() }.into();
// TODO support all Paren<Expr>
ArrayAccessLHS = { Ident, Call, Array, BlockExpr };
// Statements
Assign: Stmt = "let"? <assigns:List1<Assignment, ",">> ";" =>
ast::Assign(assigns).into();
Assignment: ast::Assignment = <lhs:IdentTerm> "=" <rhs:Expr> =>
ast::Assignment { lhs, rhs };
FnDef: Stmt = {
"fn" <ident:IdentTerm> "(" <signature:List0<IdentTerm, ",">> ")" "=" <body:Expr> ";" =>
ast::FnDef { ident, signature, body }.into(),
"fn" <ident:IdentTerm> "(" <signature:List0<IdentTerm, ",">> ")" "{" <body:Block> "}" ";"? =>
ast::FnDef { ident, signature, body }.into(),
}
// An xpub or compressed standalone public key (uncomporessed is unsupported), with optional bip32 origin
PubKey: Expr = <s:r"(\[[a-f0-9]{8}(/\d+['h]?)*\])?([a-f0-9]{66}|([xt]pub[0-9a-zA-Z]{100,120}))"> =>
Expr::PubKey(<>.into());
Hash: Expr = <s:r"[a-f0-9]{64}|[a-f0-9]{40}"> =>
Expr::Hash(<>.into());
ChildDerive: Expr = {
<parent:SimpleExpr> "/" <path:List1<SimpleExpr, "/">> <wildcard:"/*"?> =>
ast::ChildDerive { parent: parent.into(), path, is_wildcard: wildcard.is_some() }.into(),
<parent:SimpleExpr> "/*" =>
ast::ChildDerive { parent: parent.into(), path: vec![], is_wildcard: true }.into(),
};
// Duration and times
Duration = { DurationBlocks, DurationClock };
DurationBlocks: Expr = r"\d+\s+blocks?" =>
ast::Duration::BlockHeight(parse_str_prefix(<>)).into();
DurationClock: Expr = <heightwise:"heightwise"?> <parts:DurationClockPart+> =>
ast::Duration::BlockTime { parts, heightwise: heightwise.is_some() }.into();
DurationClockPart: ast::DurationPart = {
r"(\d+(?:\.\d+)?)\s+years?" => ast::DurationPart::Years(parse_str_prefix(<>)),
r"(\d+(?:\.\d+)?)\s+months?" => ast::DurationPart::Months(parse_str_prefix(<>)),
r"(\d+(?:\.\d+)?)\s+weeks?" => ast::DurationPart::Weeks(parse_str_prefix(<>)),
r"(\d+(?:\.\d+)?)\s+days?" => ast::DurationPart::Days(parse_str_prefix(<>)),
r"(\d+(?:\.\d+)?)\s+hours?" => ast::DurationPart::Hours(parse_str_prefix(<>)),
r"(\d+(?:\.\d+)?)\s+min(ute)?s?" => ast::DurationPart::Minutes(parse_str_prefix(<>)),
r"(\d+(?:\.\d+)?)\s+sec(ond)?s?" => ast::DurationPart::Seconds(parse_str_prefix(<>)),
}
DateTime: Expr = r"\d{4}-\d{1,2}-\d{1,2}(\s+\d{1,2}:\d{1,2})?" =>
Expr::DateTime(<>.into());
// Helpers
// A `S`-separated list of zero or more `T` values
List0<T, S>: Vec<T> = <l:(<T> S)*> <t:T?> => concat(l, t);
// A `S`-separated list of one or more `T` values
List1<T, S>: Vec<T> = <l:(<T> S)*> <t:T> => concat(l, Some(t));
// A `S`-separated list of two or more `T` values
List2<T, S>: Vec<T> = <l:(<T> S)+> <t:T> => concat(l, Some(t));
Paren<T> = "(" <T> ")";