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