use crate::{
error::{Error, Handler, ParseError},
expr::*,
lexer,
macros::{MacroCall, MacroDecl},
parser::{
ParserContext,
UseTree::{self, Path as UseTreePath},
},
predicate::{
BlockStatement, CallKey, Const, ConstraintDecl, ExprKey, IfDecl, InterfaceDecl,
MatchDecl, MatchDeclBranch, Predicate, PredicateInterface,
StorageVar, Param,
},
span::Spanned,
types::{NewTypeDecl, PrimitiveKind, Type, UnionDecl, UnionVariant},
};
grammar<'a>(
context: &mut ParserContext<'a>,
handler: &Handler,
);
pub Pint: () = Decl*;
////////////////////
/// Declarations ///
////////////////////
// Decls allowed at the top level, outside predicates.
Decl: () = {
ConstDecl ";",
Interface,
MacroCallDecl ";",
MacroDecl,
NewTypeDecl ";",
PredicateDecl,
StorageDecl,
UnionDecl ";",
UseStatement ";",
}
// Decls allowed inside predicates.
PredicateInnerDecl: () = {
<l:@L> ConstDecl ";" <r:@R> => {
let span = (context.span_from)(l, r);
handler.emit_err(Error::Parse {
error: ParseError::UnsupportedConstLocation { span: span.clone() },
});
},
ConstraintDecl ";",
IfDecl,
MatchDecl,
MacroCallDecl ";",
LetDecl ";",
UseStatement ";",
}
// Decls allowed inside macro bodies, which is ALL of them except `MacroDecl`, since we don't want
// nested macros (yet?), and `MatchDecl`. We have a conflict between match decls and match exprs in
// macros, so we special case them. See MacroBodyAtDecl and MacroBodyAtExpr below.
DeclInnerMacroBody: () = {
ConstDecl ";",
ConstraintDecl ";",
IfDecl,
Interface,
MacroCallDecl ";",
NewTypeDecl ";",
PredicateDecl,
LetDecl ";",
StorageDecl,
UnionDecl ";",
UseStatement ";",
}
PredicateDecl: () = {
PredicateOpen "(" SepList<PredicateParam, ","> ")" "{" PredicateInnerDecl* PredicateClose,
}
PredicateParam: () = {
<l:@L> <name:Ident> ":" <ty:Type> <r:@R> => {
let mod_prefix = context.mod_prefix;
let mut full_name_ident = name.clone();
if let Ok(full_name) = context
.current_pred()
.expect("adding an argument to the predicate")
.symbols
.add_symbol(handler, mod_prefix, None, &name, name.span.clone())
{
let span = (context.span_from)(l, r);
full_name_ident.name = full_name;
context
.current_pred()
.expect("adding an argument to the predicate")
.params
.push(Param {
name: full_name_ident,
ty,
span,
});
}
}
}
PredicateOpen: () = {
<l:@L> "predicate" <name:Ident> <r:@R> => {
// To detect name clashes
let name = context.add_top_level_symbol(
handler,
Ident {
name: name.to_string(),
hygienic: false,
span: (context.span_from)(l, r),
},
context.mod_prefix,
);
// Brand new Pred in the contract
let pred_key = context
.contract
.preds
.insert(Predicate::new(name, (context.span_from)(l, r)));
// Keep track of the macro call in this new predicate
context
.macro_calls
.insert(pred_key, slotmap::SecondaryMap::default());
// Switch the current_pred_key so that the parser inserts items in the right Pred until the
// predicate closes
context.current_pred_key = Some(pred_key);
}
}
PredicateClose: () = {
"}" => {
// At the end of the predicate declaration, return back to no pred.
context.current_pred_key = None;
}
}
StorageVar: StorageVar = {
<l:@L> <name:Ident> ":" <ty:Type> <r:@R> => {
let span = (context.span_from)(l, r);
StorageVar { name, ty, span }
}
}
StorageBlock: Vec<StorageVar> = {
"storage" "{" <storage_vars:SepList<StorageVar, ",">> "}" => {
context.parse_storage_block(handler, storage_vars)
}
}
StorageDecl: () = {
<l:@L> <storage_vars:StorageBlock> <r:@R> => {
context.parse_storage_decl(handler, storage_vars, (l, r))
},
}
InterfacePredicateParam: Param = {
<l:@L> <name:Ident> ":" <ty:Type> <r:@R> => {
Param {
name,
ty,
span: (context.span_from)(l, r),
}
}
}
PredicateInterface: PredicateInterface = {
<l:@L> "predicate" <name:Ident> "(" <params:SepList<InterfacePredicateParam, ",">> ")" ";" <r:@R> => {
PredicateInterface {
name,
params,
span: (context.span_from)(l, r),
}
},
}
InterfaceDecl: InterfaceDecl = {
<l:@L> <storage_vars:StorageBlock> <r:@R> => {
InterfaceDecl::StorageDecl((storage_vars, (context.span_from)(l, r)))
},
<predicate_interface:PredicateInterface> => {
InterfaceDecl::PredicateInterface(predicate_interface)
},
}
Interface: () = {
<l:@L> "interface" <name:Ident> "{" <interface_decls:InterfaceDecl*> "}" <r:@R> => {
context.parse_interface(handler, name, interface_decls, (l, r));
}
}
UseStatement: () = {
"use" <l:@L> <abs:"::"?> <ut:UseTree> <r:@R> => {
context.parse_use_statement(handler, abs.is_some(), ut, (l, r))
}
}
UsePathIdent: Ident = {
Ident,
IdentFromToken<"self">,
MacroName,
}
UseTree: UseTree = {
<name:UsePathIdent> => UseTree::Name { name },
<prefix:UsePathIdent> "::" <suffix:UseTree> => UseTreePath { prefix, suffix: Box::new(suffix) },
"{" <imports:SepList<UseTree, ",">> "}" => UseTree::Group { imports },
<name:UsePathIdent> "as" <alias:Ident> => UseTree::Alias { name, alias },
}
LetName: (Ident, Option<&'a str>) = {
<l:@L> <id:"ident"> <r:@R> => context.parse_let_name(id, (l, r)),
}
LetDecl: () = {
<l:@L> "let" <name:LetName> <ty:(":" <Type>)?> "=" <init:Expr> <r:@R> => {
let span = (context.span_from)(l, r);
let mod_prefix = context.mod_prefix;
let _ = context
.current_pred()
.expect("adding a variable to the predicate")
.insert_variable(handler, mod_prefix, name.1, &name.0, ty, init, span)
.map(|_| ());
},
<l:@L> "let" <name:LetName> <ty:(":" <Type>)> "=" <init:AsmBlockExpr> <r:@R> => {
let span = (context.span_from)(l, r);
let mod_prefix = context.mod_prefix;
let _ = context
.current_pred()
.expect("adding a variable to the predicate")
.insert_variable(handler, mod_prefix, name.1, &name.0, Some(ty), init, span)
.map(|_| ());
},
}
ConstDecl: () = {
<l:@L> "const" <name:Ident> <ty:(":" <Type>)?> "=" <init:Expr> <r:@R> => {
// consts are all added to the root Pred.
if let Ok(full_name) = context.contract.symbols.add_symbol(
handler,
context.mod_prefix,
None,
&name,
name.span.clone(),
) {
let decl_ty = ty.unwrap_or_else(|| Type::Unknown(name.span.clone()));
context.contract.consts.push((
Ident {
name: full_name,
..name
},
Const {
expr: init,
decl_ty,
},
));
}
}
}
ConstraintDecl: () = {
<constraint: Constraint> => context.current_pred()
.expect("adding a contraint to the predicate")
.constraints.push(constraint),
}
Constraint: ConstraintDecl = {
<l:@L> "constraint" <expr:Expr> <r:@R> => {
ConstraintDecl {
expr,
span: (context.span_from)(l, r),
}
}
}
BlockStatement: BlockStatement = {
<constraint: Constraint> ";" => BlockStatement::Constraint(constraint),
<if_decl: If> => BlockStatement::If(if_decl),
<match_decl: Match> => BlockStatement::Match(match_decl),
}
BlockStmts: Vec<BlockStatement> = {
"{" <block_statements:BlockStatement*> "}" => block_statements
}
IfBlockElse: Vec<BlockStatement> = {
"else" "{" <block_statements:BlockStatement*> "}" => block_statements,
"else" <if_decl:If> => vec![BlockStatement::If(if_decl)],
}
If: IfDecl = {
<l:@L> "if" <condition:Expr> <then_block:BlockStmts> <else_block:IfBlockElse?> <r:@R> => {
IfDecl {
condition,
then_block,
else_block,
span: (context.span_from)(l, r),
}
}
}
IfDecl: () = {
<if_decl: If> => {
context
.current_pred()
.expect("adding an if-decl to the predicate")
.if_decls
.push(if_decl)
}
}
MatchBlock: MatchDeclBranch = {
<l:@L> <name:Path> <r:@R> <binding:("(" <Ident> ")")?> "=>" <block:BlockStmts> ","? => {
MatchDeclBranch {
name,
name_span: (context.span_from)(l, r),
binding,
block,
}
}
}
MatchBlockElse: Vec<BlockStatement> = {
"else" "=>" <BlockStmts> ","?,
}
Match: MatchDecl = {
<l:@L> "match" <match_expr:Expr> "{" <match_branches:MatchBlock+> <else_branch:MatchBlockElse?> "}" <r:@R> => {
MatchDecl {
match_expr,
match_branches,
else_branch,
span: (context.span_from)(l, r),
}
}
}
MatchDecl: () = {
<match_decl: Match> => {
context
.current_pred()
.expect("adding a match-decl to the predicate")
.match_decls
.push(match_decl)
}
}
NewTypeDecl: () = {
<l:@L> "type" <name:Ident> "=" <ty:Type> <r:@R> => {
let new_type_decl = NewTypeDecl {
name: context.add_top_level_symbol(handler, name, context.mod_prefix),
ty,
span: (context.span_from)(l, r),
};
context.contract.new_types.push(new_type_decl);
}
}
UnionDecl: () = {
<l:@L> "union" <name:Ident> "=" <variants:Sep1ListNoTrail<UnionVariant, "|">> <r:@R> => {
let union_decl = UnionDecl {
name: context.add_top_level_symbol(handler, name, context.mod_prefix),
variants,
span: (context.span_from)(l, r),
};
context.contract.unions.insert(union_decl);
}
}
UnionVariant: UnionVariant = {
<variant_name:Ident> <ty:("(" <Type> ")")?> => {
UnionVariant { variant_name, ty }
}
}
MacroName: Ident = IdentFromToken<"macro_name">;
MacroDecl: () = {
"macro" <l:@L> <mut name:MacroName> "("
<params:MacroParamList>
")" <r:@R>
<body:"macro_body"> => {
// Prefix the name with the module. We can't use `add_top_level_symbol()` to do this as we
// must avoid the regular name clash error we'd get with variadic macros.
name.name = context.mod_prefix.to_string() + &name.name;
context.macros.push(MacroDecl {
name,
params: params.0,
pack: params.1,
body,
sig_span: (context.span_from)(l, r),
});
}
}
MacroParamList: (Vec<Ident>, Option<Ident>) = {
<mut v: (<MacroParam> ",")*> <e:MacroParam>
<p:("," <IdentFromToken<"macro_param_pack">>)?> ","? => {
v.push(e);
(v, p)
},
// Empty parameter lists
=> (Vec::new(), None),
}
MacroParam: Ident = IdentFromToken<"macro_param">;
MacroCallDecl: () = {
<keys:MacroCallExpr> => {
// Update the call data to tag it as a decl rather than an expr.
if let Some((_, call_data)) = context
.macro_calls
.get_mut(
&context
.current_pred_key
.expect("updating macro call as decl should have a predicate key"),
)
.unwrap()
.get_mut(keys.0)
{
call_data.is_at_decl = true;
}
}
}
// We have two separate macro body parsers here. One is for macros called as decls and the other
// for macros called as exprs. This is because we have `match` decl and `match` expr rules and
// they'll conflict if placed in the same context (i.e., the macro body).
//
// So `MacroBodyAtExpr` will parse all the `DeclInnerMacroBody` decls (like `constraint`) and then a
// final expr and return it. It must be used to parse macro calls which are made as expressions.
// `MacroBodyAtDecl` will parse the same decls *plus* `match` decls and doesn't return an
// expression. And so it must be used to parse macro calls which are 'top-level' decls.
// We keep the expr as optional though to help quash *parser* error messages about the missing expr
// if it's not there, and report it as missing later on.
pub MacroBodyAtExpr: Option<ExprKey> = {
"{" DeclInnerMacroBody* <Expr?> "}"
}
pub MacroBodyAtDecl: Option<ExprKey> = {
"{" MacroBodyInnerDecls* "}" => None,
}
MacroBodyInnerDecls: () = {
DeclInnerMacroBody,
MatchDecl,
}
/////////////
/// Types ///
/////////////
Type: Type = {
<ArrayType>,
<OptionalType>,
<TypeAtom>,
}
TypeAtom: Type = {
<l:@L> <kind:PrimitiveType> <r:@R> => {
let span = (context.span_from)(l, r);
if !context.experimental_types {
if let PrimitiveKind::Real = kind {
handler.emit_err(Error::Parse {
error: ParseError::TypeNotSupported {
ty: kind.to_string(),
span: span.clone(),
},
});
}
}
Type::Primitive { kind, span }
},
<l:@L> "{" <fields:TupleFields> "}" <r:@R> => {
Type::Tuple {
fields,
span: (context.span_from)(l, r),
}
},
<l:@L> "{" "}" <r:@R> => {
let span = (context.span_from)(l, r);
handler.emit_err(Error::Parse {
error: ParseError::EmptyTupleType { span: span.clone() },
});
// Recover with a malformed type
Type::Error(span)
},
<MapType>,
<l:@L> <name:Path> <r:@R> => Type::Custom {
name,
span: (context.span_from)(l, r),
},
}
ArrayType: Type = {
<l:@L> <ty:TypeAtom> <ranges: ("[" <Expr> "]")+ > <r:@R> => {
// Multi-dimensional arrays have their innermost dimension on the far
// right. Hence, we need to reverse the iterator.
// For example, `int[3][5]` is actually an array of size 3 that
// contains arrays of size 5 of `int`s.
ranges.iter().rev().fold(ty, |acc, range| Type::FixedArray {
ty: Box::new(acc),
range: Some(*range),
size: None,
span: (context.span_from)(l, r),
})
},
<l:@L> <ty:TypeAtom> "[" "]" <r:@R> => {
// Unsized arrays may not be multi-dimensional.
Type::UnsizedArray {
ty: Box::new(ty),
span: (context.span_from)(l, r),
}
},
<l:@L> <ty:OptionalType> <ranges: ("[" <Expr> "]")+ > <r:@R> => {
// Multi-dimensional arrays have their innermost dimension on the far
// right. Hence, we need to reverse the iterator.
// For example, `int[3][5]` is actually an array of size 3 that
// contains arrays of size 5 of `int`s.
ranges.iter().rev().fold(ty, |acc, range| Type::FixedArray {
ty: Box::new(acc),
range: Some(*range),
size: None,
span: (context.span_from)(l, r),
})
},
<l:@L> <ty:OptionalType> "[" "]" <r:@R> => {
// Unsized arrays may not be multi-dimensional.
Type::UnsizedArray {
ty: Box::new(ty),
span: (context.span_from)(l, r),
}
}
}
PrimitiveType: PrimitiveKind = {
"int_ty" => PrimitiveKind::Int,
"real_ty" => PrimitiveKind::Real,
"bool_ty" => PrimitiveKind::Bool,
"b256_ty" => PrimitiveKind::B256,
}
TupleFields: Vec<(Option<Ident>, Type)> = {
<field:TupleField> => vec![field],
<Sep1List<TupleField, ",">>,
}
TupleField: (Option<Ident>, Type) = {
<id:(<Ident> ":")?> <ty:Type> => (id, ty),
}
OptionalType: Type = {
<l:@L> <ty: Type> "?" <r:@R> => {
Type::Optional {
ty: Box::new(ty),
span: (context.span_from)(l, r),
}
}
}
/////////////////////
/// Storage Types ///
/////////////////////
MapType: Type = {
<l:@L> "(" <ty_from:Type> "=>" <ty_to:Type> ")" <r:@R> => Type::Map {
ty_from: Box::new(ty_from),
ty_to: Box::new(ty_to),
span: (context.span_from)(l, r),
}
}
///////////////////
/// Expressions ///
///////////////////
Expr: ExprKey = Concat;
Concat: ExprKey = {
<l:@L> <lhs:Concat> "++" <rhs:KeyValue> <r:@R> => {
let span = (context.span_from)(l, r);
context.contract.exprs.insert(
Expr::BinaryOp {
op: BinaryOp::Concat,
lhs,
rhs,
span: span.clone(),
},
Type::Unknown(span),
)
},
<KeyValue>,
}
KeyValue: ExprKey = {
<l:@L> <lhs:KeyValue> ":=" <rhs:SelectExpr> <r:@R> => {
let span = (context.span_from)(l, r);
context.contract.exprs.insert(
Expr::KeyValue {
lhs,
rhs,
span: span.clone(),
},
Type::Unknown(span),
)
},
<SelectExpr>,
}
SelectExpr: ExprKey = {
<l:@L> <condition:LogicalOrOp> "?" <then_expr:SelectExpr> ":" <else_expr:SelectExpr> <r:@R> => {
let span = (context.span_from)(l, r);
context.contract.exprs.insert(
Expr::Select {
condition,
then_expr,
else_expr,
span: span.clone(),
},
Type::Unknown(span),
)
},
<LogicalOrOp>
}
LogicalOrOp: ExprKey = {
<l:@L> <lhs:LogicalOrOp> "||" <rhs:LogicalAndOp> <r:@R> => {
let span = (context.span_from)(l, r);
context.contract.exprs.insert(
Expr::BinaryOp {
op: BinaryOp::LogicalOr,
lhs,
rhs,
span: span.clone(),
},
Type::Unknown(span),
)
},
<LogicalAndOp>,
}
LogicalAndOp: ExprKey = {
<l:@L> <lhs:LogicalAndOp> "&&" <rhs:Comparison> <r:@R> => {
let span = (context.span_from)(l, r);
context.contract.exprs.insert(
Expr::BinaryOp {
op: BinaryOp::LogicalAnd,
lhs,
rhs,
span: span.clone(),
},
Type::Unknown(span),
)
},
<Comparison>,
}
Comparison: ExprKey = {
<l:@L> <lhs:Comparison> <op:RelOpOp> <rhs:InOp> <r:@R> => {
let span = (context.span_from)(l, r);
context.contract.exprs.insert(
Expr::BinaryOp {
op,
lhs,
rhs,
span: span.clone(),
},
Type::Unknown(span),
)
},
<InOp>,
}
RelOpOp: BinaryOp = {
"==" => BinaryOp::Equal,
"!=" => BinaryOp::NotEqual,
"<" => BinaryOp::LessThan,
"<=" => BinaryOp::LessThanOrEqual,
">" => BinaryOp::GreaterThan,
">=" => BinaryOp::GreaterThanOrEqual,
}
InOp: ExprKey = {
<l:@L> <value:InOp> "in" <collection:Additive> <r:@R> => {
let span = (context.span_from)(l, r);
context.contract.exprs.insert(
Expr::In {
value,
collection,
span: span.clone(),
},
Type::Unknown(span),
)
},
<l:@L> <value:InOp> "in" <collection:Range> <r:@R> => {
let span = (context.span_from)(l, r);
context.contract.exprs.insert(
Expr::In {
value,
collection,
span: span.clone(),
},
Type::Unknown(span),
)
},
<Additive>,
}
Additive: ExprKey = {
<l:@L> <lhs:Additive> <op:AddOpOp> <rhs:Multiplicative> <r:@R> => {
let span = (context.span_from)(l, r);
context.contract.exprs.insert(
Expr::BinaryOp {
op,
lhs,
rhs,
span: span.clone(),
},
Type::Unknown(span),
)
},
<Multiplicative>,
}
AddOpOp: BinaryOp = {
"+" => BinaryOp::Add,
"-" => BinaryOp::Sub,
}
Multiplicative: ExprKey = {
<l:@L> <lhs:Multiplicative> <op:MultOpOp> <rhs:AsOp> <r:@R> => {
let span = (context.span_from)(l, r);
context.contract.exprs.insert(
Expr::BinaryOp {
op,
lhs,
rhs,
span: span.clone(),
},
Type::Unknown(span),
)
},
<AsOp>,
}
MultOpOp: BinaryOp = {
"*" => BinaryOp::Mul,
"/" => BinaryOp::Div,
"%" => BinaryOp::Mod,
}
AsOp: ExprKey = {
<l:@L> <value:AsOp> "as" <ty:TypeAtom> <r:@R> => {
let span = (context.span_from)(l, r);
context.contract.exprs.insert(
Expr::Cast {
value,
ty,
span: span.clone(),
},
Type::Unknown(span),
)
},
<UnaryOp>,
}
UnaryOpOp: UnaryOp = {
<l:@L> "+" <r:@R> => {
handler.emit_err(Error::Parse {
error: ParseError::UnsupportedLeadingPlus {
span: (context.span_from)(l, r),
},
});
UnaryOp::Error
},
"-" => UnaryOp::Neg,
"!" => UnaryOp::Not,
}
UnaryOp: ExprKey = {
<l:@L> <op:UnaryOpOp> <expr:UnaryOp> <r:@R> => {
let span = (context.span_from)(l, r);
context.contract.exprs.insert(
Expr::UnaryOp {
op,
expr,
span: span.clone(),
},
Type::Unknown(span),
)
},
<PostfixOp>,
}
PostfixOp: ExprKey = {
<l:@L> <expr:PostfixOp> "[" <index:Expr> "]" <r:@R> => {
let span = (context.span_from)(l, r);
context.contract.exprs.insert(
Expr::Index {
expr,
index,
span: span.clone(),
},
Type::Unknown(span),
)
},
<l:@L> <expr:PostfixOp> "[" "]" <r:@R> => {
let span = (context.span_from)(l, r);
handler.emit_err(Error::Parse {
error: ParseError::EmptyIndexAccess { span: span.clone() },
});
// Recover with a malformed expression
context
.contract
.exprs
.insert(Expr::Error(span.clone()), Type::Unknown(span))
},
<l:@L> <tuple:PostfixOp> "." <name:Ident> <r:@R> => {
context.parse_tuple_field_op_with_ident(tuple, name, (l, r))
},
<l:@L> <tuple:PostfixOp> "." <m:@L> <num_str:"int_lit"> <r:@R> => {
context.parse_tuple_field_op_with_int(handler, tuple, num_str, (l, m, r))
},
<l:@L> <tuple:PostfixOp> "." <m:@L> <num_str:"real_lit"> <r:@R> => {
context.parse_tuple_field_op_with_real(handler, tuple, num_str, (l, m, r))
},
<l:@L> <expr:PostfixOp> "'" <r:@R> => {
let span = (context.span_from)(l, r);
context.contract.exprs.insert(
Expr::UnaryOp {
op: UnaryOp::NextState,
expr,
span: span.clone(),
},
Type::Unknown(span),
)
},
<l:@L> <expr:PostfixOp> "!" <r:@R> => {
let span = (context.span_from)(l, r);
context.contract.exprs.insert(
Expr::UnaryOp {
op: UnaryOp::Unwrap,
expr,
span: span.clone(),
},
Type::Unknown(span),
)
},
<Term>,
}
Term: ExprKey = {
<e:TermInner> => {
let span = e.span().clone();
context.contract.exprs.insert(e, Type::Unknown(span))
},
<keys:MacroCallExpr> => keys.1,
CondExpr,
MatchExpr,
"(" <Expr> ")",
}
TermInner: Expr = {
<l:@L> <imm:Immediate> <r:@R> => {
let span = (context.span_from)(l, r);
if !context.experimental_types {
if let Immediate::Real(_) = imm {
handler.emit_err(Error::Parse {
error: ParseError::LiteralNotSupported {
kind: "real".to_string(),
span: span.clone(),
},
});
}
}
Expr::Immediate { value: imm, span }
},
GeneratorExpr,
LoopExpr,
IntrinsicCallExpr,
PredicateCallExpr,
ArrayExpr,
TupleExpr,
<l:@L> "nil" <m:@R> <r:@R> => Expr::Nil((context.span_from)(l, r)),
UnionExpr,
<l:@L> <path:Path> <r:@R> => Expr::Path(path, (context.span_from)(l, r)),
<StoragePath>,
}
GeneratorRange: (Ident, ExprKey) = {
<index:Ident> "in" <range:Range> => {
// Generators for `forall`/`exists` are always `int`s and need to be saved in the ephemeral
// list.
let mod_prefix = context.mod_prefix;
let _ = context
.current_pred()
.expect("adding an ephemeral to the predicate")
.insert_ephemeral(
mod_prefix,
&index,
Type::Primitive {
kind: PrimitiveKind::Int,
span: index.span.clone(),
},
);
(index, range)
}
}
GeneratorExpr: Expr = {
<l:@L> "forall" <gen_ranges:Sep1ListNoTrail<GeneratorRange, ",">>
<conditions: ("where" <Sep1ListNoTrail<Expr, ",">>)?>
"{" <body:Expr> "}" <r:@R> => {
Expr::Generator {
kind: GeneratorKind::ForAll,
gen_ranges,
conditions: conditions.unwrap_or_default(),
body,
span: (context.span_from)(l, r),
}
},
<l:@L> "exists" <gen_ranges:Sep1ListNoTrail<GeneratorRange, ",">>
<conditions: ("where" <Sep1ListNoTrail<Expr, ",">>)?>
"{" <body:Expr> "}" <r:@R> => {
Expr::Generator {
kind: GeneratorKind::Exists,
gen_ranges,
conditions: conditions.unwrap_or_default(),
body,
span: (context.span_from)(l, r),
}
}
}
LoopExpr: Expr = {
MapExpr,
}
MapExpr: Expr = {
<l:@L> "map" <param:Ident> "in" <range:Expr> "{" <body:Expr> "}" <r:@R> => {
Expr::Map {
param,
range,
body,
span: (context.span_from)(l, r),
}
}
}
CondBranch: (ExprKey, ExprKey) = {
<condition:Expr> "=>" <result:Expr> "," => (condition, result)
}
ElseBranch: ExprKey = {
"else" "=>" <else_result:Expr> ","? => else_result
}
CondExpr: ExprKey = {
<l:@L> "cond" "{" <cond_branches: (<CondBranch>)*> <else_branch: ElseBranch> "}" <r:@R> => {
// De-sugar a `cond` into a select chain.
//
// Build the select chain starting with the else branch and going up,
// hence the need to reverse the iterator below.
cond_branches
.iter()
.rev()
.fold(else_branch, |acc, (condition, result)| {
let span = (context.span_from)(l, r);
context.contract.exprs.insert(
Expr::Select {
condition: *condition,
then_expr: *result,
else_expr: acc,
span: span.clone(),
},
Type::Unknown(span),
)
})
}
}
MatchExpr: ExprKey = {
<l:@L> "match" <expr:Expr> "{" <match_branches:Sep1List<MatchBranch, ",">> "}" <r:@R> => {
let span = (context.span_from)(l, r);
context.contract.exprs.insert(
Expr::Match {
match_expr: expr,
match_branches,
else_branch: None,
span: span.clone(),
},
Type::Unknown(span),
)
},
<l:@L> "match" <expr:Expr> "{" <match_branches:(<MatchBranch> ",")*> <else_branch:MatchElse> "}" <r:@R> => {
let span = (context.span_from)(l, r);
context.contract.exprs.insert(
Expr::Match {
match_expr: expr,
match_branches,
else_branch: Some(else_branch),
span: span.clone(),
},
Type::Unknown(span),
)
},
}
MatchBranch: MatchBranch = {
<l:@L> <name:Path> <r:@R> <binding:("(" <Ident> ")")?> "=>" <expr:Expr> => {
MatchBranch {
name,
name_span: (context.span_from)(l, r),
binding,
constraints: Vec::default(),
expr,
}
},
// The block must contain a constraint, otherwise there's an ambiguity between a single
// expression in block `{ expr }` or a tuple literal `{ field_expr }`.
<l:@L> <name:Path> <r:@R> <binding:("(" <Ident> ")")?> "=>" "{"
<cdecls:(<Constraint> ";")+>
<expr:Expr>
"}" => {
let constraints = cdecls.into_iter().map(|ConstraintDecl { expr, .. }| expr).collect();
MatchBranch {
name,
name_span: (context.span_from)(l, r),
binding,
constraints,
expr
}
}
}
MatchElse: MatchElse = {
"else" "=>" <else_expr:Expr> ","? => {
MatchElse {
constraints: Vec::default(),
expr: else_expr,
}
},
"else" "=>" "{"
<cdecls:(<Constraint> ";")+>
<expr:Expr>
"}" => {
let constraints = cdecls.into_iter().map(|ConstraintDecl { expr, .. }| expr).collect();
MatchElse { constraints, expr }
}
}
MacroCallExpr: (CallKey, ExprKey) = {
<l:@L> <name:MacroPath> <tag:"macro_tag"?> <args:"macro_call_args"> <r:@L> => {
let call_key = context
.current_pred()
.expect("adding a macro call to the predicate")
.calls
.insert(name.clone());
let span = (context.span_from)(l, r);
let call_data = MacroCall {
name: name.clone(),
mod_path: context.mod_path.to_vec(),
args,
span: span.clone(),
parent_tag: tag.flatten(),
is_at_decl: false,
};
let call_expr_key = context.contract.exprs.insert(
Expr::MacroCall {
path: name,
call: call_key,
span: span.clone(),
},
Type::Unknown(span),
);
context
.macro_calls
.get_mut(
&context
.current_pred_key
.expect("adding a macro call to the predicate"),
)
.unwrap()
.insert(call_key, (call_expr_key, call_data));
(call_key, call_expr_key)
},
}
IntrinsicArg: ExprKey = {
Expr,
<l:@L> <s:"str_lit"> <r:@R> => {
// Only intrinsic args are allowed to be string literals. This is a bit hacky but probably
// fine for now.
let span = (context.span_from)(l, r);
context.contract.exprs.insert(
Expr::Immediate {
value: Immediate::String(s),
span: span.clone(),
},
Type::Unknown(span),
)
},
}
IntrinsicCallExpr: Expr = {
<l:@L> <name:IntrinsicName> "(" <args:SepList<IntrinsicArg, ",">> ")" <r:@R> => {
context.parse_intrinsic_call(handler, name, args, (l, r))
},
}
PredicateCallExpr: Expr = {
<l:@L> <predicate: Path> "@" "[" "]"
"(" <args:SepList<Expr, ",">> ")" <r:@R> => {
Expr::LocalPredicateCall {
predicate,
args,
span: (context.span_from)(l, r),
}
},
<l:@L> <interface: Path> "@" "[" <c_addr: Expr> "]"
"::" <predicate: Ident> "@" "[" <p_addr: Expr> "]"
"(" <args:SepList<Expr, ",">> ")" <r:@R> => {
Expr::ExternalPredicateCall {
interface,
c_addr,
predicate: predicate.name,
p_addr,
args,
span: (context.span_from)(l, r),
}
},
}
ArrayExpr: Expr = {
<l:@L> "[" <elements:SepList<Expr, ",">> "]" <r:@R> => {
context.build_array_expr(elements, (context.span_from)(l, r))
},
}
TupleExpr: Expr = {
<l:@L> "{" <fields:TupleExprFields> "}" <r:@R> => {
Expr::Tuple {
fields,
span: (context.span_from)(l, r),
}
},
<l:@L> "{" "}" <r:@R> => {
let span = (context.span_from)(l, r);
handler.emit_err(Error::Parse {
error: ParseError::EmptyTupleExpr { span: span.clone() },
});
Expr::Error(span)
},
}
TupleExprFields: Vec<(Option<Ident>, ExprKey)> = {
<id:Ident> ":" <expr:Expr> => {
// Special case for a single field with a field label which does not require the trailing
// comma to distinguish it from a block expression.
vec![(Some(id), expr)]
},
<expr:Expr> => {
// Special case for a single field which does not require the trailing
// comma to distinguish it from a block expression.
vec![(None, expr)]
},
<Sep1List<TupleExprField, ",">>,
}
TupleExprField: (Option<Ident>, ExprKey) = {
<id:(<Ident> ":")?> <expr:Expr> => (id, expr),
}
UnionExpr: Expr = {
<l:@L> <path:Path> <m:@R> "(" <value:Expr> ")" <r:@R> => {
Expr::UnionVariant {
path,
path_span: (context.span_from)(l, m),
value: Some(value),
span: (context.span_from)(l, r),
}
}
}
Range: ExprKey = {
<l:@L> <lb:Additive> ".." <ub:Additive> <r:@R> => {
let span = (context.span_from)(l, r);
context.contract.exprs.insert(
Expr::Range {
lb,
ub,
span: span.clone(),
},
Type::Unknown(span),
)
}
}
Path: String = PathWithLast<Ident>;
MacroPath: String = PathWithLast<IdentFromToken<"macro_name">>;
PathWithLast<Last>: String = {
<l:@L> "::" <els:(<Ident> "::")*> <last:Last> <r:@R> => {
let span = (context.span_from)(l, r);
context.parse_absolute_path(els, last, true, span)
},
<l:@L> <els:(<Ident> "::")*> <last:Last> <r:@R> => {
let span = (context.span_from)(l, r);
context.parse_relative_path(els, last, true, span)
},
}
StoragePath: Expr = {
<l:@L> "storage" "::" <name:Ident> <r:@R> => {
let span = (context.span_from)(l, r);
if !context.mod_path.is_empty() {
// `storage` blocks in sub-modules are not allowed
handler.emit_err(Error::Parse {
error: ParseError::StorageAccessMustBeTopLevel { span: span.clone() },
});
Expr::Error(span.clone())
} else {
Expr::LocalStorageAccess {
name: name.to_string(),
span,
}
}
},
<l:@L> <interface: Path> "@" "[" <address: Expr> "]"
"::" "storage" "::" <name:Ident> <r:@R> => {
Expr::ExternalStorageAccess {
interface,
address,
name: name.to_string(),
span: (context.span_from)(l, r),
}
},
}
Immediate: Immediate = {
<l:@L> <s:"int_lit"> <r:@R> => context.parse_int_immediate(handler, s, (l, r)),
<s:"real_lit"> => Immediate::Real(s.replace('_', "").parse().unwrap()),
"true" => Immediate::Bool(true),
"false" => Immediate::Bool(false),
};
Ident: Ident = {
<l:@L> <id:"ident"> <r:@R> => {
let name = id.0.to_string();
let span = (context.span_from)(l, r);
Ident {
name,
hygienic: id.1,
span,
}
}
}
IntrinsicName: Ident = {
<l:@L> <id:"intrinsic_name"> <r:@R> => Ident {
name: id,
hygienic: false,
span: (context.span_from)(l, r),
}
}
IdentFromToken<Tok>: Ident = {
<l:@L> <id:Tok> <r:@R> => Ident {
name: id.to_string(),
hygienic: false,
span: (context.span_from)(l, r),
}
}
/////////////////
// ASM Blocks ///
/////////////////
AsmBlockExpr: ExprKey = {
<l:@L> "asm" "(" <args:SepList<Expr, ",">> ")" "{" <ops:AsmOp*> "}" <r:@R> => {
let span = (context.span_from)(l, r);
context.contract.exprs.insert(
Expr::AsmBlock {
args,
ops,
span: span.clone(),
},
Type::Unknown(span),
)
}
}
AsmOp: AsmOp = {
<l:@L> <minus:"-"?> <imm:"int_lit"> <r:@R> => {
let span = (context.span_from)(l, r);
match context.parse_int_immediate(handler, imm, (l, r)) {
Immediate::Int(val) => {
AsmOp::Imm(if minus.is_some() { -val } else { val }, span.clone())
}
_ => {
handler.emit_err(Error::Parse {
error: ParseError::ExpectedIntegerLiteral { span: span.clone() },
});
AsmOp::Imm(0, span)
}
}
},
<op:Ident> => AsmOp::Op(op)
}
/////////////
/// Utils ///
/////////////
// List of zero or more Ty separated by Sep, allowing a trailing Sep.
SepList<Ty, Sep>: Vec<Ty> = {
<mut v: (<Ty> Sep)*> <e:Ty?> => {
if let Some(e) = e {
v.push(e);
}
v
}
}
// List of one or more Ty separated by Sep, allowing a trailing Sep. The separator is required in
// the single element case.
Sep1List<Ty, Sep>: Vec<Ty> = {
<mut v: (<Ty> Sep)+> <e:Ty?> => {
if let Some(e) = e {
v.push(e);
}
v
}
}
// List of one or more Ty separated by Sep, disallowing a trailing Sep.
Sep1ListNoTrail<Ty, Sep>: Vec<Ty> = {
<mut v: (<Ty> Sep)*> <e:Ty> => {
v.push(e);
v
}
}
pub(crate) TestDelegate: crate::parser::TestWrapper = {
"test_expr" <Expr> => crate::parser::TestWrapper::Expr(<>),
"test_range" <Range> => crate::parser::TestWrapper::Expr(<>),
"test_type" <Type> => crate::parser::TestWrapper::Type(<>),
"test_sv_type" <Type> => crate::parser::TestWrapper::Type(<>),
"test_ident" <Ident> => crate::parser::TestWrapper::Ident(<>),
"test_intrinsic" <IntrinsicName> => crate::parser::TestWrapper::Ident(<>),
"test_usetree" <UseTree> => crate::parser::TestWrapper::UseTree(<>),
}
/////////////
/// Lexer ///
/////////////
extern {
type Location = usize;
type Error = ParseError;
enum lexer::Token {
":" => lexer::Token::Colon,
"::" => lexer::Token::DoubleColon,
";" => lexer::Token::Semi,
"," => lexer::Token::Comma,
"=" => lexer::Token::Eq,
"?" => lexer::Token::QuestionMark,
":=" => lexer::Token::ColonEq,
"==" => lexer::Token::EqEq,
"!=" => lexer::Token::NotEq,
"<" => lexer::Token::Lt,
"<=" => lexer::Token::LtEq,
">" => lexer::Token::Gt,
">=" => lexer::Token::GtEq,
"&&" => lexer::Token::DoubleAmpersand,
"||" => lexer::Token::DoublePipe,
"*" => lexer::Token::Star,
"/" => lexer::Token::Div,
"%" => lexer::Token::Mod,
"+" => lexer::Token::Plus,
"++" => lexer::Token::PlusPlus,
"-" => lexer::Token::Minus,
"!" => lexer::Token::Bang,
"'" => lexer::Token::SingleQuote,
"(" => lexer::Token::ParenOpen,
")" => lexer::Token::ParenClose,
"{" => lexer::Token::BraceOpen,
"}" => lexer::Token::BraceClose,
"[" => lexer::Token::BracketOpen,
"]" => lexer::Token::BracketClose,
"->" => lexer::Token::Arrow,
"=>" => lexer::Token::HeavyArrow,
"." => lexer::Token::Dot,
".." => lexer::Token::TwoDots,
"|" => lexer::Token::Pipe,
"@" => lexer::Token::At,
"int_ty" => lexer::Token::Int,
"real_ty" => lexer::Token::Real,
"bool_ty" => lexer::Token::Bool,
"string_ty" => lexer::Token::String,
"b256_ty" => lexer::Token::B256,
"int_lit" => lexer::Token::IntLiteral(<String>),
"real_lit" => lexer::Token::RealLiteral(<String>),
"str_lit" => lexer::Token::StringLiteral(<String>),
"true" => lexer::Token::True,
"false" => lexer::Token::False,
"nil" => lexer::Token::Nil,
"if" => lexer::Token::If,
"else" => lexer::Token::Else,
"cond" => lexer::Token::Cond,
"match" => lexer::Token::Match,
"asm" => lexer::Token::Asm,
"use" => lexer::Token::Use,
"self" => lexer::Token::SelfTok,
"let" => lexer::Token::Let,
"const" => lexer::Token::Const,
"storage" => lexer::Token::Storage,
"interface" => lexer::Token::Interface,
"type" => lexer::Token::Type,
"union" => lexer::Token::Union,
"constraint" => lexer::Token::Constraint,
"as" => lexer::Token::As,
"predicate" => lexer::Token::Predicate,
"in" => lexer::Token::In,
"forall" => lexer::Token::ForAll,
"exists" => lexer::Token::Exists,
"map" => lexer::Token::Map,
"where" => lexer::Token::Where,
"ident" => lexer::Token::Ident(<(String, bool)>),
"intrinsic_name" => lexer::Token::IntrinsicName(<String>),
"macro" => lexer::Token::Macro,
"macro_name" => lexer::Token::MacroName(<String>),
"macro_param" => lexer::Token::MacroParam(<String>),
"macro_param_pack" => lexer::Token::MacroParamPack(<String>),
"macro_body" => lexer::Token::MacroBody(<lexer::MacroBody>),
"macro_call_args" => lexer::Token::MacroCallArgs(<lexer::MacroCallArgs>),
"macro_tag" => lexer::Token::MacroTag(<Option<usize>>),
"test_expr" => lexer::Token::TestMarkerExpr,
"test_range" => lexer::Token::TestMarkerRange,
"test_type" => lexer::Token::TestMarkerType,
"test_sv_type" => lexer::Token::TestMarkerSVType,
"test_ident" => lexer::Token::TestMarkerIdent,
"test_intrinsic" => lexer::Token::TestMarkerIntrinsic,
"test_usetree" => lexer::Token::TestMarkerUseTree,
}
}