From f9f2fb2852a7feed5e1fdca4a771588b15758762 Mon Sep 17 00:00:00 2001 From: slavek-kucera <53339291+slavek-kucera@users.noreply.github.com> Date: Mon, 30 Aug 2021 16:34:25 +0200 Subject: [PATCH] feat: Support START and MHELP instructions (#171) --- clients/vscode-hlasmplugin/CHANGELOG.md | 1 + parser_library/src/context/hlasm_context.cpp | 11 +- parser_library/src/context/hlasm_context.h | 19 ++- parser_library/src/context/id_storage.cpp | 2 +- parser_library/src/context/id_storage.h | 2 +- parser_library/src/context/instruction.cpp | 1 + parser_library/src/diagnostic.cpp | 19 +++ parser_library/src/diagnostic.h | 6 + parser_library/src/parsing/parser_impl.cpp | 13 +- .../instruction_sets/asm_processor.cpp | 52 ++++++++ .../instruction_sets/asm_processor.h | 1 + .../instruction_sets/ca_processor.cpp | 42 ++++++ .../instruction_sets/ca_processor.h | 2 + .../instruction_sets/macro_processor.cpp | 9 +- .../test/debugging/debugger_test.cpp | 2 +- .../test/diagnostics_check_test.cpp | 4 +- parser_library/test/processing/CMakeLists.txt | 1 + .../test/processing/ca_instr_test.cpp | 67 ++++++++++ parser_library/test/processing/start_test.cpp | 123 ++++++++++++++++++ 19 files changed, 358 insertions(+), 19 deletions(-) create mode 100644 parser_library/test/processing/start_test.cpp diff --git a/clients/vscode-hlasmplugin/CHANGELOG.md b/clients/vscode-hlasmplugin/CHANGELOG.md index aba028211..378aa1a53 100644 --- a/clients/vscode-hlasmplugin/CHANGELOG.md +++ b/clients/vscode-hlasmplugin/CHANGELOG.md @@ -5,6 +5,7 @@ #### Added - Document outline support - CNOP instruction implementation (limited) +- START, MHELP instructions #### Fixed - Preserve mixed-case labels on macro calls diff --git a/parser_library/src/context/hlasm_context.cpp b/parser_library/src/context/hlasm_context.cpp index d4f9e3737..e6b4b7ffc 100644 --- a/parser_library/src/context/hlasm_context.cpp +++ b/parser_library/src/context/hlasm_context.cpp @@ -73,9 +73,8 @@ void hlasm_context::add_system_vars_to_scope() auto val_ndx = std::make_shared>(SYSNDX, true, false); std::string value = std::to_string(SYSNDX_); - int tmp_size = (int)value.size(); - for (int i = 0; i < 4 - tmp_size; ++i) - value.insert(value.begin(), '0'); + if (auto value_len = value.size(); value_len < 4) + value.insert(0, 4 - value_len, '0'); val_ndx->set_value(std::move(value)); curr_scope()->variables.insert({ SYSNDX, val_ndx }); @@ -256,7 +255,6 @@ hlasm_context::hlasm_context(std::string file_name, asm_option asm_options, std: , opencode_file_name_(file_name) , asm_options_(std::move(asm_options)) , instruction_map_(init_instruction_map()) - , SYSNDX_(0) , ord_ctx(*ids_, *this) { scope_stack_.emplace_back(); @@ -727,16 +725,19 @@ bool hlasm_context::is_in_macro() const { return scope_stack_.back().is_in_macro macro_invo_ptr hlasm_context::enter_macro(id_index name, macro_data_ptr label_param_data, std::vector params) { + assert(SYSNDX_ <= SYSNDX_limit); + macro_def_ptr macro_def = get_macro_definition(name); assert(macro_def); - auto invo((macro_def->call(std::move(label_param_data), std::move(params), ids().add("SYSLIST")))); + auto invo = macro_def->call(std::move(label_param_data), std::move(params), ids().add("SYSLIST")); scope_stack_.emplace_back(invo, macro_def); add_system_vars_to_scope(); visited_files_.insert(macro_def->definition_location.file); ++SYSNDX_; + return invo; } diff --git a/parser_library/src/context/hlasm_context.h b/parser_library/src/context/hlasm_context.h index 0297c4f15..8387e1c09 100644 --- a/parser_library/src/context/hlasm_context.h +++ b/parser_library/src/context/hlasm_context.h @@ -15,13 +15,13 @@ #ifndef CONTEXT_HLASM_CONTEXT_H #define CONTEXT_HLASM_CONTEXT_H -#include #include #include #include #include #include "code_scope.h" +#include "compiler_options.h" #include "operation_code.h" #include "ordinary_assembly/ordinary_assembly_context.h" #include "processing_context.h" @@ -69,13 +69,17 @@ class hlasm_context // Compiler options asm_option asm_options_; + static constexpr alignment sectalgn = doubleword; // map of all instruction in HLASM const instruction_storage instruction_map_; instruction_storage init_instruction_map(); // value of system variable SYSNDX - size_t SYSNDX_; + unsigned long SYSNDX_ = 1; + static constexpr unsigned long SYSNDX_limit_max = 9999999UL; + unsigned long SYSNDX_limit = SYSNDX_limit_max; + void add_system_vars_to_scope(); void add_global_system_vars(); @@ -252,6 +256,17 @@ class hlasm_context return val; } + + unsigned long next_sysndx() const { return SYSNDX_; } + void sysndx_limit(unsigned long limit) + { + assert(limit <= SYSNDX_limit_max); + SYSNDX_limit = limit; + } + unsigned long sysndx_limit() const { return SYSNDX_limit; } + static constexpr unsigned long sysndx_limit_max() { return SYSNDX_limit_max; } + + alignment section_alignment() const { return sectalgn; } }; } // namespace hlasm_plugin::parser_library::context diff --git a/parser_library/src/context/id_storage.cpp b/parser_library/src/context/id_storage.cpp index cb7723679..8ab1b65fa 100644 --- a/parser_library/src/context/id_storage.cpp +++ b/parser_library/src/context/id_storage.cpp @@ -69,10 +69,10 @@ id_storage::well_known_strings::well_known_strings(std::unordered_set& ptr); } const well_known; diff --git a/parser_library/src/context/instruction.cpp b/parser_library/src/context/instruction.cpp index ae0a4ccf8..e39c9e5a0 100644 --- a/parser_library/src/context/instruction.cpp +++ b/parser_library/src/context/instruction.cpp @@ -118,6 +118,7 @@ const std::vector instruction::ca_instructions = { { "MACRO", true }, { "MEND", true }, { "MEXIT", true }, + { "MHELP", false }, { "AEJECT", true }, { "AREAD", false }, { "ASPACE", false }, diff --git a/parser_library/src/diagnostic.cpp b/parser_library/src/diagnostic.cpp index bf4399d3d..d4d109c25 100644 --- a/parser_library/src/diagnostic.cpp +++ b/parser_library/src/diagnostic.cpp @@ -1142,6 +1142,14 @@ diagnostic_op diagnostic_op::warning_A249_sequence_symbol_expected(const range& return diagnostic_op(diagnostic_severity::warning, "A249", "Sequence symbol expected", range); } +diagnostic_op diagnostic_op::error_A250_absolute_with_known_symbols(const range& range) +{ + return diagnostic_op(diagnostic_severity::error, + "A250", + "Operand must be an absolute expression of previously defined symbols", + range); +} + diagnostic_op diagnostic_op::warning_A300_op_apostrophes_missing(const std::string& instr_name, const range& range) { return diagnostic_op(diagnostic_severity::warning, @@ -1828,6 +1836,17 @@ diagnostic_op diagnostic_op::error_E071(const range& range) return diagnostic_op(diagnostic_severity::error, "E071", "Macro prototype expected.", range); } +diagnostic_op diagnostic_op::error_E072(const range& range) +{ + return diagnostic_op(diagnostic_severity::error, "E072", "SYSNDX limit reached, macro call supressed.", range); +} + +diagnostic_op diagnostic_op::error_E073(const range& range) +{ + return diagnostic_op( + diagnostic_severity::error, "E073", "Illegal START instruction - CSECT already exists.", range); +} + diagnostic_op diagnostic_op::warning_W010(const std::string& message, const range& range) { return diagnostic_op(diagnostic_severity::warning, "W010", message + " not expected", range); diff --git a/parser_library/src/diagnostic.h b/parser_library/src/diagnostic.h index a8fc6c40e..99fb3be86 100644 --- a/parser_library/src/diagnostic.h +++ b/parser_library/src/diagnostic.h @@ -386,6 +386,8 @@ struct diagnostic_op static diagnostic_op warning_A249_sequence_symbol_expected(const range& range); + static diagnostic_op error_A250_absolute_with_known_symbols(const range& range); + // other static diagnostic_op warning_A300_op_apostrophes_missing(const std::string& instr_name, const range& range); @@ -574,6 +576,10 @@ struct diagnostic_op static diagnostic_op error_E071(const range& range); + static diagnostic_op error_E072(const range& range); + + static diagnostic_op error_E073(const range& range); + static diagnostic_op warning_W010(const std::string& message, const range& range); static diagnostic_op warning_W011(const range& range); diff --git a/parser_library/src/parsing/parser_impl.cpp b/parser_library/src/parsing/parser_impl.cpp index 3c709f8ae..e0719f8ad 100644 --- a/parser_library/src/parsing/parser_impl.cpp +++ b/parser_library/src/parsing/parser_impl.cpp @@ -143,26 +143,27 @@ void parser_impl::resolve_expression(std::vector& expr void parser_impl::resolve_expression(expressions::ca_expr_ptr& expr) const { auto [_, opcode] = *proc_status; - if (opcode.value == hlasm_ctx->ids().well_known.SETA || opcode.value == hlasm_ctx->ids().well_known.ACTR - || opcode.value == hlasm_ctx->ids().well_known.ASPACE || opcode.value == hlasm_ctx->ids().well_known.AGO) + const auto& wk = hlasm_ctx->ids().well_known; + if (opcode.value == wk.SETA || opcode.value == wk.ACTR || opcode.value == wk.ASPACE || opcode.value == wk.AGO + || opcode.value == wk.MHELP) resolve_expression(expr, context::SET_t_enum::A_TYPE); - else if (opcode.value == hlasm_ctx->ids().well_known.SETB) + else if (opcode.value == wk.SETB) { if (!expr->is_compatible(ca_expression_compatibility::setb)) expr->add_diagnostic(diagnostic_op::error_CE016_logical_expression_parenthesis(expr->expr_range)); resolve_expression(expr, context::SET_t_enum::B_TYPE); } - else if (opcode.value == hlasm_ctx->ids().well_known.AIF) + else if (opcode.value == wk.AIF) { if (!expr->is_compatible(ca_expression_compatibility::aif)) expr->add_diagnostic(diagnostic_op::error_CE016_logical_expression_parenthesis(expr->expr_range)); resolve_expression(expr, context::SET_t_enum::B_TYPE); } - else if (opcode.value == hlasm_ctx->ids().well_known.SETC) + else if (opcode.value == wk.SETC) resolve_expression(expr, context::SET_t_enum::C_TYPE); - else if (opcode.value == hlasm_ctx->ids().well_known.AREAD) + else if (opcode.value == wk.AREAD) { // aread operand is just enumeration } diff --git a/parser_library/src/processing/instruction_sets/asm_processor.cpp b/parser_library/src/processing/instruction_sets/asm_processor.cpp index 80d098ad7..54bcf16c8 100644 --- a/parser_library/src/processing/instruction_sets/asm_processor.cpp +++ b/parser_library/src/processing/instruction_sets/asm_processor.cpp @@ -641,6 +641,7 @@ asm_processor::process_table_t asm_processor::create_table(context::hlasm_contex table.emplace(h_ctx.ids().add("CCW0"), [this](rebuilt_statement stmt) { process_CCW(std::move(stmt)); }); table.emplace(h_ctx.ids().add("CCW1"), [this](rebuilt_statement stmt) { process_CCW(std::move(stmt)); }); table.emplace(h_ctx.ids().add("CNOP"), [this](rebuilt_statement stmt) { process_CNOP(std::move(stmt)); }); + table.emplace(h_ctx.ids().add("START"), [this](rebuilt_statement stmt) { process_START(std::move(stmt)); }); return table; } @@ -765,4 +766,55 @@ void asm_processor::process_CNOP(rebuilt_statement stmt) hlasm_ctx.ord_ctx.reserve_storage_area(0, context::alignment { (size_t)*byte_value, (size_t)*boundary_value }); } + +void asm_processor::process_START(rebuilt_statement stmt) +{ + auto sect_name = find_label_symbol(stmt); + + if (std::any_of(hlasm_ctx.ord_ctx.sections().begin(), hlasm_ctx.ord_ctx.sections().end(), [](const auto& s) { + return s->kind == context::section_kind::EXECUTABLE || s->kind == context::section_kind::READONLY; + })) + { + add_diagnostic(diagnostic_op::error_E073(stmt.stmt_range_ref())); + return; + } + + if (hlasm_ctx.ord_ctx.symbol_defined(sect_name)) + { + add_diagnostic(diagnostic_op::error_E031("symbol", stmt.label_ref().field_range)); + return; + } + + auto sym_loc = hlasm_ctx.processing_stack().back().proc_location; + sym_loc.pos.column = 0; + hlasm_ctx.ord_ctx.set_section(sect_name, context::section_kind::EXECUTABLE, std::move(sym_loc)); + + const auto& ops = stmt.operands_ref().value; + if (ops.size() != 1) + { + check(stmt, hlasm_ctx, checker_, *this); + return; + } + + auto initial_offset = try_get_abs_value(ops.front().get()); + if (!initial_offset.has_value()) + { + add_diagnostic(diagnostic_op::error_A250_absolute_with_known_symbols(ops.front()->operand_range)); + return; + } + + size_t start_section_alignment = hlasm_ctx.section_alignment().boundary; + size_t start_section_alignment_mask = start_section_alignment - 1; + + auto offset = initial_offset.value(); + if (offset & start_section_alignment_mask) + { + // TODO: generate informational message? + offset += start_section_alignment_mask; + offset &= ~start_section_alignment_mask; + } + + hlasm_ctx.ord_ctx.set_available_location_counter_value(start_section_alignment, offset); +} + } // namespace hlasm_plugin::parser_library::processing diff --git a/parser_library/src/processing/instruction_sets/asm_processor.h b/parser_library/src/processing/instruction_sets/asm_processor.h index 200704b63..47dc38f7f 100644 --- a/parser_library/src/processing/instruction_sets/asm_processor.h +++ b/parser_library/src/processing/instruction_sets/asm_processor.h @@ -63,6 +63,7 @@ class asm_processor : public low_language_processor void process_AINSERT(rebuilt_statement stmt); void process_CCW(rebuilt_statement stmt); void process_CNOP(rebuilt_statement stmt); + void process_START(rebuilt_statement stmt); template void process_data_instruction(rebuilt_statement stmt); diff --git a/parser_library/src/processing/instruction_sets/ca_processor.cpp b/parser_library/src/processing/instruction_sets/ca_processor.cpp index dce23e46c..a88af7c91 100644 --- a/parser_library/src/processing/instruction_sets/ca_processor.cpp +++ b/parser_library/src/processing/instruction_sets/ca_processor.cpp @@ -71,6 +71,7 @@ ca_processor::process_table_t ca_processor::create_table(context::hlasm_context& table.emplace(h_ctx.ids().add("AREAD"), std::bind(&ca_processor::process_AREAD, this, std::placeholders::_1)); table.emplace(h_ctx.ids().add("ASPACE"), std::bind(&ca_processor::process_ASPACE, this, std::placeholders::_1)); table.emplace(h_ctx.ids().add("AEJECT"), std::bind(&ca_processor::process_AEJECT, this, std::placeholders::_1)); + table.emplace(h_ctx.ids().add("MHELP"), [this](const semantics::complete_statement& stmt) { process_MHELP(stmt); }); return table; } @@ -638,3 +639,44 @@ template void ca_processor::process_GBL_LCL(const semantics template void ca_processor::process_GBL_LCL(const semantics::complete_statement& stmt); template void ca_processor::process_GBL_LCL(const semantics::complete_statement& stmt); template void ca_processor::process_GBL_LCL(const semantics::complete_statement& stmt); + +void ca_processor::process_MHELP(const semantics::complete_statement& stmt) +{ + register_seq_sym(stmt); + + const auto& ops = stmt.operands_ref().value; + if (ops.size() > 1) + { + add_diagnostic(diagnostic_op::error_E020("operand", stmt.instruction_ref().field_range)); + return; + } + if (ops.size() < 1) + { + add_diagnostic(diagnostic_op::error_E021("operand", stmt.instruction_ref().field_range)); + return; + } + + const auto* ca_op = ops[0]->access_ca(); + assert(ca_op); + if (!ca_op) + return; + + uint32_t value = 0; + if (ca_op->kind == semantics::ca_kind::EXPR) + { + value = ca_op->access_expr()->expression->evaluate(eval_ctx); + } + else if (ca_op->kind == semantics::ca_kind::VAR) + { + auto val = ca_op->access_var()->variable_symbol->evaluate(eval_ctx); + if (val.type == context::SET_t_enum::A_TYPE) + value = val.access_a(); + } + else + { + add_diagnostic(diagnostic_op::error_E010("operand", ca_op->operand_range)); + } + value &= ~0xffUL; // ignore the option part + if (value & 0xff00UL) // rest is considered only when byte 3 is non-zero + hlasm_ctx.sysndx_limit(std::min((unsigned long)value, context::hlasm_context::sysndx_limit_max())); +} diff --git a/parser_library/src/processing/instruction_sets/ca_processor.h b/parser_library/src/processing/instruction_sets/ca_processor.h index 8480bfbf9..c0e8a22d4 100644 --- a/parser_library/src/processing/instruction_sets/ca_processor.h +++ b/parser_library/src/processing/instruction_sets/ca_processor.h @@ -94,6 +94,8 @@ class ca_processor : public instruction_processor void process_AREAD(const semantics::complete_statement& stmt); void process_empty(const semantics::complete_statement&); + + void process_MHELP(const semantics::complete_statement& stmt); }; } // namespace hlasm_plugin::parser_library::processing diff --git a/parser_library/src/processing/instruction_sets/macro_processor.cpp b/parser_library/src/processing/instruction_sets/macro_processor.cpp index 92dc900dd..87ad4f3e1 100644 --- a/parser_library/src/processing/instruction_sets/macro_processor.cpp +++ b/parser_library/src/processing/instruction_sets/macro_processor.cpp @@ -29,8 +29,15 @@ macro_processor::macro_processor( void macro_processor::process(std::shared_ptr stmt) { - auto args = get_args(*stmt); + const auto next_sysndx = hlasm_ctx.next_sysndx(); + const auto sysndx_limit = hlasm_ctx.sysndx_limit(); + if (next_sysndx > sysndx_limit) + { + add_diagnostic(diagnostic_op::error_E072(stmt->stmt_range_ref())); + return; + } + auto args = get_args(*stmt); hlasm_ctx.enter_macro(stmt->opcode_ref().value, std::move(args.name_param), std::move(args.symbolic_params)); } diff --git a/parser_library/test/debugging/debugger_test.cpp b/parser_library/test/debugging/debugger_test.cpp index 755e7bee4..2e5f83687 100644 --- a/parser_library/test/debugging/debugger_test.cpp +++ b/parser_library/test/debugging/debugger_test.cpp @@ -355,7 +355,7 @@ TEST(debugger, test) }), }, { "&SYSECT", "" }, - { "&SYSNDX", "0000" }, + { "&SYSNDX", "0001" }, { "&SYSSTYP", "" }, { "&SYSLOC", "" }, { "&SYSNEST", 1 }, diff --git a/parser_library/test/diagnostics_check_test.cpp b/parser_library/test/diagnostics_check_test.cpp index 8dca49476..e6f856a2b 100644 --- a/parser_library/test/diagnostics_check_test.cpp +++ b/parser_library/test/diagnostics_check_test.cpp @@ -246,6 +246,7 @@ TEST(diagnostics, { std::string input( R"( +S START 32 ACONTROL NOAFPR,COMPAT(CASE,NOCASE),FLAG(USING0,AL),OPTABLE(ZS5,LIST) ACONTROL NOTYPECHECK,TYPECHECK(MAGNITUDE,NOREG),OPTABLE(DOS) ADATA -300,2*100,2,3,'test' @@ -256,7 +257,7 @@ TEST(diagnostics, CEJECT 10/2 CNOP 6,8 COM - CSECT +S CSECT END ,(MYCOMPIlER,0101,00273) EXITCTL LISTING,256,*+128,,-2 EXITCTL SOURCE,,, @@ -280,7 +281,6 @@ lr OPSYN RMODE 24 label1 RSECT SPACE 4 - START 34 TITLE 'string' remark USING (3,3),12 USING 1,3,15,0,0/0 diff --git a/parser_library/test/processing/CMakeLists.txt b/parser_library/test/processing/CMakeLists.txt index 08d2ded13..f15ee307f 100644 --- a/parser_library/test/processing/CMakeLists.txt +++ b/parser_library/test/processing/CMakeLists.txt @@ -25,5 +25,6 @@ target_sources(library_test PRIVATE occurence_collector_test.cpp opsyn_test.cpp org_test.cpp + start_test.cpp ) diff --git a/parser_library/test/processing/ca_instr_test.cpp b/parser_library/test/processing/ca_instr_test.cpp index 700df9c22..4d6fee6d9 100644 --- a/parser_library/test/processing/ca_instr_test.cpp +++ b/parser_library/test/processing/ca_instr_test.cpp @@ -395,6 +395,73 @@ TEST(ACTR, infinite_ACTR) ASSERT_EQ(a.diags().size(), (size_t)1); } +TEST(MHELP, SYSNDX_limit) +{ + std::string input = R"( + GBLC &LASTNDX + MACRO + MAC + GBLC &LASTNDX +&LASTNDX SETC '&SYSNDX' + MEND + + MHELP 256 +&I SETA 0 +.NEXT AIF (&I GT 256).DONE +&I SETA &I+1 + MAC + AGO .NEXT +.DONE ANOP , + )"; + analyzer a(input); + a.analyze(); + + a.collect_diags(); + + EXPECT_TRUE(matches_message_codes(a.diags(), { "E072" })); + EXPECT_EQ(get_var_value(a.hlasm_ctx(), "LASTNDX"), "0256"); +} + +TEST(MHELP, invalid_operands) +{ + std::string input = R"( + MHELP + MHELP 1,1 + MHELP , + MHELP ABC + MHELP (1).ABC +ABC EQU 1 +)"; + analyzer a(input); + a.analyze(); + + a.collect_diags(); + + EXPECT_TRUE(matches_message_codes(a.diags(), { "E021", "E020", "E020", "CE012", "E010" })); +} + +TEST(MHELP, valid_operands) +{ + std::string input = R"( +ABC EQU 1 +&VAR SETA 1 + MHELP 1 + MHELP X'1' + MHELP B'1' + MHELP ABC + MHELP ABC+ABC + MHELP ABC*5 + MHELP &VAR+1 + MHELP &VAR*&VAR +)"; + analyzer a(input); + a.analyze(); + + a.collect_diags(); + + EXPECT_TRUE(a.diags().empty()); +} + TEST(SET, conversions_valid) { std::string input(R"( diff --git a/parser_library/test/processing/start_test.cpp b/parser_library/test/processing/start_test.cpp new file mode 100644 index 000000000..124d0ab4e --- /dev/null +++ b/parser_library/test/processing/start_test.cpp @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2021 Broadcom. + * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Broadcom, Inc. - initial API and implementation + */ + +#include "gtest/gtest.h" + +#include "../common_testing.h" + +TEST(START, defines_section) +{ + std::string input(R"( +S START +)"); + analyzer a(input); + a.analyze(); + + a.collect_diags(); + EXPECT_TRUE(a.diags().empty()); + + EXPECT_TRUE(a.hlasm_ctx().ord_ctx.section_defined(a.hlasm_ctx().ids().add("S"), section_kind::EXECUTABLE)); +} + +TEST(START, section_with_offset) +{ + std::string input(R"( +S START 7 +E EQU * +)"); + analyzer a(input); + a.analyze(); + + a.collect_diags(); + EXPECT_TRUE(a.diags().empty()); + + const auto* s = a.hlasm_ctx().ord_ctx.get_section(a.hlasm_ctx().ids().add("S")); + ASSERT_TRUE(s); + const auto* e = a.hlasm_ctx().ord_ctx.get_symbol(a.hlasm_ctx().ids().add("E")); + ASSERT_TRUE(e); + + const auto& e_value = e->value(); + ASSERT_EQ(e_value.value_kind(), symbol_value_kind::RELOC); + + const auto& reloc = e_value.get_reloc(); + ASSERT_EQ(reloc.bases().size(), 1); + EXPECT_EQ(reloc.bases().front().first.owner, s); + EXPECT_EQ(reloc.offset(), 8); +} + +TEST(START, generates_csect) +{ + std::string input(R"( +S START +S CSECT +)"); + analyzer a(input); + a.analyze(); + + a.collect_diags(); + EXPECT_TRUE(a.diags().empty()); +} + +TEST(START, already_started) +{ + std::string input(R"( +S CSECT +S START +)"); + analyzer a(input); + a.analyze(); + + a.collect_diags(); + EXPECT_TRUE(matches_message_codes(a.diags(), { "E073" })); +} + +TEST(START, already_started_private_csect) +{ + std::string input(R"( +E EQU 0 +S START +)"); + analyzer a(input); + a.analyze(); + + a.collect_diags(); + EXPECT_TRUE(matches_message_codes(a.diags(), { "E073" })); +} + +TEST(START, dsect_not_csect) +{ + std::string input(R"( +D DSECT +E EQU 0 +S START +)"); + analyzer a(input); + a.analyze(); + + a.collect_diags(); + EXPECT_TRUE(a.diags().empty()); +} + +TEST(START, symbol_not_known_yet) +{ + std::string input(R"( +S START E +E EQU 0 +)"); + analyzer a(input); + a.analyze(); + + a.collect_diags(); + EXPECT_TRUE(matches_message_codes(a.diags(), { "A250" })); +}