Implement elsif
This commit is contained in:
		
							
								
								
									
										87
									
								
								example.elna
									
									
									
									
									
								
							
							
						
						
									
										87
									
								
								example.elna
									
									
									
									
									
								
							| @@ -1,5 +1,4 @@ | |||||||
| type | type | ||||||
|   T = array 5 of Int, |  | ||||||
|   TokenValue = union |   TokenValue = union | ||||||
|     intValue: Int; |     intValue: Int; | ||||||
|     stringValue: String |     stringValue: String | ||||||
| @@ -57,63 +56,58 @@ begin | |||||||
|   write(0, @value, 1) |   write(0, @value, 1) | ||||||
| end; | end; | ||||||
|  |  | ||||||
|  | proc write_i(value: Int); | ||||||
|  | var | ||||||
|  |   digit: Int, n: Int, | ||||||
|  |   buffer: array 10 of Char; | ||||||
|  | begin | ||||||
|  |   n := 9; | ||||||
|  |   buffer[9] := '0'; | ||||||
|  |  | ||||||
|  |   while value /= 0 do | ||||||
|  |     digit := value % 10; | ||||||
|  | 	value := value / 10; | ||||||
|  |  | ||||||
|  | 	buffer[n] := Char(Int('0') + digit); | ||||||
|  | 	n := n - 1 | ||||||
|  |   end; | ||||||
|  |   while n < 10 do | ||||||
|  |     n := n + 1; | ||||||
|  |     write_c(buffer[n]) | ||||||
|  |   end | ||||||
|  | end; | ||||||
|  |  | ||||||
|  | proc write_u(value: Word); | ||||||
|  | begin | ||||||
|  |   write_i(value) | ||||||
|  | end; | ||||||
|  |  | ||||||
| -- | -- | ||||||
| -- End of standard procedures. | -- End of standard procedures. | ||||||
| -- | -- | ||||||
|  |  | ||||||
| proc test_array(); |  | ||||||
| var a: T, x: Int; |  | ||||||
| begin |  | ||||||
|   a[0] := 2; |  | ||||||
|   a[1] := 5; |  | ||||||
|  |  | ||||||
|   writei(""); |  | ||||||
|   writei("Test array:"); |  | ||||||
|  |  | ||||||
|   x := 0; |  | ||||||
|   while x < 2 do |  | ||||||
|     writei(a[x]); |  | ||||||
| 	x := x + 1 |  | ||||||
|   end |  | ||||||
| end; |  | ||||||
|  |  | ||||||
| proc test_record(); | proc test_record(); | ||||||
| var r: Token; | var r: Token; | ||||||
| begin | begin | ||||||
|   writei(""); |   write_s("\nTest record:\n"); | ||||||
|   writei("Test record:"); |  | ||||||
|  |  | ||||||
|   r.kind := 4; |   r.kind := 4; | ||||||
|   r.value.intValue := 8; |   r.value.intValue := 8; | ||||||
|  |  | ||||||
|   writei(r.value.intValue) |   write_i(r.value.intValue) | ||||||
| end; | end; | ||||||
|  |  | ||||||
| proc test_primitive(); | proc test_primitive(); | ||||||
| var u: Word, z: Float; |  | ||||||
| begin | begin | ||||||
|   u := 25u; |   write_s("\nTest primitives:\n"); | ||||||
|   z := 8.2; |   write_u(25u); | ||||||
|  |   write_c('\n'); | ||||||
|  |  | ||||||
|   writei(""); |   write_i(8); | ||||||
|   writei("Test primitives:"); |   write_c('\n'); | ||||||
|   writei(u); |  | ||||||
|   writei(z) |  | ||||||
| end; |  | ||||||
|  |  | ||||||
| proc test_param(d: Int, e: Int); |   write_b(true); | ||||||
| begin |   write_c('\n') | ||||||
|   writei(""); |  | ||||||
|   writei("Test param"); |  | ||||||
|   writei(d); |  | ||||||
|   writei(e) |  | ||||||
| end; |  | ||||||
|  |  | ||||||
| proc test_return_int(): Int; |  | ||||||
| begin |  | ||||||
|   writei(""); |  | ||||||
|   writei("Test return int:"); |  | ||||||
|   return 5 |  | ||||||
| end; | end; | ||||||
|  |  | ||||||
| proc read_source(filename: String): pointer to Char; | proc read_source(filename: String): pointer to Char; | ||||||
| @@ -153,15 +147,10 @@ begin | |||||||
| end; | end; | ||||||
|  |  | ||||||
| begin | begin | ||||||
|   test_primitive(); |  | ||||||
|   test_array(); |  | ||||||
|   test_record(); |  | ||||||
|   test_param(8, 7); |  | ||||||
|   writei(test_return_int()); |  | ||||||
|   write_b(true); |  | ||||||
|   write_c('\n'); |  | ||||||
|  |  | ||||||
|   compile(); |   compile(); | ||||||
|  |  | ||||||
|  |   test_record(); | ||||||
|  |   test_primitive(); | ||||||
|  |  | ||||||
|   exit(0) |   exit(0) | ||||||
| end. | end. | ||||||
|   | |||||||
| @@ -24,7 +24,8 @@ namespace gcc | |||||||
|     { |     { | ||||||
|         auto symbol = this->symbol_map->lookup(statement->name()); |         auto symbol = this->symbol_map->lookup(statement->name()); | ||||||
|  |  | ||||||
|         if (statement->name() == "writei") |         if (statement->name() == "Int" || statement->name() == "Word" || statement->name() == "Bool" | ||||||
|  |                 || statement->name() == "Float" || statement->name() == "Char") | ||||||
|         { |         { | ||||||
|             if (statement->arguments().size() != 1) |             if (statement->arguments().size() != 1) | ||||||
|             { |             { | ||||||
| @@ -35,51 +36,10 @@ namespace gcc | |||||||
|             } |             } | ||||||
|             auto& argument = statement->arguments().at(0); |             auto& argument = statement->arguments().at(0); | ||||||
|             argument->accept(this); |             argument->accept(this); | ||||||
|             auto argument_type = TREE_TYPE(this->current_expression); |             tree argument_type = TREE_TYPE(this->current_expression); | ||||||
|   |   | ||||||
|             const char *format_number{ nullptr }; |             this->current_expression = build1_loc(get_location(&argument->position()), CONVERT_EXPR, | ||||||
|             if (argument_type == integer_type_node) |                     symbol->payload, this->current_expression); | ||||||
|             { |  | ||||||
|                 format_number = "%d\n"; |  | ||||||
|             } |  | ||||||
|             else if (argument_type == unsigned_type_node) |  | ||||||
|             { |  | ||||||
|                 format_number = "%u\n"; |  | ||||||
|             } |  | ||||||
|             else if (argument_type == double_type_node) |  | ||||||
|             { |  | ||||||
|                 format_number = "%f\n"; |  | ||||||
|             } |  | ||||||
|             else if (is_pointer_type(argument_type)) |  | ||||||
|             { |  | ||||||
|                 format_number = "%p\n"; |  | ||||||
|             } |  | ||||||
|             else |  | ||||||
|             { |  | ||||||
|                 error_at(get_location(&argument->position()), |  | ||||||
|                         "invalid argument of type %s for procedure %s", |  | ||||||
|                         print_type(argument_type), statement->name().c_str()); |  | ||||||
|                 this->current_expression = error_mark_node; |  | ||||||
|                 return; |  | ||||||
|             } |  | ||||||
|             tree args[] = { |  | ||||||
|                 build_string_literal(strlen(format_number) + 1, format_number), |  | ||||||
|                 this->current_expression |  | ||||||
|             }; |  | ||||||
|             tree fndecl_type_param[] = { |  | ||||||
|                 build_pointer_type(build_qualified_type(char_type_node, TYPE_QUAL_CONST)) /* const char* */ |  | ||||||
|             }; |  | ||||||
|             tree fndecl_type = build_varargs_function_type_array(integer_type_node, 1, fndecl_type_param); |  | ||||||
|  |  | ||||||
|             tree printf_fn_decl = build_fn_decl("printf", fndecl_type); |  | ||||||
|             DECL_EXTERNAL(printf_fn_decl) = 1; |  | ||||||
|  |  | ||||||
|             tree printf_fn = build1(ADDR_EXPR, build_pointer_type(fndecl_type), printf_fn_decl); |  | ||||||
|  |  | ||||||
|             tree stmt = build_call_array(integer_type_node, printf_fn, 2, args); |  | ||||||
|  |  | ||||||
|             append_to_statement_list(stmt, &this->current_statements); |  | ||||||
|             this->current_expression = NULL_TREE; |  | ||||||
|         } |         } | ||||||
|         else if (symbol) |         else if (symbol) | ||||||
|         { |         { | ||||||
| @@ -733,70 +693,62 @@ namespace gcc | |||||||
|  |  | ||||||
|     void generic_visitor::visit(source::if_statement *statement) |     void generic_visitor::visit(source::if_statement *statement) | ||||||
|     { |     { | ||||||
|         statement->body().prerequisite().accept(this); |         tree endif_label_decl = build_label_decl("endif", UNKNOWN_LOCATION); | ||||||
|  |         tree goto_endif = build1(GOTO_EXPR, void_type_node, endif_label_decl); | ||||||
|  |  | ||||||
|         if (TREE_TYPE(this->current_expression) != boolean_type_node) |         make_if_branch(statement->body(), goto_endif); | ||||||
|  |  | ||||||
|  |         for (const auto branch : statement->branches) | ||||||
|         { |         { | ||||||
|             error_at(get_location(&statement->body().prerequisite().position()), |             make_if_branch(*branch, goto_endif); | ||||||
|                     "expected expression of boolean type but its type is %s", |  | ||||||
|                     print_type(TREE_TYPE(this->current_expression))); |  | ||||||
|             this->current_expression = error_mark_node; |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|         auto then_location = get_location(&statement->position()); |  | ||||||
|         auto prerequisite_location = get_location(&statement->body().prerequisite().position()); |  | ||||||
|  |  | ||||||
|         auto then_label_decl = build_label_decl("then", then_location); |  | ||||||
|         auto endif_label_decl = build_label_decl("end_if", then_location); |  | ||||||
|  |  | ||||||
|         auto goto_then = build1_loc(prerequisite_location, GOTO_EXPR, |  | ||||||
|                 void_type_node, then_label_decl); |  | ||||||
|         auto goto_endif = build1_loc(prerequisite_location, GOTO_EXPR, |  | ||||||
|                 void_type_node, endif_label_decl); |  | ||||||
|  |  | ||||||
|         tree else_label_decl = NULL_TREE; |  | ||||||
|         tree goto_else_or_endif = NULL_TREE; |  | ||||||
|         if (statement->alternative() != nullptr) |  | ||||||
|         { |  | ||||||
|             auto else_location = get_location(&statement->position()); |  | ||||||
|             else_label_decl = build_label_decl("else", else_location); |  | ||||||
|             goto_else_or_endif = build1_loc(else_location, GOTO_EXPR, void_type_node, else_label_decl); |  | ||||||
|         } |  | ||||||
|         else |  | ||||||
|         { |  | ||||||
|             goto_else_or_endif = goto_endif; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         auto cond_expr = build3_loc(prerequisite_location, COND_EXPR, |  | ||||||
|                 void_type_node, this->current_expression, goto_then, goto_else_or_endif); |  | ||||||
|         append_to_statement_list(cond_expr, &this->current_statements); |  | ||||||
|  |  | ||||||
|         auto then_label_expr = build1_loc(then_location, LABEL_EXPR, |  | ||||||
|                 void_type_node, then_label_decl); |  | ||||||
|         append_to_statement_list(then_label_expr, &this->current_statements); |  | ||||||
|  |  | ||||||
|         for (const auto body_statement : statement->body().statements) |  | ||||||
|         { |  | ||||||
|             body_statement->accept(this); |  | ||||||
|         } |         } | ||||||
|         if (statement->alternative() != nullptr) |         if (statement->alternative() != nullptr) | ||||||
|         { |         { | ||||||
|             append_to_statement_list(goto_endif, &this->current_statements); |  | ||||||
|  |  | ||||||
|             auto else_label_expr = build1(LABEL_EXPR, void_type_node, else_label_decl); |  | ||||||
|             append_to_statement_list(else_label_expr, &this->current_statements); |  | ||||||
|  |  | ||||||
|             for (const auto body_statement : *statement->alternative()) |             for (const auto body_statement : *statement->alternative()) | ||||||
|             { |             { | ||||||
|                 body_statement->accept(this); |                 body_statement->accept(this); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         auto endif_label_expr = build1(LABEL_EXPR, void_type_node, endif_label_decl); |  | ||||||
|         append_to_statement_list(endif_label_expr, &this->current_statements); |  | ||||||
|  |  | ||||||
|  |         tree endif_label_expr = build1(LABEL_EXPR, void_type_node, endif_label_decl); | ||||||
|  |         append_to_statement_list(endif_label_expr, &this->current_statements); | ||||||
|         this->current_expression = NULL_TREE; |         this->current_expression = NULL_TREE; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     void generic_visitor::make_if_branch(source::conditional_statements& branch, tree goto_endif) | ||||||
|  |     { | ||||||
|  |         branch.prerequisite().accept(this); | ||||||
|  |  | ||||||
|  |         if (TREE_TYPE(this->current_expression) != boolean_type_node) | ||||||
|  |         { | ||||||
|  |             error_at(get_location(&branch.prerequisite().position()), | ||||||
|  |                     "expected expression of boolean type but its type is %s", | ||||||
|  |                     print_type(TREE_TYPE(this->current_expression))); | ||||||
|  |             this->current_expression = error_mark_node; | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |         tree then_label_decl = build_label_decl("then", UNKNOWN_LOCATION); | ||||||
|  |         tree goto_then = build1(GOTO_EXPR, void_type_node, then_label_decl); | ||||||
|  |  | ||||||
|  |         tree else_label_decl = build_label_decl("else", UNKNOWN_LOCATION); | ||||||
|  |         tree goto_else = build1(GOTO_EXPR, void_type_node, else_label_decl); | ||||||
|  |  | ||||||
|  |         auto cond_expr = build3(COND_EXPR, void_type_node, this->current_expression, goto_then, goto_else); | ||||||
|  |         append_to_statement_list(cond_expr, &this->current_statements); | ||||||
|  |  | ||||||
|  |         tree then_label_expr = build1(LABEL_EXPR, void_type_node, then_label_decl); | ||||||
|  |         append_to_statement_list(then_label_expr, &this->current_statements); | ||||||
|  |  | ||||||
|  |         for (const auto body_statement : branch.statements) | ||||||
|  |         { | ||||||
|  |             body_statement->accept(this); | ||||||
|  |         } | ||||||
|  |         append_to_statement_list(goto_endif, &this->current_statements); | ||||||
|  |  | ||||||
|  |         tree else_label_expr = build1(LABEL_EXPR, void_type_node, else_label_decl); | ||||||
|  |         append_to_statement_list(else_label_expr, &this->current_statements); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     tree generic_visitor::build_label_decl(const char *name, location_t loc) |     tree generic_visitor::build_label_decl(const char *name, location_t loc) | ||||||
|     { |     { | ||||||
|         auto label_decl = build_decl(loc, |         auto label_decl = build_decl(loc, | ||||||
|   | |||||||
| @@ -34,6 +34,8 @@ namespace gcc | |||||||
|         void build_binary_operation(bool condition, source::binary_expression *expression, |         void build_binary_operation(bool condition, source::binary_expression *expression, | ||||||
|             tree_code operator_code, tree left, tree right, tree target_type); |             tree_code operator_code, tree left, tree right, tree target_type); | ||||||
|  |  | ||||||
|  |         void make_if_branch(source::conditional_statements& branch, tree goto_endif); | ||||||
|  |  | ||||||
|     public: |     public: | ||||||
|         generic_visitor(std::shared_ptr<source::symbol_table<tree>> symbol_table); |         generic_visitor(std::shared_ptr<source::symbol_table<tree>> symbol_table); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -617,6 +617,8 @@ namespace source | |||||||
|         std::vector<statement *> *m_alternative; |         std::vector<statement *> *m_alternative; | ||||||
|  |  | ||||||
|     public: |     public: | ||||||
|  |         std::vector<conditional_statements *> branches; | ||||||
|  |  | ||||||
|         if_statement(const struct position position, conditional_statements *body, |         if_statement(const struct position position, conditional_statements *body, | ||||||
|                 std::vector<statement *> *alternative = nullptr); |                 std::vector<statement *> *alternative = nullptr); | ||||||
|         virtual void accept(parser_visitor *visitor) override; |         virtual void accept(parser_visitor *visitor) override; | ||||||
|   | |||||||
| @@ -898,7 +898,10 @@ namespace source | |||||||
|     if_statement::~if_statement() |     if_statement::~if_statement() | ||||||
|     { |     { | ||||||
|         delete m_body; |         delete m_body; | ||||||
|  |         for (const auto branch : branches) | ||||||
|  |         { | ||||||
|  |             delete branch; | ||||||
|  |         } | ||||||
|         if (m_alternative != nullptr) |         if (m_alternative != nullptr) | ||||||
|         { |         { | ||||||
|             delete m_alternative; |             delete m_alternative; | ||||||
|   | |||||||
| @@ -96,6 +96,7 @@ | |||||||
| %type <elna::source::block *> block; | %type <elna::source::block *> block; | ||||||
| %type <std::pair<std::string, elna::source::type_expression *>> field_declaration; | %type <std::pair<std::string, elna::source::type_expression *>> field_declaration; | ||||||
| %type <std::vector<std::pair<std::string, elna::source::type_expression *>>> field_list; | %type <std::vector<std::pair<std::string, elna::source::type_expression *>>> field_list; | ||||||
|  | %type <std::vector<elna::source::conditional_statements *>> elsif_statement_list; | ||||||
| %% | %% | ||||||
| program: | program: | ||||||
|     type_part constant_part procedure_part variable_part BEGIN_BLOCK optional_statements END_BLOCK DOT |     type_part constant_part procedure_part variable_part BEGIN_BLOCK optional_statements END_BLOCK DOT | ||||||
| @@ -121,7 +122,7 @@ program: | |||||||
|             { |             { | ||||||
|                 *value_definition++ = variable; |                 *value_definition++ = variable; | ||||||
|             } |             } | ||||||
|             auto tree = new elna::source::program(elna::source::position{}, |             auto tree = new elna::source::program(elna::source::make_position(@5), | ||||||
|                  std::move(definitions), std::move(value_definitions), std::move($6)); |                  std::move(definitions), std::move(value_definitions), std::move($6)); | ||||||
|  |  | ||||||
|             driver.tree.reset(tree); |             driver.tree.reset(tree); | ||||||
| @@ -139,28 +140,28 @@ block: constant_part variable_part BEGIN_BLOCK optional_statements END_BLOCK | |||||||
|             { |             { | ||||||
|                 *definition++ = variable; |                 *definition++ = variable; | ||||||
|             } |             } | ||||||
|             $$ = new elna::source::block(elna::source::position{}, |             $$ = new elna::source::block(elna::source::make_position(@3), | ||||||
|                  std::move(definitions), std::move($4)); |                  std::move(definitions), std::move($4)); | ||||||
|         } |         } | ||||||
| procedure_definition: | procedure_definition: | ||||||
|     PROCEDURE IDENTIFIER formal_parameter_list SEMICOLON block SEMICOLON |     PROCEDURE IDENTIFIER formal_parameter_list SEMICOLON block SEMICOLON | ||||||
|         { |         { | ||||||
|             $$ = new elna::source::procedure_definition(elna::source::position{}, |             $$ = new elna::source::procedure_definition(elna::source::make_position(@1), | ||||||
|                  $2, std::move($3), nullptr, $5); |                  $2, std::move($3), nullptr, $5); | ||||||
|         } |         } | ||||||
|     | PROCEDURE IDENTIFIER formal_parameter_list SEMICOLON EXTERN SEMICOLON |     | PROCEDURE IDENTIFIER formal_parameter_list SEMICOLON EXTERN SEMICOLON | ||||||
|         { |         { | ||||||
|             $$ = new elna::source::procedure_definition(elna::source::position{}, |             $$ = new elna::source::procedure_definition(elna::source::make_position(@1), | ||||||
|                  $2, std::move($3), nullptr, nullptr); |                  $2, std::move($3), nullptr, nullptr); | ||||||
|         } |         } | ||||||
|     | PROCEDURE IDENTIFIER formal_parameter_list COLON type_expression SEMICOLON block SEMICOLON |     | PROCEDURE IDENTIFIER formal_parameter_list COLON type_expression SEMICOLON block SEMICOLON | ||||||
|         { |         { | ||||||
|             $$ = new elna::source::procedure_definition(elna::source::position{}, |             $$ = new elna::source::procedure_definition(elna::source::make_position(@1), | ||||||
|                  $2, std::move($3), $5, $7); |                  $2, std::move($3), $5, $7); | ||||||
|         } |         } | ||||||
|     | PROCEDURE IDENTIFIER formal_parameter_list COLON type_expression SEMICOLON EXTERN SEMICOLON |     | PROCEDURE IDENTIFIER formal_parameter_list COLON type_expression SEMICOLON EXTERN SEMICOLON | ||||||
|         { |         { | ||||||
|             $$ = new elna::source::procedure_definition(elna::source::position{}, |             $$ = new elna::source::procedure_definition(elna::source::make_position(@1), | ||||||
|                  $2, std::move($3), $5, nullptr); |                  $2, std::move($3), $5, nullptr); | ||||||
|         } |         } | ||||||
| procedure_definitions: | procedure_definitions: | ||||||
| @@ -188,19 +189,30 @@ while_statement: WHILE expression DO optional_statements END_BLOCK | |||||||
|             std::swap($4, body->statements); |             std::swap($4, body->statements); | ||||||
|             $$ = new elna::source::while_statement(elna::source::make_position(@1), body); |             $$ = new elna::source::while_statement(elna::source::make_position(@1), body); | ||||||
|         } |         } | ||||||
|  | elsif_statement_list: | ||||||
|  |     ELSIF expression THEN optional_statements elsif_statement_list | ||||||
|  |         { | ||||||
|  |             elna::source::conditional_statements *branch = new elna::source::conditional_statements($2); | ||||||
|  |             std::swap(branch->statements, $4); | ||||||
|  |             std::swap($5, $$); | ||||||
|  |             $$.emplace($$.begin(), branch); | ||||||
|  |         } | ||||||
|  |     | {} | ||||||
| if_statement: | if_statement: | ||||||
|     IF expression THEN optional_statements END_BLOCK |     IF expression THEN optional_statements elsif_statement_list END_BLOCK | ||||||
|         { |         { | ||||||
|             auto then = new elna::source::conditional_statements($2); |             auto then = new elna::source::conditional_statements($2); | ||||||
|             std::swap($4, then->statements); |             std::swap($4, then->statements); | ||||||
|             $$ = new elna::source::if_statement(elna::source::make_position(@1), then); |             $$ = new elna::source::if_statement(elna::source::make_position(@1), then); | ||||||
|  |             std::swap($5, $$->branches); | ||||||
|         } |         } | ||||||
|     | IF expression THEN optional_statements ELSE optional_statements END_BLOCK |     | IF expression THEN optional_statements elsif_statement_list ELSE optional_statements END_BLOCK | ||||||
|         { |         { | ||||||
|             auto then = new elna::source::conditional_statements($2); |             auto then = new elna::source::conditional_statements($2); | ||||||
|             std::swap($4, then->statements); |             std::swap($4, then->statements); | ||||||
|             auto _else = new std::vector<elna::source::statement *>(std::move($6)); |             auto _else = new std::vector<elna::source::statement *>(std::move($7)); | ||||||
|             $$ = new elna::source::if_statement(elna::source::make_position(@1), then, _else); |             $$ = new elna::source::if_statement(elna::source::make_position(@1), then, _else); | ||||||
|  |             std::swap($5, $$->branches); | ||||||
|         } |         } | ||||||
| return_statement: | return_statement: | ||||||
|     RETURN expression |     RETURN expression | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user