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 
21 struct pio_assembler {
22 public:
23     using syntax_error = yy::parser::syntax_error;
24     using location_type = yy::parser::location_type;
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 
39     int write_output();
40 
add_programpio_assembler41     bool add_program(const yy::location &l, const std::string &name) {
42         if (std::find_if(programs.begin(), programs.end(), [&](const program &p) { return p.name == name; }) ==
43             programs.end()) {
44             programs.emplace_back(this, l, name);
45             return true;
46         } else {
47             return false;
48         }
49     }
50 
get_dummy_global_programpio_assembler51     program &get_dummy_global_program() {
52         if (!dummy_global_program) {
53             dummy_global_program = std::shared_ptr<program>(new program(this, yy::location(&source), ""));
54         }
55         return *dummy_global_program;
56     }
57 
58     program &get_current_program(const location_type &l, const std::string &requiring_program,
59                                  bool before_any_instructions = false, bool disallow_global = true) {
60         if (programs.empty()) {
61             if (disallow_global) {
62                 std::stringstream msg;
63                 msg << requiring_program << " is invalid outside of a program";
64                 throw syntax_error(l, msg.str());
65             }
66             return get_dummy_global_program();
67         }
68         auto &p = programs[programs.size() - 1];
69         if (before_any_instructions && !p.instructions.empty()) {
70             std::stringstream msg;
71             msg << requiring_program << " must preceed any program instructions";
72             throw syntax_error(l, msg.str());
73         }
74         return p;
75     }
76 
77     // note p may be null for global symbols only
get_symbolpio_assembler78     std::shared_ptr<symbol> get_symbol(const std::string &name, const program *p) {
79         const auto &i = get_dummy_global_program().symbols.find(name);
80         if (i != get_dummy_global_program().symbols.end())
81             return i->second;
82 
83         if (p) {
84             const auto &i2 = p->symbols.find(name);
85             if (i2 != p->symbols.end())
86                 return i2->second;
87         }
88         return nullptr;
89     }
90 
91     std::vector<compiled_source::symbol> public_symbols(program &program);
92     int generate(std::shared_ptr<output_format> _format, const std::string &_source, const std::string &_dest,
93                  const std::vector<std::string> &_options = std::vector<std::string>());
94 
95     // Handling the scanner.
96     void scan_begin();
97     void scan_end();
98 
99     // The token's location used by the scanner.
100     yy::location location;
101 };
102 
103 #endif