| /* |
| * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. |
| * |
| * SPDX-License-Identifier: BSD-3-Clause |
| */ |
| |
| #ifndef _PIO_ASSEMBLER_H |
| #define _PIO_ASSEMBLER_H |
| |
| #include <algorithm> |
| #include "parser.hpp" |
| #include "output_format.h" |
| |
| // Give Flex the prototype of yylex we want ... |
| # define YY_DECL \ |
| yy::parser::symbol_type yylex (pio_assembler& pioasm) |
| // ... and declare it for the parser's sake. |
| YY_DECL; |
| |
| struct pio_assembler { |
| public: |
| using syntax_error = yy::parser::syntax_error; |
| using location_type = yy::parser::location_type; |
| using position = yy::position; |
| |
| std::shared_ptr<program> dummy_global_program; |
| std::vector<program> programs; |
| int error_count = 0; |
| |
| pio_assembler(); |
| |
| std::shared_ptr<output_format> format; |
| // The name of the file being parsed. |
| std::string source; |
| // name of the output file or "-" for stdout |
| std::string dest; |
| std::vector<std::string> options; |
| int default_pio_version = 0; |
| |
| int write_output(); |
| |
| bool add_program(const yy::location &l, const std::string &name) { |
| if (std::find_if(programs.begin(), programs.end(), [&](const program &p) { return p.name == name; }) == |
| programs.end()) { |
| programs.emplace_back(this, l, name); |
| programs[programs.size()-1].pio_version = get_default_pio_version(); |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| program &get_dummy_global_program() { |
| if (!dummy_global_program) { |
| dummy_global_program = std::shared_ptr<program>(new program(this, yy::location(&source), "")); |
| dummy_global_program->pio_version = default_pio_version; |
| } |
| return *dummy_global_program; |
| } |
| |
| program &get_current_program(const location_type &l, const std::string &requiring_program, |
| bool before_any_instructions = false, bool disallow_global = true) { |
| if (programs.empty()) { |
| if (disallow_global) { |
| std::stringstream msg; |
| msg << requiring_program << " is invalid outside of a program"; |
| throw syntax_error(l, msg.str()); |
| } |
| return get_dummy_global_program(); |
| } |
| auto &p = programs[programs.size() - 1]; |
| if (before_any_instructions && !p.instructions.empty()) { |
| std::stringstream msg; |
| msg << requiring_program << " must precede any program instructions"; |
| throw syntax_error(l, msg.str()); |
| } |
| return p; |
| } |
| |
| int get_default_pio_version() { |
| return get_dummy_global_program().pio_version; |
| } |
| |
| int get_current_pio_version() { |
| if (!programs.empty()) { |
| auto &p = programs[programs.size() - 1]; |
| return p.pio_version; |
| } |
| return get_default_pio_version(); |
| } |
| |
| // note p may be null for global symbols only |
| std::shared_ptr<symbol> get_symbol(const std::string &name, const program *p) { |
| const auto &i = get_dummy_global_program().symbols.find(name); |
| if (i != get_dummy_global_program().symbols.end()) |
| return i->second; |
| |
| if (p) { |
| const auto &i2 = p->symbols.find(name); |
| if (i2 != p->symbols.end()) |
| return i2->second; |
| } |
| return nullptr; |
| } |
| |
| void check_version(int min_version, const location_type &l, std::string feature) { |
| if (get_current_pio_version() < min_version) { |
| std::stringstream msg; |
| msg << "PIO version " << min_version << " is required for '" << feature << "'"; |
| throw syntax_error(l, msg.str()); |
| } |
| } |
| |
| std::string version_string(int min_version, std::string a, std::string b) { |
| return get_current_pio_version() >= min_version ? a : b; |
| } |
| |
| std::vector<compiled_source::symbol> public_symbols(program &program); |
| int generate(std::shared_ptr<output_format> _format, const std::string &_source, const std::string &_dest, |
| const std::vector<std::string> &_options = std::vector<std::string>()); |
| |
| // Handling the scanner. |
| void scan_begin(); |
| void scan_end(); |
| |
| // The token's location used by the scanner. |
| yy::location location; |
| }; |
| |
| #endif |