use lalrpop_util::ParseError;
use super::pt::*;
use super::lexer::{Token, LexicalError};
use lalrpop_util::ErrorRecovery;
grammar<'input, 'err>(input: &'input str, file_no: usize , parser_errors: &'err mut Vec<ErrorRecovery<usize, Token<'input>, LexicalError>> );
pub SourceUnit: SourceUnit = {
SourceUnitPart* => SourceUnit(<>)
}
SourceUnitPart: SourceUnitPart = {
ContractDefinition => SourceUnitPart::ContractDefinition(<>),
PragmaDirective => SourceUnitPart::PragmaDirective(<>.into()),
ImportDirective => <>,
EnumDefinition => SourceUnitPart::EnumDefinition(<>),
StructDefinition => SourceUnitPart::StructDefinition(<>),
EventDefinition => SourceUnitPart::EventDefinition(<>),
ErrorDefinition => SourceUnitPart::ErrorDefinition(<>),
FunctionDefinition => SourceUnitPart::FunctionDefinition(<>),
VariableDefinition => SourceUnitPart::VariableDefinition(<>),
TypeDefinition => SourceUnitPart::TypeDefinition(<>),
Annotation => SourceUnitPart::Annotation(<>),
Using => SourceUnitPart::Using(<>),
<l:@L> ";" <r:@R> => SourceUnitPart::StraySemicolon(Loc::File(file_no, l, r)),
}
ImportPath: ImportPath = {
StringLiteral => ImportPath::Filename(<>),
SolIdentifierPath => ImportPath::Path(<>),
}
ImportDirective: SourceUnitPart = {
<l:@L> "import" <s:ImportPath> <r:@R> ";" => SourceUnitPart::ImportDirective(Import::Plain(s, Loc::File(file_no, l, r))),
<l:@L> "import" <s:ImportPath> "as" <id:SolIdentifier> <r:@R> ";" => SourceUnitPart::ImportDirective(Import::GlobalSymbol(s, id, Loc::File(file_no, l, r))),
<l:@L> "import" "*" "as" <id:SolIdentifier> <from:SolIdentifier> <s:ImportPath> <r:@R> ";" => {
if from.name != "from" {
let error = ErrorRecovery {
error: ParseError::User {
error: LexicalError::ExpectedFrom(from.loc, from.name)
},
dropped_tokens: Vec::new(),
};
parser_errors.push(error);
}
SourceUnitPart::ImportDirective(Import::GlobalSymbol(s, id, Loc::File(file_no, l, r)))
},
<l:@L> "import" "{" <rename:CommaOne<ImportRename>> "}" <from:SolIdentifier> <s:ImportPath> <r:@R> ";" =>? {
if from.name != "from" {
let error = ErrorRecovery {
error: ParseError::User {
error: LexicalError::ExpectedFrom(from.loc, from.name),
},
dropped_tokens: Vec::new(),
};
parser_errors.push(error);
}
Ok(SourceUnitPart::ImportDirective(Import::Rename(s,rename,Loc::File(file_no, l, r))))
},
}
ImportRename: (Identifier, Option<Identifier>) = {
<SolIdentifier> => (<>, None),
<from:SolIdentifier> "as" <to:SolIdentifier> => (from, Some(to)),
}
PragmaDirective: PragmaDirective = {
<l:@L> "pragma" <i:SolIdentifier> <s:Identifier> <r:@R> ";" => {
PragmaDirective::Identifier(Loc::File(file_no, l, r), Some(i), Some(s))
},
<l:@L> "pragma" <i:SolIdentifier> <s:StringLiteral> <r:@R> ";" => {
PragmaDirective::StringLiteral(Loc::File(file_no, l, r), i, s)
},
<l:@L> "pragma" <i:SolIdentifier> <comp:VersionComparator1+> <r:@R> ";" => {
PragmaDirective::Version(Loc::File(file_no, l, r), i, comp)
},
<l:@L> "pragma" <false_token:!> <r:@R> ";" => {
parser_errors.push(false_token);
PragmaDirective::Identifier(Loc::File(file_no, l, r), None, None)
}
}
VersionComparator1: VersionComparator = {
<l:@L> <left:VersionComparator1> "||" <right:VersionComparator> <r:@R> => VersionComparator::Or {
loc: Loc::File(file_no, l, r),
left: left.into(),
right: right.into()
},
VersionComparator
}
VersionComparator: VersionComparator = {
<l:@L> <version:Version> <r:@R> => VersionComparator::Plain {
loc: Loc::File(file_no, l, r),
version
},
<l:@L> <op:VersionOp> <version:Version> <r:@R> => VersionComparator::Operator {
loc: Loc::File(file_no, l, r),
op,
version
},
<l:@L> <from:Version> "-" <to:Version> <r:@R> => VersionComparator::Range {
loc: Loc::File(file_no, l, r),
from,
to
},
}
Version: Vec<String> = {
<major:number> <minors:("." <number>)*> => {
let mut v = minors;
v.insert(0, major);
v.into_iter().map(|(b,_)| b.to_string()).collect()
}
}
VersionOp: VersionOp = {
"=" => VersionOp::Exact,
">" => VersionOp::Greater,
">=" => VersionOp::GreaterEq,
"<" => VersionOp::Less,
"<=" => VersionOp::LessEq,
"~" => VersionOp::Tilde,
"^" => VersionOp::Caret,
"*" => VersionOp::Wildcard,
}
Type: Type = {
NoFunctionType,
FunctionType
}
NoFunctionType: Type = {
"bool" => Type::Bool,
"address" => Type::Address,
"address" "payable" => Type::AddressPayable,
// payable is only used as a cast in solc
"payable" => Type::Payable,
"string" => Type::String,
"bytes" => Type::DynamicBytes,
Uint => Type::Uint(<>),
Int => Type::Int(<>),
Bytes => Type::Bytes(<>),
// prior to 0.8.0 `byte` used to be an alias for `bytes1`
"byte" => Type::Bytes(1),
<l:@L> "mapping" "(" <k:Precedence0> <key_name:SolIdentifier?> "=>" <v:Precedence0> <value_name:SolIdentifier?> ")" <r:@R> => {
Type::Mapping {
loc: Loc::File(file_no, l, r),
key: Box::new(k),
key_name,
value: Box::new(v),
value_name }
},
}
FunctionType: Type = {
"function" <params:ParameterList>
<attributes:FunctionTypeAttribute*>
<returns:("returns" <ParameterList> <FunctionTypeAttribute*>)?> => {
Type::Function {
params,
attributes,
returns,
}
}
}
FunctionTypeAttribute: FunctionAttribute = {
<l:@L> "immutable" <r:@R> => FunctionAttribute::Immutable(Loc::File(file_no, l, r)),
Mutability => FunctionAttribute::Mutability(<>),
Visibility => FunctionAttribute::Visibility(<>),
}
ArrayDimension: Option<Expression> = {
"[" "]" => None,
"[" <Expression> "]" => Some(<>)
}
StorageLocation: StorageLocation = {
<l:@L> "memory" <r:@R> => StorageLocation::Memory(Loc::File(file_no, l, r)),
<l:@L> "storage" <r:@R> => StorageLocation::Storage(Loc::File(file_no, l, r)),
<l:@L> "calldata" <r:@R> => StorageLocation::Calldata(Loc::File(file_no, l, r)),
}
Identifier: Identifier = {
<l:@L> <n:identifier> <r:@R> => Identifier{loc: Loc::File(file_no, l, r), name: n.to_string()}
}
SolIdentifier: Identifier = {
<l:@L> <n:identifier> <r:@R> => Identifier{loc: Loc::File(file_no, l, r), name: n.to_string()},
<l:@L> "switch" <r:@R> => Identifier{loc: Loc::File(file_no, l, r), name: "switch".to_string()},
<l:@L> "leave" <r:@R> => Identifier{loc: Loc::File(file_no, l, r), name: "leave".to_string()},
<l:@L> "case" <r:@R> => Identifier{loc: Loc::File(file_no, l, r), name: "case".to_string()},
<l:@L> "default" <r:@R> => Identifier{loc: Loc::File(file_no, l, r), name: "default".to_string()},
<l:@L> "revert" <r:@R> => Identifier{loc: Loc::File(file_no, l, r), name: "revert".to_string()},
}
SolAnnotation: Identifier = {
<l:@L> <a:annotation> <r:@R> => Identifier{loc: Loc::File(file_no, l, r), name: a.to_string()},
}
SolIdentifierOrError: Option<Identifier> = {
SolIdentifier => Some(<>),
! => {
parser_errors.push(<>);
None
}
}
SolNoRevertIdentifier: Identifier = {
<l:@L> <n:identifier> <r:@R> => Identifier{loc: Loc::File(file_no, l, r), name: n.to_string()},
<l:@L> "switch" <r:@R> => Identifier{loc: Loc::File(file_no, l, r), name: "switch".to_string()},
<l:@L> "leave" <r:@R> => Identifier{loc: Loc::File(file_no, l, r), name: "leave".to_string()},
<l:@L> "case" <r:@R> => Identifier{loc: Loc::File(file_no, l, r), name: "case".to_string()},
<l:@L> "default" <r:@R> => Identifier{loc: Loc::File(file_no, l, r), name: "default".to_string()},
}
SolIdentifierPath: IdentifierPath = {
<l:@L> <e:SolIdentifier> <v:("." <SolIdentifier>)*> <r:@R> => {
let mut v = v;
v.insert(0, e);
IdentifierPath { loc: Loc::File(file_no, l, r), identifiers: v }
}
}
VariableDeclaration: VariableDeclaration = {
<l:@L> <ty:Precedence0> <storage:StorageLocation?> <name:SolIdentifierOrError> <r:@R> => VariableDeclaration {
loc: Loc::File(file_no, l, r), ty, storage, name
},
}
StructDefinition: Box<StructDefinition> = {
<l:@L> "struct" <name:SolIdentifierOrError> "{" <fields:(<VariableDeclaration> ";")*> "}" <r:@R> => {
Box::new(StructDefinition{loc: Loc::File(file_no, l, r), name, fields})
}
}
ContractTy: ContractTy = {
<l:@L> "abstract" "contract" <r:@R> => ContractTy::Abstract(Loc::File(file_no, l, r)),
<l:@L> "contract" <r:@R> => ContractTy::Contract(Loc::File(file_no, l, r)),
<l:@L> "interface" <r:@R> => ContractTy::Interface(Loc::File(file_no, l, r)),
<l:@L> "library" <r:@R> => ContractTy::Library(Loc::File(file_no, l, r)),
}
ContractPart: ContractPart = {
StructDefinition => ContractPart::StructDefinition(<>),
EventDefinition => ContractPart::EventDefinition(<>),
ErrorDefinition => ContractPart::ErrorDefinition(<>),
EnumDefinition => ContractPart::EnumDefinition(<>),
VariableDefinition => ContractPart::VariableDefinition(<>),
FunctionDefinition => ContractPart::FunctionDefinition(<>),
ModifierDefinition => ContractPart::FunctionDefinition(<>),
ConstructorDefinition => ContractPart::FunctionDefinition(<>),
TypeDefinition => ContractPart::TypeDefinition(<>),
Annotation => ContractPart::Annotation(<>),
<l:@L> ";" <r:@R> => ContractPart::StraySemicolon(Loc::File(file_no, l, r)),
Using => ContractPart::Using(<>),
}
Bases: Vec<Base> = {
=> Vec::new(),
"is" <CommaOne<Base>> => <>,
}
Base: Base = {
<l:@L> <name:SolIdentifierPath> <args:("(" <Comma<Expression>> ")")?> <r:@R> => Base {
loc: Loc::File(file_no, l, r),
name,
args
}
}
ContractDefinition: Box<ContractDefinition> = {
<l:@L> <ty:ContractTy> <name:SolIdentifierOrError> <base:Bases>
"{" <parts:(<ContractPart>)*> "}" <r:@R> => {
Box::new(ContractDefinition{loc: Loc::File(file_no, l, r), ty, name, base, parts})
}
}
EventParameter: EventParameter = {
<l:@L> <ty:Precedence0> <i:"indexed"?> <name:SolIdentifier?> <r:@R> => EventParameter{
loc: Loc::File(file_no, l, r), ty, indexed: i.is_some(), name
}
}
ErrorParameter: ErrorParameter = {
<l:@L> <ty:Precedence0> <name:SolIdentifier?> <r:@R> => ErrorParameter{
loc: Loc::File(file_no, l, r), ty, name
}
}
EventDefinition: Box<EventDefinition> = {
<l:@L> "event" <name:SolIdentifierOrError> "(" <v:Comma<EventParameter>> ")" <a:"anonymous"?> <r:@R> ";" => {
Box::new(EventDefinition{
loc: Loc::File(file_no, l, r), name, fields: v, anonymous: a.is_some()
})
},
}
ErrorDefinition: Box<ErrorDefinition> = {
<l:@L> <keyword:NoFunctionTyPrecedence0> <name:SolIdentifierOrError> "(" <fields:Comma<ErrorParameter>> ")" <r:@R> ";" => {
Box::new(ErrorDefinition{
loc: Loc::File(file_no, l, r), keyword, name, fields
})
}
}
EnumDefinition: Box<EnumDefinition> = {
<l:@L> "enum" <name:SolIdentifierOrError> "{" <values:Comma<SolIdentifierOrError>> "}" <r:@R> => {
Box::new(EnumDefinition{loc: Loc::File(file_no, l, r), name, values})
}
}
VariableDefinition: Box<VariableDefinition> = {
<l:@L> <ty:NoFunctionTyPrecedence0> <attrs:VariableAttribute*> <name:SolIdentifierOrError> <e:("=" <Expression>)?> <r:@R> ";" => {
Box::new(VariableDefinition{
loc: Loc::File(file_no, l, r), ty, attrs, name, initializer: e,
})
},
<l:@L> <ty:NoFunctionTyPrecedence0> <attrs:VariableAttribute*> <name:SolIdentifier> <false_token:!> <r:@R> ";" => {
parser_errors.push (false_token);
Box::new(VariableDefinition{
loc: Loc::File(file_no, l, r), ty, attrs, name: Some(name), initializer: None,
})
},
// attributes cause shift-reduce errors with function type, since a function type contract variable can be declare as
// contract foo {
// // f is a variable with internal visibility referencing an external function
// function() external internal f;
// }
<l:@L> <ty:FunctionTyPrecedence0> <name:SolIdentifier> <e:("=" <Expression>)?> <r:@R> ";" => {
Box::new(VariableDefinition{
loc: Loc::File(file_no, l, r), ty, attrs: Vec::new(), name: Some(name), initializer: e,
})
}
}
TypeDefinition: Box<TypeDefinition> = {
<l:@L> "type" <name:SolIdentifier> "is" <ty:Precedence0> <r:@R> ";" => {
Box::new(TypeDefinition{
loc: Loc::File(file_no, l, r), name, ty
})
},
}
Annotation: Box<Annotation> = {
<l:@L> <id:SolAnnotation> "(" <value:Expression> ")" <r:@R> => {
Box::new(Annotation {
loc: Loc::File(file_no, l, r), id, value: Some(value)
})
}
}
Visibility: Visibility = {
<l:@L> "public" <r:@R> => Visibility::Public(Some(Loc::File(file_no, l, r))),
<l:@L> "external" <r:@R> => Visibility::External(Some(Loc::File(file_no, l, r))),
<l:@L> "internal" <r:@R> => Visibility::Internal(Some(Loc::File(file_no, l, r))),
<l:@L> "private" <r:@R> => Visibility::Private(Some(Loc::File(file_no, l, r))),
}
StorageType: StorageType = {
<l:@L> "persistent" <r:@R> => StorageType::Persistent(Some(Loc::File(file_no, l, r))),
<l:@R> "temporary" <r:@R> => StorageType::Temporary(Some(Loc::File(file_no, l, r))),
<l:@R> "instance" <r:@R> => StorageType::Instance(Some(Loc::File(file_no, l, r))),
}
VariableAttribute: VariableAttribute = {
Visibility => VariableAttribute::Visibility(<>),
StorageType => VariableAttribute::StorageType(<>),
<l:@L> "constant" <r:@R> => VariableAttribute::Constant(Loc::File(file_no, l, r)),
<l:@L> "immutable" <r:@R> => VariableAttribute::Immutable(Loc::File(file_no, l, r)),
<l:@L> "override" <r:@R> => VariableAttribute::Override(Loc::File(file_no, l, r), Vec::new()),
<l:@L> "override" "(" <list:CommaOne<SolIdentifierPath>> ")" <r:@R> => {
VariableAttribute::Override(Loc::File(file_no, l, r), list)
}
}
Expression: Expression = {
Precedence14,
}
Precedence14: Expression = {
<a:@L> <l:Precedence13> "=" <r:Precedence14> <b:@R> => Expression::Assign(Loc::File(file_no, a, b), Box::new(l), Box::new(r)),
<a:@L> <l:Precedence13> "|=" <r:Precedence14> <b:@R> => Expression::AssignOr(Loc::File(file_no, a, b), Box::new(l), Box::new(r)),
<a:@L> <l:Precedence13> "^=" <r:Precedence14> <b:@R> => Expression::AssignXor(Loc::File(file_no, a, b), Box::new(l), Box::new(r)),
<a:@L> <l:Precedence13> "&=" <r:Precedence14> <b:@R> => Expression::AssignAnd(Loc::File(file_no, a, b), Box::new(l), Box::new(r)),
<a:@L> <l:Precedence13> "<<=" <r:Precedence14> <b:@R> => Expression::AssignShiftLeft(Loc::File(file_no, a, b), Box::new(l), Box::new(r)),
<a:@L> <l:Precedence13> ">>=" <r:Precedence14> <b:@R> => Expression::AssignShiftRight(Loc::File(file_no, a, b), Box::new(l), Box::new(r)),
<a:@L> <l:Precedence13> "+=" <r:Precedence14> <b:@R> => Expression::AssignAdd(Loc::File(file_no, a, b), Box::new(l), Box::new(r)),
<a:@L> <l:Precedence13> "-=" <r:Precedence14> <b:@R> => Expression::AssignSubtract(Loc::File(file_no, a, b), Box::new(l), Box::new(r)),
<a:@L> <l:Precedence13> "*=" <r:Precedence14> <b:@R> => Expression::AssignMultiply(Loc::File(file_no, a, b), Box::new(l), Box::new(r)),
<a:@L> <l:Precedence13> "/=" <r:Precedence14> <b:@R> => Expression::AssignDivide(Loc::File(file_no, a, b), Box::new(l), Box::new(r)),
<a:@L> <l:Precedence13> "%=" <r:Precedence14> <b:@R> => Expression::AssignModulo(Loc::File(file_no, a, b), Box::new(l), Box::new(r)),
<a:@L> <c:Precedence13> "?" <l:Precedence14> ":" <r:Precedence14> <b:@R> => {
Expression::ConditionalOperator(Loc::File(file_no, a, b), Box::new(c), Box::new(l), Box::new(r))
},
Precedence13,
}
Precedence13: Expression = {
<a:@L> <l:Precedence13> "||" <r:Precedence12> <b:@R> => Expression::Or(Loc::File(file_no, a, b), Box::new(l), Box::new(r)),
Precedence12,
}
Precedence12: Expression = {
<a:@L> <l:Precedence12> "&&" <r:Precedence11> <b:@R> => Expression::And(Loc::File(file_no, a, b), Box::new(l), Box::new(r)),
Precedence11,
}
Precedence11: Expression = {
<a:@L> <l:Precedence11> "==" <r:Precedence10> <b:@R> => Expression::Equal(Loc::File(file_no, a, b), Box::new(l), Box::new(r)),
<a:@L> <l:Precedence11> "!=" <r:Precedence10> <b:@R> => Expression::NotEqual(Loc::File(file_no, a, b), Box::new(l), Box::new(r)),
Precedence10,
}
Precedence10: Expression = {
<a:@L> <l:Precedence10> "<" <r:Precedence9> <b:@R> => Expression::Less(Loc::File(file_no, a, b), Box::new(l), Box::new(r)),
<a:@L> <l:Precedence10> ">" <r:Precedence9> <b:@R> => Expression::More(Loc::File(file_no, a, b), Box::new(l), Box::new(r)),
<a:@L> <l:Precedence10> "<=" <r:Precedence9> <b:@R> => Expression::LessEqual(Loc::File(file_no, a, b), Box::new(l), Box::new(r)),
<a:@L> <l:Precedence10> ">=" <r:Precedence9> <b:@R> => Expression::MoreEqual(Loc::File(file_no, a, b), Box::new(l), Box::new(r)),
Precedence9,
}
Precedence9: Expression = {
<a:@L> <l:Precedence9> "|" <r:Precedence8> <b:@R> => Expression::BitwiseOr(Loc::File(file_no, a, b), Box::new(l), Box::new(r)),
Precedence8,
}
Precedence8: Expression = {
<a:@L> <l:Precedence8> "^" <r:Precedence7> <b:@R> => Expression::BitwiseXor(Loc::File(file_no, a, b), Box::new(l), Box::new(r)),
Precedence7,
}
Precedence7: Expression = {
<a:@L> <l:Precedence7> "&" <r:Precedence6> <b:@R> => Expression::BitwiseAnd(Loc::File(file_no, a, b), Box::new(l), Box::new(r)),
Precedence6,
}
Precedence6: Expression = {
<a:@L> <l:Precedence6> "<<" <r:Precedence5> <b:@R> => Expression::ShiftLeft(Loc::File(file_no, a, b), Box::new(l), Box::new(r)),
<a:@L> <l:Precedence6> ">>" <r:Precedence5> <b:@R> => Expression::ShiftRight(Loc::File(file_no, a, b), Box::new(l), Box::new(r)),
Precedence5,
}
Precedence5: Expression = {
<a:@L> <l:Precedence5> "+" <r:Precedence4> <b:@R> => Expression::Add(Loc::File(file_no, a, b), Box::new(l), Box::new(r)),
<a:@L> <l:Precedence5> "-" <r:Precedence4> <b:@R> => Expression::Subtract(Loc::File(file_no, a, b), Box::new(l), Box::new(r)),
Precedence4,
}
Precedence4: Expression = {
<a:@L> <l:Precedence4> "*" <r:Precedence3> <b:@R> => Expression::Multiply(Loc::File(file_no, a, b), Box::new(l), Box::new(r)),
<a:@L> <l:Precedence4> "/" <r:Precedence3> <b:@R> => Expression::Divide(Loc::File(file_no, a, b), Box::new(l), Box::new(r)),
<a:@L> <l:Precedence4> "%" <r:Precedence3> <b:@R> => Expression::Modulo(Loc::File(file_no, a, b), Box::new(l), Box::new(r)),
Precedence3,
}
Precedence3: Expression = {
<a:@L> <l:Precedence2> "**" <r:Precedence3> <b:@R> => Expression::Power(Loc::File(file_no, a, b), Box::new(l), Box::new(r)),
Precedence2,
}
Precedence2: Expression = {
<a:@L> "!" <e:Precedence2> <b:@R> => Expression::Not(Loc::File(file_no, a, b), Box::new(e)),
<a:@L> "~" <e:Precedence2> <b:@R> => Expression::BitwiseNot(Loc::File(file_no, a, b), Box::new(e)),
<a:@L> "delete" <e:Precedence2> <b:@R> => Expression::Delete(Loc::File(file_no, a, b), Box::new(e)),
<a:@L> "new" <call:Precedence2> <b:@R> => Expression::New(Loc::File(file_no, a, b), Box::new(call)),
<a:@L> "++" <e:Precedence2> <b:@R> => Expression::PreIncrement(Loc::File(file_no, a, b), Box::new(e)),
<a:@L> "--" <e:Precedence2> <b:@R> => Expression::PreDecrement(Loc::File(file_no, a, b), Box::new(e)),
<a:@L> "+" <e:Precedence2> <b:@R> => Expression::UnaryPlus(Loc::File(file_no, a, b), Box::new(e)),
<a:@L> "-" <e:Precedence2> <b:@R> => Expression::Negate(Loc::File(file_no, a, b), Box::new(e)),
<l:@L> "revert" <r:@R> => Expression::Variable(Identifier{loc: Loc::File(file_no, l, r), name: "revert".to_string()}),
Precedence1
}
Precedence1: Expression = {
<l:@L> <n:number> <unit:SolNoRevertIdentifier?> <r:@R> => {
let integer: String = n.0.chars().filter(|v| *v != '_').collect();
let exp: String = n.1.chars().filter(|v| *v != '_').collect();
Expression::NumberLiteral(Loc::File(file_no, l, r), integer, exp, unit)
},
<l:@L> <n:rational> <unit:SolNoRevertIdentifier?> <r:@R> => {
let integer: String = n.0.chars().filter(|v| *v != '_').collect();
let fraction: String = n.1.chars().filter(|v| *v != '_').collect();
let exp: String = n.2.chars().filter(|v| *v != '_').collect();
Expression::RationalNumberLiteral(Loc::File(file_no, l, r), integer, fraction, exp, unit)
},
<l:@L> <n:hexnumber> <unit:SolNoRevertIdentifier?> <r:@R> => {
Expression::HexNumberLiteral(Loc::File(file_no, l, r), n.to_owned(), unit)
},
Precedence0
}
NamedArgument: NamedArgument = {
<l:@L> <name:SolIdentifier> ":" <expr:Expression> <r:@R> => {
NamedArgument{ loc: Loc::File(file_no, l, r), name, expr }
},
<l:@L> "address" <ar:@R> ":" <expr:Expression> <r:@R> => {
let name = Identifier { loc: Loc::File(file_no, l, ar), name: "address".into() };
NamedArgument{ loc: Loc::File(file_no, l, r), name, expr }
},
}
FunctionCall: Expression = {
<a:@L> <i:FunctionCallPrecedence> "(" <v:Comma<Expression>> ")" <b:@R> => {
Expression::FunctionCall(Loc::File(file_no, a, b), Box::new(i), v)
},
<l:@L> <i:FunctionCallPrecedence> <block:BlockStatement> <r:@R> => {
Expression::FunctionCallBlock(Loc::File(file_no, l, r), Box::new(i), Box::new(block))
},
<a:@L> <i:FunctionCallPrecedence> "(" "{" <v:Comma<NamedArgument>> "}" ")" <b:@R> => {
Expression::NamedFunctionCall(Loc::File(file_no, a, b), Box::new(i), v)
},
}
FunctionCallPrecedence: Expression = {
NoFunctionTyPrecedence0,
<l:@L> "type" <r:@R> => Expression::Variable(Identifier{loc: Loc::File(file_no, l, r), name: "type".to_string()}),
}
Precedence0: Expression = {
NoFunctionTyPrecedence0,
FunctionTyPrecedence0
}
FunctionTyPrecedence0: Expression = {
<l:@L> <ty:FunctionType> <r:@R> => Expression::Type(Loc::File(file_no, l, r), ty),
}
NoFunctionTyPrecedence0: Expression = {
<a:@L> <e:Precedence1> "++" <b:@R> => Expression::PostIncrement(Loc::File(file_no, a, b), Box::new(e)),
<a:@L> <e:Precedence1> "--" <b:@R> => Expression::PostDecrement(Loc::File(file_no, a, b), Box::new(e)),
<FunctionCall> => <>,
<a:@L> <e:Precedence1> "[" <i:Expression?> "]" <b:@R> => Expression::ArraySubscript(Loc::File(file_no, a, b), Box::new(e), i.map(Box::new)),
<a:@L> <e:Precedence1> "[" <l:Expression?> ":" <r:Expression?> "]" <b:@R> => Expression::ArraySlice(Loc::File(file_no, a, b), Box::new(e), l.map(Box::new), r.map(Box::new)),
<a:@L> <e:Precedence1> "." <i:SolIdentifier> <b:@R> => Expression::MemberAccess(Loc::File(file_no, a, b), Box::new(e), i),
// Solidity has ".address" members on external function types. Address is a keyword, so special casing needed
<a:@L> <e:Precedence1> "." <al:@L> "address" <b:@R> => {
Expression::MemberAccess(Loc::File(file_no, a, b), Box::new(e),
Identifier { loc: Loc::File(file_no, al, b), name: "address".to_string() })
},
<a:@L> <e:Precedence1> "." <al:@L> "new" <b:@R> => {
Expression::MemberAccess(Loc::File(file_no, a, b), Box::new(e),
Identifier { loc: Loc::File(file_no, al, b), name: "new".to_string() })
},
<l:@L> <ty:NoFunctionType> <r:@R> => Expression::Type(Loc::File(file_no, l, r), ty),
<a:@L> "[" <v:Comma<Expression>> "]" <b:@R> => {
Expression::ArrayLiteral(Loc::File(file_no, a, b), v)
},
<SolNoRevertIdentifier> => Expression::Variable(<>),
<l:@L> <a:NamedParameterList> <r:@R> => {
if a.len() == 1 {
if let Some(Parameter{ ty, storage: None, name: None, .. }) = &a[0].1 {
// this means "(" Expression ")"
return Expression::Parenthesis(ty.loc(), Box::new(ty.clone()));
}
}
Expression::List(Loc::File(file_no, l, r), a)
},
LiteralExpression
}
LiteralExpression: Expression = {
<a:@L> "true" <b:@R> => Expression::BoolLiteral(Loc::File(file_no, a, b), true),
<a:@L> "false" <b:@R> => Expression::BoolLiteral(Loc::File(file_no, a, b), false),
<StringLiteral+> => Expression::StringLiteral(<>),
<HexLiteral+> => Expression::HexLiteral(<>),
<l:@L> <a:address> <r:@R> => {
let v = a.to_string();
let address_len = a.len() - 9;
Expression::AddressLiteral(Loc::File(file_no, l, r), a.chars().skip(8).filter(|c| *c != '"' && *c != '\'').collect())
},
}
StringLiteral: StringLiteral = {
<l:@L> <s:string> <r:@R> => {
StringLiteral{ loc: Loc::File(file_no, l, r), unicode: s.0, string: s.1.to_string() }
}
}
HexLiteral: HexLiteral = {
<l:@L> <s:hexstring> <r:@R> => {
let v = s.to_string();
let hex_len = v.len() - 5;
HexLiteral{ loc: Loc::File(file_no, l, r), hex: v.chars().skip(4).filter(|c| *c != '_' && *c != '"' && *c != '\'').collect() }
}
}
// A parameter list is used for function arguments and return.
// Destructure statements utilize NamedParameter, not Parameter
Parameter: Parameter = {
<l:@L> <annotation:ParameterAnnotation?> <ty:Precedence0> <storage:StorageLocation?> <name:SolIdentifier?> <r:@R> => {
let loc = Loc::File(file_no, l, r);
Parameter{loc, annotation, ty, storage, name}
}
}
OptParameter: (Loc, Option<Parameter>) = {
<l:@L> <p:Parameter?> <r:@R> => (Loc::File(file_no, l, r), p),
}
ParameterList: Vec<(Loc, Option<Parameter>)> = {
"(" ")" => Vec::new(),
"(" <l:@L> <p:Parameter> <r:@R> ")" => vec!((Loc::File(file_no, l, r), Some(p))),
"(" <CommaTwo<OptParameter>> ")" => <>,
"(" <false_token:!> ")"=> {
parser_errors.push(<>);
Vec::new()
}
}
ParameterAnnotation: Annotation = {
<l:@L> <id:SolAnnotation> <r:@R> => Annotation {
loc: Loc::File(file_no, l, r), id, value: None,
}
}
NamedParameter: Parameter = {
<l:@L> <ty:Precedence0> <storage:StorageLocation?> <name:SolIdentifier> <r:@R> => {
let loc = Loc::File(file_no, l, r);
Parameter{ loc, annotation: None, ty, storage, name: Some(name) }
},
<l:@L> <ty:Expression> <r:@R> => {
let loc = Loc::File(file_no, l, r);
Parameter{ loc, annotation: None, ty, storage: None, name: None }
}
}
OptNamedParameter: (Loc, Option<Parameter>) = {
<l:@L> <p:NamedParameter?> <r:@R> => (Loc::File(file_no, l, r), p),
}
NamedParameterList: Vec<(Loc, Option<Parameter>)> = {
"(" ")" => Vec::new(),
"(" <l:@L> <p:NamedParameter> <r:@R> ")" => vec!((Loc::File(file_no, l, r), Some(p))),
"(" <CommaTwo<OptNamedParameter>> ")" => <>,
"(" <false_token:!> ")"=> {
parser_errors.push(<>);
Vec::new()
}
}
Mutability: Mutability = {
<l:@L> "pure" <r:@R> => Mutability::Pure(Loc::File(file_no, l, r)),
<l:@L> "constant" <r:@R> => Mutability::Constant(Loc::File(file_no, l, r)),
<l:@L> "view" <r:@R> => Mutability::View(Loc::File(file_no, l, r)),
<l:@L> "payable" <r:@R> => Mutability::Payable(Loc::File(file_no, l, r)),
}
FunctionAttribute: FunctionAttribute = {
Mutability => FunctionAttribute::Mutability(<>),
Visibility => FunctionAttribute::Visibility(<>),
<l:@L> "immutable" <r:@R> => FunctionAttribute::Immutable(Loc::File(file_no, l, r)),
<l:@L> "virtual" <r:@R> => FunctionAttribute::Virtual(Loc::File(file_no, <>)),
<l:@L> "override" <r:@R> => FunctionAttribute::Override(Loc::File(file_no, <>), Vec::new()),
<l:@L> "override" "(" <list:CommaOne<SolIdentifierPath>> ")" <r:@R> => {
FunctionAttribute::Override(Loc::File(file_no, l, r), list)
},
<l:@L> <base:Base> <r:@R> => FunctionAttribute::BaseOrModifier(Loc::File(file_no, l, r), base),
<l:@L> <false_token:!> <r:@R> => {
parser_errors.push(false_token);
FunctionAttribute::Error(Loc::File(file_no, l ,r))
},
}
// Modifier and function have special case
FunctionTy: FunctionTy = {
"fallback" => FunctionTy::Fallback,
"receive" => FunctionTy::Receive,
"constructor" => FunctionTy::Constructor,
}
BlockStatementOrSemiColon: Option<Statement> = {
<@L> <@R> ";" => None,
BlockStatement => Some(<>),
}
returns: Option<Loc> = {
"returns" => None,
<l:@L> "return" <r:@R> => Some(Loc::File(file_no, l, r)),
}
// Modifiers can't have attributes or return values, but we parse them anyway so we can give nice
// error messages. The parameter list is optional
ModifierDefinition: Box<FunctionDefinition> = {
<l:@L> "modifier" <nl:@L> <name:SolIdentifier> <nr:@R> <params:ParameterList?>
<attributes:FunctionAttribute*>
<returns:(returns ParameterList)?> <pr:@R> <body:BlockStatementOrSemiColon> <r:@R> => {
let params = params.unwrap_or(Vec::new());
let (return_not_returns, returns) = returns.unwrap_or((None, Vec::new()));
Box::new(FunctionDefinition{
loc_prototype: Loc::File(file_no, l, pr),
loc: Loc::File(file_no, l, r),
ty: FunctionTy::Modifier,
name: Some(name),
name_loc: Loc::File(file_no, nl, nr),
params,
attributes,
return_not_returns,
returns,
body,
})
},
}
ConstructorDefinition: Box<FunctionDefinition> = {
<l:@L> <ty:FunctionTy> <nl:@L> <name:SolIdentifier?> <nr:@R> <params:ParameterList>
<attributes:FunctionAttribute*>
<returns:(returns ParameterList)?> <pr:@R> <body:BlockStatementOrSemiColon> <r:@R> => {
let (return_not_returns, returns) = returns.unwrap_or((None, Vec::new()));
Box::new(FunctionDefinition{
loc_prototype: Loc::File(file_no, l, pr),
loc: Loc::File(file_no, l, r),
ty,
name,
name_loc: Loc::File(file_no, nl, nr),
params,
attributes,
return_not_returns,
returns,
body,
})
},
}
FunctionDefinition: Box<FunctionDefinition> = {
<l:@L> "function" <nl:@L> <name:SolIdentifierOrError> <nr:@R> <params:ParameterList>
<attributes:FunctionAttribute*>
<returns:(returns ParameterList)?> <pr:@R> <body:BlockStatementOrSemiColon> <r:@R> => {
let (return_not_returns, returns) = returns.unwrap_or((None, Vec::new()));
Box::new(FunctionDefinition{
loc_prototype: Loc::File(file_no, l, pr),
loc: Loc::File(file_no, l, r),
ty: FunctionTy::Function,
name,
name_loc: Loc::File(file_no, nl, nr),
params,
attributes,
return_not_returns,
returns,
body,
})
},
// Old-style fallback function without name. Sema will give a nice error message
// with some instructions how to update your syntax
<l:@L> <ft:FunctionType> <pr:@R> <body:BlockStatementOrSemiColon> <r:@R> => {
match ft {
// we're dropping the trailing attributes, but this production is for
// generating an error messages
Type::Function { params, attributes, returns, .. } => {
Box::new(FunctionDefinition{
loc_prototype: Loc::File(file_no, l, pr),
loc: Loc::File(file_no, l, r),
ty: FunctionTy::Function,
name: None,
name_loc: Loc::File(file_no, l, pr),
params,
attributes,
return_not_returns: None,
returns: returns.map(|(v, _)| v).unwrap_or_default(),
body,
})
},
_ => unreachable!(),
}
}
}
Using: Box<Using> = {
<l:@L> "using" <list:UsingList> "for" <ty:Precedence0> <global:SolIdentifier?> <r:@R> ";" => Box::new(Using {
loc: Loc::File(file_no, l, r),
list,
ty: Some(ty),
global,
}),
<l:@L> "using" <list:UsingList> "for" "*" <global:SolIdentifier?> <r:@R> ";" => Box::new(Using {
loc: Loc::File(file_no, l, r),
list,
ty: None,
global,
}),
<l:@L> "using" <false_token:!> <r:@R> ";" => {
parser_errors.push(false_token);
let list = UsingList::Error;
let global = None;
Box::new(Using {
loc: Loc::File(file_no, l, r),
list,
ty: None,
global,
})
},
}
UsingList: UsingList = {
SolIdentifierPath => UsingList::Library(<>),
"{" <Comma<UsingFunction>> "}" => UsingList::Functions(<>),
}
UsingFunction: UsingFunction = {
<l:@L> <path:SolIdentifierPath> <oper:UserDefinedOperator?> <r:@R> => {
UsingFunction { loc: Loc::File(file_no, l, r), path, oper }
}
}
UserDefinedOperator: UserDefinedOperator = {
"as" "&" => UserDefinedOperator::BitwiseAnd,
"as" "~" => UserDefinedOperator::BitwiseNot,
"as" "|" => UserDefinedOperator::BitwiseOr,
"as" "^" => UserDefinedOperator::BitwiseXor,
"as" "+" => UserDefinedOperator::Add,
"as" "/" => UserDefinedOperator::Divide,
"as" "%" => UserDefinedOperator::Modulo,
"as" "*" => UserDefinedOperator::Multiply,
"as" "-" => UserDefinedOperator::Subtract,
"as" "==" => UserDefinedOperator::Equal,
"as" ">" => UserDefinedOperator::More,
"as" ">=" => UserDefinedOperator::MoreEqual,
"as" "<" => UserDefinedOperator::Less,
"as" "<=" => UserDefinedOperator::LessEqual,
"as" "!=" => UserDefinedOperator::NotEqual,
}
BlockStatement: Statement = {
<l:@L> "{" <statements:Statement*> "}" <r:@R> => {
Statement::Block { loc: Loc::File(file_no, l, r), unchecked: false, statements }
},
<l:@L> "{" <v:CommaOne<NamedArgument>> "}" <r:@R> => Statement::Args(Loc::File(file_no, l, r), v),
}
OpenStatement: Statement = {
<l:@L> "if" "(" <cond:Expression> ")" <body:Statement> <r:@R> => {
Statement::If(Loc::File(file_no, l, r), cond, Box::new(body), None)
},
<l:@L> "if" "(" <cond:Expression> ")" <body:ClosedStatement> "else" <o:OpenStatement> <r:@R> => {
Statement::If(Loc::File(file_no, l, r), cond, Box::new(body), Some(Box::new(o)))
},
<l:@L> "for" "(" <b:SimpleStatement?> ";" <c:Expression?> ";" <n:Expression?> ")" <block:OpenStatement> <r:@R> => {
Statement::For(Loc::File(file_no, l, r), b.map(Box::new), c.map(Box::new), n.map(Box::new), Some(Box::new(block)))
},
<l:@L> "while" "(" <e:Expression> ")" <b:OpenStatement> <r:@R> => {
Statement::While(Loc::File(file_no, l, r), e, Box::new(b))
}
}
ClosedStatement: Statement = {
NonIfStatement,
<l:@L> "if" "(" <cond:Expression> ")" <body:ClosedStatement> "else" <o:ClosedStatement> <r:@R> => {
Statement::If(Loc::File(file_no, l, r), cond, Box::new(body), Some(Box::new(o)))
},
<l:@L> "while" "(" <e:Expression> ")" <b:ClosedStatement> <r:@R> => {
Statement::While(Loc::File(file_no, l, r), e, Box::new(b))
},
<l:@L> "for" "(" <b:SimpleStatement?> ";" <c:Expression?> ";" <n:Expression?> ")" <block:ClosedStatement> <r:@R> => {
Statement::For(Loc::File(file_no, l, r), b.map(Box::new), c.map(Box::new), n.map(Box::new), Some(Box::new(block)))
},
<l:@L> "for" "(" <b:SimpleStatement?> ";" <c:Expression?> ";" <n:Expression?> ")" <r:@R> ";" => {
Statement::For(Loc::File(file_no, l, r), b.map(Box::new), c.map(Box::new), n.map(Box::new), None)
}
}
Statement: Statement = {
OpenStatement,
ClosedStatement,
<l:@L> <false_token:!> <r:@R> => {
parser_errors.push(false_token);
Statement::Error(Loc::File(file_no, l, r))
},
}
SimpleStatement: Statement = {
<l:@L> <v:VariableDeclaration> <e:("=" <Expression>)?> <r:@R> => {
Statement::VariableDefinition(Loc::File(file_no, l, r), v, e)
},
<l:@L> <e:Expression> <r:@R> => {
Statement::Expression(Loc::File(file_no, l, r), e)
}
}
CatchClause: CatchClause = {
<l:@L> "catch" <param:("(" <Parameter> ")")?> <block:BlockStatement> <r:@R> => {
CatchClause::Simple(Loc::File(file_no, l, r), param, block)
},
<l:@L> "catch" <id:SolIdentifier> "(" <param:Parameter> ")" <block:BlockStatement> <r:@R> => {
CatchClause::Named(Loc::File(file_no, l, r), id, param, block)
}
}
TryReturns: (Vec<(Loc, Option<Parameter>)>, Box<Statement>) = {
"returns" <list:ParameterList> <ok:BlockStatement> => (list, Box::new(ok))
}
TryExpression: Expression = {
<a:@L> "new" <call:FunctionCall> <b:@R> => Expression::New(Loc::File(file_no, a, b), Box::new(call)),
FunctionCall
}
NonIfStatement: Statement = {
BlockStatement => <>,
<l:@L> "unchecked" "{" <statements:Statement*> "}" <r:@R> => {
Statement::Block { loc: Loc::File(file_no, l, r), unchecked: true, statements }
},
<l:@L> "assembly" <dialect:StringLiteral?> <flags:AssemblyFlags?> <block:YulBlock> <r:@R> => {
Statement::Assembly {
loc: Loc::File(file_no, l, r),
dialect: dialect.map(|dialect| dialect.to_owned()),
flags: flags.map(|flag| flag.to_owned()),
block
}
},
<SimpleStatement> ";" => <>,
<l:@L> "do" <b:Statement> "while" "(" <e:Expression> ")" <r:@R> ";" => {
Statement::DoWhile(Loc::File(file_no, l, r), Box::new(b), e)
},
<l:@L> "continue" <r:@R> ";" => {
Statement::Continue(Loc::File(file_no, l, r))
},
<l:@L> "break" <r:@R> ";" => {
Statement::Break(Loc::File(file_no, l, r))
},
<l:@L> "return" <r:@R> ";" => {
Statement::Return(Loc::File(file_no, l, r), None)
},
<l:@L> "return" <e:Expression> <r:@R> ";" => {
Statement::Return(Loc::File(file_no, l, r), Some(e))
},
<l:@L> "try" <e:TryExpression> <returns:TryReturns?> <clauses:CatchClause+> <r:@R> => {
Statement::Try(Loc::File(file_no, l, r), e, returns, clauses)
},
<l:@L> "emit" <ty:FunctionCall> <r:@R> ";" => {
Statement::Emit(Loc::File(file_no, l, r), ty)
},
<l:@L> "revert" <error:SolIdentifierPath?> "(" <v:Comma<Expression>> ")" <r:@R> ";" => {
Statement::Revert(Loc::File(file_no, l, r), error, v)
},
<l:@L> "revert" <error:SolIdentifierPath?> "(" "{" <v:Comma<NamedArgument>> "}" ")" <r:@R> ";" => {
Statement::RevertNamedArgs(Loc::File(file_no, l, r), error, v)
},
}
AssemblyFlags: Vec<StringLiteral> = {
"(" <CommaOne<StringLiteral>> ")" => <>,
}
YulIdentifier: Identifier = {
<l:@L> <n:identifier> <r:@R> => Identifier{loc: Loc::File(file_no, l, r), name: n.to_string()},
<l:@L> "return" <r:@R> => Identifier{loc: Loc::File(file_no, l, r), name: "return".to_string()},
<l:@L> "revert" <r:@R> => Identifier{loc: Loc::File(file_no, l, r), name: "revert".to_string()},
<l:@L> "address" <r:@L> => Identifier{loc: Loc::File(file_no, l, r), name: "address".to_string()},
<l:@L> "abstract" <r:@L> => Identifier{loc: Loc::File(file_no, l, r), name: "abstract".to_string()},
<l:@L> "anonymous" <r:@L> => Identifier{loc: Loc::File(file_no, l, r), name: "anonymous".to_string()},
<l:@L> "as" <r:@L> => Identifier{loc: Loc::File(file_no, l, r), name: "as".to_string()},
<l:@L> "assembly" <r:@L> => Identifier{loc: Loc::File(file_no, l, r), name: "assembly".to_string()},
<l:@L> "bool" <r:@R> => Identifier{loc: Loc::File(file_no, l, r), name: "bool".to_string()},
<l:@L> "byte" <r:@L> => Identifier{loc: Loc::File(file_no, l, r), name: "byte".to_string()},
<l:@L> "bytes" <r:@L> => Identifier{loc: Loc::File(file_no, l, r), name: "bytes".to_string()},
<l:@L> "catch" <r:@L> => Identifier{loc: Loc::File(file_no, l, r), name: "catch".to_string()},
<l:@L> "calldata" <r:@L> => Identifier{loc: Loc::File(file_no, l, r), name: "calldata".to_string()},
<l:@L> "constant" <r:@L> => Identifier{loc: Loc::File(file_no, l, r), name: "constant".to_string()},
<l:@L> "constructor" <r:@L> => Identifier{loc: Loc::File(file_no, l, r), name: "constructor".to_string()},
<l:@L> "contract" <r:@L> => Identifier{loc: Loc::File(file_no, l, r), name: "contract".to_string()},
<l:@L> "do" <r:@L> => Identifier{loc: Loc::File(file_no, l, r), name: "do".to_string()},
<l:@L> "else" <r:@L> => Identifier{loc: Loc::File(file_no, l, r), name: "else".to_string()},
<l:@L> "enum" <r:@L> => Identifier{loc: Loc::File(file_no, l, r), name: "enum".to_string()},
<l:@L> "emit" <r:@L> => Identifier{loc: Loc::File(file_no, l, r), name: "emit".to_string()},
<l:@L> "event" <r:@L> => Identifier{loc: Loc::File(file_no, l, r), name: "event".to_string()},
<l:@L> "external" <r:@L> => Identifier{loc: Loc::File(file_no, l, r), name: "external".to_string()},
<l:@L> "fallback" <r:@L> => Identifier{loc: Loc::File(file_no, l, r), name: "fallback".to_string()},
<l:@L> "indexed" <r:@L> => Identifier{loc: Loc::File(file_no, l, r), name: "indexed".to_string()},
<l:@L> "interface" <r:@L> => Identifier{loc: Loc::File(file_no, l, r), name: "interface".to_string()},
<l:@L> "internal" <r:@L> => Identifier{loc: Loc::File(file_no, l, r), name: "internal".to_string()},
<l:@L> "immutable" <r:@L> => Identifier{loc: Loc::File(file_no, l, r), name: "immutable".to_string()},
<l:@L> "import" <r:@L> => Identifier{loc: Loc::File(file_no, l, r), name: "import".to_string()},
<l:@L> "is" <r:@L> => Identifier{loc: Loc::File(file_no, l, r), name: "is".to_string()},
<l:@L> "library" <r:@L> => Identifier{loc: Loc::File(file_no, l, r), name: "library".to_string()},
<l:@L> "mapping" <r:@L> => Identifier{loc: Loc::File(file_no, l, r), name: "mapping".to_string()},
<l:@L> "memory" <r:@L> => Identifier{loc: Loc::File(file_no, l, r), name: "memory".to_string()},
<l:@L> "modifier" <r:@L> => Identifier{loc: Loc::File(file_no, l, r), name: "modifier".to_string()},
<l:@L> "new" <r:@L> => Identifier{loc: Loc::File(file_no, l, r), name: "new".to_string()},
<l:@L> "override" <r:@L> => Identifier{loc: Loc::File(file_no, l, r), name: "override".to_string()},
<l:@L> "payable" <r:@L> => Identifier{loc: Loc::File(file_no, l, r), name: "payable".to_string()},
<l:@L> "public" <r:@L> => Identifier{loc: Loc::File(file_no, l, r), name: "public".to_string()},
<l:@L> "pragma" <r:@L> => Identifier{loc: Loc::File(file_no, l, r), name: "pragma".to_string()},
<l:@L> "private" <r:@L> => Identifier{loc: Loc::File(file_no, l, r), name: "private".to_string()},
<l:@L> "pure" <r:@L> => Identifier{loc: Loc::File(file_no, l, r), name: "pure".to_string()},
<l:@L> "receive" <r:@L> => Identifier{loc: Loc::File(file_no, l, r), name: "receive".to_string()},
<l:@L> "returns" <r:@L> => Identifier{loc: Loc::File(file_no, l, r), name: "returns".to_string()},
<l:@L> "storage" <r:@L> => Identifier{loc: Loc::File(file_no, l, r), name: "storage".to_string()},
<l:@L> "struct" <r:@L> => Identifier{loc: Loc::File(file_no, l, r), name: "struct".to_string()},
<l:@L> "throw" <r:@L> => Identifier{loc: Loc::File(file_no, l, r), name: "throw".to_string()},
<l:@L> "try" <r:@L> => Identifier{loc: Loc::File(file_no, l, r), name: "try".to_string()},
<l:@L> "using" <r:@L> => Identifier{loc: Loc::File(file_no, l, r), name: "using".to_string()},
<l:@L> "view" <r:@L> => Identifier{loc: Loc::File(file_no, l, r), name: "view".to_string()},
<l:@L> "virtual" <r:@L> => Identifier{loc: Loc::File(file_no, l, r), name: "virtual".to_string()},
<l:@L> "while" <r:@L> => Identifier{loc: Loc::File(file_no, l, r), name: "while".to_string()},
<l:@L> <i:Int> <r:@L> => Identifier{loc: Loc::File(file_no, l, r), name: format!("int{}", i)},
<l:@L> <u:Uint> <r:@L> => Identifier{loc: Loc::File(file_no, l, r), name: format!("uint{}", u)},
<l:@L> "string" <r:@L> => Identifier{loc: Loc::File(file_no, l, r), name: "string".to_string()},
<l:@L> "unchecked" <r:@L> => Identifier{loc: Loc::File(file_no, l, r), name: "unchecked".to_string()},
}
YulStatement: YulStatement = {
<YulBlock> => YulStatement::Block(<>),
<YulVariableDeclaration> => <>,
<YulAssignment> => <>,
<YulFunctionCall> => YulStatement::FunctionCall(Box::new(<>)),
<YulIf> => <>,
<YulFor> => <>,
<YulSwitch> => <>,
<YulFunctionDefinition> => <>,
<l:@L> "leave" <r:@R> => {
YulStatement::Leave(Loc::File(file_no, l, r))
},
<l:@L> "break" <r:@R> => {
YulStatement::Break(Loc::File(file_no, l, r))
},
<l:@L> "continue" <r:@R> => {
YulStatement::Continue(Loc::File(file_no, l, r))
},
<l:@L><false_token:!><r:@R> => {
parser_errors.push(false_token);
YulStatement::Error(Loc::File(file_no, l ,r))
},
}
YulBlock: YulBlock = {
<l:@L> "{" <statements:YulStatement*> "}" <r:@R> => YulBlock{loc: Loc::File(file_no, l, r), statements},
}
YulLiteral: YulExpression = {
<a:@L> "true" <t_type:(":" <YulIdentifier>)?> <b:@R> => YulExpression::BoolLiteral(Loc::File(file_no, a, b), true, t_type),
<a:@L> "false" <t_type:(":" <YulIdentifier>)?> <b:@R> => YulExpression::BoolLiteral(Loc::File(file_no, a, b), false, t_type),
<l:@L> <n:number> <t_type:(":" <YulIdentifier>)?> <r:@R> => {
let integer: String = n.0.chars().filter(|v| *v != '_').collect();
let exp: String = n.1.chars().filter(|v| *v != '_').collect();
YulExpression::NumberLiteral(Loc::File(file_no, l, r), integer, exp, t_type)
},
<l:@L> <n:hexnumber> <t_type:(":" <YulIdentifier>)?> <r:@R> => {
YulExpression::HexNumberLiteral(Loc::File(file_no, l, r), n.to_owned(), t_type)
},
<l:@L> <s:hexstring> <t_type:(":" <YulIdentifier>)?> <r:@R> => {
let v = s.to_string();
let hex_len = v.len() - 5;
YulExpression::HexStringLiteral(HexLiteral{ loc: Loc::File(file_no, l, r), hex: v.chars().skip(4).filter(|c| *c != '_' && *c != '"' && *c != '\'').collect()},
t_type)
},
<str:StringLiteral> <t_type:(":" <YulIdentifier>)?> => {
YulExpression::StringLiteral(str, t_type)
},
}
YulFunctionCall: YulFunctionCall = {
<l:@L> <id:YulIdentifier> "(" <arguments:Comma<YulExpression>> ")" <r:@R> => {
YulFunctionCall{loc: Loc::File(file_no, l, r), id, arguments}
}
}
YulPath: YulExpression = {
<YulIdentifier> => YulExpression::Variable(<>),
<l:@L> <array:YulPath> "." <member:YulIdentifier> <r:@R> => {
YulExpression::SuffixAccess(Loc::File(file_no, l, r), Box::new(array), member)
},
}
YulExpression: YulExpression = {
<YulPath> => <>,
<YulFunctionCall> => YulExpression::FunctionCall(Box::new(<>)),
<YulLiteral> => <>,
}
YulFunctionDefinition: YulStatement = {
<l:@L> "function" <name: YulIdentifier> "(" <params: YulTypedIdentifierList?> ")" <returns:("->" <YulTypedIdentifierList>)?>
<body: YulBlock> <r:@R> => {
YulStatement::FunctionDefinition(Box::new(YulFunctionDefinition {
loc: Loc::File(file_no, l, r),
id: name,
params: params.unwrap_or_default(),
returns: returns.unwrap_or_default(),
body
}))
}
}
YulTypedIdentifierList: Vec<YulTypedIdentifier> = {
CommaOne<<YulType>> => <>
}
YulType: YulTypedIdentifier = {
<l:@L> <var_name:YulIdentifier> ":" <var_type:YulIdentifier> <r:@R> => {
YulTypedIdentifier {
loc: Loc::File(file_no, l, r),
id: var_name, ty: Some(var_type)
}
},
<l:@L> <id:YulIdentifier> <r:@R> => {
YulTypedIdentifier{loc: Loc::File(file_no, l, r), id, ty: None}
},
}
YulSwitch: YulStatement = {
<l:@L> "switch" <condition:YulExpression> <default:YulSwitchDefault> <r:@R> =>
YulStatement::Switch(YulSwitch{
loc: Loc::File(file_no, l, r),
condition,
cases: Vec::new(),
default: Some(default)
}),
<l:@L> "switch" <condition:YulExpression> <cases:YulSwitchCase+> <default:YulSwitchDefault?> <r:@R> =>
YulStatement::Switch(YulSwitch{
loc: Loc::File(file_no, l, r),
condition,
cases,
default
}),
}
YulSwitchDefault: YulSwitchOptions = {
<l:@L> "default" <body:YulBlock> <r:@R> => {
YulSwitchOptions::Default(Loc::File(file_no, l, r), body)
}
}
YulSwitchCase: YulSwitchOptions = {
<l:@L> "case" <case:YulLiteral> <body:YulBlock> <r:@R> => {
YulSwitchOptions::Case(Loc::File(file_no, l, r), case, body)
}
}
YulFor: YulStatement = {
<l:@L> "for" <init:YulBlock> <cond:YulExpression> <post_iter:YulBlock> <body:YulBlock> <r:@R> => {
YulStatement::For(YulFor{
loc: Loc::File(file_no, l, r),
init_block: init,
condition: cond,
post_block: post_iter,
execution_block: body
})
},
}
YulIf: YulStatement = {
<l:@L> "if" <cond:YulExpression> <body:YulBlock> <r:@R> => {
YulStatement::If(Loc::File(file_no, l, r), cond, body)
},
}
YulAssignment: YulStatement = {
<l:@L> <paths:CommaOne<YulPath>> ":=" <expr:YulExpression> <r:@R> => {
YulStatement::Assign(Loc::File(file_no, l, r), paths, expr)
}
}
YulVariableDeclaration: YulStatement = {
<l:@L> "let" <names:YulTypedIdentifierList> <expr:(":=" <YulExpression>)?> <r:@R> => {
YulStatement::VariableDeclaration(Loc::File(file_no, l, r), names, expr)
}
}
Comma<T>: Vec<T> = {
=> Vec::new(),
CommaOne<T> => <>,
};
CommaOne<T>: Vec<T> = {
<e:T> <v:("," <T>)*> => {
let mut v = v;
v.insert(0, e);
v
}
};
CommaTwo<T>: Vec<T> = {
<e:T> <v:("," <T>)+> => {
let mut v = v;
v.insert(0, e);
v
}
};
extern {
type Location = usize;
type Error = LexicalError;
enum Token<'input> {
identifier => Token::Identifier(<&'input str>),
annotation => Token::Annotation(<&'input str>),
string => Token::StringLiteral(<bool>, <&'input str>),
hexstring => Token::HexLiteral(<&'input str>),
address => Token::AddressLiteral(<&'input str>),
number => Token::Number(<&'input str>, <&'input str>),
rational => Token::RationalNumber(<&'input str>, <&'input str>, <&'input str>),
hexnumber => Token::HexNumber(<&'input str>),
";" => Token::Semicolon,
"{" => Token::OpenCurlyBrace,
"}" => Token::CloseCurlyBrace,
"(" => Token::OpenParenthesis,
")" => Token::CloseParenthesis,
"=" => Token::Assign,
"==" => Token::Equal,
"=>" => Token::Arrow,
"->" => Token::YulArrow,
"|=" => Token::BitwiseOrAssign,
"^=" => Token::BitwiseXorAssign,
"&=" => Token::BitwiseAndAssign,
"<<=" => Token::ShiftLeftAssign,
">>=" => Token::ShiftRightAssign,
"+=" => Token::AddAssign,
"-=" => Token::SubtractAssign,
"*=" => Token::MulAssign,
"/=" => Token::DivideAssign,
"%=" => Token::ModuloAssign,
"?" => Token::Question,
":" => Token::Colon,
":=" => Token::ColonAssign,
"||" => Token::Or,
"&&" => Token::And,
"!=" => Token::NotEqual,
"<" => Token::Less,
"<=" => Token::LessEqual,
">" => Token::More,
">=" => Token::MoreEqual,
"|" => Token::BitwiseOr,
"&" => Token::BitwiseAnd,
"^" => Token::BitwiseXor,
"<<" => Token::ShiftLeft,
">>" => Token::ShiftRight,
"+" => Token::Add,
"-" => Token::Subtract,
"*" => Token::Mul,
"/" => Token::Divide,
"%" => Token::Modulo,
"**" => Token::Power,
"!" => Token::Not,
"~" => Token::BitwiseNot,
"++" => Token::Increment,
"--" => Token::Decrement,
"[" => Token::OpenBracket,
"]" => Token::CloseBracket,
"." => Token::Member,
"," => Token::Comma,
Uint => Token::Uint(<u16>),
Int => Token::Int(<u16>),
Bytes => Token::Bytes(<u8>),
"byte" => Token::Byte,
"struct" => Token::Struct,
"memory" => Token::Memory,
"calldata" => Token::Calldata,
"storage" => Token::Storage,
"import" => Token::Import,
"contract" => Token::Contract,
"pragma" => Token::Pragma,
"bool" => Token::Bool,
"address" => Token::Address,
"string" => Token::String,
"bytes" => Token::DynamicBytes,
"delete" => Token::Delete,
"new" => Token::New,
"interface" => Token::Interface,
"library" => Token::Library,
"event" => Token::Event,
"enum" => Token::Enum,
"type" => Token::Type,
"public" => Token::Public,
"private" => Token::Private,
"external" => Token::External,
"internal" => Token::Internal,
"constant" => Token::Constant,
"true" => Token::True,
"false" => Token::False,
"pure" => Token::Pure,
"view" => Token::View,
"payable" => Token::Payable,
"constructor" => Token::Constructor,
"function" => Token::Function,
"returns" => Token::Returns,
"return" => Token::Return,
"revert" => Token::Revert,
"if" => Token::If,
"for" => Token::For,
"while" => Token::While,
"else" => Token::Else,
"do" => Token::Do,
"continue" => Token::Continue,
"break" => Token::Break,
"throw" => Token::Throw,
"emit" => Token::Emit,
"anonymous" => Token::Anonymous,
"indexed" => Token::Indexed,
"mapping" => Token::Mapping,
"try" => Token::Try,
"catch" => Token::Catch,
"receive" => Token::Receive,
"fallback" => Token::Fallback,
"as" => Token::As,
"is" => Token::Is,
"abstract" => Token::Abstract,
"virtual" => Token::Virtual,
"override" => Token::Override,
"using" => Token::Using,
"modifier" => Token::Modifier,
"immutable" => Token::Immutable,
"unchecked" => Token::Unchecked,
"assembly" => Token::Assembly,
"let" => Token::Let,
"leave" => Token::Leave,
"switch" => Token::Switch,
"case" => Token::Case,
"default" => Token::Default,
"persistent" => Token::Persistent,
"temporary" => Token::Temporary,
"instance" => Token::Instance,
}
}