%{ /* * cola.y * * $Id$ * * Cola compiler for Parrot * * Copyright (C) 2002 Melvin Smith * * Borrows heavily from Java and C# * Java is a registered trademark of Sun Microsystems * C# is a registered trademark of Microsoft Corporation * * The parser. Grammar for Bison. */ #include #include #include #include "cola.h" int yyerror(char *); int yylex(); extern char yytext[]; long line; int indent; AST *ast_start = NULL; /* Pointers to the builtin type entries in the symbol table */ %} %union { int ival; Symbol * sym; Type * type; AST * ast; void * p; } %token ASM %token ABSTRACT BOOL BREAK BYTE CHAR CLASS CONST %token CONTINUE %token DOUBLE DECIMAL ELSE EXTERN %token FALSE FLOAT FOR %token GET GOTO %token IF INT INTERNAL %token LONG METHOD MODIFIER NEW NAMESPACE NULLVAL %token OUT OVERRIDE OBJECT PRIVATE PROTECTED PUBLIC %token REF READONLY RETURN %token SBYTE SET SHORT STRING STATIC %token THIS TRUE TYPE %token UINT USHORT ULONG USING VIRTUAL VOID WHILE %token IDENTIFIER LITERAL RANKSPEC %token INC DEC LOGICAL_AND LOGICAL_OR LOGICAL_EQ LOGICAL_NE %token LOGICAL_LTE LOGICAL_GTE %token LEFT_SHIFT RIGHT_SHIFT INDEX %token TYPE METHOD %type type array_type %type predefined_type integral_type %type qualified_name member_access %type namespace_scope_start class_scope_start %type formal_param_list fixed_params fixed_param %type var_declarator var_declarators %type using_directives using_directive attribute_list %type namespace_member_decls namespace_member_decl %type namespace_decl class_decl namespace_body %type return_statement jump_statement asm_block %type statement statement_list %type decl_statement method_decl field_decl %type class_body class_member_decl_list class_member_decl %type local_var_decl %type method_header method_body %type block labeled_statement embedded_statement %type expr_statement selection_statement if_statement %type iteration_statement while_statement for_statement %type expr_list expr statement_expr assignment %type primary_expr boolean_expr equality_expr element_access %type post_inc_expr post_dec_expr pre_inc_expr pre_dec_expr %type new_expr new_object_expr %type conditional_expr conditional_or_expr %type conditional_and_expr inclusive_or_expr and_expr %type shift_expr exclusive_or_expr relational_expr %type unary_expr add_expr mult_expr %type method_call arg arg_list %type modifier modifiers opt_modifiers %type relational_op %type rank_specifiers %left '-' '+' %left '*' '/' %nonassoc IF %nonassoc ELSE %right '=' %nonassoc UMINUS INC DEC %left '(' %left '[' '{' %left '.' %start compilation_unit %% optional_semi : | ';' ; compilation_unit: using_directives attribute_list namespace_member_decls { unshift_ast(&ast_start, $1); unshift_ast(&ast_start, $2); unshift_ast(&ast_start, $3); printf("DONE: compilation_unit.\n"); } ; using_directives: /*NULL*/ { $$ = NULL; } | using_directives using_directive { unshift_ast(&($$), $1); } ; using_directive: USING IDENTIFIER '=' IDENTIFIER ';' { printf("using_alias_directive\n"); } | USING IDENTIFIER ';' { printf("using_directive\n"); /*load_module($2->name);*/ } ; attribute_list : /*NULL*/ { $$ = NULL; } ; namespace_decl: namespace_scope_start namespace_body { pop_namespace(); $$ = new_ast(KIND_DECL, ASTT_NAMESPACE_DECL, $2, NULL); $$->sym = $1; } ; namespace_scope_start: NAMESPACE qualified_name { Symbol *n, *t, *last = current_namespace; if (lookup_type_symbol($2)) { printf("Error, redefinition of [%s]\n", $2->name); exit(0); } for (n = split(".", $2->name); n; n = n->tnext) { n->kind = $2->kind; t = mk_namespace_symbol(n); store_symbol(last->table, t); last = n; } push_namespace(t); $$ = t; } ; qualified_name: IDENTIFIER { $$ = $1; fprintf(stderr, "qualified_name <- IDENTIFIER\n"); } | qualified_name '.' IDENTIFIER { $$ = symbol_join3($1, new_symbol("."), $3); fprintf(stderr, "qualified_name <- qualified_name . IDENTIFIER\n"); } ; namespace_body: '{' using_directives namespace_member_decls '}' { $$ = $2; if ($3) unshift_ast(&($$), $3); } ; namespace_member_decls: /*NULL*/ { $$ = NULL; } | namespace_member_decls namespace_member_decl { $$ = $1; if ($2) unshift_ast(&($$), $2); } ; namespace_member_decl: namespace_decl | class_decl ; class_decl: opt_modifiers class_scope_start class_body optional_semi { pop_namespace(); $$ = new_ast(KIND_DECL, ASTT_CLASS_DECL, NULL, NULL); $$->Attr.Class.body = $3; $$->sym = $2; $$->sym->flags = $1; } ; opt_modifiers: { $$ = 0; } | modifiers ; modifiers: modifiers modifier { $$ = $1 | $2; } | modifier ; modifier: PUBLIC { $$ = MOD_PUBLIC; } | PRIVATE { $$ = MOD_PRIVATE; } | PROTECTED { $$ = MOD_PROTECTED; } | STATIC { $$ = MOD_STATIC; } | VIRTUAL { $$ = MOD_VIRTUAL; } ; class_body: '{' class_member_decl_list '}' { $$ = $2; } ; class_scope_start: CLASS IDENTIFIER { /* Create a new namespace for class and put it in effect */ Symbol * c; #if DEBUG fprintf(stderr, "\nclass_scope_start <- CLASS IDENTIFIER (%s)\n", $2->name); #endif c = mk_class_symbol($2); push_namespace(c); /* push_scope(); */ $$ = c; } ; class_member_decl_list: { $$ = NULL; } | class_member_decl_list class_member_decl { $$ = $1; unshift_ast(&($$), $2); } ; class_member_decl: field_decl | method_decl /* | property_decl | event_decl | indexer_decl | operator_decl | constructor_decl | destructor_decl | static_constructor_decl | type_decl */ ; decl_statement: local_var_decl ';' { $$ = $1; } | CONST type IDENTIFIER '=' LITERAL ';' { $3->typename = $2; $3->literal = $5; check_id_redecl(current_symbol_table, $3->name); store_symbol(current_symbol_table, $3); $$ = new_statement(ASTT_CONSTANT_DECL, NULL, NULL); $$->typename = $2; if (lookup_symbol_in_tab(current_symbol_table, $$->sym->name)) { printf("Warning: declaration of '%s' shadows previous instance.\n", $$->sym->name); } } ; local_var_decl: type var_declarators { AST * decl; if ($1 == NULL) { printf("Internal compiler error: local_var_decl: type is NULL\n"); abort(); } for (decl=$2; decl; decl = decl->next) { #if DEBUG fprintf(stderr, "local_var: [%s] typename [%s]\n", decl->arg1->sym->name, $1->name); #endif decl->arg1->sym->typename = $1; } $$ = $2; } ; field_decl: opt_modifiers type var_declarators ';' { AST * decl; if ($2 == NULL) { printf("Internal compiler error: field_decl: type is NULL\n"); abort(); } for (decl=$3; decl; decl = decl->next) { #if DEBUG fprintf(stderr, "field: [%s] typename [%s]\n", decl->arg1->sym->name, $2->name); #endif decl->arg1->sym->typename = $2; } $$ = $3; } ; var_declarators: var_declarator | var_declarators ',' var_declarator { $$ = $1; unshift_ast(&($$), $3); } ; var_declarator: IDENTIFIER '=' expr { AST * decl, * init; decl = new_expr(ASTT_IDENTIFIER, NULL, NULL); decl->sym = $1; init = new_expr(ASTT_ASSIGN, decl, $3); $$ = new_statement(ASTT_FIELD_DECL, decl, init); #if DEBUG fprintf(stderr, " var_declarator <- IDENTIFER(%s)=init_expr\n", $1->name); #endif } | IDENTIFIER { AST * decl = new_expr(ASTT_IDENTIFIER, NULL, NULL); decl->sym = $1; $$ = new_statement(ASTT_FIELD_DECL, decl, NULL); #if DEBUG fprintf(stderr, " var_declarator <- IDENTIFIER(%s)\n", $1->name); #endif } ; method_decl: method_header method_body { $$ = $1; $$->Attr.Method.body = $2; } ; method_header: opt_modifiers type IDENTIFIER '(' formal_param_list ')' { Symbol * param; $$ = new_statement(ASTT_METHOD_DECL, NULL, NULL); $3->kind = METHOD; $3->typename = $2; $3->flags = $1; $$->sym = $3; $$->Attr.Method.params = $5; /* Methods/Fields stored at scope 0 of class namespace. * We can store these at parse time. */ store_symbol(current_symbol_table, $$->sym); if ($1 & MOD_STATIC) { if (!strcmp($3->name, "Main")) { if (main_method) fprintf(stderr, "Warning: multiple definitions of a static Main()\n"); main_method = $3; } } } | opt_modifiers VOID IDENTIFIER '(' formal_param_list ')' { Symbol * param; $$ = new_statement(ASTT_METHOD_DECL, NULL, NULL); $3->kind = METHOD; $3->typename = new_type_symbol("void"); $3->flags = $1; $$->sym = $3; $$->Attr.Method.params = $5; /* Methods/Fields stored at scope 0 of class namespace. * We can store these at parse time. */ store_symbol(current_symbol_table, $$->sym); if ($1 & MOD_STATIC) { if (!strcmp($3->name, "Main")) { if (main_method) fprintf(stderr, "Warning: multiple definitions of a static Main()\n"); main_method = $3; } } } ; formal_param_list: /*NULL*/ { $$ = NULL; } | fixed_params { $$ = $1; } /* | fixed_params ',' param_array | param_array */ ; fixed_params: fixed_param | fixed_params ',' fixed_param { $$ = $1; tunshift_sym(&($$), $3); } ; fixed_param: type IDENTIFIER { #if DEBUG fprintf(stderr, " fixed_param <- type IDENTIFIER(%s)\n", $2->name); #endif $2->typename = $1; $$ = $2; } ; /* param_modifier: REF | OUT ; param_array: attributes PARAMS array_type IDENTIFIER ; */ method_body: block { $$ = $1; } ; block: '{' block_scope statement_list '}' { $$ = $3; if ($$) { $$->vars = pop_scope(); } else { discard_scope(); } } ; block_scope: { push_scope(); } ; embedded_statement: block {$$ = $1;} | expr_statement { fprintf(stderr, "##End of expr_statement\n\n"); $$ = $1; } | selection_statement { fprintf(stderr, "##End of selection_statement\n\n"); $$ = $1; } | iteration_statement { fprintf(stderr, "##End of iteration_statement\n\n"); $$ = $1; } | jump_statement { fprintf(stderr, "##End of jump_statement\n\n"); $$ = $1; } | return_statement { fprintf(stderr, "##End of return_statement\n\n"); $$ = $1; } | asm_block { $$ = $1; } ; statement: embedded_statement | decl_statement { fprintf(stderr, "##End of decl_statement\n\n"); $$ = $1; } | labeled_statement { fprintf(stderr, "##End of labeled_statement\n\n"); $$ = $1; } ; statement_list: /* NULL */ { $$ = NULL; } | statement { $$ = $1; } | statement_list statement { $$ = $1; unshift_ast(&($$), $2); } ; labeled_statement: IDENTIFIER ':' statement { $$ = $3; $$->sym = $1; } ; expr_statement: statement_expr ';' { $$ = $1; } ; jump_statement: BREAK ';' { $$ = new_statement(ASTT_BREAK, NULL, NULL); } | CONTINUE ';' { $$ = new_statement(ASTT_CONTINUE, NULL, NULL); } | GOTO IDENTIFIER ';' { $$ = NULL; printf("GOTO not supported yet.\n"); exit(0); } ; return_statement: RETURN expr ';' { $$ = new_statement(ASTT_RETURN, $2, NULL); } | RETURN ';' { $$ = new_statement(ASTT_RETURN, NULL, NULL); } ; asm_block: ASM '(' LITERAL ')' ';' { $$ = NULL; } ; arg_list: arg | arg_list ',' arg { $$ = $1; unshift_ast(&($$), $3); } ; arg: { $$ = NULL; } | expr { $$ = $1; } | REF { $$ = NULL; } | OUT { $$ = NULL; } ; selection_statement: if_statement ; if_statement: IF '(' boolean_expr ')' embedded_statement ELSE embedded_statement { $$ = new_if($3, $5, $7); } | IF '(' boolean_expr ')' embedded_statement { $$ = new_if($3, $5, NULL); } ; iteration_statement: while_statement | for_statement ; while_statement: WHILE '(' boolean_expr ')' embedded_statement { $$ = new_while($3, $5); } ; for_statement: FOR '(' statement_expr ';' boolean_expr ';' statement_expr ')' embedded_statement { if ($3 == NULL) { printf("for_statement: NULL init statement\n"); exit(0); } $$ = new_for($3, $5, $7, $9); } ; type: qualified_name { fprintf(stderr, "!TYPE[%s]\n", $1->name); $$ = $1; } | predefined_type | array_type ; predefined_type: BOOL { $$ = new_type_symbol("bool"); } | STRING { $$ = new_type_symbol("string"); } | FLOAT { $$ = new_type_symbol("num"); } | integral_type ; integral_type: INT { $$ = new_type_symbol("int"); } | BYTE { $$ = new_type_symbol("byte"); } ; array_type: type rank_specifiers { $$ = symbol_concat($1, $2); #if DEBUG fprintf(stderr, " array_type: %s\n", $1->name); #endif } ; rank_specifiers: RANKSPEC { /* $1 is the dimension of the current rank */ $$ = $1; #if DEBUG fprintf(stderr, " rank_spec /%s/\n", $1->name); #endif } | rank_specifiers RANKSPEC { $$ = symbol_concat($1, $2); } ; /* * Expressions */ statement_expr: method_call | assignment | post_inc_expr | post_dec_expr | pre_inc_expr | pre_dec_expr ; assignment: unary_expr '=' expr { $$ = new_expr(ASTT_ASSIGN, $1, $3); } /* | unary_expr compound_assign_op expr { $$ = new_expr(ASTT_ASSIGN, $1, new_op_expr($1, $2, $3)); } */ ; /* * Expressions */ pre_inc_expr: INC unary_expr { $$ = new_expr(ASTT_PREINC, $2, NULL); $$->op = INC; } ; pre_dec_expr: DEC unary_expr { $$ = new_expr(ASTT_PREINC, $2, NULL); $$->op = DEC; } ; post_inc_expr: primary_expr INC { $$ = new_expr(ASTT_POSTINC, $1, NULL); $$->op = INC; } ; post_dec_expr: primary_expr DEC { $$ = new_expr(ASTT_POSTINC, $1, NULL); $$->op = DEC; } ; expr: conditional_expr { $$ = $1; #if DEBUG fprintf(stderr, " expr <- conditional_expr\n"); #endif } | assignment ; expr_list: expr | expr_list ',' expr ; boolean_expr: expr ; primary_expr: LITERAL { $$ = new_expr(ASTT_LITERAL, NULL, NULL); $$->sym = $1; #if DEBUG fprintf(stderr, " primary_expr <- LITERAL(%s)\n", $1->name); #endif } | IDENTIFIER { Symbol * orig; $$ = new_expr(ASTT_IDENTIFIER, NULL, NULL); $$->sym = $1; #if DEBUG fprintf(stderr, " primary_expr <- IDENTIFIER(%s)\n", $1->name); #endif } | '(' expr ')' { $$ = $2; } | element_access | method_call | post_inc_expr | post_dec_expr | new_expr | member_access { $$ = new_expr(ASTT_IDENTIFIER, NULL, NULL); $$->sym = $1; } ; unary_expr: primary_expr { $$ = $1; } | '+' unary_expr { $$ = $2; $$->op = '+'; } | '-' unary_expr { $$ = $2; $$->op = '-'; } | '!' unary_expr { $$ = $2; $$->op = '!'; } | '~' unary_expr { $$ = $2; $$->op = '~'; } | pre_inc_expr | pre_dec_expr ; method_call: primary_expr '(' arg_list ')' { if ($1->asttype != ASTT_IDENTIFIER) { fprintf(stderr, "Error (line %d), method call must be a simple name or member access.\n", line); exit(0); } $$ = new_expr(ASTT_METHOD_CALL, $1, $3); #if DEBUG fprintf(stderr, " method_call <- primary_expr ( arg_list )\n"); #endif } ; member_access: qualified_name { $$ = $1; #if DEBUG fprintf(stderr, " member_access <- qualified_name\n"); #endif } ; element_access: primary_expr '[' expr ']' { $$ = new_expr(ASTT_INDEX, $1, $3); $$->op = INDEX; #if DEBUG fprintf(stderr, " element_access <- primary-expr(pex[ex])\n"); #endif } ; new_expr: new_object_expr /* | new_array_expr | new_delegate_expr */ ; new_object_expr: NEW type '(' arg_list ')' { $$ = new_expr(ASTT_NEW_OBJECT, $4, NULL); /* $2 is a Symbol of a typename, will resolve to a real type * in semantic pass. */ $$->typename = $2; } ; mult_expr: unary_expr | mult_expr '*' unary_expr { $$ = new_op_expr($1, '*', $3); } | mult_expr '/' unary_expr { $$ = new_op_expr($1, '/', $3); } | mult_expr '%' unary_expr { $$ = new_op_expr($1, '%', $3); } ; add_expr: mult_expr | add_expr '+' mult_expr { $$ = new_op_expr($1, '+', $3); } | add_expr '-' mult_expr { $$ = new_op_expr($1, '-', $3); } ; conditional_expr: conditional_or_expr | conditional_or_expr '?' expr ':' expr { /* Ternary is just a if/then/else statement which can return a value */ $$ = new_conditional($1, $3, $5); } ; conditional_and_expr: inclusive_or_expr | conditional_and_expr LOGICAL_AND inclusive_or_expr { $$ = new_logical_expr($1, LOGICAL_AND, $3); } ; conditional_or_expr: conditional_and_expr | conditional_or_expr LOGICAL_OR conditional_and_expr { $$ = new_logical_expr($1, LOGICAL_OR, $3); } ; and_expr: equality_expr | and_expr '&' equality_expr { $$ = new_op_expr($1, '&', $3); } ; exclusive_or_expr: and_expr | exclusive_or_expr '^' and_expr { $$ = new_op_expr($1, '~', $3); } ; inclusive_or_expr: exclusive_or_expr | inclusive_or_expr '|' exclusive_or_expr { $$ = new_op_expr($1, '|', $3); } ; relational_op: '<' {$$ = '<';} | '>' {$$ = '>';} | LOGICAL_LTE {$$ = LOGICAL_LTE;} | LOGICAL_GTE {$$ = LOGICAL_GTE;} ; relational_expr: shift_expr | relational_expr relational_op shift_expr { $$ = new_expr(ASTT_COMPARISON, $1, $3); $$->op = $2; } ; equality_expr: relational_expr | equality_expr LOGICAL_EQ relational_expr { $$ = new_expr(ASTT_COMPARISON, $1, $3); $$->op = LOGICAL_EQ; } | equality_expr LOGICAL_NE relational_expr { $$ = new_expr(ASTT_COMPARISON, $1, $3); $$->op = LOGICAL_NE; } ; shift_expr: add_expr | shift_expr LEFT_SHIFT add_expr { $$ = new_op_expr($1, LEFT_SHIFT, $3); } | shift_expr RIGHT_SHIFT add_expr { $$ = new_op_expr($1, RIGHT_SHIFT, $3); } ; %% extern FILE *yyin; int main(int argc, char * argv[]) { fprintf(stderr, "Cola - Copyright (C) 2002 Melvin Smith \n"); fprintf(stderr, "colac version %s\n\n", COLA_VERSION); if (argc > 1) { if (!(yyin = fopen(argv[1], "r"))) { printf("Error reading source file %s.\n", argv[1]); exit(0); } } else { printf("No source file specified.\n"); exit(0); } line = 1; init_symbol_tables(); init_builtin_types(); /* Kludge - fake routines by adding them to symbol table * and emitting them. */ { store_method(current_symbol_table, "puts", t_void); store_method(current_symbol_table, "puti", t_void); store_method(current_symbol_table, "putf", t_void); store_method(current_symbol_table, "gets", t_string); store_method(current_symbol_table, "substr", t_string); store_method(current_symbol_table, "strlen", t_int32); store_method(current_symbol_table, "strchop", t_string); store_method(current_symbol_table, "strrep", t_string); store_method(current_symbol_table, "ord", t_int32); store_method(current_symbol_table, "sleep", t_void); } fprintf(stderr, "Pass 1: Starting parse...\n"); yyparse(); fclose(yyin); fprintf(stderr, "Pass 2: Type checking...\n"); fprintf(stderr, "Pass 2: Start scope is [%d]\n", scope); build_ast(ast_start); fprintf(stderr, "Pass 3: Semantic checking...\n"); /*semant_ast(ast_start); */ freopen("a.pir", "w", stdout); fprintf(stderr, "Compiling intermediate code to a.pir\n"); /* printf("#Dump of global namespace:\n"); indent = 0; dump_namespace(current_namespace); */ printf("# Cola (%s) generated\n#\n", COLA_VERSION); fprintf(stderr, "Pass 4: Code generation...\n"); if (ast_start) { gen_ast(ast_start); } printf("# Include standard API stuff\n"); printf(".include \"core.pir\"\n\n"); fflush(stdout); fprintf(stderr, "%ld lines compiled to PIR.\n", line); fprintf(stderr, "Generating PASM\n"); system("../../parrot -o a.pasm a.pir"); fprintf(stderr, "Generating PBC (Bytecode)\n"); system("../../parrot -o a.pbc a.pir"); return 0; } int yyerror(char * s) { /* fprintf(stderr, "last token = [%s]\n", yylval.sym->name); */ fprintf(stderr, "(error) line %ld: %s\n", line, s); fprintf(stderr, "Didn't create output asm.\n"); exit(0); } /* * Local variables: * c-file-style: "parrot" * End: * vim: expandtab shiftwidth=4: */