sway-core 0.2.1

Sway core language.
Documentation
// tweakable things
var_decl_keyword    =  {"let"}
fn_decl_keyword     =  {"fn"}
trait_decl_keyword  =  {"trait"}
return_keyword      =  {"return"}
storage_keyword     =  {"storage"}
use_keyword         =  {"use"}
as_keyword          =  {"as"}
enum_keyword        = @{"enum"}
struct_keyword      = @{"struct"}
impl_keyword        =  {"impl"}
asm_keyword         =  {"asm"}
while_keyword       =  {"while"}
match_keyword       =  {"match"}
mut_keyword         =  {"mut"}
assign              = _{"="}
line_comment_open   =  {"//"}
block_comment_open  =  {"/*"}
block_comment_close =  {"*/"}
fn_returns          =  {"->"}
path_separator      =  {"::"}
include_keyword     =  {"dep"}
abi_keyword         =  {"abi"}
ref_keyword         =  {"ref "}
deref_keyword       =  {"deref "}
true_keyword        =  {"true"}
false_keyword       =  {"false"}
const_decl_keyword  =  {"const"}
impurity_keyword    =  {"impure"}

// top level
program =  {SOI ~ (library|contract|script|predicate)?  ~ EOI}

library      =  {"library" ~ library_name ~ ";" ~ (non_var_decl|use_statement|include_statement)* }
library_name =  {ident}
contract     =  {"contract" ~ ";" ~ (non_var_decl|use_statement|include_statement)*}
script       =  {"script" ~ ";" ~ (non_var_decl|use_statement|include_statement)*}
predicate    =  {"predicate" ~ ";" ~ (non_var_decl|use_statement|include_statement)*}

// including other files
file_path         = { ident ~ ("/" ~ ident)* }
include_statement = { include_keyword ~ file_path ~ alias? ~ ";"}
alias             = { as_keyword ~ ident }
// expressions
expr_inner               = _{unary_op_expr|asm_expression|match_expression|abi_cast|if_exp|code_block|func_app|literal_value|struct_expression|method_exp|struct_field_access|delineated_path|array_index|var_exp|array_exp|parenthesized_expression|tuple_expr}
parenthesized_expression =  {"(" ~ expr ~ ")"}
unary_op_expr = { unary_op ~ expr_inner }
// // op exps built in to expr to prevent left recursion
expr                     =  {expr_inner ~ (op ~ expr_inner)*}
func_app                 =  {fn_name ~ type_args? ~ fn_args}
type_args                =  {path_separator ~ type_params}
fn_args                  =  { "(" ~ (expr ~ ("," ~ expr)*)? ~ ")" }
fn_name                  =  {var_exp}
var_exp                  =  {var_name_ident}
var_name_ident           =  {ident}
struct_field_access      =  {subfield_path}
method_exp               =  {subfield_exp | fully_qualified_method}
subfield_exp             =  {subfield_path ~ fn_args}
// TODO subfield path should allow parenthesized expressions _or_ just idents
subfield_path            =  {(sub_subfield_path ~ ".")+ ~ call_item}
sub_subfield_path        =  {array_index|call_item}
fully_qualified_method   =  {path_separator? ~ (path_ident ~ path_separator)* ~ "~" ~ type_name ~ path_separator ~ call_item ~ fn_args}
call_item                =  {ident | "(" ~ expr ~ ")" }
delineated_path          =  {path_component ~ fn_args?}
path_component           =  {path_ident ~ (path_separator ~ path_ident)+}
path_ident               =  {ident}
array_index              =  {call_item ~ "[" ~ expr ~ "]" ~ ("[" ~ expr ~ "]")*}

// abi blocks and abi casting
abi_cast = {abi_keyword ~ "(" ~ trait_name ~ "," ~ expr ~ ")"}
abi_decl = {abi_keyword ~ abi_name ~ trait_methods}
abi_name = {ident}


if_exp =  {"if" ~ expr ~ code_block ~ ("else" ~ (code_block|if_exp))?}

op       =  {"+"|"-"|"/"|"*"|"=="|"!="|"<="|">="|"||"|"|"|"&&"|"&"|"^"|"%"|"<"|">"}
unary_op =  {"!"|ref_keyword|deref_keyword}

literal_value =  {integer|byte|string|boolean}

boolean          =  {true_keyword|false_keyword}
string           = ${"\"" ~ char* ~ "\""}
integer          =  {(u8_integer|u16_integer|u32_integer|u64_integer)}
basic_integer    = @{!("0b"|"0x") ~ ASCII_DIGIT ~ (ASCII_DIGIT|"_")*}
u8_integer       =  {basic_integer ~ "u8"}
u16_integer      =  {basic_integer ~ "u16"}
u32_integer      =  {basic_integer ~ "u32"}
// default is u64
u64_integer      =  {basic_integer ~ "u64"?}
byte             =  {binary_byte|hex_byte}
binary_byte      = @{"0b" ~ ("1"|"0"|"_")*}
hex_byte         = @{"0x" ~ hex_digit*}
hex_digit        =  {"a"|"b"|"c"|"d"|"e"|"f"|"A"|"B"|"C"|"D"|"E"|"F"|"_"|ASCII_DIGIT}

match_expression =  {"match" ~ expr ~ "{" ~ match_branch+ ~ "}"}
match_branch     =  {match_scrutinee ~ "=>" ~ (code_block|expr) ~ ","}
match_scrutinee  =  {scrutinee|catch_all}
catch_all        =  {"_"}
scrutinee        =  {enum_scrutinee|struct_scrutinee|tuple_scrutinee|literal_value|ident}
struct_scrutinee =  {struct_name ~ "{" ~ struct_scrutinee_fields ~"}"}
struct_scrutinee_fields = {struct_scrutinee_field ~ ("," ~ struct_scrutinee_field)* ~ ","?}
struct_scrutinee_field = {ident ~ field_scrutinee?}
field_scrutinee  =  {":" ~ scrutinee}
enum_scrutinee   =  {enum_scrutinee_component ~ fn_args_scrutinee?}
enum_scrutinee_component = {path_ident ~ (path_separator ~ path_ident)+}
fn_args_scrutinee =  { "(" ~ (scrutinee ~ ("," ~ scrutinee)*)? ~ ")" }
tuple_scrutinee  =  { "(" ~ (scrutinee ~ ("," ~ scrutinee)* ~ ","?)? ~ ")" }


code_block =  {"{" ~ (declaration|control_flow|expr_statement)* ~ (expr)? ~ "}"}

struct_expression  =  {struct_name ~ "{" ~ struct_expr_fields ~ "}"}
struct_expr_fields =  {(struct_field_name ~ ":" ~ expr ~ ("," ~ struct_field_name ~ ":" ~ expr)* ~ ","?)?}
array_exp          =  {"[" ~ array_elems?  ~ "]"}
// Strictly speaking the [val; count] initialiser for a static array can have any constant expression
// for the value and the count, but Sway doesn't yet have constant expression resolution, so for now
// we can use a literal and an integer.
array_elems        =  {literal_value ~ ";" ~ u64_integer|expr ~ ("," ~ expr)*}

// declarations
declaration               =  {(non_var_decl|var_decl|reassignment)}
non_var_decl              =  {(enum_decl|storage_decl|fn_decl|trait_decl|abi_decl|struct_decl|impl_trait|impl_self|const_decl)}
var_decl                  =  {var_decl_keyword ~ mut_keyword? ~ var_name ~ type_ascription? ~ assign ~ expr ~ ";"}
type_ascription           =  {":" ~ type_name}
fn_decl                   =  {visibility ~ fn_signature ~ code_block}
fn_signature              =  {impurity_keyword? ~ fn_decl_keyword ~ fn_decl_name ~ type_params? ~ fn_decl_params ~ (fn_returns ~ type_name)? ~ trait_bounds?}
var_name                  =  {ident}
reassignment              =  {variable_reassignment | struct_field_reassignment}
variable_reassignment     =  {var_exp ~ assign ~ expr ~ ";"}
struct_field_reassignment =  {struct_field_access ~ assign ~ expr ~ ";" }
const_decl                =  {visibility ~ const_decl_keyword ~ var_name ~ type_ascription? ~ assign ~ literal_value ~ ";"}

visibility =  {"pub"?}

struct_decl       =  {visibility ~ struct_keyword ~ struct_name ~ type_params? ~ trait_bounds? ~ "{" ~ struct_fields ~ "}"}
storage_decl      =  {storage_keyword ~ "{" ~ storage_fields ~ "}"}
storage_fields    =  {storage_field ~ ("," ~ storage_field)* ~ ","?}
storage_field     =  {ident ~ ":" ~ type_name ~ assign ~ expr}
struct_name       =  {ident}
struct_fields     =  {(struct_field_name ~ ":" ~ type_name ~ ("," ~ struct_field_name ~ ":" ~ type_name)* ~ ","?)?}
struct_field_name =  {ident}
// // enum declaration
enum_decl         =  {visibility ~ enum_keyword ~ enum_name ~ type_params? ~ trait_bounds? ~ "{" ~ enum_fields ~ "}"}
enum_fields       =  {(enum_field_name ~ ":" ~ type_name ~ ("," ~ enum_field_name ~ ":" ~ type_name)* ~ ","?)?}
enum_name         =  {ident}
enum_field_name   =  {ident}

impl_self =  {impl_keyword ~ type_params? ~ type_name ~  trait_bounds? ~ ("{" ~ fn_decl* ~ "}")}

// // fn declaration
fn_decl_params     =  {"(" ~ (fn_decl_param ~ ("," ~ fn_decl_param)*)? ~ ")"}
type_params        =  {"<" ~ generic_type_param ~ (", " ~ generic_type_param)* ~ ">"}
fn_decl_param      =  {("self")|(fn_decl_param_name ~ ":" ~ type_name)}
fn_decl_param_name =  {ident}
fn_decl_name       =  {ident}
type_name          =  {str_type|ident ~ type_params?|tuple_type|array_type}
str_type           =  { "str" ~ "[" ~ basic_integer ~ "]" }
trait_bounds       =  {"where" ~ (generic_type_param ~ ":" ~ trait_name) ~ ("," ~ generic_type_param ~ ":" ~ trait_name)*}
generic_type_param =  {ident}
// Array size can be any constant u64 expression, but we don't properly support constant expressions.  See `array_elems rule above.
array_type         =  {"[" ~ type_name ~ ";" ~ u64_integer~ "]"}

// statements
// // statements are basically non-expressions that don't alter the namespace like declarations do
return_statement =  {return_keyword ~ expr? ~ ";"}
expr_statement   =  {expr ~ ";"}

// traits
trait_decl    =  {visibility ~ trait_decl_keyword ~ trait_name ~ type_params? ~ trait_bounds? ~ trait_methods}
trait_methods =  {"{" ~ (fn_signature ~ ";")* ~ "}" ~ ("{" ~ fn_decl* ~ "}")*}
trait_name    =  {ident ~ (path_separator ~ ident)*}
impl_trait    =  {impl_keyword ~ trait_name ~ type_params? ~ "for" ~ type_name ~ type_params? ~ trait_bounds? ~ ("{" ~ fn_decl* ~ "}")}

// imports
use_statement           =  {relative_use_statement | absolute_use_statement}
relative_use_statement  =  {use_keyword ~ import_path ~ ";"}
absolute_use_statement  =  {use_keyword ~ path_separator ~ import_path ~ ";"}
import_path             =  {star|(ident ~ (path_separator ~ ident)* ~ ((path_separator ~ star)|import_items|alias)?)}
import_items            =  {path_separator ~ "{" ~ import_path ~ alias? ~  ("," ~ import_path ~ alias?)* ~ "}"}
star                    =  {"*"}

// loops
while_loop =  {while_keyword ~ expr ~ code_block}

// asm inlining
asm_expression           =  {asm_keyword ~ asm_registers ~ "{" ~ asm_op* ~ asm_register? ~ (":" ~ type_name)? ~ "}"}
asm_registers            =  {"(" ~ (asm_register_declaration ~ ("," ~ asm_register_declaration)*)? ~ ")"}
asm_register_declaration =  {ident ~ (":" ~ expr)?}
asm_op                   =  {opcode ~ (asm_immediate|asm_register)* ~ ";"}
asm_register             =  {ident}
asm_immediate            =  {"i" ~ u64_integer}
opcode                   =  {ident}

// control flow
control_flow = _{while_loop|return_statement}

// boilerplate
WHITESPACE     = _{(" "|"\t"|"\r"|"\n")+}
COMMENT        = _{block_comment|line_comment}
block_comment  = @{block_comment_open ~ (!block_comment_close ~ ANY)* ~ block_comment_close}
line_comment   = @{line_comment_open ~ (!("\r"|"\n") ~ ANY)*}
char           = @{
    !("\""|"\\") ~ ANY
  | "\\" ~ ("\""|"\\"|"/"|"b"|"f"|"n"|"r"|"t")
  | "\\" ~ ("u" ~ ASCII_HEX_DIGIT{4})
}
tuple_type     =  {"(" ~ (type_name ~ ("," ~ type_name)* ~ ","?)? ~ ")" }
tuple_expr     =  {"(" ~ (expr ~ ("," ~ expr)* ~ ","?)? ~ ")" }
ident          = @{ ASCII_ALPHA ~ (ASCII_ALPHANUMERIC|"_")* }
reserved_words = @{(true_keyword|false_keyword|asm_keyword|ref_keyword|deref_keyword|abi_keyword|while_keyword|struct_keyword|enum_keyword|match_keyword|use_keyword|var_decl_keyword|fn_decl_keyword|trait_decl_keyword|return_keyword|include_keyword) ~ !(ASCII_ALPHANUMERIC|"_")}