Create a formal lexer+parser for typespecs and function signatures

This commit is contained in:
Levi Aul 2018-01-15 19:11:07 -08:00
parent 8d4b281b79
commit 7147b405aa
4 changed files with 118 additions and 0 deletions

4
.gitignore vendored
View File

@ -18,3 +18,7 @@ erl_crash.dump
# Also ignore archive artifacts (built via "mix archive.build").
*.ez
# Ignore generated code from yecc and leex
src/*_lexer.erl
src/*_parser.erl

21
lib/abi/parser.ex Normal file
View File

@ -0,0 +1,21 @@
defmodule ABI.Parser do
@moduledoc false
@doc false
def parse!(str, opts \\ []) do
{:ok, tokens, _} = str |> String.to_charlist |> :ethereum_abi_lexer.string
tokens = case opts[:as] do
nil -> tokens
:type -> [{:"expecting type", 1} | tokens]
:selector -> [{:"expecting selector", 1} | tokens]
end
{:ok, ast} = :ethereum_abi_parser.parse(tokens)
case ast do
{:type, type} -> type
{:selector, selector_parts} -> struct!(ABI.FunctionSelector, selector_parts)
end
end
end

View File

@ -0,0 +1,21 @@
Definitions.
INT = [0-9]+
LETTERS = [a-z_]+
WHITESPACE = [\s\t\n\r]
TYPES = uint|int|address|bool|fixed|uint|ufixed|bytes|function|string
Rules.
{TYPES} : {token, {atom, TokenLine, list_to_atom(TokenChars)}}.
{INT} : {token, {int, TokenLine, list_to_integer(TokenChars)}}.
{LETTERS} : {token, {binary, TokenLine, list_to_binary(TokenChars)}}.
\[ : {token, {'[', TokenLine}}.
\] : {token, {']', TokenLine}}.
\( : {token, {'(', TokenLine}}.
\) : {token, {')', TokenLine}}.
, : {token, {',', TokenLine}}.
-> : {token, {'->', TokenLine}}.
{WHITESPACE}+ : skip_token.
Erlang code.

View File

@ -0,0 +1,72 @@
Terminals '(' ')' '[' ']' ',' '->' int atom binary 'expecting selector' 'expecting type'.
Nonterminals dispatch selector nontrivial_selector comma_delimited_types type_with_subscripts array_subscripts tuple array_subscript identifier type typespec.
Rootsymbol dispatch.
dispatch -> 'expecting type' type_with_subscripts : {type, '$2'}.
dispatch -> 'expecting selector' selector : {selector, '$2'}.
dispatch -> type_with_subscripts : {selector, #{function => nil, types => ['$1'], returns => nil}}.
dispatch -> nontrivial_selector : {selector, '$1'}.
selector -> typespec : #{function => nil, types => '$1', returns => nil}.
selector -> nontrivial_selector : '$1'.
nontrivial_selector -> typespec '->' type : #{function => nil, types => '$1', returns => '$3'}.
nontrivial_selector -> identifier typespec : #{function => '$1', types => '$2', returns => nil}.
nontrivial_selector -> identifier typespec '->' type : #{function => '$1', types => '$2', returns => '$4'}.
typespec -> '(' ')' : [].
typespec -> '(' comma_delimited_types ')' : '$2'.
tuple -> '(' ')' : {tuple, []}.
tuple -> '(' comma_delimited_types ')' : {tuple, '$2'}.
comma_delimited_types -> type_with_subscripts : ['$1'].
comma_delimited_types -> type_with_subscripts ',' comma_delimited_types : ['$1' | '$3'].
identifier -> atom : atom_to_list(v('$1')).
identifier -> binary : v('$1').
type_with_subscripts -> type : '$1'.
type_with_subscripts -> type array_subscripts : with_subscripts('$1', '$2').
array_subscripts -> array_subscript : ['$1'].
array_subscripts -> array_subscript array_subscripts : ['$1' | '$2'].
array_subscript -> '[' ']' : variable.
array_subscript -> '[' int ']' : v('$2').
type -> atom :
plain_type(v('$1')).
type -> atom int :
juxt_type(v('$1'), v('$2')).
type -> atom int identifier int :
double_juxt_type(v('$1'), v('$4'), v('$2'), v('$3')).
type -> tuple : '$1'.
Erlang code.
v({_Token, _Line, Value}) -> Value.
plain_type(address) -> address;
plain_type(bool) -> bool;
plain_type(function) -> function;
plain_type(string) -> string;
plain_type(bytes) -> bytes;
plain_type(int) -> juxt_type(int, 256);
plain_type(uint) -> juxt_type(uint, 256);
plain_type(fixed) -> double_juxt_type(fixed, "x", 128, 19);
plain_type(ufixed) -> double_juxt_type(ufixed, "x", 128, 19).
with_subscripts(Type, []) -> Type;
with_subscripts(Type, [H | T]) -> with_subscripts(with_subscript(Type, H), T).
with_subscript(Type, variable) -> {array, Type};
with_subscript(Type, N) when is_integer(N), N >= 0 -> {array, Type, N}.
juxt_type(int, M) when M > 0, M =< 256, (M rem 8) =:= 0 -> {int, M};
juxt_type(uint, M) when M > 0, M =< 256, (M rem 8) =:= 0 -> {uint, M};
juxt_type(bytes, M) when M > 0, M =< 32 -> {bytes, M}.
double_juxt_type(fixed, "x", M, N) when M >= 0, M =< 256, (M rem 8) =:= 0, N > 0, N =< 80 -> {fixed, M, N};
double_juxt_type(ufixed, "x", M, N) when M >= 0, M =< 256, (M rem 8) =:= 0, N > 0, N =< 80 -> {ufixed, M, N}.