1 /*
2  * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 #ifndef _PIO_ASSEMBLER_H
8 #define _PIO_ASSEMBLER_H
9 
10 #include <algorithm>
11 #include "parser.hpp"
12 #include "output_format.h"
13 
14 // Give Flex the prototype of yylex we want ...
15 # define YY_DECL \
16   yy::parser::symbol_type yylex (pio_assembler& pioasm)
17 // ... and declare it for the parser's sake.
18 YY_DECL;
19 
20 struct pio_assembler {
21 public:
22     using syntax_error = yy::parser::syntax_error;
23     using location_type = yy::parser::location_type;
24     using position = yy::position;
25 
26     std::shared_ptr<program> dummy_global_program;
27     std::vector<program> programs;
28     int error_count = 0;
29 
30     pio_assembler();
31 
32     std::shared_ptr<output_format> format;
33     // The name of the file being parsed.
34     std::string source;
35     // name of the output file or "-" for stdout
36     std::string dest;
37     std::vector<std::string> options;
38     int default_pio_version = 0;
39 
40     int write_output();
41 
add_programpio_assembler42     bool add_program(const yy::location &l, const std::string &name) {
43         if (std::find_if(programs.begin(), programs.end(), [&](const program &p) { return p.name == name; }) ==
44             programs.end()) {
45             programs.emplace_back(this, l, name);
46             programs[programs.size()-1].pio_version = get_default_pio_version();
47             return true;
48         } else {
49             return false;
50         }
51     }
52 
get_dummy_global_programpio_assembler53     program &get_dummy_global_program() {
54         if (!dummy_global_program) {
55             dummy_global_program = std::shared_ptr<program>(new program(this, yy::location(&source), ""));
56             dummy_global_program->pio_version = default_pio_version;
57         }
58         return *dummy_global_program;
59     }
60 
61     program &get_current_program(const location_type &l, const std::string &requiring_program,
62                                  bool before_any_instructions = false, bool disallow_global = true) {
63         if (programs.empty()) {
64             if (disallow_global) {
65                 std::stringstream msg;
66                 msg << requiring_program << " is invalid outside of a program";
67                 throw syntax_error(l, msg.str());
68             }
69             return get_dummy_global_program();
70         }
71         auto &p = programs[programs.size() - 1];
72         if (before_any_instructions && !p.instructions.empty()) {
73             std::stringstream msg;
74             msg << requiring_program << " must precede any program instructions";
75             throw syntax_error(l, msg.str());
76         }
77         return p;
78     }
79 
get_default_pio_versionpio_assembler80     int get_default_pio_version() {
81         return get_dummy_global_program().pio_version;
82     }
83 
get_current_pio_versionpio_assembler84     int get_current_pio_version() {
85         if (!programs.empty()) {
86             auto &p = programs[programs.size() - 1];
87             return p.pio_version;
88         }
89         return get_default_pio_version();
90     }
91 
92     // note p may be null for global symbols only
get_symbolpio_assembler93     std::shared_ptr<symbol> get_symbol(const std::string &name, const program *p) {
94         const auto &i = get_dummy_global_program().symbols.find(name);
95         if (i != get_dummy_global_program().symbols.end())
96             return i->second;
97 
98         if (p) {
99             const auto &i2 = p->symbols.find(name);
100             if (i2 != p->symbols.end())
101                 return i2->second;
102         }
103         return nullptr;
104     }
105 
check_versionpio_assembler106     void check_version(int min_version, const location_type &l, std::string feature) {
107         if (get_current_pio_version() < min_version) {
108             std::stringstream msg;
109             msg << "PIO version " << min_version << " is required for '" << feature << "'";
110             throw syntax_error(l, msg.str());
111         }
112     }
113 
version_stringpio_assembler114     std::string version_string(int min_version, std::string a, std::string b) {
115         return get_current_pio_version() >= min_version ? a : b;
116     }
117 
118     std::vector<compiled_source::symbol> public_symbols(program &program);
119     int generate(std::shared_ptr<output_format> _format, const std::string &_source, const std::string &_dest,
120                  const std::vector<std::string> &_options = std::vector<std::string>());
121 
122     // Handling the scanner.
123     void scan_begin();
124     void scan_end();
125 
126     // The token's location used by the scanner.
127     yy::location location;
128 };
129 
130 #endif