// Copyright (c) 2023 Yuki Kimoto // MIT License %pure-parser %parse-param { SPVM_COMPILER* compiler } %lex-param { SPVM_COMPILER* compiler } %{ #include #include "spvm_compiler.h" #include "spvm_yacc_util.h" #include "spvm_toke.h" #include "spvm_op.h" #include "spvm_dumper.h" #include "spvm_constant.h" #include "spvm_type.h" #include "spvm_block.h" #include "spvm_list.h" #include "spvm_attribute.h" #include "spvm_string.h" %} %token CLASS HAS METHOD OUR ENUM MY USE AS REQUIRE ALIAS ALLOW OUTMOST_CLASS MUTABLE %token ATTRIBUTE MAKE_READ_ONLY INTERFACE EVAL_ERROR_ID ARGS_WIDTH VERSION_DECL VERSION_FROM %token IF UNLESS ELSIF ELSE FOR WHILE LAST NEXT SWITCH CASE DEFAULT BREAK EVAL %token SYMBOL_NAME VAR_NAME CONSTANT EXCEPTION_VAR COPY_FIELDS %token UNDEF VOID BYTE SHORT INT LONG FLOAT DOUBLE STRING OBJECT TRUE FALSE END_OF_FILE %token FATCAMMA RW RO WO INIT NEW OF BASIC_TYPE_ID EXTENDS SUPER %token RETURN WEAKEN DIE WARN PRINT SAY OUTMOST_CLASS_NAME UNWEAKEN '[' '{' '(' %type grammar %type field_name method_name class_name %type type qualified_type basic_type array_type opt_basic_type %type array_type_with_length ref_type return_type type_comment opt_type_comment union_type %type opt_classes classes class class_block opt_extends version_decl version_from %type opt_definitions definitions definition %type enumeration enumeration_block opt_enumeration_items enumeration_items enumeration_item %type method anon_method opt_args args arg use require class_alias our has anon_method_fields anon_method_field interface allow %type opt_attributes attributes %type opt_statements statements statement if_statement else_statement %type for_statement while_statement foreach_statement %type switch_statement case_statement case_statements opt_case_statements default_statement %type block eval_block init_statement switch_block if_require_statement %type die %type var_decl var %type operator opt_operators operators opt_operator %type void_return_operator warn %type unary_operator array_length %type inc dec %type binary_operator arithmetic_operator bit_operator comparison_operator string_concatenation logical_operator defined_or ternary_operator %type assign %type new array_init %type type_check type_cast can %type call_method %type array_access field_access %type weaken_field unweaken_field isweak_field %type sequential copy_fields %right ASSIGN SPECIAL_ASSIGN %left LOGICAL_OR DEFINED_OR %left LOGICAL_AND %left BIT_OR BIT_XOR %left BIT_AND %nonassoc NUMEQ NUMNE STREQ STRNE %nonassoc NUMGT NUMGE NUMLT NUMLE STRGT STRGE STRLT STRLE ISA ISA_ERROR IS_TYPE IS_ERROR IS_COMPILE_TYPE NUMERIC_CMP STRING_CMP CAN %left SHIFT %left '+' '-' '.' %left '*' DIVIDE DIVIDE_UNSIGNED_INT DIVIDE_UNSIGNED_LONG MODULO MODULO_UNSIGNED_INT MODULO_UNSIGNED_LONG %right LOGICAL_NOT BIT_NOT '@' REFERENCE DEREFERENCE PLUS MINUS CONVERT SCALAR STRING_LENGTH ISWEAK TYPE_NAME COMPILE_TYPE_NAME DUMP NEW_STRING_LEN IS_READ_ONLY COPY %nonassoc INC DEC %left ARROW %% grammar : opt_classes field_name : SYMBOL_NAME method_name : SYMBOL_NAME class_name : SYMBOL_NAME qualified_type : type | MUTABLE type { $$ = SPVM_OP_build_mutable_type(compiler, $2); } type : basic_type | array_type | ref_type basic_type : SYMBOL_NAME { $$ = SPVM_OP_build_basic_type(compiler, $1); } | BYTE { SPVM_OP* op_type = SPVM_OP_new_op_byte_type(compiler, $1->file, $1->line); $$ = op_type; } | SHORT { SPVM_OP* op_type = SPVM_OP_new_op_short_type(compiler, $1->file, $1->line); $$ = op_type; } | INT { SPVM_OP* op_type = SPVM_OP_new_op_int_type(compiler, $1->file, $1->line); $$ = op_type; } | LONG { SPVM_OP* op_type = SPVM_OP_new_op_long_type(compiler, $1->file, $1->line); $$ = op_type; } | FLOAT { SPVM_OP* op_type = SPVM_OP_new_op_float_type(compiler, $1->file, $1->line); $$ = op_type; } | DOUBLE { SPVM_OP* op_type = SPVM_OP_new_op_double_type(compiler, $1->file, $1->line); $$ = op_type; } | OBJECT { SPVM_OP* op_type = SPVM_OP_new_op_any_object_type(compiler, $1->file, $1->line); $$ = op_type; } | STRING { SPVM_OP* op_type = SPVM_OP_new_op_string_type(compiler, $1->file, $1->line); $$ = op_type; } ref_type : basic_type '*' { $$ = SPVM_OP_build_ref_type(compiler, $1); } array_type : basic_type '[' ']' { $$ = SPVM_OP_build_array_type(compiler, $1, NULL); } | array_type '[' ']' { $$ = SPVM_OP_build_array_type(compiler, $1, NULL); } array_type_with_length : basic_type '[' operator ']' { $$ = SPVM_OP_build_array_type(compiler, $1, $3); } | array_type '[' operator ']' { $$ = SPVM_OP_build_array_type(compiler, $1, $3); } return_type : qualified_type opt_type_comment | VOID { $$ = SPVM_OP_new_op_void_type(compiler, compiler->current_file, compiler->current_line); } opt_type_comment : /* Empty */ { $$ = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_DO_NOTHING, compiler->current_file, compiler->current_line); } | type_comment type_comment : OF union_type { $$ = $2; } union_type : union_type BIT_OR type { SPVM_OP* op_list; if ($1->id == SPVM_OP_C_ID_LIST) { op_list = $1; } else { op_list = SPVM_OP_new_op_list(compiler, $1->file, $1->line); SPVM_OP_insert_child(compiler, op_list, op_list->last, $1); } SPVM_OP_insert_child(compiler, op_list, op_list->last, $3); $$ = op_list; } | type { $$ = $1; } opt_classes : /* Empty */ { $$ = SPVM_OP_new_op_list(compiler, compiler->current_file, compiler->current_line); } | classes { if ($1->id == SPVM_OP_C_ID_LIST) { $$ = $1; } else { SPVM_OP* op_list = SPVM_OP_new_op_list(compiler, $1->file, $1->line); SPVM_OP_insert_child(compiler, op_list, op_list->last, $1); $$ = op_list; } } classes : classes class { SPVM_OP* op_list; if ($1->id == SPVM_OP_C_ID_LIST) { op_list = $1; } else { op_list = SPVM_OP_new_op_list(compiler, $1->file, $1->line); SPVM_OP_insert_child(compiler, op_list, op_list->last, $1); } SPVM_OP_insert_child(compiler, op_list, op_list->last, $2); $$ = op_list; } | class class : CLASS opt_basic_type opt_extends class_block END_OF_FILE { $$ = SPVM_OP_build_class(compiler, $1, $2, $4, NULL, $3); } | CLASS opt_basic_type opt_extends ':' opt_attributes class_block END_OF_FILE { $$ = SPVM_OP_build_class(compiler, $1, $2, $6, $5, $3); } | CLASS opt_basic_type opt_extends ';' END_OF_FILE { $$ = SPVM_OP_build_class(compiler, $1, $2, NULL, NULL, $3); } | CLASS opt_basic_type opt_extends ':' opt_attributes ';' END_OF_FILE { $$ = SPVM_OP_build_class(compiler, $1, $2, NULL, $5, $3); } opt_basic_type : /* Empty */ { $$ = NULL; } | basic_type opt_extends : /* Empty */ { $$ = NULL; } | EXTENDS basic_type { $$ = SPVM_OP_build_extends(compiler, $1, $2); } class_block : '{' opt_definitions '}' { SPVM_OP* op_class_block = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_CLASS_BLOCK, $1->file, $1->line); SPVM_OP_insert_child(compiler, op_class_block, op_class_block->last, $2); $$ = op_class_block; } opt_definitions : /* Empty */ { $$ = SPVM_OP_new_op_list(compiler, compiler->current_file, compiler->current_line); } | definitions { if ($1->id == SPVM_OP_C_ID_LIST) { $$ = $1; } else { $$ = SPVM_OP_new_op_list(compiler, $1->file, $1->line); SPVM_OP_insert_child(compiler, $$, $$->last, $1); } } definitions : definitions definition { SPVM_OP* op_list; if ($1->id == SPVM_OP_C_ID_LIST) { op_list = $1; } else { op_list = SPVM_OP_new_op_list(compiler, $1->file, $1->line); SPVM_OP_insert_child(compiler, op_list, op_list->last, $1); } SPVM_OP_insert_child(compiler, op_list, op_list->last, $2); $$ = op_list; } | definition definition : version_decl | version_from | use | class_alias | allow | interface | init_statement | enumeration | our | has ';' | method init_statement : INIT block { $$ = SPVM_OP_build_init_statement(compiler, $1, $2); } version_decl : VERSION_DECL CONSTANT ';' { $$ = SPVM_OP_build_version_decl(compiler, $1, $2); } version_from : VERSION_FROM basic_type ';' { $$ = SPVM_OP_build_version_from(compiler, $1, $2); } use : USE basic_type ';' { int32_t is_require = 0; SPVM_OP* op_name_class_alias = NULL; $$ = SPVM_OP_build_use(compiler, $1, $2, op_name_class_alias, is_require); } | USE basic_type AS class_name ';' { int32_t is_require = 0; $$ = SPVM_OP_build_use(compiler, $1, $2, $4, is_require); } require : REQUIRE basic_type { SPVM_OP* op_use = SPVM_OP_new_op_use(compiler, compiler->current_file, compiler->current_line); int32_t is_require = 1; $$ = SPVM_OP_build_use(compiler, op_use, $2, NULL, is_require); } class_alias : ALIAS basic_type AS class_name ';' { SPVM_OP* op_use = SPVM_OP_new_op_use(compiler, compiler->current_file, compiler->current_line); $$ = SPVM_OP_build_alias(compiler, op_use, $2, $4); } allow : ALLOW basic_type ';' { $$ = SPVM_OP_build_allow(compiler, $1, $2); } interface : INTERFACE basic_type ';' { $$ = SPVM_OP_build_interface_statement(compiler, $1, $2); } enumeration : opt_attributes ENUM enumeration_block { $$ = SPVM_OP_build_enumeration(compiler, $2, $3, $1); } enumeration_block : '{' opt_enumeration_items '}' { SPVM_OP* op_enum_block = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_ENUM_BLOCK, $1->file, $1->line); SPVM_OP_insert_child(compiler, op_enum_block, op_enum_block->last, $2); $$ = op_enum_block; } opt_enumeration_items : /* Empty */ { $$ = SPVM_OP_new_op_list(compiler, compiler->current_file, compiler->current_line); } | enumeration_items { if ($1->id == SPVM_OP_C_ID_LIST) { $$ = $1; } else { SPVM_OP* op_list = SPVM_OP_new_op_list(compiler, $1->file, $1->line); SPVM_OP_insert_child(compiler, op_list, op_list->last, $1); $$ = op_list; } } enumeration_items : enumeration_items ',' enumeration_item { SPVM_OP* op_list; if ($1->id == SPVM_OP_C_ID_LIST) { op_list = $1; } else { op_list = SPVM_OP_new_op_list(compiler, $1->file, $1->line); SPVM_OP_insert_child(compiler, op_list, op_list->last, $1); } SPVM_OP_insert_child(compiler, op_list, op_list->last, $3); $$ = op_list; } | enumeration_items ',' | enumeration_item enumeration_item : method_name { $$ = SPVM_OP_build_enumeration_item(compiler, $1, NULL); } | method_name ASSIGN CONSTANT { $$ = SPVM_OP_build_enumeration_item(compiler, $1, $3); } our : OUR VAR_NAME ':' opt_attributes qualified_type opt_type_comment ';' { $$ = SPVM_OP_build_class_var(compiler, $1, $2, $4, $5); } has : HAS field_name ':' opt_attributes qualified_type opt_type_comment { $$ = SPVM_OP_build_field(compiler, $1, $2, $4, $5); } method : opt_attributes METHOD method_name ':' return_type '(' opt_args ')' block { $$ = SPVM_OP_build_method(compiler, $2, $3, $5, $7, $1, $9); } | opt_attributes METHOD method_name ':' return_type '(' opt_args ')' ';' { $$ = SPVM_OP_build_method(compiler, $2, $3, $5, $7, $1, NULL); } | opt_attributes METHOD ':' return_type '(' opt_args ')' block { $$ = SPVM_OP_build_method(compiler, $2, NULL, $4, $6, $1, $8); } | opt_attributes METHOD ':' return_type '(' opt_args ')' ';' { $$ = SPVM_OP_build_method(compiler, $2, NULL, $4, $6, $1, NULL); } anon_method : opt_attributes METHOD ':' return_type '(' opt_args ')' block { $$ = SPVM_OP_build_method(compiler, $2, NULL, $4, $6, $1, $8); } | '[' anon_method_fields ']' opt_attributes METHOD ':' return_type '(' opt_args ')' block { SPVM_OP* op_anon_method_fields; if ($2->id == SPVM_OP_C_ID_LIST) { op_anon_method_fields = $2; } else { op_anon_method_fields = SPVM_OP_new_op_list(compiler, $2->file, $2->line); SPVM_OP_insert_child(compiler, op_anon_method_fields, op_anon_method_fields->last, $2); } $$ = SPVM_OP_build_method(compiler, $5, NULL, $7, $9, $4, $11); SPVM_OP_attach_anon_method_fields(compiler, $$, op_anon_method_fields); } opt_args : /* Empty */ { $$ = SPVM_OP_new_op_list(compiler, compiler->current_file, compiler->current_line); } | args { if ($1->id == SPVM_OP_C_ID_LIST) { $$ = $1; } else { SPVM_OP* op_list = SPVM_OP_new_op_list(compiler, $1->file, $1->line); SPVM_OP_insert_child(compiler, op_list, op_list->last, $1); $$ = op_list; } } args : args ',' arg { SPVM_OP* op_list; if ($1->id == SPVM_OP_C_ID_LIST) { op_list = $1; } else { op_list = SPVM_OP_new_op_list(compiler, $1->file, $1->line); SPVM_OP_insert_child(compiler, op_list, op_list->last, $1); } SPVM_OP_insert_child(compiler, op_list, op_list->last, $3); $$ = op_list; } | args ',' | arg arg : var ':' qualified_type opt_type_comment { $$ = SPVM_OP_build_arg(compiler, $1, $3, NULL, NULL); } | var ':' qualified_type opt_type_comment ASSIGN operator { $$ = SPVM_OP_build_arg(compiler, $1, $3, NULL, $6); } anon_method_fields : anon_method_fields ',' anon_method_field { SPVM_OP* op_list; if ($1->id == SPVM_OP_C_ID_LIST) { op_list = $1; } else { op_list = SPVM_OP_new_op_list(compiler, $1->file, $1->line); SPVM_OP_insert_child(compiler, op_list, op_list->last, $1); } SPVM_OP_insert_child(compiler, op_list, op_list->last, $3); $$ = op_list; } | anon_method_fields ',' | anon_method_field anon_method_field : HAS field_name ':' opt_attributes qualified_type opt_type_comment { $$ = SPVM_OP_build_anon_method_field(compiler, $1, $2, $4, $5, NULL); } | HAS field_name ':' opt_attributes qualified_type opt_type_comment ASSIGN operator { $$ = SPVM_OP_build_anon_method_field(compiler, $1, $2, $4, $5, $8); } | var ':' opt_attributes qualified_type opt_type_comment { SPVM_OP* op_field = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_FIELD, $1->file, $1->line); $$ = SPVM_OP_build_anon_method_field(compiler, op_field, NULL, $3, $4, $1); } | var ':' opt_attributes qualified_type opt_type_comment ASSIGN operator { SPVM_OP* op_field = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_FIELD, $1->file, $1->line); $$ = SPVM_OP_build_anon_method_field(compiler, op_field, $1, $3, $4, $7); } opt_attributes : /* Empty */ { $$ = SPVM_OP_new_op_list(compiler, compiler->current_file, compiler->current_line); } | attributes { if ($1->id == SPVM_OP_C_ID_LIST) { $$ = $1; } else { SPVM_OP* op_list = SPVM_OP_new_op_list(compiler, $1->file, $1->line); SPVM_OP_insert_child(compiler, op_list, op_list->last, $1); $$ = op_list; } } attributes : attributes ATTRIBUTE { SPVM_OP* op_list; if ($1->id == SPVM_OP_C_ID_LIST) { op_list = $1; } else { op_list = SPVM_OP_new_op_list(compiler, $1->file, $1->line); SPVM_OP_insert_child(compiler, op_list, op_list->last, $1); } SPVM_OP_insert_child(compiler, op_list, op_list->last, $2); $$ = op_list; } | ATTRIBUTE opt_statements : /* Empty */ { $$ = SPVM_OP_new_op_list(compiler, compiler->current_file, compiler->current_line); } | statements { if ($1->id == SPVM_OP_C_ID_LIST) { $$ = $1; } else { SPVM_OP* op_list = SPVM_OP_new_op_list(compiler, $1->file, $1->line); SPVM_OP_insert_child(compiler, op_list, op_list->last, $1); $$ = op_list; } } statements : statements statement { SPVM_OP* op_list; if ($1->id == SPVM_OP_C_ID_LIST) { op_list = $1; } else { op_list = SPVM_OP_new_op_list(compiler, $1->file, $1->line); SPVM_OP_insert_child(compiler, op_list, op_list->last, $1); } SPVM_OP_insert_child(compiler, op_list, op_list->last, $2); $$ = op_list; } | statement statement : if_statement | for_statement | foreach_statement | while_statement | block | switch_statement | case_statement | default_statement | eval_block | if_require_statement | LAST ';' | NEXT ';' | BREAK ';' | RETURN ';' { $$ = SPVM_OP_build_return(compiler, $1, NULL); } | RETURN operator ';' { $$ = SPVM_OP_build_return(compiler, $1, $2); } | operator ';' { $$ = SPVM_OP_build_operator_statement(compiler, $1); } | void_return_operator ';' | ';' { $$ = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_DO_NOTHING, compiler->current_file, compiler->current_line); } | die ';' | copy_fields copy_fields : COPY_FIELDS operator ',' operator ',' type';' { $$ = SPVM_OP_build_copy_fields(compiler, $1, $2, $4, $6); } die : DIE operator { $$ = SPVM_OP_build_die(compiler, $1, $2, NULL); } | DIE { $$ = SPVM_OP_build_die(compiler, $1, NULL, NULL); } | DIE type operator { $$ = SPVM_OP_build_die(compiler, $1, $3, $2); } | DIE type { $$ = SPVM_OP_build_die(compiler, $1, NULL, $2); } | DIE operator ',' operator { $$ = SPVM_OP_build_die(compiler, $1, $4, $2); } void_return_operator : warn | PRINT operator { $$ = SPVM_OP_build_print(compiler, $1, $2); } | SAY operator { $$ = SPVM_OP_build_print(compiler, $1, $2); } | weaken_field | unweaken_field | MAKE_READ_ONLY operator { $$ = SPVM_OP_build_make_read_only(compiler, $1, $2); } warn : WARN operator { $$ = SPVM_OP_build_warn(compiler, $1, $2); } | WARN { $$ = SPVM_OP_build_warn(compiler, $1, NULL); } for_statement : FOR '(' opt_operator ';' operator ';' opt_operator ')' block { $$ = SPVM_OP_build_for_statement(compiler, $1, $3, $5, $7, $9); } foreach_statement : FOR var_decl '(' '@' operator ')' block { $$ = SPVM_OP_build_foreach_statement(compiler, $1, $2, $5, $7); } | FOR var_decl '(' '@' '{' operator '}' ')' block { $$ = SPVM_OP_build_foreach_statement(compiler, $1, $2, $6, $9); } while_statement : WHILE '(' operator ')' block { $$ = SPVM_OP_build_while_statement(compiler, $1, $3, $5); } switch_statement : SWITCH '(' operator ')' switch_block { $$ = SPVM_OP_build_switch_statement(compiler, $1, $3, $5); } switch_block : '{' opt_case_statements '}' { SPVM_OP* op_block = SPVM_OP_new_op_block(compiler, compiler->current_file, compiler->current_line); op_block->uv.block->id = SPVM_BLOCK_C_ID_SWITCH; $$ = SPVM_OP_build_switch_block(compiler, op_block, $2, NULL); } | '{' opt_case_statements default_statement '}' { SPVM_OP* op_block = SPVM_OP_new_op_block(compiler, compiler->current_file, compiler->current_line); op_block->uv.block->id = SPVM_BLOCK_C_ID_SWITCH; $$ = SPVM_OP_build_switch_block(compiler, op_block, $2, $3); } opt_case_statements : /* Empty */ { $$ = SPVM_OP_new_op_list(compiler, compiler->current_file, compiler->current_line); } | case_statements { if ($1->id == SPVM_OP_C_ID_LIST) { $$ = $1; } else { SPVM_OP* op_list = SPVM_OP_new_op_list(compiler, $1->file, $1->line); SPVM_OP_insert_child(compiler, op_list, op_list->last, $1); $$ = op_list; } } case_statements : case_statements case_statement { SPVM_OP* op_list; if ($1->id == SPVM_OP_C_ID_LIST) { op_list = $1; } else { op_list = SPVM_OP_new_op_list(compiler, $1->file, $1->line); SPVM_OP_insert_child(compiler, op_list, op_list->last, $1); } SPVM_OP_insert_child(compiler, op_list, op_list->last, $2); $$ = op_list; } | case_statement { $$ = $1; } case_statement : CASE operator ':' block { $$ = SPVM_OP_build_case_statement(compiler, $1, $2, $4); } | CASE operator ':' { $$ = SPVM_OP_build_case_statement(compiler, $1, $2, NULL); } default_statement : DEFAULT ':' block { $$ = SPVM_OP_build_default_statement(compiler, $1, $3); } | DEFAULT ':' { $$ = SPVM_OP_build_default_statement(compiler, $1, NULL); } if_require_statement : IF '(' require ')' block { SPVM_OP* op_if_require = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_IF_REQUIRE, compiler->current_file, compiler->current_line); $$ = SPVM_OP_build_if_require_statement(compiler, op_if_require, $3, $5, NULL); } | IF '(' require ')' block ELSE block { SPVM_OP* op_if_require = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_IF_REQUIRE, compiler->current_file, compiler->current_line); $$ = SPVM_OP_build_if_require_statement(compiler, op_if_require, $3, $5, $7); } if_statement : IF '(' operator ')' block else_statement { SPVM_OP* op_if = SPVM_OP_build_if_statement(compiler, $1, $3, $5, $6, 0); // if is wraped with block to allow the following syntax // if (var_decl $var = 3) { ... } SPVM_OP* op_block = SPVM_OP_new_op_block(compiler, $1->file, $1->line); SPVM_OP_insert_child(compiler, op_block, op_block->last, op_if); $$ = op_block; } | UNLESS '(' operator ')' block else_statement { SPVM_OP* op_if = SPVM_OP_build_if_statement(compiler, $1, $3, $5, $6, 0); // if is wraped with block to allow the following syntax // if (var_decl $var = 3) { ... } SPVM_OP* op_block = SPVM_OP_new_op_block(compiler, $1->file, $1->line); SPVM_OP_insert_child(compiler, op_block, op_block->last, op_if); $$ = op_block; } else_statement : /* NULL */ { $$ = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_DO_NOTHING, compiler->current_file, compiler->current_line); }; | ELSE block { $$ = $2; } | ELSIF '(' operator ')' block else_statement { $$ = SPVM_OP_build_if_statement(compiler, $1, $3, $5, $6, 0); } block : '{' opt_statements '}' { SPVM_OP* op_block = SPVM_OP_new_op_block(compiler, $1->file, $1->line); SPVM_OP_insert_child(compiler, op_block, op_block->last, $2); $$ = op_block; } eval_block : EVAL block { $$ = SPVM_OP_build_eval(compiler, $1, $2); } var_decl : MY var ':' qualified_type opt_type_comment { $$ = SPVM_OP_build_var_decl(compiler, $1, $2, $4, NULL); } | MY var { $$ = SPVM_OP_build_var_decl(compiler, $1, $2, NULL, NULL); } var : VAR_NAME { $$ = SPVM_OP_build_var(compiler, $1); } opt_operators : /* Empty */ { $$ = SPVM_OP_new_op_list(compiler, compiler->current_file, compiler->current_line); } | operators { if ($1->id == SPVM_OP_C_ID_LIST) { $$ = $1; } else { SPVM_OP* op_list = SPVM_OP_new_op_list(compiler, $1->file, $1->line); SPVM_OP_insert_child(compiler, op_list, op_list->last, $1); $$ = op_list; } } opt_operator : /* Empty */ { $$ = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_DO_NOTHING, compiler->current_file, compiler->current_line); } | operator operator : var | EXCEPTION_VAR | CONSTANT | UNDEF | type_cast | new | var_decl | EVAL_ERROR_ID | ARGS_WIDTH | TRUE { $$ = SPVM_OP_new_op_true(compiler, $1); } | FALSE { $$ = SPVM_OP_new_op_false(compiler, $1); } | OUTMOST_CLASS_NAME | unary_operator | binary_operator | ternary_operator | assign | inc | dec | type_check | BASIC_TYPE_ID type { $$ = SPVM_OP_build_basic_type_id(compiler, $1, $2); } | can | array_init | array_access | field_access | isweak_field | call_method | sequential sequential : '(' operators ')' { if ($2->id == SPVM_OP_C_ID_LIST) { SPVM_OP* op_operator = $2->first; SPVM_OP* op_sequence = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_SEQUENCE, compiler->current_file, compiler->current_line); while ((op_operator = SPVM_OP_sibling(compiler, op_operator))) { SPVM_OP* op_stab = SPVM_OP_cut_op(compiler, op_operator); SPVM_OP_insert_child(compiler, op_sequence, op_sequence->last, op_operator); op_operator = op_stab; } $$ = op_sequence; } else { $$ = $2; } } | '(' operators ',' if_statement ',' operator ')' { SPVM_OP* op_operator = $2->first; SPVM_OP* op_sequence = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_SEQUENCE, compiler->current_file, compiler->current_line); SPVM_OP_insert_child(compiler, op_sequence, op_sequence->last, $2); SPVM_OP_insert_child(compiler, op_sequence, op_sequence->last, $4); SPVM_OP_insert_child(compiler, op_sequence, op_sequence->last, $6); $$ = op_sequence; } operators : operators ',' operator { SPVM_OP* op_list; if ($1->id == SPVM_OP_C_ID_LIST) { op_list = $1; } else { op_list = SPVM_OP_new_op_list(compiler, $1->file, $1->line); SPVM_OP_insert_child(compiler, op_list, op_list->last, $1); } SPVM_OP_insert_child(compiler, op_list, op_list->last, $3); $$ = op_list; } | operators ',' { $$ = $1; } | operator { $$ = $1; } unary_operator : '+' operator %prec PLUS { SPVM_OP* operator = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_PLUS, $1->file, $1->line); $$ = SPVM_OP_build_unary_op(compiler, operator, $2); } | '-' operator %prec MINUS { SPVM_OP* op_negate = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_MINUS, $1->file, $1->line); $$ = SPVM_OP_build_unary_op(compiler, op_negate, $2); } | BIT_NOT operator { $$ = SPVM_OP_build_unary_op(compiler, $1, $2); } | TYPE_NAME operator { $$ = SPVM_OP_build_unary_op(compiler, $1, $2); } | COMPILE_TYPE_NAME operator { $$ = SPVM_OP_build_unary_op(compiler, $1, $2); } | STRING_LENGTH operator { $$ = SPVM_OP_build_unary_op(compiler, $1, $2); } | DUMP operator { $$ = SPVM_OP_build_unary_op(compiler, $1, $2); } | DEREFERENCE var { $$ = SPVM_OP_build_unary_op_var(compiler, $1, $2); } | REFERENCE operator { $$ = SPVM_OP_build_unary_op_var(compiler, $1, $2); } | NEW_STRING_LEN operator { $$ = SPVM_OP_build_unary_op(compiler, $1, $2); } | COPY operator { $$ = SPVM_OP_build_unary_op(compiler, $1, $2); } | IS_READ_ONLY operator { $$ = SPVM_OP_build_unary_op(compiler, $1, $2); } | array_length array_length : '@' operator { SPVM_OP* op_array_length = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_ARRAY_LENGTH, compiler->current_file, compiler->current_line); $$ = SPVM_OP_build_array_length(compiler, op_array_length, $2); } | '@' '{' operator '}' { SPVM_OP* op_array_length = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_ARRAY_LENGTH, compiler->current_file, compiler->current_line); $$ = SPVM_OP_build_array_length(compiler, op_array_length, $3); } | SCALAR '@' operator { SPVM_OP* op_array_length = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_ARRAY_LENGTH, compiler->current_file, compiler->current_line); $$ = SPVM_OP_build_array_length(compiler, op_array_length, $3); } | SCALAR '@' '{' operator '}' { SPVM_OP* op_array_length = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_ARRAY_LENGTH, compiler->current_file, compiler->current_line); $$ = SPVM_OP_build_array_length(compiler, op_array_length, $4); } inc : INC operator { SPVM_OP* operator = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_PRE_INC, $1->file, $1->line); $$ = SPVM_OP_build_inc(compiler, operator, $2); } | operator INC { SPVM_OP* operator = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_POST_INC, $2->file, $2->line); $$ = SPVM_OP_build_inc(compiler, operator, $1); } dec : DEC operator { SPVM_OP* operator = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_PRE_DEC, $1->file, $1->line); $$ = SPVM_OP_build_dec(compiler, operator, $2); } | operator DEC { SPVM_OP* operator = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_POST_DEC, $2->file, $2->line); $$ = SPVM_OP_build_dec(compiler, operator, $1); } binary_operator : arithmetic_operator | bit_operator | comparison_operator | string_concatenation | logical_operator | defined_or arithmetic_operator : operator '+' operator { SPVM_OP* operator = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_ADD, $2->file, $2->line); $$ = SPVM_OP_build_binary_op(compiler, operator, $1, $3); } | operator '-' operator { SPVM_OP* operator = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_SUBTRACT, $2->file, $2->line); $$ = SPVM_OP_build_binary_op(compiler, operator, $1, $3); } | operator '*' operator { SPVM_OP* operator = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_MULTIPLY, $2->file, $2->line); $$ = SPVM_OP_build_binary_op(compiler, operator, $1, $3); } | operator DIVIDE operator { $$ = SPVM_OP_build_binary_op(compiler, $2, $1, $3); } | operator DIVIDE_UNSIGNED_INT operator { $$ = SPVM_OP_build_binary_op(compiler, $2, $1, $3); } | operator DIVIDE_UNSIGNED_LONG operator { $$ = SPVM_OP_build_binary_op(compiler, $2, $1, $3); } | operator MODULO operator { $$ = SPVM_OP_build_binary_op(compiler, $2, $1, $3); } | operator MODULO_UNSIGNED_INT operator { $$ = SPVM_OP_build_binary_op(compiler, $2, $1, $3); } | operator MODULO_UNSIGNED_LONG operator { $$ = SPVM_OP_build_binary_op(compiler, $2, $1, $3); } ternary_operator : operator '?' operator ':' operator { SPVM_OP* ternary_op = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_TERNARY_OP, $1->file, $1->line); $$ = SPVM_OP_build_ternary_op(compiler, ternary_op, $1, $3, $5); } bit_operator : operator BIT_XOR operator { $$ = SPVM_OP_build_binary_op(compiler, $2, $1, $3); } | operator BIT_AND operator { $$ = SPVM_OP_build_binary_op(compiler, $2, $1, $3); } | operator BIT_OR operator { $$ = SPVM_OP_build_binary_op(compiler, $2, $1, $3); } | operator SHIFT operator { $$ = SPVM_OP_build_binary_op(compiler, $2, $1, $3); } comparison_operator : operator NUMEQ operator { $$ = SPVM_OP_build_binary_op(compiler, $2, $1, $3); } | operator NUMNE operator { $$ = SPVM_OP_build_binary_op(compiler, $2, $1, $3); } | operator NUMGT operator { $$ = SPVM_OP_build_binary_op(compiler, $2, $1, $3); } | operator NUMGE operator { $$ = SPVM_OP_build_binary_op(compiler, $2, $1, $3); } | operator NUMLT operator { $$ = SPVM_OP_build_binary_op(compiler, $2, $1, $3); } | operator NUMLE operator { $$ = SPVM_OP_build_binary_op(compiler, $2, $1, $3); } | operator NUMERIC_CMP operator { $$ = SPVM_OP_build_binary_op(compiler, $2, $1, $3); } | operator STREQ operator { $$ = SPVM_OP_build_binary_op(compiler, $2, $1, $3); } | operator STRNE operator { $$ = SPVM_OP_build_binary_op(compiler, $2, $1, $3); } | operator STRGT operator { $$ = SPVM_OP_build_binary_op(compiler, $2, $1, $3); } | operator STRGE operator { $$ = SPVM_OP_build_binary_op(compiler, $2, $1, $3); } | operator STRLT operator { $$ = SPVM_OP_build_binary_op(compiler, $2, $1, $3); } | operator STRLE operator { $$ = SPVM_OP_build_binary_op(compiler, $2, $1, $3); } | operator STRING_CMP operator { $$ = SPVM_OP_build_binary_op(compiler, $2, $1, $3); } string_concatenation : operator '.' operator { $$ = SPVM_OP_build_binary_op(compiler, $2, $1, $3); } logical_operator : operator LOGICAL_OR operator { $$ = SPVM_OP_build_logical_or(compiler, $2, $1, $3); } | operator LOGICAL_AND operator { $$ = SPVM_OP_build_logical_and(compiler, $2, $1, $3); } | LOGICAL_NOT operator { $$ = SPVM_OP_build_logical_not(compiler, $1, $2); } defined_or : operator DEFINED_OR operator { $$ = SPVM_OP_build_defined_or(compiler, $2, $1, $3); } type_check : operator ISA type { $$ = SPVM_OP_build_type_check(compiler, $2, $1, $3); } | operator ISA_ERROR type { $$ = SPVM_OP_build_type_check(compiler, $2, $1, $3); } | operator IS_TYPE type { $$ = SPVM_OP_build_type_check(compiler, $2, $1, $3); } | operator IS_ERROR type { $$ = SPVM_OP_build_type_check(compiler, $2, $1, $3); } | operator IS_COMPILE_TYPE qualified_type { $$ = SPVM_OP_build_type_check(compiler, $2, $1, $3); } type_cast : '(' qualified_type ')' operator %prec CONVERT { SPVM_OP* op_type_cast = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_TYPE_CAST, $2->file, $2->line); $$ = SPVM_OP_build_type_cast(compiler, op_type_cast, $2, $4); } | operator ARROW '(' qualified_type ')' %prec CONVERT { SPVM_OP* op_type_cast = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_TYPE_CAST, $4->file, $4->line); $$ = SPVM_OP_build_type_cast(compiler, op_type_cast, $4, $1); } can : operator CAN method_name { $$ = SPVM_OP_build_can(compiler, $2, $1, $3); } | operator CAN CONSTANT { $$ = SPVM_OP_build_can(compiler, $2, $1, $3); } assign : operator ASSIGN operator { $$ = SPVM_OP_build_assign(compiler, $2, $1, $3); } | operator SPECIAL_ASSIGN operator { $$ = SPVM_OP_build_special_assign(compiler, $2, $1, $3); } new : NEW basic_type { $$ = SPVM_OP_build_new(compiler, $1, $2, NULL); } | NEW array_type_with_length { SPVM_OP* op_length = $2->last; SPVM_OP_cut_op(compiler, $2->last); $$ = SPVM_OP_build_new(compiler, $1, $2, op_length); } | anon_method { $$ = SPVM_OP_build_anon_method(compiler, $1); } array_init : '[' opt_operators ']' { SPVM_OP* op_array_init = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_ARRAY_INIT, compiler->current_file, compiler->current_line); int32_t is_key_values = 0; $$ = SPVM_OP_build_array_init(compiler, op_array_init, $2, is_key_values); } | '{' operators '}' { SPVM_OP* op_array_init = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_ARRAY_INIT, compiler->current_file, compiler->current_line); int32_t is_key_values = 1; $$ = SPVM_OP_build_array_init(compiler, op_array_init, $2, is_key_values); } | '{' '}' { SPVM_OP* op_array_init = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_ARRAY_INIT, compiler->current_file, compiler->current_line); int32_t is_key_values = 1; SPVM_OP* op_list_elements = SPVM_OP_new_op_list(compiler, compiler->current_file, compiler->current_line); $$ = SPVM_OP_build_array_init(compiler, op_array_init, op_list_elements, is_key_values); } call_method : OUTMOST_CLASS SYMBOL_NAME '(' opt_operators ')' { SPVM_OP* op_call_method = SPVM_OP_new_op_call_method(compiler, compiler->current_file, compiler->current_line); $$ = SPVM_OP_build_call_method(compiler, op_call_method, $1, $2, $4); } | OUTMOST_CLASS SYMBOL_NAME { SPVM_OP* op_call_method = SPVM_OP_new_op_call_method(compiler, compiler->current_file, compiler->current_line); SPVM_OP* op_operators = SPVM_OP_new_op_list(compiler, $1->file, $2->line); $$ = SPVM_OP_build_call_method(compiler, op_call_method, $1, $2, op_operators); } | basic_type ARROW method_name '(' opt_operators ')' { SPVM_OP* op_call_method = SPVM_OP_new_op_call_method(compiler, compiler->current_file, compiler->current_line); $$ = SPVM_OP_build_call_method(compiler, op_call_method, $1, $3, $5); } | basic_type ARROW method_name { SPVM_OP* op_call_method = SPVM_OP_new_op_call_method(compiler, compiler->current_file, compiler->current_line); SPVM_OP* op_operators = SPVM_OP_new_op_list(compiler, $1->file, $2->line); $$ = SPVM_OP_build_call_method(compiler, op_call_method, $1, $3, op_operators); } | operator ARROW method_name '(' opt_operators ')' { SPVM_OP* op_call_method = SPVM_OP_new_op_call_method(compiler, compiler->current_file, compiler->current_line); $$ = SPVM_OP_build_call_method(compiler, op_call_method, $1, $3, $5); } | operator ARROW method_name { SPVM_OP* op_call_method = SPVM_OP_new_op_call_method(compiler, compiler->current_file, compiler->current_line); SPVM_OP* op_operators = SPVM_OP_new_op_list(compiler, $1->file, $2->line); $$ = SPVM_OP_build_call_method(compiler, op_call_method, $1, $3, op_operators); } | operator ARROW '(' opt_operators ')' { SPVM_OP* op_call_method = SPVM_OP_new_op_call_method(compiler, compiler->current_file, compiler->current_line); SPVM_OP* op_method_name = SPVM_OP_new_op_name(compiler, "", $2->file, $2->line); $$ = SPVM_OP_build_call_method(compiler, op_call_method, $1, op_method_name, $4); } array_access : operator ARROW '[' operator ']' { SPVM_OP* op_array_access = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_ELEMENT_ACCESS, compiler->current_file, compiler->current_line); $$ = SPVM_OP_build_element_access(compiler, op_array_access, $1, $4); } | array_access '[' operator ']' { SPVM_OP* op_array_access = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_ELEMENT_ACCESS, compiler->current_file, compiler->current_line); $$ = SPVM_OP_build_element_access(compiler, op_array_access, $1, $3); } | field_access '[' operator ']' { SPVM_OP* op_array_access = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_ELEMENT_ACCESS, compiler->current_file, compiler->current_line); $$ = SPVM_OP_build_element_access(compiler, op_array_access, $1, $3); } field_access : operator ARROW '{' field_name '}' { SPVM_OP* op_field_access = SPVM_OP_new_op_field_access(compiler, compiler->current_file, compiler->current_line); $$ = SPVM_OP_build_field_access(compiler, op_field_access, $1, $4); } | field_access '{' field_name '}' { SPVM_OP* op_field_access = SPVM_OP_new_op_field_access(compiler, compiler->current_file, compiler->current_line); $$ = SPVM_OP_build_field_access(compiler, op_field_access, $1, $3); } | array_access '{' field_name '}' { SPVM_OP* op_array_access = $1; SPVM_OP* op_field_access = SPVM_OP_new_op_field_access(compiler, compiler->current_file, compiler->current_line); $$ = SPVM_OP_build_field_access(compiler, op_field_access, op_array_access, $3); } weaken_field : WEAKEN var ARROW '{' field_name '}' { SPVM_OP* op_field_access = SPVM_OP_new_op_field_access(compiler, compiler->current_file, compiler->current_line); SPVM_OP_build_field_access(compiler, op_field_access, $2, $5); $$ = SPVM_OP_build_weaken_field(compiler, $1, op_field_access); } unweaken_field : UNWEAKEN var ARROW '{' field_name '}' { SPVM_OP* op_field_access = SPVM_OP_new_op_field_access(compiler, compiler->current_file, compiler->current_line); SPVM_OP_build_field_access(compiler, op_field_access, $2, $5); $$ = SPVM_OP_build_unweaken_field(compiler, $1, op_field_access); } isweak_field : ISWEAK var ARROW '{' field_name '}' { SPVM_OP* op_field_access = SPVM_OP_new_op_field_access(compiler, compiler->current_file, compiler->current_line); SPVM_OP_build_field_access(compiler, op_field_access, $2, $5); $$ = SPVM_OP_build_isweak_field(compiler, $1, op_field_access); } %%