1 // Copyright 2017 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #pragma once
6 
7 #include <stdint.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 
12 #include <fstream>
13 #include <string>
14 #include <vector>
15 
16 std::vector<std::string>& operator+=(std::vector<std::string>& v1,
17                                      const std::vector<std::string>& v2);
18 std::vector<std::string> tokenize_string(const std::string& str);
19 
20 struct FileCtx {
21     const char* file;
22     const char* last_token;
23     int line_start;
24     int line_end;
25     bool verbose;
26 
FileCtxFileCtx27     FileCtx(const char* file, bool verbose)
28         : file(file), last_token(nullptr),
29           line_start(0), line_end(0),
30           verbose(verbose) {}
31 
FileCtxFileCtx32     FileCtx(const FileCtx& src, int start)
33         : file(src.file), last_token(src.last_token),
34           line_start(start), line_end(src.line_start),
35           verbose(src.verbose) {}
36 
37     void print_error(const char* what, const std::string& extra) const;
38     void print_info(const char* what) const;
39 };
40 
41 class TokenStream {
42 public:
TokenStream(const std::vector<std::string> & tokens,const FileCtx & fc)43     TokenStream(const std::vector<std::string>& tokens, const FileCtx& fc)
44         : fc_(fc), ix_(0u), tokens_(tokens) {}
45 
46     const std::string& curr();
47     const std::string& next();
48     const std::string& peek_next() const;
49     const FileCtx& filectx();
50 
51 private:
52     FileCtx fc_;
53     size_t ix_;
54     const std::vector<std::string>& tokens_;
55 };
56 
57 // ======================= generic parsing machinery =============================================
58 template <typename P>
59 using ProcFn = bool (*)(P* parser, TokenStream& ts);
60 
61 template <typename P>
62 struct Dispatch {
63     const char* first_token;
64     const char* last_token;
65     ProcFn<P> fn;
66 };
67 
68 template <typename P>
process_line(P * parser,const Dispatch<P> * table,const std::vector<std::string> & tokens,const FileCtx & fc)69 bool process_line(P* parser, const Dispatch<P>* table,
70                   const std::vector<std::string>& tokens,
71                   const FileCtx& fc) {
72     static std::vector<std::string> acc;
73     static int start = 0;
74 
75     auto& first = acc.empty() ? tokens[0] : acc[0];
76     auto& last = tokens.back();
77 
78     start = acc.empty() ? fc.line_start : start;
79 
80     size_t ix = 0;
81     while (table[ix].fn) {
82         auto& d = table[ix++];
83         if (first == d.first_token) {
84 
85             TokenStream ts(tokens, fc);
86             if (!d.last_token)
87                 return d.fn(parser, ts);
88 
89             if (last == d.last_token) {
90                 if (acc.empty()) {
91                     // single line case.
92                     return d.fn(parser, ts);
93                 } else {
94                     // multiline case.
95                     std::vector<std::string> t(std::move(acc));
96                     t += tokens;
97                     TokenStream mts(t, FileCtx(fc, start));
98                     return d.fn(parser, mts);
99                 }
100             } else {
101                 // more input is needed.
102                 acc += tokens;
103                 return true;
104             }
105         }
106     }
107 
108     if (!acc.empty())
109         fc.print_error("missing terminator", tokens[0]);
110     else
111         fc.print_error("unknown token", tokens[0]);
112     return false;
113 }
114 
115 template <typename P>
run_parser(P * parser,const Dispatch<P> * table,const char * input,bool verbose)116 bool run_parser(P* parser, const Dispatch<P>* table, const char* input, bool verbose) {
117     std::ifstream infile;
118     infile.open(input, std::ifstream::in);
119 
120     if (!infile.good()) {
121         fprintf(stderr, "error: unable to open %s\n", input);
122         return false;
123     }
124 
125     if (verbose)
126         fprintf(stderr, "abigen: processing file %s\n", input);
127 
128     bool error = false;
129     FileCtx fc(input, verbose);
130     std::string line;
131 
132     while (!infile.eof()) {
133         getline(infile, line);
134         ++fc.line_start;
135         auto tokens = tokenize_string(line);
136         if (tokens.empty())
137             continue;
138 
139         if (!process_line(parser, table, tokens, fc)) {
140             error = true;
141             break;
142         }
143     }
144 
145     if (error) {
146         fprintf(stderr, "** stopping at line %d. parsing %s failed.\n", fc.line_start, input);
147         return false;
148     }
149 
150     return true;
151 }
152