diff --git a/boot/ast.cc b/boot/ast.cc index 8b21165..9e65eb6 100644 --- a/boot/ast.cc +++ b/boot/ast.cc @@ -90,6 +90,14 @@ namespace boot } } + void empty_visitor::visit(defer_statement *defer) + { + for (statement *const body_statement : defer->statements) + { + body_statement->accept(this); + } + } + void empty_visitor::visit(block *block) { for (constant_definition *const constant : block->constants) @@ -204,7 +212,7 @@ namespace boot { } - void empty_visitor::visit(string_literal *) + void empty_visitor::visit(number_literal *) { } @@ -535,19 +543,22 @@ namespace boot { } - string_literal::string_literal(const struct position position, const std::string& value) - : literal(position), m_string(value) + defer_statement::defer_statement(const struct position position) + : statement(position) { } - void string_literal::accept(parser_visitor *visitor) + void defer_statement::accept(parser_visitor *visitor) { visitor->visit(this); } - const std::string& string_literal::string() const + defer_statement::~defer_statement() { - return m_string; + for (statement *body_statement : statements) + { + delete body_statement; + } } designator_expression::designator_expression(const struct position position) diff --git a/boot/driver.cc b/boot/driver.cc index dab1436..6ba7a3c 100644 --- a/boot/driver.cc +++ b/boot/driver.cc @@ -41,36 +41,36 @@ namespace boot return m_errors; } - std::optional escape_char(char escape) + char escape_char(char escape) { switch (escape) { case 'n': - return std::make_optional('\n'); + return '\n'; case 'a': - return std::make_optional('\a'); + return '\a'; case 'b': - return std::make_optional('\b'); + return '\b'; case 't': - return std::make_optional('\t'); + return '\t'; case 'f': - return std::make_optional('\f'); + return '\f'; case 'r': - return std::make_optional('\r'); + return '\r'; case 'v': - return std::make_optional('\v'); + return '\v'; case '\\': - return std::make_optional('\\'); + return '\\'; case '\'': - return std::make_optional('\''); + return '\''; case '"': - return std::make_optional('"'); + return '"'; case '?': - return std::make_optional('\?'); + return '\?'; case '0': - return std::make_optional('\0'); + return '\0'; default: - return std::nullopt; + return escape_invalid_char; } } } diff --git a/boot/lexer.ll b/boot/lexer.ll index 96d2b66..366d20a 100644 --- a/boot/lexer.ll +++ b/boot/lexer.ll @@ -127,6 +127,9 @@ as { sizeof { return yy::parser::make_SIZEOF(this->location); } +defer { + return yy::parser::make_DEFER(this->location); + } [A-Za-z_][A-Za-z0-9_]* { return yy::parser::make_IDENTIFIER(yytext, this->location); } @@ -155,15 +158,12 @@ sizeof { return yy::parser::make_CHARACTER(std::string(&character, 1), this->location); } '\\[0nabtfrv\\'"?]' { - std::optional escape = elna::boot::escape_char(yytext[2]); - if (escape.has_value()) - { - return yy::parser::make_CHARACTER(std::string(&escape.value(), 1), this->location); - } - else + char escape = elna::boot::escape_char(yytext[2]); + if (escape == escape_invalid_char) { REJECT; } + return yy::parser::make_CHARACTER(std::string(&escape, 1), this->location); } \"[[:print:]]*\" { std::string result; @@ -191,15 +191,12 @@ sizeof { { ++current_position; - std::optional escape = elna::boot::escape_char(*current_position); - if (escape.has_value()) - { - result.push_back(escape.value()); - } - else + char escape = elna::boot::escape_char(*current_position); + if (escape == elna::boot::escape_invalid_char) { REJECT; } + result.push_back(escape); } else { diff --git a/boot/parser.yy b/boot/parser.yy index 041c617..c0ab077 100644 --- a/boot/parser.yy +++ b/boot/parser.yy @@ -71,7 +71,7 @@ %token BOOLEAN %token IF WHILE DO THEN ELSE ELSIF RETURN %token CONST VAR PROCEDURE ARRAY OF TYPE RECORD POINTER TO UNION -%token BEGIN_BLOCK END_BLOCK EXTERN +%token BEGIN_BLOCK END_BLOCK EXTERN DEFER %token LEFT_PAREN RIGHT_PAREN LEFT_SQUARE RIGHT_SQUARE SEMICOLON DOT COMMA %token AND OR NOT CAST AS SIZEOF %token GREATER_EQUAL LESS_EQUAL LESS_THAN GREATER_THAN NOT_EQUAL EQUALS @@ -109,6 +109,7 @@ %type >> field_list; %type > elsif_statement_list; %type cast_expression; +%type defer_statement; %% program: constant_part type_part variable_part procedure_part BEGIN_BLOCK optional_statements END_BLOCK DOT @@ -206,11 +207,15 @@ if_statement: $$ = new elna::boot::if_statement(elna::boot::make_position(@1), then, _else); std::swap($5, $$->branches); } -return_statement: - RETURN expression +return_statement: RETURN expression { $$ = new elna::boot::return_statement(elna::boot::make_position(@1), $2); } +defer_statement: DEFER optional_statements END_BLOCK + { + $$ = new elna::boot::defer_statement(elna::boot::make_position(@1)); + std::swap($2, $$->statements); + } literal: INTEGER { @@ -238,7 +243,7 @@ literal: } | STRING { - $$ = new elna::boot::string_literal(elna::boot::make_position(@1), $1); + $$ = new elna::boot::number_literal(elna::boot::make_position(@1), $1); } operand: literal { $$ = $1; } @@ -367,6 +372,7 @@ statement: { $$ = new elna::boot::call_statement(elna::boot::make_position(@1), $1); } + | defer_statement { $$ = $1; } statements: statement SEMICOLON statements { diff --git a/gcc/Make-lang.in b/gcc/Make-lang.in index a0fbc0d..6f63adb 100644 --- a/gcc/Make-lang.in +++ b/gcc/Make-lang.in @@ -87,17 +87,18 @@ elna.stagefeedback: stagefeedback-start -mv elna/*$(objext) stagefeedback/elna ELNA_INCLUDES = -I $(srcdir)/elna/include -I elna/generated +ELNA_CXXFLAGS = -std=c++14 elna/%.o: elna/boot/%.cc elna/generated/parser.hh elna/generated/location.hh - $(COMPILE) $(ELNA_INCLUDES) $< + $(COMPILE) $(ELNA_CXXFLAGS) $(ELNA_INCLUDES) $< $(POSTCOMPILE) elna/%.o: elna/generated/%.cc elna/generated/parser.hh elna/generated/location.hh - $(COMPILE) $(ELNA_INCLUDES) $< + $(COMPILE) $(ELNA_CXXFLAGS) $(ELNA_INCLUDES) $< $(POSTCOMPILE) elna/%.o: elna/gcc/%.cc elna/generated/parser.hh elna/generated/location.hh - $(COMPILE) $(ELNA_INCLUDES) $< + $(COMPILE) $(ELNA_CXXFLAGS) $(ELNA_INCLUDES) $< $(POSTCOMPILE) elna/generated/parser.cc: elna/boot/parser.yy diff --git a/gcc/elna-generic.cc b/gcc/elna-generic.cc index a352059..2ae9a1c 100644 --- a/gcc/elna-generic.cc +++ b/gcc/elna-generic.cc @@ -50,7 +50,7 @@ namespace gcc if (return_type == void_type_node) { - append_to_statement_list(stmt, &this->current_statements); + this->scope.front().append_statement(stmt); this->current_expression = NULL_TREE; } else @@ -133,7 +133,7 @@ namespace gcc tree set_result = build2(INIT_EXPR, void_type_node, DECL_RESULT(main_fndecl), build_int_cst_type(integer_type_node, 0)); tree return_stmt = build1(RETURN_EXPR, void_type_node, set_result); - append_to_statement_list(return_stmt, &this->current_statements); + this->scope.front().append_statement(return_stmt); tree_symbol_mapping mapping = leave_scope(); BLOCK_SUPERCONTEXT(mapping.block()) = this->main_fndecl; @@ -214,19 +214,29 @@ namespace gcc void generic_visitor::enter_scope() { - this->current_statements = alloc_stmt_list(); - this->variable_chain = tree_chain(); + scope.emplace_front(); this->symbol_map = std::make_shared>(this->symbol_map); } tree_symbol_mapping generic_visitor::leave_scope() { - tree new_block = build_block(variable_chain.head(), - NULL_TREE, NULL_TREE, NULL_TREE); - tree bind_expr = build3(BIND_EXPR, void_type_node, variable_chain.head(), - this->current_statements, new_block); + tree new_block = build_block(this->scope.front().variables.head(), + this->scope.front().blocks.head(), NULL_TREE, NULL_TREE); + + for (tree it = this->scope.front().blocks.head(); it != NULL_TREE; it = BLOCK_CHAIN(it)) + { + BLOCK_SUPERCONTEXT(it) = new_block; + } + tree bind_expr = build3(BIND_EXPR, void_type_node, this->scope.front().variables.head(), + this->scope.front().chain_defer(), new_block); this->symbol_map = this->symbol_map->scope(); + scope.pop_front(); + + if (!scope.empty()) + { + scope.front().blocks.append(new_block); + } return tree_symbol_mapping{ bind_expr, new_block }; } @@ -278,9 +288,9 @@ namespace gcc this->current_expression = null_pointer_node; } - void generic_visitor::visit(boot::string_literal *string) + void generic_visitor::visit(boot::number_literal *string) { - this->current_expression = build_string_literal(string->string().size() + 1, string->string().c_str()); + this->current_expression = build_string_literal(string->number().size() + 1, string->number().c_str()); } tree generic_visitor::build_arithmetic_operation(boot::binary_expression *expression, @@ -433,9 +443,12 @@ namespace gcc TREE_CONSTANT(definition_tree) = 1; TREE_READONLY(definition_tree) = 1; - auto declaration_statement = build1_loc(definition_location, DECL_EXPR, - void_type_node, definition_tree); - append_to_statement_list(declaration_statement, &this->current_statements); + if (!scope.empty()) + { + auto declaration_statement = build1_loc(definition_location, DECL_EXPR, + void_type_node, definition_tree); + this->scope.front().append_statement(declaration_statement); + } } else { @@ -450,27 +463,9 @@ namespace gcc { tree tree_type = build_type(definition->body()); - if (tree_type == NULL_TREE) + if (!this->symbol_map->enter(definition->identifier(), tree_type)) { - return; - } - location_t definition_location = get_location(&definition->position()); - tree definition_tree = build_decl(definition_location, TYPE_DECL, - get_identifier(definition->identifier().c_str()), tree_type); - auto result = this->symbol_map->enter(definition->identifier(), tree_type); - - if (result) - { - DECL_CONTEXT(definition_tree) = this->main_fndecl; - variable_chain.append(definition_tree); - - auto declaration_statement = build1_loc(definition_location, DECL_EXPR, - void_type_node, definition_tree); - append_to_statement_list(declaration_statement, &this->current_statements); - } - else - { - error_at(definition_location, + error_at(get_location(&definition->position()), "type '%s' already declared in this scope", definition->identifier().c_str()); } @@ -606,11 +601,11 @@ namespace gcc else { DECL_CONTEXT(declaration_tree) = this->main_fndecl; - variable_chain.append(declaration_tree); + this->scope.front().variables.append(declaration_tree); auto declaration_statement = build1_loc(declaration_location, DECL_EXPR, void_type_node, declaration_tree); - append_to_statement_list(declaration_statement, &this->current_statements); + this->scope.front().append_statement(declaration_statement); } } @@ -705,7 +700,7 @@ namespace gcc tree assignment = build2_loc(statement_location, MODIFY_EXPR, void_type_node, lvalue, this->current_expression); - append_to_statement_list(assignment, &this->current_statements); + this->scope.front().append_statement(assignment); this->current_expression = NULL_TREE; } else @@ -731,14 +726,16 @@ namespace gcc } if (statement->alternative() != nullptr) { + enter_scope(); for (const auto body_statement : *statement->alternative()) { body_statement->accept(this); } + tree_symbol_mapping mapping = leave_scope(); + scope.front().append_statement(mapping.bind_expression()); } - tree endif_label_expr = build1(LABEL_EXPR, void_type_node, endif_label_decl); - append_to_statement_list(endif_label_expr, &this->current_statements); + this->scope.front().append_statement(endif_label_expr); this->current_expression = NULL_TREE; } @@ -761,19 +758,22 @@ namespace gcc 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); + this->scope.front().append_statement(cond_expr); tree then_label_expr = build1(LABEL_EXPR, void_type_node, then_label_decl); - append_to_statement_list(then_label_expr, &this->current_statements); + this->scope.front().append_statement(then_label_expr); + enter_scope(); for (const auto body_statement : branch.statements) { body_statement->accept(this); } - append_to_statement_list(goto_endif, &this->current_statements); + tree_symbol_mapping mapping = leave_scope(); + this->scope.front().append_statement(mapping.bind_expression()); + this->scope.front().append_statement(goto_endif); tree else_label_expr = build1(LABEL_EXPR, void_type_node, else_label_decl); - append_to_statement_list(else_label_expr, &this->current_statements); + this->scope.front().append_statement(else_label_expr); } tree generic_visitor::build_label_decl(const char *name, location_t loc) @@ -798,13 +798,15 @@ namespace gcc this->current_expression = error_mark_node; return; } + enter_scope(); + auto prerequisite_location = get_location(&statement->body().prerequisite().position()); auto body_location = get_location(&statement->position()); auto prerequisite_label_decl = build_label_decl("while_check", prerequisite_location); auto prerequisite_label_expr = build1_loc(prerequisite_location, LABEL_EXPR, void_type_node, prerequisite_label_decl); - append_to_statement_list(prerequisite_label_expr, &this->current_statements); + this->scope.front().append_statement(prerequisite_label_expr); auto body_label_decl = build_label_decl("while_body", body_location); auto end_label_decl = build_label_decl("end_while", UNKNOWN_LOCATION); @@ -816,21 +818,24 @@ namespace gcc auto cond_expr = build3_loc(prerequisite_location, COND_EXPR, void_type_node, this->current_expression, goto_body, goto_end); - append_to_statement_list(cond_expr, &this->current_statements); + this->scope.front().append_statement(cond_expr); auto body_label_expr = build1_loc(body_location, LABEL_EXPR, void_type_node, body_label_decl); - append_to_statement_list(body_label_expr, &this->current_statements); + this->scope.front().append_statement(body_label_expr); for (const auto body_statement : statement->body().statements) { body_statement->accept(this); } + tree_symbol_mapping mapping = leave_scope(); + this->scope.front().append_statement(mapping.bind_expression()); + auto goto_check = build1(GOTO_EXPR, void_type_node, prerequisite_label_decl); - append_to_statement_list(goto_check, &this->current_statements); + this->scope.front().append_statement(goto_check); auto endif_label_expr = build1(LABEL_EXPR, void_type_node, end_label_decl); - append_to_statement_list(endif_label_expr, &this->current_statements); + this->scope.front().append_statement(endif_label_expr); this->current_expression = NULL_TREE; } @@ -838,7 +843,8 @@ namespace gcc void generic_visitor::visit(boot::call_statement *statement) { statement->body().accept(this); - append_to_statement_list(this->current_expression, &this->current_statements); + this->scope.front().append_statement(this->current_expression); + this->current_expression = NULL_TREE; } void generic_visitor::visit(boot::return_statement *statement) @@ -854,7 +860,18 @@ namespace gcc tree set_result = build2(INIT_EXPR, void_type_node, DECL_RESULT(main_fndecl), this->current_expression); tree return_stmt = build1(RETURN_EXPR, void_type_node, set_result); - append_to_statement_list(return_stmt, &this->current_statements); + this->scope.front().append_statement(return_stmt); + } + + void generic_visitor::visit(boot::defer_statement *statement) + { + enter_scope(); + for (boot::statement *const body_statement : statement->statements) + { + body_statement->accept(this); + } + tree_symbol_mapping mapping = leave_scope(); + scope.front().defer(mapping.bind_expression()); } } } diff --git a/gcc/elna-tree.cc b/gcc/elna-tree.cc index e1d77c5..cc35aed 100644 --- a/gcc/elna-tree.cc +++ b/gcc/elna-tree.cc @@ -60,6 +60,11 @@ namespace gcc TREE_CHAIN(this->last) = t; } + void block_chain::chain(tree t) + { + BLOCK_CHAIN(this->last) = t; + } + tree_symbol_mapping::tree_symbol_mapping(tree bind_expression, tree block) : m_bind_expression(bind_expression), m_block(block) { @@ -75,6 +80,47 @@ namespace gcc return m_block; } + block_scope::block_scope() + : m_statement_list(alloc_stmt_list()) + { + } + + void block_scope::append_statement(tree statement_tree) + { + if (!defers.empty()) + { + append_to_statement_list(statement_tree, &this->defers.front().second); + } + else + { + append_to_statement_list(statement_tree, &this->m_statement_list); + } + } + + void block_scope::defer(tree statement_tree) + { + defers.push_front({ statement_tree, alloc_stmt_list() }); + } + + tree block_scope::chain_defer() + { + if (this->defers.empty()) + { + return m_statement_list; + } + std::forward_list>::iterator defer_iterator = + this->defers.begin(); + tree defer_tree = build2(TRY_FINALLY_EXPR, void_type_node, defer_iterator->second, defer_iterator->first); + + ++defer_iterator; + for (; defer_iterator != this->defers.end(); ++defer_iterator) + { + append_to_statement_list(defer_tree, &defer_iterator->second); + defer_tree = build2(TRY_FINALLY_EXPR, void_type_node, defer_iterator->second, defer_iterator->first); + } + return build2(COMPOUND_EXPR, TREE_TYPE(defer_tree), m_statement_list, defer_tree); + } + std::shared_ptr> builtin_symbol_table() { std::shared_ptr> initial_table = diff --git a/include/elna/boot/ast.h b/include/elna/boot/ast.h index 4e6cfdf..c4b747a 100644 --- a/include/elna/boot/ast.h +++ b/include/elna/boot/ast.h @@ -64,7 +64,7 @@ namespace boot class dereference_expression; template class number_literal; - class string_literal; + class defer_statement; /** * Interface for AST visitors. @@ -83,6 +83,7 @@ namespace boot virtual void visit(if_statement *) = 0; virtual void visit(while_statement *) = 0; virtual void visit(return_statement *) = 0; + virtual void visit(defer_statement *) = 0; virtual void visit(block *) = 0; virtual void visit(program *) = 0; virtual void visit(binary_expression *) = 0; @@ -102,7 +103,7 @@ namespace boot virtual void visit(number_literal *) = 0; virtual void visit(number_literal *) = 0; virtual void visit(number_literal *) = 0; - virtual void visit(string_literal *) = 0; + virtual void visit(number_literal *) = 0; }; /** @@ -122,6 +123,7 @@ namespace boot virtual void visit(if_statement *) override; virtual void visit(while_statement *) override; virtual void visit(return_statement *) override; + virtual void visit(defer_statement *defer) override; virtual void visit(block *block) override; virtual void visit(program *program) override; virtual void visit(binary_expression *expression) override; @@ -141,7 +143,7 @@ namespace boot virtual void visit(number_literal *) override; virtual void visit(number_literal *) override; virtual void visit(number_literal *) override; - virtual void visit(string_literal *) override; + virtual void visit(number_literal *) override; }; /** @@ -676,21 +678,21 @@ namespace boot visitor->visit(this); } - T number() const + const T& number() const { return m_number; } }; - class string_literal : public literal + class defer_statement : public statement { - std::string m_string; - public: - string_literal(const struct position position, const std::string& value); + std::vector statements; + + defer_statement(const struct position position); virtual void accept(parser_visitor *visitor) override; - const std::string& string() const; + virtual ~defer_statement() override; }; class binary_expression : public expression diff --git a/include/elna/boot/driver.h b/include/elna/boot/driver.h index 00c6e4f..343de8d 100644 --- a/include/elna/boot/driver.h +++ b/include/elna/boot/driver.h @@ -39,6 +39,8 @@ namespace boot const std::list>& errors() const noexcept; }; - std::optional escape_char(char escape); + constexpr char escape_invalid_char = '\xff'; + + char escape_char(char escape); } } diff --git a/include/elna/gcc/elna-generic.h b/include/elna/gcc/elna-generic.h index 2f84e0b..9d85ec4 100644 --- a/include/elna/gcc/elna-generic.h +++ b/include/elna/gcc/elna-generic.h @@ -10,7 +10,7 @@ #include "tree.h" #include "tree-iterator.h" -#include +#include #include namespace elna @@ -19,11 +19,10 @@ namespace gcc { class generic_visitor final : public boot::empty_visitor { - tree current_statements{ NULL_TREE }; + std::forward_list scope; tree current_expression{ NULL_TREE }; std::shared_ptr> symbol_map; tree main_fndecl{ NULL_TREE }; - tree_chain variable_chain; tree build_label_decl(const char *name, location_t loc); tree build_type(boot::type_expression& type); @@ -58,7 +57,7 @@ namespace gcc void visit(boot::number_literal *boolean) override; void visit(boot::number_literal *character) override; void visit(boot::number_literal *) override; - void visit(boot::string_literal *string) override; + void visit(boot::number_literal *string) override; void visit(boot::binary_expression *expression) override; void visit(boot::unary_expression *expression) override; void visit(boot::constant_definition *definition) override; @@ -73,6 +72,7 @@ namespace gcc void visit(boot::while_statement *statement) override; void visit(boot::call_statement *statement) override; void visit(boot::return_statement *statement) override; + void visit(boot::defer_statement *statement) override; }; } } diff --git a/include/elna/gcc/elna-tree.h b/include/elna/gcc/elna-tree.h index e445a12..9db2f3b 100644 --- a/include/elna/gcc/elna-tree.h +++ b/include/elna/gcc/elna-tree.h @@ -1,9 +1,12 @@ #pragma once +#include + #include "config.h" #include "system.h" #include "coretypes.h" #include "tree.h" +#include "tree-iterator.h" #include "elna/boot/ast.h" #include "elna/boot/symbol.h" @@ -43,6 +46,13 @@ namespace gcc class tree_chain final : public tree_chain_base { + protected: + void chain(tree t) override; + }; + + class block_chain final : public tree_chain_base + { + protected: void chain(tree t) override; }; @@ -58,6 +68,22 @@ namespace gcc tree block(); }; + class block_scope + { + tree m_statement_list{ NULL_TREE }; + std::forward_list> defers; + + public: + tree_chain variables; + block_chain blocks; + + block_scope(); + + void append_statement(tree statement_tree); + void defer(tree statement_tree); + tree chain_defer(); + }; + std::shared_ptr> builtin_symbol_table(); tree do_pointer_arithmetic(boot::binary_operator binary_operator, tree left, tree right); diff --git a/source.elna b/source.elna index 38a7a8b..0fe4da6 100644 --- a/source.elna +++ b/source.elna @@ -14,7 +14,7 @@ const TOKEN_PLUS = 43; TOKEN_MINUS = 44; TOKEN_MULTIPLICATION = 45; TOKEN_DIVISION = 46; TOKEN_REMAINDER = 47; TOKEN_ASSIGNMENT = 48; TOKEN_COLON = 49; TOKEN_HAT = 50; TOKEN_AT = 51; TOKEN_COMMENT = 52; TOKEN_INTEGER = 53; TOKEN_WORD = 54; - TOKEN_CHARACTER = 55; TOKEN_STRING = 56; + TOKEN_CHARACTER = 55; TOKEN_STRING = 56, TOKEN_DEFER = 57; type Position = record @@ -41,7 +41,8 @@ type end, CommandLine = record input: pointer to Char; - tokenize: Bool + tokenize: Bool; + syntax_tree: Bool end, Literal = record value: Int @@ -457,6 +458,8 @@ begin write_s("c>") elsif current_token^.kind = TOKEN_STRING then write_s("\"...\"") + elsif current_token^.kind = TOKEN_DEFER then + write_s("DEFER") else write_s("UNKNOWN<"); write_i(current_token^.kind); @@ -533,6 +536,8 @@ begin current_token.kind := TOKEN_AS elsif strncmp("sizeof", input_pointer, token_length) = 0 then current_token.kind := TOKEN_SIZEOF + elsif strncmp("defer", input_pointer, token_length) = 0 then + current_token.kind := TOKEN_DEFER else current_token.kind := TOKEN_IDENTIFIER; current_token.value.string_value := cast(calloc(token_length + 1, 1) as pointer to Char); @@ -765,6 +770,7 @@ begin i := 1; result := cast(malloc(sizeof(CommandLine)) as pointer to CommandLine); result^.tokenize := false; + result^.syntax_tree := false; result^.input := nil; while i < argc do @@ -772,6 +778,8 @@ begin if strcmp(parameter^, "--tokenize") = 0 then result^.tokenize := true + elsif strcmp(parameter^, "--syntax-tree") = 0 then + result^.syntax_tree := true elsif parameter^^ <> '-' then result^.input := parameter^ else @@ -802,10 +810,10 @@ var command_line: pointer to CommandLine; begin command_line := parse_command_line(argc, argv); - - if cast(command_line as Word) = 0u then + if command_line = nil then return 2 end; + input := read_source(command_line^.input); if input = nil then perror(command_line^.input); @@ -816,8 +824,9 @@ begin if command_line^.tokenize then print_tokens(tokens, tokens_size) end; - - parse_program(@tokens, @tokens_size); + if command_line^.syntax_tree then + parse_program(@tokens, @tokens_size) + end; return 0 end;