diff --git a/docs/Solidity.g4 b/docs/Solidity.g4 deleted file mode 100644 index dee054492..000000000 --- a/docs/Solidity.g4 +++ /dev/null @@ -1,487 +0,0 @@ -// Copyright 2020 Gonçalo Sá -// Copyright 2016-2019 Federico Bond -// Licensed under the MIT license. See LICENSE file in the project root for details. - -// This grammar is much less strict than what Solidity currently parses -// to allow this to pass with older versions of Solidity. - -grammar Solidity; - -sourceUnit - : (pragmaDirective | importDirective | structDefinition | enumDefinition | functionDefinition | contractDefinition)* EOF ; - -pragmaDirective - : 'pragma' pragmaName ( ~';' )* ';' ; - -pragmaName - : identifier ; - -importDirective - : 'import' StringLiteralFragment ('as' identifier)? ';' - | 'import' ('*' | identifier) ('as' identifier)? 'from' StringLiteralFragment ';' - | 'import' '{' importDeclaration ( ',' importDeclaration )* '}' 'from' StringLiteralFragment ';' ; - -importDeclaration - : identifier ('as' identifier)? ; - -contractDefinition - : 'abstract'? ( 'contract' | 'interface' | 'library' ) identifier - ( 'is' inheritanceSpecifier (',' inheritanceSpecifier )* )? - '{' contractPart* '}' ; - -inheritanceSpecifier - : userDefinedTypeName ( '(' expressionList? ')' )? ; - -contractPart - : stateVariableDeclaration - | usingForDeclaration - | structDefinition - | modifierDefinition - | functionDefinition - | eventDefinition - | enumDefinition ; - -stateVariableDeclaration - : typeName - ( PublicKeyword | InternalKeyword | PrivateKeyword | ConstantKeyword | ImmutableKeyword | overrideSpecifier )* - identifier ('=' expression)? ';' ; - -overrideSpecifier : 'override' ( '(' userDefinedTypeName (',' userDefinedTypeName)* ')' )? ; - -usingForDeclaration - : 'using' identifier 'for' ('*' | typeName) ';' ; - -structDefinition - : 'struct' identifier - '{' ( variableDeclaration ';' (variableDeclaration ';')* )? '}' ; - -modifierDefinition - : 'modifier' identifier parameterList? ( VirtualKeyword | overrideSpecifier )* ( ';' | block ) ; - -functionDefinition - : functionDescriptor parameterList modifierList returnParameters? ( ';' | block ) ; - -functionDescriptor - : 'function' ( identifier | ReceiveKeyword | FallbackKeyword )? - | ConstructorKeyword - | FallbackKeyword - | ReceiveKeyword ; - -returnParameters - : 'returns' parameterList ; - -modifierList - : ( modifierInvocation | stateMutability | ExternalKeyword - | PublicKeyword | InternalKeyword | PrivateKeyword | VirtualKeyword | overrideSpecifier )* ; - -modifierInvocation - : identifier ( '(' expressionList? ')' )? ; - -eventDefinition - : 'event' identifier eventParameterList AnonymousKeyword? ';' ; - -enumDefinition - : 'enum' identifier '{' enumValue? (',' enumValue)* '}' ; - -enumValue - : identifier ; - -parameterList - : '(' ( parameter (',' parameter)* )? ')' ; - -parameter - : typeName storageLocation? identifier? ; - -eventParameterList - : '(' ( eventParameter (',' eventParameter)* )? ')' ; - -eventParameter - : typeName IndexedKeyword? identifier? ; - -variableDeclaration - : typeName storageLocation? identifier ; - -typeName - : elementaryTypeName - | userDefinedTypeName - | mapping - | typeName '[' expression? ']' - | functionTypeName ; - -userDefinedTypeName - : identifier ( '.' identifier )* ; - -mapping - : 'mapping' '(' mappingKey '=>' typeName ')' ; - -mappingKey - : elementaryTypeName - | userDefinedTypeName ; - -functionTypeName - : 'function' parameterList modifierList returnParameters? ; - -storageLocation - : 'memory' | 'storage' | 'calldata'; - -stateMutability - : PureKeyword | ConstantKeyword | ViewKeyword | PayableKeyword ; - -block - : '{' statement* '}' ; - -statement - : ifStatement - | tryStatement - | whileStatement - | forStatement - | block - | inlineAssemblyStatement - | doWhileStatement - | continueStatement - | breakStatement - | returnStatement - | throwStatement - | emitStatement - | simpleStatement ; - -expressionStatement - : expression ';' ; - -ifStatement - : 'if' '(' expression ')' statement ( 'else' statement )? ; - -tryStatement : 'try' expression returnParameters? block catchClause+ ; - -// In reality catch clauses still are not processed as below -// the identifier can only be a set string: "Error". But plans -// of the Solidity team include possible expansion so we'll -// leave this as is, befitting with the Solidity docs. -catchClause : 'catch' ( identifier? parameterList )? block ; - -whileStatement - : 'while' '(' expression ')' statement ; - -forStatement - : 'for' '(' ( simpleStatement | ';' ) ( expressionStatement | ';' ) expression? ')' statement ; - -simpleStatement - : ( variableDeclarationStatement | expressionStatement ) ; - -inlineAssemblyStatement - : 'assembly' StringLiteralFragment? assemblyBlock ; - -doWhileStatement - : 'do' statement 'while' '(' expression ')' ';' ; - -continueStatement - : 'continue' ';' ; - -breakStatement - : 'break' ';' ; - -returnStatement - : 'return' expression? ';' ; - -// throw is no longer supported by latest Solidity. -throwStatement - : 'throw' ';' ; - -emitStatement - : 'emit' functionCall ';' ; - -// 'var' is no longer supported by latest Solidity. -variableDeclarationStatement - : ( 'var' identifierList | variableDeclaration | '(' variableDeclarationList ')' ) ( '=' expression )? ';'; - -variableDeclarationList - : variableDeclaration? (',' variableDeclaration? )* ; - -identifierList - : '(' ( identifier? ',' )* identifier? ')' ; - -elementaryTypeName - : 'address' PayableKeyword? | 'bool' | 'string' | 'var' | Int | Uint | 'byte' | Byte | Fixed | Ufixed ; - -Int - : 'int' | 'int8' | 'int16' | 'int24' | 'int32' | 'int40' | 'int48' | 'int56' | 'int64' | 'int72' | 'int80' | 'int88' | 'int96' | 'int104' | 'int112' | 'int120' | 'int128' | 'int136' | 'int144' | 'int152' | 'int160' | 'int168' | 'int176' | 'int184' | 'int192' | 'int200' | 'int208' | 'int216' | 'int224' | 'int232' | 'int240' | 'int248' | 'int256' ; - -Uint - : 'uint' | 'uint8' | 'uint16' | 'uint24' | 'uint32' | 'uint40' | 'uint48' | 'uint56' | 'uint64' | 'uint72' | 'uint80' | 'uint88' | 'uint96' | 'uint104' | 'uint112' | 'uint120' | 'uint128' | 'uint136' | 'uint144' | 'uint152' | 'uint160' | 'uint168' | 'uint176' | 'uint184' | 'uint192' | 'uint200' | 'uint208' | 'uint216' | 'uint224' | 'uint232' | 'uint240' | 'uint248' | 'uint256' ; - -Byte - : 'bytes' | 'bytes1' | 'bytes2' | 'bytes3' | 'bytes4' | 'bytes5' | 'bytes6' | 'bytes7' | 'bytes8' | 'bytes9' | 'bytes10' | 'bytes11' | 'bytes12' | 'bytes13' | 'bytes14' | 'bytes15' | 'bytes16' | 'bytes17' | 'bytes18' | 'bytes19' | 'bytes20' | 'bytes21' | 'bytes22' | 'bytes23' | 'bytes24' | 'bytes25' | 'bytes26' | 'bytes27' | 'bytes28' | 'bytes29' | 'bytes30' | 'bytes31' | 'bytes32' ; - -Fixed - : 'fixed' | ( 'fixed' [0-9]+ 'x' [0-9]+ ) ; - -Ufixed - : 'ufixed' | ( 'ufixed' [0-9]+ 'x' [0-9]+ ) ; - -expression - : expression ('++' | '--') - | 'new' typeName - | expression '[' expression? ']' - | expression '[' expression? ':' expression? ']' - | expression '.' identifier - | expression '{' nameValueList '}' - | expression '(' functionCallArguments ')' - | PayableKeyword '(' expression ')' - | '(' expression ')' - | ('++' | '--') expression - | ('+' | '-') expression - | ('after' | 'delete') expression - | '!' expression - | '~' expression - | expression '**' expression - | expression ('*' | '/' | '%') expression - | expression ('+' | '-') expression - | expression ('<<' | '>>') expression - | expression '&' expression - | expression '^' expression - | expression '|' expression - | expression ('<' | '>' | '<=' | '>=') expression - | expression ('==' | '!=') expression - | expression '&&' expression - | expression '||' expression - | expression '?' expression ':' expression - | expression ('=' | '|=' | '^=' | '&=' | '<<=' | '>>=' | '+=' | '-=' | '*=' | '/=' | '%=') expression - | primaryExpression ; - -primaryExpression - : BooleanLiteral - | numberLiteral - | hexLiteral - | stringLiteral - | unicodeStringLiteral - | identifier ('[' ']')? - | TypeKeyword - | tupleExpression - | typeNameExpression ('[' ']')? ; - -expressionList - : expression (',' expression)* ; - -nameValueList - : nameValue (',' nameValue)* ','? ; - -nameValue - : identifier ':' expression ; - -functionCallArguments - : '{' nameValueList? '}' - | expressionList? ; - -functionCall - : expression '(' functionCallArguments ')' ; - -tupleExpression - : '(' ( expression? ( ',' expression? )* ) ')' - | '[' ( expression ( ',' expression )* )? ']' ; - -typeNameExpression - : elementaryTypeName - | userDefinedTypeName ; - -assemblyItem - : identifier - | assemblyBlock - | assemblyExpression - | assemblyLocalDefinition - | assemblyAssignment - | assemblyStackAssignment - | labelDefinition - | assemblySwitch - | assemblyFunctionDefinition - | assemblyFor - | assemblyIf - | BreakKeyword - | ContinueKeyword - | LeaveKeyword - | subAssembly - | numberLiteral - | stringLiteral - | hexLiteral ; - -assemblyBlock - : '{' assemblyItem* '}' ; - -assemblyExpression - : assemblyCall | assemblyLiteral | assemblyIdentifier ; - -assemblyCall - : ( 'return' | 'address' | 'byte' | identifier ) ( '(' assemblyExpression? ( ',' assemblyExpression )* ')' )? ; - -assemblyLocalDefinition - : 'let' assemblyIdentifierList ( ':=' assemblyExpression )? ; - -assemblyAssignment - : assemblyIdentifierList ':=' assemblyExpression ; - -assemblyIdentifierList - : assemblyIdentifier ( ',' assemblyIdentifier )* ; - -assemblyIdentifier - : identifier ( '.' identifier )* ; - -assemblyStackAssignment - : '=:' identifier ; - -labelDefinition - : identifier ':' ; - -assemblySwitch - : 'switch' assemblyExpression assemblyCase* ; - -assemblyCase - : 'case' assemblyLiteral assemblyType? assemblyBlock - | 'default' assemblyBlock ; - -assemblyFunctionDefinition - : 'function' identifier '(' assemblyTypedVariableList? ')' - assemblyFunctionReturns? assemblyBlock ; - -assemblyFunctionReturns - : ( '-' '>' assemblyTypedVariableList ) ; - -assemblyFor - : 'for' assemblyBlock assemblyExpression assemblyBlock assemblyBlock ; - -assemblyIf - : 'if' assemblyExpression assemblyBlock ; - -assemblyLiteral - : ( stringLiteral | DecimalNumber | HexNumber | hexLiteral | BooleanLiteral ) assemblyType? ; - -assemblyTypedVariableList - : identifier assemblyType? ( ',' assemblyTypedVariableList )? ; - -assemblyType - : ':' identifier ; - -subAssembly - : 'assembly' identifier assemblyBlock ; - -// 'finney' and 'szabo' are no longer supported as denominations by latest Solidity. -numberLiteral - : (DecimalNumber | HexNumber) (NumberUnit | Gwei | Finney | Szabo)?; - -identifier - : (Gwei | Finney | Szabo | 'from' | 'calldata' | 'address' | Identifier) ; - -BooleanLiteral - : 'true' | 'false' ; - -DecimalNumber - : ( DecimalDigits | (DecimalDigits? '.' DecimalDigits) ) ( [eE] '-'? DecimalDigits )? ; - -fragment -DecimalDigits - : [0-9] ( '_'? [0-9] )* ; - -HexNumber - : '0' [xX] HexDigits ; - -fragment -HexDigits - : HexCharacter ( '_'? HexCharacter )* ; - -NumberUnit - : 'wei' | 'ether' - | 'seconds' | 'minutes' | 'hours' | 'days' | 'weeks' | 'years' ; - -Gwei: 'gwei' ; -Szabo: 'szabo' ; -Finney: 'finney' ; - -HexLiteralFragment - : 'hex' (('"' HexDigits? '"') | ('\'' HexDigits? '\'')) ; - -hexLiteral : HexLiteralFragment+ ; - -fragment -HexPair - : HexCharacter HexCharacter ; - -fragment -HexCharacter - : [0-9A-Fa-f] ; - -ReservedKeyword - : 'after' - | 'case' - | 'default' - | 'final' - | 'in' - | 'inline' - | 'let' - | 'match' - | 'null' - | 'of' - | 'relocatable' - | 'static' - | 'switch' - | 'typeof' ; - -AnonymousKeyword : 'anonymous' ; -BreakKeyword : 'break' ; -ConstantKeyword : 'constant' ; -ImmutableKeyword : 'immutable' ; -ContinueKeyword : 'continue' ; -LeaveKeyword : 'leave' ; -ExternalKeyword : 'external' ; -IndexedKeyword : 'indexed' ; -InternalKeyword : 'internal' ; -PayableKeyword : 'payable' ; -PrivateKeyword : 'private' ; -PublicKeyword : 'public' ; -VirtualKeyword : 'virtual' ; -PureKeyword : 'pure' ; -TypeKeyword : 'type' ; -ViewKeyword : 'view' ; - -ConstructorKeyword : 'constructor' ; -FallbackKeyword : 'fallback' ; -ReceiveKeyword : 'receive' ; - -Identifier - : IdentifierStart IdentifierPart* ; - -fragment -IdentifierStart - : [a-zA-Z$_] ; - -fragment -IdentifierPart - : [a-zA-Z0-9$_] ; - -stringLiteral - : StringLiteralFragment+ ; - -StringLiteralFragment - : '"' DoubleQuotedStringCharacter* '"' - | '\'' SingleQuotedStringCharacter* '\'' ; - -unicodeStringLiteral - : UnicodeStringLiteralFragment+ ; - -UnicodeStringLiteralFragment - : 'unicode"' DoubleQuotedStringCharacter* '"' - | 'unicode\'' SingleQuotedStringCharacter* '\'' ; - -fragment -DoubleQuotedStringCharacter - : ~["\r\n\\] | ('\\' .) ; - -fragment -SingleQuotedStringCharacter - : ~['\r\n\\] | ('\\' .) ; - -WS - : [ \t\r\n\u000C]+ -> skip ; - -COMMENT - : '/*' .*? '*/' -> channel(HIDDEN) ; - -LINE_COMMENT - : '//' ~[\r\n]* -> channel(HIDDEN) ; diff --git a/docs/conf.py b/docs/conf.py index 1ff794722..191955c9a 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -39,7 +39,9 @@ def setup(sphinx): # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. -extensions = [] +extensions = [ 'sphinx_a4doc' ] + +a4_base_path = os.path.dirname(__file__) + '/grammar' # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] @@ -83,7 +85,7 @@ else: # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. -exclude_patterns = ['_build', 'contracts', 'types', 'examples'] +exclude_patterns = ['_build', 'contracts', 'types', 'examples', 'grammar'] # The reST default role (used for this markup: `text`) to use for all # documents. diff --git a/docs/grammar.rst b/docs/grammar.rst index c7a22dd7e..d69f86b08 100644 --- a/docs/grammar.rst +++ b/docs/grammar.rst @@ -2,5 +2,12 @@ Language Grammar **************** -.. literalinclude:: Solidity.g4 - :language: antlr +.. a4:autogrammar:: Solidity + :only-reachable-from: Solidity.sourceUnit + :undocumented: + :cc-to-dash: + +.. a4:autogrammar:: SolidityLexer + :only-reachable-from: Solidity.sourceUnit + :fragments: + :cc-to-dash: \ No newline at end of file diff --git a/docs/grammar/Solidity.g4 b/docs/grammar/Solidity.g4 new file mode 100644 index 000000000..626ffc87b --- /dev/null +++ b/docs/grammar/Solidity.g4 @@ -0,0 +1,511 @@ +/** + * Solidity is a statically typed, contract-oriented, high-level language for implementing smart contracts on the Ethereum platform. + */ +grammar Solidity; + +options { tokenVocab=SolidityLexer; } + +/** + * On top level, Solidity allows pragmas, import directives, and + * definitions of contracts, interfaces, libraries, structs and enums. + */ +sourceUnit: ( + pragmaDirective + | importDirective + | contractDefinition + | interfaceDefinition + | libraryDefinition + | functionDefinition + | structDefinition + | enumDefinition +)* EOF; + +//@doc: inline +pragmaDirective: Pragma PragmaToken+ PragmaSemicolon; + +/** + * Import directives import identifiers from different files. + */ +importDirective: + Import ( + (path (As unitAlias=identifier)?) + | (symbolAliases From path) + | (Mul As unitAlias=identifier From path) + ) Semicolon; +//@doc: inline +//@doc:name aliases +importAliases: symbol=identifier (As alias=identifier)?; +/** + * Path of a file to be imported. + */ +path: NonEmptyStringLiteral; +/** + * List of aliases for symbols to be imported. + */ +symbolAliases: LBrace aliases+=importAliases (Comma aliases+=importAliases)* RBrace; + +/** + * Top-level definition of a contract. + */ +contractDefinition: + Abstract? Contract name=identifier + inheritanceSpecifierList? + LBrace contractBodyElement* RBrace; +/** + * Top-level definition of an interface. + */ +interfaceDefinition: + Interface name=identifier + inheritanceSpecifierList? + LBrace contractBodyElement* RBrace; +/** + * Top-level definition of a library. + */ +libraryDefinition: Library name=identifier LBrace contractBodyElement* RBrace; + +//@doc:inline +inheritanceSpecifierList: + Is inheritanceSpecifiers+=inheritanceSpecifier + (Comma inheritanceSpecifiers+=inheritanceSpecifier)*?; +/** + * Inheritance specifier for contracts and interfaces. + * Can optionally supply base constructor arguments. + */ +inheritanceSpecifier: name=userDefinedTypeName arguments=callArgumentList?; + +/** + * Declarations that can be used in contracts, interfaces and libraries. + * + * Note that interfaces and libraries may not contain constructors, interfaces may not contain state variables + * and libraries may not contain fallback, receive functions nor non-constant state variables. + */ +contractBodyElement: + constructorDefinition + | functionDefinition + | modifierDefinition + | fallbackReceiveFunctionDefinition + | structDefinition + | enumDefinition + | stateVariableDeclaration + | eventDefinition + | usingDirective; +//@doc:inline +namedArgument: name=identifier Colon value=expression; +/** + * Arguments when calling a function or a similar callable object. + * The arguments are either given as comma separated list or as map of named arguments. + */ +callArgumentList: LParen ((expression (Comma expression)*)? | LBrace (namedArgument (Comma namedArgument)*)? RBrace) RParen; +/** + * Qualified name of a user defined type. + */ +userDefinedTypeName: identifier (Period identifier)*; + +/** + * Call to a modifier. If the modifier takes no arguments, the argument list can be skipped entirely + * (including opening and closing parentheses). + */ +modifierInvocation: identifier callArgumentList?; +/** + * Visibility for functions and function types. + */ +visibility: Internal | External | Private | Public; +/** + * A list of parameters, such as function arguments or return values. + */ +parameterList: parameters+=parameterDeclaration (Comma parameters+=parameterDeclaration)*; +//@doc:inline +parameterDeclaration: type=typeName location=dataLocation? name=identifier?; +/** + * Definition of a constructor. + * Must always supply an implementation. + * Note that specifying internal or public visibility is deprecated. + */ +constructorDefinition +locals[boolean payableSet = false, boolean visibilitySet = false] +: + Constructor LParen (arguments=parameterList)? RParen + ( + modifierInvocation + | {!$payableSet}? Payable {$payableSet = true;} + | {!$visibilitySet}? Internal {$visibilitySet = true;} + | {!$visibilitySet}? Public {$visibilitySet = true;} + )* + body=block; + +/** + * State mutability for function types. + * The default mutability 'non-payable' is assumed if no mutability is specified. + */ +stateMutability: Pure | View | Payable; +/** + * An override specifier used for functions, modifiers or state variables. + * In cases where there are ambiguous declarations in several base contracts being overridden, + * a complete list of base contracts has to be given. + */ +overrideSpecifier: Override (LParen overrides+=userDefinedTypeName (Comma overrides+=userDefinedTypeName)* RParen)?; +/** + * The definition of contract, library and interface functions. + * Depending on the context in which the function is defined, further restrictions may apply, + * e.g. functions in interfaces have to be unimplemented, i.e. may not contain a body block. + */ +functionDefinition +locals[ + boolean visibilitySet = false, + boolean mutabilitySet = false, + boolean virtualSet = false, + boolean overrideSpecifierSet = false +] +: + Function (identifier | Fallback | Receive) + LParen (arguments=parameterList)? RParen + ( + {!$visibilitySet}? visibility {$visibilitySet = true;} + | {!$mutabilitySet}? stateMutability {$mutabilitySet = true;} + | modifierInvocation + | {!$virtualSet}? Virtual {$virtualSet = true;} + | {!$overrideSpecifierSet}? overrideSpecifier {$overrideSpecifierSet = true;} + )* + (Returns LParen returnParameters=parameterList RParen)? + (Semicolon | body=block); +/** + * The definition of a modifier. + * Note that within the body block of a modifier, the underscore cannot be used as identifier, + * but is used as placeholder statement for the body of a function to which the modifier is applied. + */ +modifierDefinition +locals[ + boolean virtualSet = false, + boolean overrideSpecifierSet = false +] +: + Modifier name=identifier + (LParen (arguments=parameterList)? RParen)? + ( + {!$virtualSet}? Virtual {$virtualSet = true;} + | {!$overrideSpecifierSet}? overrideSpecifier {$overrideSpecifierSet = true;} + )* + (Semicolon | body=block); + +/** + * Definitions of the special fallback and receive functions. + */ +fallbackReceiveFunctionDefinition +locals[ + boolean visibilitySet = false, + boolean mutabilitySet = false, + boolean virtualSet = false, + boolean overrideSpecifierSet = false +] +: + kind=(Fallback | Receive) LParen RParen + ( + {!$visibilitySet}? visibility {$visibilitySet = true;} + | {!$mutabilitySet}? stateMutability {$mutabilitySet = true;} + | modifierInvocation + | {!$virtualSet}? Virtual {$virtualSet = true;} + | {!$overrideSpecifierSet}? overrideSpecifier {$overrideSpecifierSet = true;} + )* + (Semicolon | body=block); + +/** + * Definition of a struct. Can occur at top-level within a source unit or within a contract, library or interface. + */ +structDefinition: Struct name=identifier LBrace members=structMember+ RBrace; +/** + * The declaration of a named struct member. + */ +structMember: type=typeName name=identifier Semicolon; +/** + * Definition of an enum. Can occur at top-level within a source unit or within a contract, library or interface. + */ +enumDefinition: Enum name=identifier LBrace enumValues+=identifier (Comma enumValues+=identifier)* RBrace; + +/** + * The declaration of a state variable. + */ +stateVariableDeclaration +locals [boolean constantnessSet = false, boolean visibilitySet = false, boolean overrideSpecifierSet = false] +: + type=typeName + ( + {!$visibilitySet}? Public {$visibilitySet = true;} + | {!$visibilitySet}? Private {$visibilitySet = true;} + | {!$visibilitySet}? Internal {$visibilitySet = true;} + | {!$constantnessSet}? Constant {$constantnessSet = true;} + | {!$overrideSpecifierSet}? overrideSpecifier {$overrideSpecifierSet = true;} + | {!$constantnessSet}? Immutable {$constantnessSet = true;} + )* + name=identifier + (Assign initialValue=expression)? + Semicolon; + +/** + * Parameter of an event. + */ +eventParameter: type=typeName Indexed? name=identifier?; +/** + * Definition of an event. Can occur in contracts, libraries or interfaces. + */ +eventDefinition: + Event name=identifier + LParen (parameters+=eventParameter (Comma parameters+=eventParameter)*)? RParen + Anonymous? + Semicolon; + +/** + * Using directive to bind library functions to types. + * Can occur within contracts and libraries. + */ +usingDirective: Using userDefinedTypeName For (Mul | typeName) Semicolon; +/** + * A type name can be an elementary type, a function type, a mapping type, a user-defined type + * (e.g. a contract or struct) or an array type. + */ +typeName: elementaryTypeName[true] | functionTypeName | mappingType | userDefinedTypeName | typeName LBrack expression? RBrack; +elementaryTypeName[boolean allowAddressPayable]: Address | {$allowAddressPayable}? Address Payable | Bool | String | Bytes | SignedIntegerType | UnsignedIntegerType | FixedBytes | Fixed | Ufixed; +functionTypeName +locals [boolean visibilitySet = false, boolean mutabilitySet = false] +: + Function LParen (arguments=parameterList)? RParen + ( + {!$visibilitySet}? visibility {$visibilitySet = true;} + | {!$mutabilitySet}? stateMutability {$mutabilitySet = true;} + )* + (Returns LParen returnParameters=parameterList RParen)?; + +/** + * The declaration of a single variable. + */ +variableDeclaration: type=typeName location=dataLocation? name=identifier; +dataLocation: Memory | Storage | Calldata; + +/** + * Complex expression. + * Can be an index access, an index range access, a member access, a function call (with optional function call options), + * a type conversion, an unary or binary expression, a comparison or assignment, a ternary expression, + * a new-expression (i.e. a contract creation or the allocation of a dynamic memory array), + * a tuple, an inline array or a primary expression (i.e. an identifier, literal or type name). + */ +expression: + expression LBrack index=expression? RBrack # IndexAccess + | expression LBrack start=expression? Colon end=expression? RBrack # IndexRangeAccess + | expression Period (identifier | Address) # MemberAccess + | expression LBrace (namedArgument (Comma namedArgument)*)? RBrace # FunctionCallOptions + | expression callArgumentList # FunctionCall + | Payable callArgumentList # PayableConversion + | Type LParen typeName RParen # MetaType + | (Inc | Dec | Not | BitNot | Delete | Sub) expression # UnaryPrefixOperation + | expression (Inc | Dec) # UnarySuffixOperation + | expression Exp expression # ExpOperation + | expression (Mul | Div | Mod) expression # MulDivModOperation + | expression (Add | Sub) expression # AddSubOperation + | expression (Shl | Sar | Shr) expression # ShiftOperation + | expression BitAnd expression # BitAndOperation + | expression BitXor expression # BitXorOperation + | expression BitOr expression # BitOrOperation + | expression (LessThan | GreaterThan | LessThanOrEqual | GreaterThanOrEqual) expression # OrderComparison + | expression (Equal | NotEqual) expression # EqualityComparison + | expression And expression # AndOperation + | expression Or expression # OrOperation + | expression Conditional expression Colon expression # Conditional + | expression assignOp expression # Assignment + | New typeName # NewExpression + | tupleExpression # Tuple + | inlineArrayExpression # InlineArray + | ( + identifier + | literal + | elementaryTypeName[false] + | userDefinedTypeName + ) # PrimaryExpression +; + +//@doc:inline +assignOp: Assign | AssignBitOr | AssignBitXor | AssignBitAnd | AssignShl | AssignSar | AssignShr | AssignAdd | AssignSub | AssignMul | AssignDiv | AssignMod; +tupleExpression: LParen (expression? ( Comma expression?)* ) RParen; +/** + * An inline array expression denotes a statically sized array of the common type of the contained expressions. + */ +inlineArrayExpression: LBrack (expression ( Comma expression)* ) RBrack; + +/** + * Besides regular non-keyword Identifiers, the 'from' keyword can also occur as identifier outside of import statements. + */ +identifier: Identifier | From; + +literal: stringLiteral | numberLiteral | booleanLiteral | hexStringLiteral | unicodeStringLiteral; +booleanLiteral: True | False; +/** + * A full string literal consists of either one or several consecutive quoted strings. + */ +stringLiteral: StringLiteral+; +/** + * A full hex string literal that consists of either one or several consecutive hex strings. + */ +hexStringLiteral: HexString+; +/** + * A full unicode string literal that consists of either one or several consecutive unicode strings. + */ +unicodeStringLiteral: UnicodeStringLiteral+; + +/** + * Number literals can be decimal or hexadecimal numbers with an optional unit. + */ +numberLiteral: (DecimalNumber | HexNumber) NumberUnit?; +/** + * A curly-braced block of statements. Opens its own scope. + */ +block: LBrace statement* RBrace; + +statement: + block + | simpleStatement + | ifStatement + | forStatement + | whileStatement + | doWhileStatement + | continueStatement + | breakStatement + | tryStatement + | returnStatement + | emitStatement + | assemblyStatement +; + +//@doc:inline +simpleStatement: variableDeclarationStatement | expressionStatement; +/** + * If statement with optional else part. + */ +ifStatement: If LParen expression RParen statement (Else statement)?; +/** + * For statement with optional init, condition and post-loop part. + */ +forStatement: For LParen (simpleStatement | Semicolon) (expressionStatement | Semicolon) expression? RParen statement; +whileStatement: While LParen expression RParen statement; +doWhileStatement: Do statement While LParen expression RParen Semicolon; +/** + * A continue statement. Only allowed inside for, while or do-while loops. + */ +continueStatement: Continue Semicolon; +/** + * A break statement. Only allowed inside for, while or do-while loops. + */ +breakStatement: Break Semicolon; +/** + * A try statement. The contained expression needs to be an external function call or a contract creation. + */ +tryStatement: Try expression (Returns LParen returnParameters=parameterList RParen)? block catchClause+; +/** + * The catch clause of a try statement. + */ +catchClause: Catch (identifier? LParen (arguments=parameterList) RParen)? block; + +returnStatement: Return expression? Semicolon; +/** + * An emit statement. The contained expression needs to refer to an event. + */ +emitStatement: Emit expression callArgumentList Semicolon; +/** + * An inline assembly block. + * The contents of an inline assembly block use a separate scanner/lexer, i.e. the set of keywords and + * allowed identifiers is different inside an inline assembly block. + */ +assemblyStatement: Assembly AssemblyDialect? AssemblyLBrace yulStatement* YulRBrace; + +//@doc:inline +variableDeclarationList: variableDeclarations+=variableDeclaration (Comma variableDeclarations+=variableDeclaration)*; +/** + * A tuple of variable names to be used in variable declarations. + * May contain empty fields. + */ +variableDeclarationTuple: + LParen + (Comma* variableDeclarations+=variableDeclaration) + (Comma (variableDeclarations+=variableDeclaration)?)* + RParen; +/** + * A variable declaration statement. + * A single variable may be declared without initial value, whereas a tuple of variables can only be + * declared with initial value. + */ +variableDeclarationStatement: ((variableDeclaration (Assign expression)?) | (variableDeclarationTuple Assign expression)) Semicolon; +expressionStatement: expression Semicolon; + +mappingType: Mapping LParen key=mappingKeyType Arrow value=typeName RParen; +/** + * Only elementary types or user defined types are viable as mapping keys. + */ +mappingKeyType: elementaryTypeName[false] | userDefinedTypeName; + +/** + * A Yul statement within an inline assembly block. + * continue and break statements are only valid within for loops. + * leave statements are only valid within function bodies. + */ +yulStatement: + yulBlock + | yulVariableDeclaration + | yulAssignment + | yulFunctionCall + | yulIfStatement + | yulForStatement + | yulSwitchStatement + | YulLeave + | YulBreak + | YulContinue + | yulFunctionDefinition; + +yulBlock: YulLBrace yulStatement* YulRBrace; + +/** + * The declaration of one or more Yul variables with optional initial value. + * If multiple variables are declared, only a function call is a valid initial value. + */ +yulVariableDeclaration: + (YulLet variables+=YulIdentifier (YulAssign yulExpression)?) + | (YulLet variables+=YulIdentifier (YulComma variables+=YulIdentifier)* (YulAssign yulFunctionCall)?); + +/** + * Any expression can be assigned to a single Yul variable, whereas + * multi-assignments require a function call on the right-hand side. + */ +yulAssignment: yulPath YulAssign yulExpression | (yulPath (YulComma yulPath)+) YulAssign yulFunctionCall; + +yulIfStatement: YulIf cond=yulExpression body=yulBlock; + +yulForStatement: YulFor init=yulBlock cond=yulExpression post=yulBlock body=yulBlock; + +//@doc:inline +yulSwitchCase: YulCase yulLiteral yulBlock; +/** + * A Yul switch statement can consist of only a default-case (deprecated) or + * one or more non-default cases optionally followed by a default-case. + */ +yulSwitchStatement: + YulSwitch yulExpression + ( + (yulSwitchCase+ (YulDefault yulBlock)?) + | (YulDefault yulBlock) + ); + +yulFunctionDefinition: + YulFunction YulIdentifier + YulLParen (arguments+=YulIdentifier (YulComma arguments+=YulIdentifier)*)? YulRParen + (YulArrow returnParameters+=YulIdentifier (YulComma returnParameters+=YulIdentifier)*)? + body=yulBlock; + +/** + * While only identifiers without dots can be declared within inline assembly, + * paths containing dots can refer to declarations outside the inline assembly block. + */ +yulPath: YulIdentifier (YulPeriod YulIdentifier)*; +/** + * A call to a function with return values can only occur as right-hand side of an assignment or + * a variable declaration. + */ +yulFunctionCall: (YulIdentifier | YulEVMBuiltin) YulLParen (yulExpression (YulComma yulExpression)*)? YulRParen; +yulBoolean: YulTrue | YulFalse; +yulLiteral: YulDecimalNumber | YulStringLiteral | YulHexNumber | yulBoolean; +yulExpression: yulPath | yulFunctionCall | yulLiteral; diff --git a/docs/grammar/SolidityLexer.g4 b/docs/grammar/SolidityLexer.g4 new file mode 100644 index 000000000..441e1f8fd --- /dev/null +++ b/docs/grammar/SolidityLexer.g4 @@ -0,0 +1,335 @@ +lexer grammar SolidityLexer; + +/** + * Keywords reserved for future use in Solidity. + */ +ReservedKeywords: + 'after' | 'alias' | 'apply' | 'auto' | 'case' | 'copyof' | 'default' | 'define' | 'final' + | 'implements' | 'in' | 'inline' | 'let' | 'macro' | 'match' | 'mutable' | 'null' | 'of' + | 'partial' | 'promise' | 'reference' | 'relocatable' | 'sealed' | 'sizeof' | 'static' + | 'supports' | 'switch' | 'typedef' | 'typeof' | 'unchecked' | 'var'; + +Pragma: 'pragma' -> pushMode(PragmaMode); +Abstract: 'abstract'; +Anonymous: 'anonymous'; +Address: 'address'; +As: 'as'; +Assembly: 'assembly' -> pushMode(AssemblyBlockMode); +Bool: 'bool'; +Break: 'break'; +Bytes: 'bytes'; +Calldata: 'calldata'; +Catch: 'catch'; +Constant: 'constant'; +Constructor: 'constructor'; +Continue: 'continue'; +Contract: 'contract'; +Delete: 'delete'; +Do: 'do'; +Else: 'else'; +Emit: 'emit'; +Enum: 'enum'; +Event: 'event'; +External: 'external'; +Fallback: 'fallback'; +False: 'false'; +Fixed: 'fixed' | ('fixed' [0-9]+ 'x' [0-9]+); +From: 'from'; +/** + * Bytes types of fixed length. + * byte is an alias of bytes1. + */ +FixedBytes: + 'byte' | 'bytes1' | 'bytes2' | 'bytes3' | 'bytes4' | 'bytes5' | 'bytes6' | 'bytes7' | 'bytes8' | + 'bytes9' | 'bytes10' | 'bytes11' | 'bytes12' | 'bytes13' | 'bytes14' | 'bytes15' | 'bytes16' | + 'bytes17' | 'bytes18' | 'bytes19' | 'bytes20' | 'bytes21' | 'bytes22' | 'bytes23' | 'bytes24' | + 'bytes25' | 'bytes26' | 'bytes27' | 'bytes28' | 'bytes29' | 'bytes30' | 'bytes31' | 'bytes32'; +For: 'for'; +Function: 'function'; +Hex: 'hex'; +If: 'if'; +Immutable: 'immutable'; +Import: 'import'; +Indexed: 'indexed'; +Interface: 'interface'; +Internal: 'internal'; +Is: 'is'; +Library: 'library'; +Mapping: 'mapping'; +Memory: 'memory'; +Modifier: 'modifier'; +New: 'new'; +/** + * Unit denomination for numbers. + */ +NumberUnit: 'wei' | 'gwei' | 'ether' | 'seconds' | 'minutes' | 'hours' | 'days' | 'weeks' | 'years'; +Override: 'override'; +Payable: 'payable'; +Private: 'private'; +Public: 'public'; +Pure: 'pure'; +Receive: 'receive'; +Return: 'return'; +Returns: 'returns'; +/** + * Sized signed integer types. + * int is an alias of int256. + */ +SignedIntegerType: + 'int' | 'int8' | 'int16' | 'int24' | 'int32' | 'int40' | 'int48' | 'int56' | 'int64' | + 'int72' | 'int80' | 'int88' | 'int96' | 'int104' | 'int112' | 'int120' | 'int128' | + 'int136' | 'int144' | 'int152' | 'int160' | 'int168' | 'int176' | 'int184' | 'int192' | + 'int200' | 'int208' | 'int216' | 'int224' | 'int232' | 'int240' | 'int248' | 'int256'; +Storage: 'storage'; +String: 'string'; +Struct: 'struct'; +True: 'true'; +Try: 'try'; +Type: 'type'; +Ufixed: 'ufixed' | ('ufixed' [0-9]+ 'x' [0-9]+); +/** + * Sized unsigned integer types. + * uint is an alias of uint256. + */ +UnsignedIntegerType: + 'uint' | 'uint8' | 'uint16' | 'uint24' | 'uint32' | 'uint40' | 'uint48' | 'uint56' | 'uint64' | + 'uint72' | 'uint80' | 'uint88' | 'uint96' | 'uint104' | 'uint112' | 'uint120' | 'uint128' | + 'uint136' | 'uint144' | 'uint152' | 'uint160' | 'uint168' | 'uint176' | 'uint184' | 'uint192' | + 'uint200' | 'uint208' | 'uint216' | 'uint224' | 'uint232' | 'uint240' | 'uint248' | 'uint256'; +Using: 'using'; +View: 'view'; +Virtual: 'virtual'; +While: 'while'; + +LParen: '('; +RParen: ')'; +LBrack: '['; +RBrack: ']'; +LBrace: '{'; +RBrace: '}'; +Colon: ':'; +Semicolon: ';'; +Period: '.'; +Conditional: '?'; +Arrow: '=>'; + +Assign: '='; +AssignBitOr: '|='; +AssignBitXor: '^='; +AssignBitAnd: '&='; +AssignShl: '<<='; +AssignSar: '>>='; +AssignShr: '>>>='; +AssignAdd: '+='; +AssignSub: '-='; +AssignMul: '*='; +AssignDiv: '/='; +AssignMod: '%='; + +Comma: ','; +Or: '||'; +And: '&&'; +BitOr: '|'; +BitXor: '^'; +BitAnd: '&'; +Shl: '<<'; +Sar: '>>'; +Shr: '>>>'; +Add: '+'; +Sub: '-'; +Mul: '*'; +Div: '/'; +Mod: '%'; +Exp: '**'; + +Equal: '=='; +NotEqual: '!='; +LessThan: '<'; +GreaterThan: '>'; +LessThanOrEqual: '<='; +GreaterThanOrEqual: '>='; +Not: '!'; +BitNot: '~'; +Inc: '++'; +Dec: '--'; + +/** + * A single quoted string literal restricted to printable characters. + */ +StringLiteral: '"' DoubleQuotedStringCharacter* '"' | '\'' SingleQuotedStringCharacter* '\''; +/** + * A single non-empty quoted string literal. + */ +NonEmptyStringLiteral: '"' DoubleQuotedStringCharacter+ '"' | '\'' SingleQuotedStringCharacter+ '\''; +// Note that this will also be used for Yul string literals. +//@doc:inline +fragment DoubleQuotedStringCharacter: DoubleQuotedPrintable | EscapeSequence; +// Note that this will also be used for Yul string literals. +//@doc:inline +fragment SingleQuotedStringCharacter: SingleQuotedPrintable | EscapeSequence; +/** + * Any printable character except single quote or back slash. + */ +fragment SingleQuotedPrintable: [\u0020-\u0026\u0028-\u005B\u005D-\u007E]; +/** + * Any printable character except double quote or back slash. + */ +fragment DoubleQuotedPrintable: [\u0020-\u0021\u0023-\u005B\u005D-\u007E]; +/** + * Escape sequence. + * Apart from common single character escape sequences, line breaks can be escaped + * as well as four hex digit unicode escapes \\uXXXX and two digit hex escape sequences \\xXX are allowed. + */ +fragment EscapeSequence: + '\\' ( + ['"\\bfnrtv\n\r] + | 'u' HexCharacter HexCharacter HexCharacter HexCharacter + | 'x' HexCharacter HexCharacter + ); +/** + * A single quoted string literal allowing arbitrary unicode characters. + */ +UnicodeStringLiteral: + 'unicode"' DoubleQuotedUnicodeStringCharacter* '"' + | 'unicode\'' SingleQuotedUnicodeStringCharacter* '\''; +//@doc:inline +fragment DoubleQuotedUnicodeStringCharacter: ~["\r\n\\] | EscapeSequence; +//@doc:inline +fragment SingleQuotedUnicodeStringCharacter: ~['\r\n\\] | EscapeSequence; + +/** + * Hex strings need to consist of an even number of hex digits that may be grouped using underscores. + */ +HexString: 'hex' (('"' EvenHexDigits? '"') | ('\'' EvenHexDigits? '\'')); +/** + * Hex numbers consist of a prefix and an arbitrary number of hex digits that may be delimited by underscores. + */ +HexNumber: '0' 'x' HexDigits; +//@doc:inline +fragment HexDigits: HexCharacter ('_'? HexCharacter)*; +//@doc:inline +fragment EvenHexDigits: HexCharacter HexCharacter ('_'? HexCharacter HexCharacter)*; +//@doc:inline +fragment HexCharacter: [0-9A-Fa-f]; + +/** + * A decimal number literal consists of decimal digits that may be delimited by underscores and + * an optional positive or negative exponent. + * If the digits contain a decimal point, the literal has fixed point type. + */ +DecimalNumber: (DecimalDigits | (DecimalDigits? '.' DecimalDigits)) ([eE] '-'? DecimalDigits)?; +//@doc:inline +fragment DecimalDigits: [0-9] ('_'? [0-9])* ; + + +/** + * An identifier in solidity has to start with a letter, a dollar-sign or an underscore and + * may additionally contain numbers after the first symbol. + */ +Identifier: IdentifierStart IdentifierPart*; +//@doc:inline +fragment IdentifierStart: [a-zA-Z$_]; +//@doc:inline +fragment IdentifierPart: [a-zA-Z0-9$_]; + +WS: [ \t\r\n\u000C]+ -> skip ; +COMMENT: '/*' .*? '*/' -> channel(HIDDEN) ; +LINE_COMMENT: '//' ~[\r\n]* -> channel(HIDDEN); + +mode AssemblyBlockMode; + +//@doc:inline +AssemblyDialect: '"evmasm"'; +AssemblyLBrace: '{' -> popMode, pushMode(YulMode); + +AssemblyBlockWS: [ \t\r\n\u000C]+ -> skip ; +AssemblyBlockCOMMENT: '/*' .*? '*/' -> channel(HIDDEN) ; +AssemblyBlockLINE_COMMENT: '//' ~[\r\n]* -> channel(HIDDEN) ; + +mode YulMode; + +YulBreak: 'break'; +YulCase: 'case'; +YulContinue: 'continue'; +YulDefault: 'default'; +YulFalse: 'false'; +YulFor: 'for'; +YulFunction: 'function'; +YulIf: 'if'; +YulLeave: 'leave'; +YulLet: 'let'; +YulSwitch: 'switch'; +YulTrue: 'true'; + +/** + * Builtin functions in the EVM Yul dialect. + */ +YulEVMBuiltin: + 'stop' | 'add' | 'sub' | 'mul' | 'div' | 'sdiv' | 'mod' | 'smod' | 'exp' | 'not' + | 'lt' | 'gt' | 'slt' | 'sgt' | 'eq' | 'iszero' | 'and' | 'or' | 'xor' | 'byte' + | 'shl' | 'shr' | 'sar' | 'addmod' | 'mulmod' | 'signextend' | 'keccak256' + | 'pop' | 'mload' | 'mstore' | 'mstore8' | 'sload' | 'sstore' | 'msize' | 'gas' + | 'address' | 'balance' | 'selfbalance' | 'caller' | 'callvalue' | 'calldataload' + | 'calldatasize' | 'calldatacopy' | 'extcodesize' | 'extcodecopy' | 'returndatasize' + | 'returndatacopy' | 'extcodehash' | 'create' | 'create2' | 'call' | 'callcode' + | 'delegatecall' | 'staticcall' | 'return' | 'revert' | 'selfdestruct' | 'invalid' + | 'log0' | 'log1' | 'log2' | 'log3' | 'log4' | 'chainid' | 'origin' | 'gasprice' + | 'blockhash' | 'coinbase' | 'timestamp' | 'number' | 'difficulty' | 'gaslimit'; + +YulLBrace: '{' -> pushMode(YulMode); +YulRBrace: '}' -> popMode; +YulLParen: '('; +YulRParen: ')'; +YulAssign: ':='; +YulPeriod: '.'; +YulComma: ','; +// TODO: remove whitespace workaround once the parser disallows it. +//@doc:name -> +YulArrow: '->' | '-' YulWS+ '>'; + +/** + * Yul identifiers consist of letters, dollar signs, underscores and numbers, but may not start with a number. + * In inline assembly there cannot be dots in user-defined identifiers. Instead see yulPath for expressions + * consisting of identifiers with dots. + */ +YulIdentifier: YulIdentifierStart YulIdentifierPart*; +//@doc:inline +fragment YulIdentifierStart: [a-zA-Z$_]; +//@doc:inline +fragment YulIdentifierPart: [a-zA-Z0-9$_]; +/** + * Hex literals in Yul consist of a prefix and one or more hexadecimal digits. + */ +YulHexNumber: '0' 'x' [0-9a-fA-F]+; +/** + * Decimal literals in Yul may be zero or any sequence of decimal digits without leading zeroes. + */ +YulDecimalNumber: '0' | ([1-9] [0-9]*); +/** + * String literals in Yul consist of one or more double-quoted or single-quoted strings + * that may contain escape sequences and printable characters except unescaped line breaks or + * unescaped double-quotes or single-quotes, respectively. + */ +YulStringLiteral: + '"' DoubleQuotedStringCharacter* '"' + | '\'' SingleQuotedStringCharacter* '\''; + + +YulWS: [ \t\r\n\u000C]+ -> skip ; +YulCOMMENT: '/*' .*? '*/' -> channel(HIDDEN) ; +YulLINE_COMMENT: '//' ~[\r\n]* -> channel(HIDDEN) ; + +mode PragmaMode; + +/** + * Pragma token. Can contain any kind of symbol except a semicolon. + * Note that currently the solidity parser only allows a subset of this. + */ +//@doc:name pragma-token +//@doc:no-diagram +PragmaToken: ~[;]+; +PragmaSemicolon: ';' -> popMode; + +PragmaWS: [ \t\r\n\u000C]+ -> skip ; +PragmaCOMMENT: '/*' .*? '*/' -> channel(HIDDEN) ; +PragmaLINE_COMMENT: '//' ~[\r\n]* -> channel(HIDDEN) ; diff --git a/docs/requirements.txt b/docs/requirements.txt index 8f67f9594..65cc192fd 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,2 +1,3 @@ sphinx_rtd_theme>=0.3.1 pygments-lexer-solidity>=0.5.1 +sphinx-a4doc>=1.2.1 diff --git a/scripts/test_antlr_grammar.sh b/scripts/test_antlr_grammar.sh index 97cbe4ab4..846cecf80 100755 --- a/scripts/test_antlr_grammar.sh +++ b/scripts/test_antlr_grammar.sh @@ -2,11 +2,10 @@ set -e -ROOT_DIR="$(dirname "$0")"/.. +ROOT_DIR=$(readlink -f "$(dirname "$0")"/..) WORKDIR="${ROOT_DIR}/build/antlr" ANTLR_JAR="${ROOT_DIR}/build/deps/antlr4.jar" -ANTLR_JAR_URI="https://www.antlr.org/download/antlr-4.7.2-complete.jar" -GRAMMAR_FILE="$(readlink -f "${ROOT_DIR}/docs/Solidity.g4")" +ANTLR_JAR_URI="https://www.antlr.org/download/antlr-4.8-complete.jar" SGR_RESET="\033[0m" SGR_BOLD="\033[1m" @@ -40,11 +39,14 @@ if [[ ! -f "${WORKDIR}/target/SolidityParser.class" ]] || \ [ "${GRAMMAR_FILE}" -nt "${WORKDIR}/target/SolidityParser.class" ] then echo "Creating parser" + ( + cd "${ROOT_DIR}"/docs/grammar # Create lexer/parser from grammar - java -jar "${ANTLR_JAR}" "${GRAMMAR_FILE}" -o "${WORKDIR}/src/" + java -jar "${ANTLR_JAR}" Solidity.g4 SolidityLexer.g4 -o "${WORKDIR}/src/" # Compile lexer/parser sources javac -classpath "${ANTLR_JAR}" "${WORKDIR}/src/"*.java -d "${WORKDIR}/target/" + ) fi # Run tests @@ -55,47 +57,87 @@ test_file() SOL_FILE="$(readlink -m "${1}")" local cur=${2} local max=${3} + local solOrYul=${4} echo -e "${SGR_BLUE}[${cur}/${max}] Testing ${SOL_FILE}${SGR_RESET} ..." local output - output=$( - java \ - -classpath "${ANTLR_JAR}:${WORKDIR}/target/" \ - "org.antlr.v4.gui.TestRig" \ - Solidity \ - sourceUnit <"${SOL_FILE}" 2>&1 - ) + if [[ "${solOrYul}" == "sol" ]]; then + output=$( + java \ + -classpath "${ANTLR_JAR}:${WORKDIR}/target/" \ + "org.antlr.v4.gui.TestRig" \ + Solidity \ + sourceUnit <"${SOL_FILE}" 2>&1 + ) + else + output=$( + echo "assembly $(cat "${SOL_FILE}")" | java \ + -classpath "${ANTLR_JAR}:${WORKDIR}/target/" \ + "org.antlr.v4.gui.TestRig" \ + Solidity \ + assemblyStatement 2>&1 + ) + fi vt_cursor_up vt_cursor_begin_of_line - if [[ "${output}" == "" ]] - then - echo -e "${SGR_BLUE}[${cur}/${max}] Testing ${SOL_FILE}${SGR_RESET} ${SGR_BOLD}${SGR_GREEN}OK${SGR_RESET}" + if grep -qE "^\/\/ ParserError" "${SOL_FILE}"; then + if [[ "${output}" != "" ]] + then + echo -e "${SGR_BLUE}[${cur}/${max}] Testing ${SOL_FILE}${SGR_RESET} ${SGR_BOLD}${SGR_GREEN}FAILED AS EXPECTED${SGR_RESET}" + else + echo -e "${SGR_BLUE}[${cur}/${max}] Testing ${SOL_FILE}${SGR_RESET} ${SGR_BOLD}${SGR_RED}SUCCEEDED DESPITE PARSER ERROR${SGR_RESET}" + echo "${output}" + failed_count=$((failed_count + 1)) + exit 1 + fi else - echo -e "${SGR_BLUE}[${cur}/${max}] Testing ${SOL_FILE}${SGR_RESET} ${SGR_BOLD}${SGR_RED}FAILED${SGR_RESET}" - echo "${output}" - failed_count=$((failed_count + 1)) - exit 1 + if [[ "${output}" == "" ]] + then + echo -e "${SGR_BLUE}[${cur}/${max}] Testing ${SOL_FILE}${SGR_RESET} ${SGR_BOLD}${SGR_GREEN}OK${SGR_RESET}" + else + echo -e "${SGR_BLUE}[${cur}/${max}] Testing ${SOL_FILE}${SGR_RESET} ${SGR_BOLD}${SGR_RED}FAILED${SGR_RESET}" + echo "${output}" + failed_count=$((failed_count + 1)) + exit 1 + fi fi } -# we only want to use files that do not contain errors or multi-source files. +# we only want to use files that do not contain excluded parser errors, analysis errors or multi-source files. SOL_FILES=() while IFS='' read -r line do SOL_FILES+=("$line") done < <( grep -riL -E \ - "^\/\/ (Syntax|Type|Parser|Declaration)Error|^==== Source:" \ + "^\/\/ (Syntax|Type|Declaration)Error|^\/\/ ParserError (6275|3716|6281|2837|6933)|^==== Source:" \ "${ROOT_DIR}/test/libsolidity/syntaxTests" \ "${ROOT_DIR}/test/libsolidity/semanticTests" \ ) +YUL_FILES=() +# Add all yul optimizer tests without objects and types. +while IFS='' read -r line +do + YUL_FILES+=("$line") +done < <( + grep -riL -E \ + "object|\:[ ]*[uib]" \ + "${ROOT_DIR}/test/libyul/yulOptimizerTests" +) + +num_tests=$((${#SOL_FILES[*]} + ${#YUL_FILES[*]})) test_count=0 for SOL_FILE in "${SOL_FILES[@]}" do test_count=$((test_count + 1)) - test_file "${SOL_FILE}" ${test_count} ${#SOL_FILES[*]} + test_file "${SOL_FILE}" ${test_count} $num_tests "sol" +done +for YUL_FILE in "${YUL_FILES[@]}" +do + test_count=$((test_count + 1)) + test_file "${YUL_FILE}" ${test_count} $num_tests "yul" done -echo "Summary: ${failed_count} of ${#SOL_FILES[*]} sources failed." +echo "Summary: ${failed_count} of $num_tests sources failed." exit ${failed_count} diff --git a/test/libsolidity/syntaxTests/parsing/constructor_internal_internal.sol b/test/libsolidity/syntaxTests/parsing/constructor_internal_internal.sol new file mode 100644 index 000000000..8b84de055 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/constructor_internal_internal.sol @@ -0,0 +1,5 @@ +contract C { + constructor() internal internal {} +} +// ---- +// ParserError 9439: (38-46): Visibility already specified as "internal". diff --git a/test/libsolidity/syntaxTests/parsing/constructor_internal_public.sol b/test/libsolidity/syntaxTests/parsing/constructor_internal_public.sol new file mode 100644 index 000000000..15d60c392 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/constructor_internal_public.sol @@ -0,0 +1,5 @@ +contract C { + constructor() internal public {} +} +// ---- +// ParserError 9439: (38-44): Visibility already specified as "internal". diff --git a/test/libsolidity/syntaxTests/parsing/constructor_payable_payable.sol b/test/libsolidity/syntaxTests/parsing/constructor_payable_payable.sol new file mode 100644 index 000000000..495e3759c --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/constructor_payable_payable.sol @@ -0,0 +1,5 @@ +contract C { + constructor() payable payable {} +} +// ---- +// ParserError 9680: (37-44): State mutability already specified as "payable". diff --git a/test/libsolidity/syntaxTests/parsing/constructor_public_internal.sol b/test/libsolidity/syntaxTests/parsing/constructor_public_internal.sol new file mode 100644 index 000000000..7d58520e5 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/constructor_public_internal.sol @@ -0,0 +1,5 @@ +contract C { + constructor() public internal {} +} +// ---- +// ParserError 9439: (36-44): Visibility already specified as "public". diff --git a/test/libsolidity/syntaxTests/parsing/constructor_public_public.sol b/test/libsolidity/syntaxTests/parsing/constructor_public_public.sol new file mode 100644 index 000000000..c0674f2e5 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/constructor_public_public.sol @@ -0,0 +1,5 @@ +contract C { + constructor() public public {} +} +// ---- +// ParserError 9439: (36-42): Visibility already specified as "public". diff --git a/test/libsolidity/syntaxTests/parsing/function_type_multiple_mutability.sol b/test/libsolidity/syntaxTests/parsing/function_type_multiple_mutability.sol new file mode 100644 index 000000000..8d67db095 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/function_type_multiple_mutability.sol @@ -0,0 +1,7 @@ +contract C { + function f() public pure { + function() pure pure g; + } +} +// ---- +// ParserError 9680: (62-66): State mutability already specified as "pure". diff --git a/test/libsolidity/syntaxTests/parsing/function_type_multiple_visibility.sol b/test/libsolidity/syntaxTests/parsing/function_type_multiple_visibility.sol new file mode 100644 index 000000000..c9b035864 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/function_type_multiple_visibility.sol @@ -0,0 +1,7 @@ +contract C { + function f() public pure { + function() public public g; + } +} +// ---- +// ParserError 9439: (64-70): Visibility already specified as "public".