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 16std::vector<std::string>& operator+=(std::vector<std::string>& v1, 17 const std::vector<std::string>& v2); 18std::vector<std::string> tokenize_string(const std::string& str); 19 20struct FileCtx { 21 const char* file; 22 const char* last_token; 23 int line_start; 24 int line_end; 25 bool verbose; 26 27 FileCtx(const char* file, bool verbose) 28 : file(file), last_token(nullptr), 29 line_start(0), line_end(0), 30 verbose(verbose) {} 31 32 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 41class TokenStream { 42public: 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 51private: 52 FileCtx fc_; 53 size_t ix_; 54 const std::vector<std::string>& tokens_; 55}; 56 57// ======================= generic parsing machinery ============================================= 58template <typename P> 59using ProcFn = bool (*)(P* parser, TokenStream& ts); 60 61template <typename P> 62struct Dispatch { 63 const char* first_token; 64 const char* last_token; 65 ProcFn<P> fn; 66}; 67 68template <typename P> 69bool 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 115template <typename P> 116bool 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