xref: /freebsd-src/usr.bin/dtc/input_buffer.hh (revision b3e7694832e81d7a904a10f525f8797b753bf0d3)
1af0dd31fSDavid Chisnall /*-
2*4d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
31de7b4b8SPedro F. Giffuni  *
4af0dd31fSDavid Chisnall  * Copyright (c) 2013 David Chisnall
5af0dd31fSDavid Chisnall  * All rights reserved.
6af0dd31fSDavid Chisnall  *
7af0dd31fSDavid Chisnall  * This software was developed by SRI International and the University of
8af0dd31fSDavid Chisnall  * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
9af0dd31fSDavid Chisnall  * ("CTSRD"), as part of the DARPA CRASH research programme.
10af0dd31fSDavid Chisnall  *
11af0dd31fSDavid Chisnall  * Redistribution and use in source and binary forms, with or without
12af0dd31fSDavid Chisnall  * modification, are permitted provided that the following conditions
13af0dd31fSDavid Chisnall  * are met:
14af0dd31fSDavid Chisnall  * 1. Redistributions of source code must retain the above copyright
15af0dd31fSDavid Chisnall  *    notice, this list of conditions and the following disclaimer.
16af0dd31fSDavid Chisnall  * 2. Redistributions in binary form must reproduce the above copyright
17af0dd31fSDavid Chisnall  *    notice, this list of conditions and the following disclaimer in the
18af0dd31fSDavid Chisnall  *    documentation and/or other materials provided with the distribution.
19af0dd31fSDavid Chisnall  *
20af0dd31fSDavid Chisnall  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21af0dd31fSDavid Chisnall  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22af0dd31fSDavid Chisnall  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23af0dd31fSDavid Chisnall  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24af0dd31fSDavid Chisnall  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25af0dd31fSDavid Chisnall  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26af0dd31fSDavid Chisnall  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27af0dd31fSDavid Chisnall  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28af0dd31fSDavid Chisnall  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29af0dd31fSDavid Chisnall  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30af0dd31fSDavid Chisnall  * SUCH DAMAGE.
31af0dd31fSDavid Chisnall  */
32af0dd31fSDavid Chisnall 
33af0dd31fSDavid Chisnall #ifndef _INPUT_BUFFER_HH_
34af0dd31fSDavid Chisnall #define _INPUT_BUFFER_HH_
35af0dd31fSDavid Chisnall #include "util.hh"
36af0dd31fSDavid Chisnall #include <assert.h>
37bbe31b70SEd Maste #include <stack>
38bbe31b70SEd Maste #include <string>
39bbe31b70SEd Maste #include <unordered_set>
40af0dd31fSDavid Chisnall 
41af0dd31fSDavid Chisnall namespace dtc
42af0dd31fSDavid Chisnall {
43af0dd31fSDavid Chisnall 
44c64a3eafSDavid Chisnall namespace {
45c64a3eafSDavid Chisnall struct expression;
46c64a3eafSDavid Chisnall typedef std::unique_ptr<expression> expression_ptr;
47c64a3eafSDavid Chisnall }
48c64a3eafSDavid Chisnall 
49af0dd31fSDavid Chisnall /**
50af0dd31fSDavid Chisnall  * Class encapsulating the input file.  Can be used as a const char*, but has
51af0dd31fSDavid Chisnall  * range checking.  Attempting to access anything out of range will return a 0
52af0dd31fSDavid Chisnall  * byte.  The input buffer can be cheaply copied, without copying the
53af0dd31fSDavid Chisnall  * underlying memory, however it is the user's responsibility to ensure that
54af0dd31fSDavid Chisnall  * such copies do not persist beyond the lifetime of the underlying memory.
55af0dd31fSDavid Chisnall  *
56af0dd31fSDavid Chisnall  * This also contains methods for reporting errors and for consuming the token
57af0dd31fSDavid Chisnall  * stream.
58af0dd31fSDavid Chisnall  */
59af0dd31fSDavid Chisnall class input_buffer
60af0dd31fSDavid Chisnall {
61bbe31b70SEd Maste 	friend class text_input_buffer;
62af0dd31fSDavid Chisnall 	protected:
63af0dd31fSDavid Chisnall 	/**
64af0dd31fSDavid Chisnall 	 * The buffer.  This class doesn't own the buffer, but the
65af0dd31fSDavid Chisnall 	 * mmap_input_buffer subclass does.
66af0dd31fSDavid Chisnall 	 */
67af0dd31fSDavid Chisnall 	const char* buffer;
68af0dd31fSDavid Chisnall 	/**
69af0dd31fSDavid Chisnall 	 * The size of the buffer.
70af0dd31fSDavid Chisnall 	 */
71af0dd31fSDavid Chisnall 	int size;
72af0dd31fSDavid Chisnall 	private:
73af0dd31fSDavid Chisnall 	/**
74af0dd31fSDavid Chisnall 	 * The current place in the buffer where we are reading.  This class
75af0dd31fSDavid Chisnall 	 * keeps a separate size, pointer, and cursor so that we can move
76af0dd31fSDavid Chisnall 	 * forwards and backwards and still have checks that we haven't fallen
77af0dd31fSDavid Chisnall 	 * off either end.
78af0dd31fSDavid Chisnall 	 */
79af0dd31fSDavid Chisnall 	int cursor;
80af0dd31fSDavid Chisnall 	/**
81af0dd31fSDavid Chisnall 	 * Private constructor.  This is used to create input buffers that
82af0dd31fSDavid Chisnall 	 * refer to the same memory, but have different cursors.
83af0dd31fSDavid Chisnall 	 */
input_buffer(const char * b,int s,int c)84af0dd31fSDavid Chisnall 	input_buffer(const char* b, int s, int c) : buffer(b), size(s),
85af0dd31fSDavid Chisnall 		cursor(c) {}
86af0dd31fSDavid Chisnall 	public:
87af0dd31fSDavid Chisnall 	/**
88bbe31b70SEd Maste 	 * Returns the file name associated with this buffer.
89bbe31b70SEd Maste 	 */
filename() const90bbe31b70SEd Maste 	virtual const std::string &filename() const
91bbe31b70SEd Maste 	{
92bbe31b70SEd Maste 		static std::string s;
93bbe31b70SEd Maste 		return s;
94bbe31b70SEd Maste 	}
95bbe31b70SEd Maste 	static std::unique_ptr<input_buffer> buffer_for_file(const std::string &path,
96bbe31b70SEd Maste 	                                                     bool warn=true);
97bbe31b70SEd Maste 	/**
98bbe31b70SEd Maste 	 * Skips all characters in the input until the specified character is
99bbe31b70SEd Maste 	 * encountered.
100bbe31b70SEd Maste 	 */
101bbe31b70SEd Maste 	void skip_to(char);
102bbe31b70SEd Maste 	/**
103bbe31b70SEd Maste 	 * Parses up to a specified character and returns the intervening
104bbe31b70SEd Maste 	 * characters as a string.
105bbe31b70SEd Maste 	 */
106bbe31b70SEd Maste 	std::string parse_to(char);
107bbe31b70SEd Maste 	/**
108a0706eb4SDavid Chisnall 	 * Return whether all input has been consumed.
109a0706eb4SDavid Chisnall 	 */
finished()110a0706eb4SDavid Chisnall 	bool finished() { return cursor >= size; }
111a0706eb4SDavid Chisnall 	/**
112af0dd31fSDavid Chisnall 	 * Virtual destructor.  Does nothing, but exists so that subclasses
113af0dd31fSDavid Chisnall 	 * that own the memory can run cleanup code for deallocating it.
114af0dd31fSDavid Chisnall 	 */
~input_buffer()115af0dd31fSDavid Chisnall 	virtual ~input_buffer() {};
116af0dd31fSDavid Chisnall 	/**
117af0dd31fSDavid Chisnall 	 * Constructs an empty buffer.
118af0dd31fSDavid Chisnall 	 */
input_buffer()119af0dd31fSDavid Chisnall 	input_buffer() : buffer(0), size(0), cursor(0) {}
120af0dd31fSDavid Chisnall 	/**
121af0dd31fSDavid Chisnall 	 * Constructs a new buffer with a specified memory region and size.
122af0dd31fSDavid Chisnall 	 */
input_buffer(const char * b,int s)123af0dd31fSDavid Chisnall 	input_buffer(const char* b, int s) : buffer(b), size(s), cursor(0){}
124af0dd31fSDavid Chisnall 	/**
125af0dd31fSDavid Chisnall 	 * Returns a new input buffer referring into this input, clamped to the
126af0dd31fSDavid Chisnall 	 * specified size.  If the requested buffer would fall outside the
127af0dd31fSDavid Chisnall 	 * range of this one, then it returns an empty buffer.
128af0dd31fSDavid Chisnall 	 *
129af0dd31fSDavid Chisnall 	 * The returned buffer shares the same underlying storage as the
130af0dd31fSDavid Chisnall 	 * original.  This is intended to be used for splitting up the various
131af0dd31fSDavid Chisnall 	 * sections of a device tree blob.  Requesting a size of 0 will give a
132af0dd31fSDavid Chisnall 	 * buffer that extends to the end of the available memory.
133af0dd31fSDavid Chisnall 	 */
134af0dd31fSDavid Chisnall 	input_buffer buffer_from_offset(int offset, int s=0);
135af0dd31fSDavid Chisnall 	/**
136af0dd31fSDavid Chisnall 	 * Dereferencing operator, allows the buffer to be treated as a char*
137af0dd31fSDavid Chisnall 	 * and dereferenced to give a character.  This returns a null byte if
138af0dd31fSDavid Chisnall 	 * the cursor is out of range.
139af0dd31fSDavid Chisnall 	 */
operator *()140af0dd31fSDavid Chisnall 	inline char operator*()
141af0dd31fSDavid Chisnall 	{
142af0dd31fSDavid Chisnall 		if (cursor >= size) { return '\0'; }
143af0dd31fSDavid Chisnall 		if (cursor < 0) { return '\0'; }
144af0dd31fSDavid Chisnall 		return buffer[cursor];
145af0dd31fSDavid Chisnall 	}
146af0dd31fSDavid Chisnall 	/**
147af0dd31fSDavid Chisnall 	 * Array subscripting operator, returns a character at the specified
148af0dd31fSDavid Chisnall 	 * index offset from the current cursor.  The offset may be negative,
149af0dd31fSDavid Chisnall 	 * to reread characters that have already been read.  If the current
150af0dd31fSDavid Chisnall 	 * cursor plus offset is outside of the range, this returns a nul
151af0dd31fSDavid Chisnall 	 * byte.
152af0dd31fSDavid Chisnall 	 */
operator [](int offset)153af0dd31fSDavid Chisnall 	inline char operator[](int offset)
154af0dd31fSDavid Chisnall 	{
155af0dd31fSDavid Chisnall 		if (cursor + offset >= size) { return '\0'; }
156af0dd31fSDavid Chisnall 		if (cursor + offset < 0) { return '\0'; }
157af0dd31fSDavid Chisnall 		return buffer[cursor + offset];
158af0dd31fSDavid Chisnall 	}
159af0dd31fSDavid Chisnall 	/**
160af0dd31fSDavid Chisnall 	 * Increments the cursor, iterating forward in the buffer.
161af0dd31fSDavid Chisnall 	 */
operator ++()162af0dd31fSDavid Chisnall 	inline input_buffer &operator++()
163af0dd31fSDavid Chisnall 	{
164af0dd31fSDavid Chisnall 		cursor++;
165af0dd31fSDavid Chisnall 		return *this;
166af0dd31fSDavid Chisnall 	}
begin()167ca84c67cSKyle Evans 	const char *begin()
168ca84c67cSKyle Evans 	{
169ca84c67cSKyle Evans 		return buffer;
170ca84c67cSKyle Evans 	}
end()171ca84c67cSKyle Evans 	const char *end()
172ca84c67cSKyle Evans 	{
173ca84c67cSKyle Evans 		return buffer + size;
174ca84c67cSKyle Evans 	}
175af0dd31fSDavid Chisnall 	/**
176af0dd31fSDavid Chisnall 	 * Consumes a character.  Moves the cursor one character forward if the
177af0dd31fSDavid Chisnall 	 * next character matches the argument, returning true.  If the current
178af0dd31fSDavid Chisnall 	 * character does not match the argument, returns false.
179af0dd31fSDavid Chisnall 	 */
consume(char c)180af0dd31fSDavid Chisnall 	inline bool consume(char c)
181af0dd31fSDavid Chisnall 	{
182bbe31b70SEd Maste 		if (*(*this) == c)
183af0dd31fSDavid Chisnall 		{
184af0dd31fSDavid Chisnall 			++(*this);
185af0dd31fSDavid Chisnall 			return true;
186af0dd31fSDavid Chisnall 		}
187af0dd31fSDavid Chisnall 		return false;
188af0dd31fSDavid Chisnall 	}
189af0dd31fSDavid Chisnall 	/**
190af0dd31fSDavid Chisnall 	 * Consumes a string.  If the (null-terminated) string passed as the
191af0dd31fSDavid Chisnall 	 * argument appears in the input, advances the cursor to the end and
192af0dd31fSDavid Chisnall 	 * returns true.  Returns false if the string does not appear at the
193af0dd31fSDavid Chisnall 	 * current point in the input.
194af0dd31fSDavid Chisnall 	 */
195af0dd31fSDavid Chisnall 	bool consume(const char *str);
196af0dd31fSDavid Chisnall 	/**
197af0dd31fSDavid Chisnall 	 * Reads an integer in base 8, 10, or 16.  Returns true and advances
198af0dd31fSDavid Chisnall 	 * the cursor to the end of the integer if the cursor points to an
199af0dd31fSDavid Chisnall 	 * integer, returns false and does not move the cursor otherwise.
200af0dd31fSDavid Chisnall 	 *
201af0dd31fSDavid Chisnall 	 * The parsed value is returned via the argument.
202af0dd31fSDavid Chisnall 	 */
203a0706eb4SDavid Chisnall 	bool consume_integer(unsigned long long &outInt);
204af0dd31fSDavid Chisnall 	/**
205c64a3eafSDavid Chisnall 	 * Reads an arithmetic expression (containing any of the normal C
206c64a3eafSDavid Chisnall 	 * operators), evaluates it, and returns the result.
207c64a3eafSDavid Chisnall 	 */
208c64a3eafSDavid Chisnall 	bool consume_integer_expression(unsigned long long &outInt);
209c64a3eafSDavid Chisnall 	/**
210bbe31b70SEd Maste 	 * Consumes two hex digits and return the resulting byte via the first
211bbe31b70SEd Maste 	 * argument.  If the next two characters are hex digits, returns true
212bbe31b70SEd Maste 	 * and advances the cursor.  If not, then returns false and leaves the
213bbe31b70SEd Maste 	 * cursor in place.
214bbe31b70SEd Maste 	 */
215bbe31b70SEd Maste 	bool consume_hex_byte(uint8_t &outByte);
216bbe31b70SEd Maste 	/**
217af0dd31fSDavid Chisnall 	 * Template function that consumes a binary value in big-endian format
218af0dd31fSDavid Chisnall 	 * from the input stream.  Returns true and advances the cursor if
219af0dd31fSDavid Chisnall 	 * there is a value of the correct size.  This function assumes that
220af0dd31fSDavid Chisnall 	 * all values must be natively aligned, and so advances the cursor to
221af0dd31fSDavid Chisnall 	 * the correct alignment before reading.
222af0dd31fSDavid Chisnall 	 */
223af0dd31fSDavid Chisnall 	template<typename T>
consume_binary(T & out)224af0dd31fSDavid Chisnall 	bool consume_binary(T &out)
225af0dd31fSDavid Chisnall 	{
226af0dd31fSDavid Chisnall 		int align = 0;
227af0dd31fSDavid Chisnall 		int type_size = sizeof(T);
228af0dd31fSDavid Chisnall 		if (cursor % type_size != 0)
229af0dd31fSDavid Chisnall 		{
230af0dd31fSDavid Chisnall 			align = type_size - (cursor % type_size);
231af0dd31fSDavid Chisnall 		}
232af0dd31fSDavid Chisnall 		if (size < cursor + align + type_size)
233af0dd31fSDavid Chisnall 		{
234af0dd31fSDavid Chisnall 			return false;
235af0dd31fSDavid Chisnall 		}
236af0dd31fSDavid Chisnall 		cursor += align;
237af0dd31fSDavid Chisnall 		assert(cursor % type_size == 0);
238af0dd31fSDavid Chisnall 		out = 0;
239af0dd31fSDavid Chisnall 		for (int i=0 ; i<type_size ; ++i)
240af0dd31fSDavid Chisnall 		{
241bbe31b70SEd Maste 			if (size < cursor)
242bbe31b70SEd Maste 			{
243bbe31b70SEd Maste 				return false;
244bbe31b70SEd Maste 			}
245af0dd31fSDavid Chisnall 			out <<= 8;
246af0dd31fSDavid Chisnall 			out |= (((T)buffer[cursor++]) & 0xff);
247af0dd31fSDavid Chisnall 		}
248af0dd31fSDavid Chisnall 		return true;
249af0dd31fSDavid Chisnall 	}
250c64a3eafSDavid Chisnall #ifndef NDEBUG
251af0dd31fSDavid Chisnall 	/**
252af0dd31fSDavid Chisnall 	 * Dumps the current cursor value and the unconsumed values in the
253af0dd31fSDavid Chisnall 	 * input buffer to the standard error.  This method is intended solely
254af0dd31fSDavid Chisnall 	 * for debugging.
255af0dd31fSDavid Chisnall 	 */
256af0dd31fSDavid Chisnall 	void dump();
257c64a3eafSDavid Chisnall #endif
258af0dd31fSDavid Chisnall };
259af0dd31fSDavid Chisnall /**
260af0dd31fSDavid Chisnall  * Explicit specialisation for reading a single byte.
261af0dd31fSDavid Chisnall  */
262af0dd31fSDavid Chisnall template<>
consume_binary(uint8_t & out)263af0dd31fSDavid Chisnall inline bool input_buffer::consume_binary(uint8_t &out)
264af0dd31fSDavid Chisnall {
265af0dd31fSDavid Chisnall 	if (size < cursor + 1)
266af0dd31fSDavid Chisnall 	{
267af0dd31fSDavid Chisnall 		return false;
268af0dd31fSDavid Chisnall 	}
269af0dd31fSDavid Chisnall 	out = buffer[cursor++];
270af0dd31fSDavid Chisnall 	return true;
271af0dd31fSDavid Chisnall }
272af0dd31fSDavid Chisnall 
273af0dd31fSDavid Chisnall /**
274bbe31b70SEd Maste  * An input buffer subclass used for parsing DTS files.  This manages a stack
275bbe31b70SEd Maste  * of input buffers to handle /input/ operations.
276af0dd31fSDavid Chisnall  */
277bbe31b70SEd Maste class text_input_buffer
278af0dd31fSDavid Chisnall {
279bbe31b70SEd Maste 	std::unordered_set<std::string> defines;
280af0dd31fSDavid Chisnall 	/**
281bbe31b70SEd Maste 	 * The cursor is the input into the input stream where we are currently reading.
282af0dd31fSDavid Chisnall 	 */
283bbe31b70SEd Maste 	int cursor = 0;
284af0dd31fSDavid Chisnall 	/**
285bbe31b70SEd Maste 	 * The current stack of includes.  The current input is always from the top
286bbe31b70SEd Maste 	 * of the stack.
287af0dd31fSDavid Chisnall 	 */
288bbe31b70SEd Maste 	std::stack<std::shared_ptr<input_buffer>> input_stack;
289bbe31b70SEd Maste 	/**
290bbe31b70SEd Maste 	 *
291bbe31b70SEd Maste 	 */
292bbe31b70SEd Maste 	const std::vector<std::string> include_paths;
293bbe31b70SEd Maste 	/**
294bbe31b70SEd Maste 	 * Reads forward past any spaces.  The DTS format is not whitespace
295bbe31b70SEd Maste 	 * sensitive and so we want to scan past whitespace when reading it.
296bbe31b70SEd Maste 	 */
297bbe31b70SEd Maste 	void skip_spaces();
298bbe31b70SEd Maste 	/**
299bbe31b70SEd Maste 	 * Returns the character immediately after the current one.
300bbe31b70SEd Maste 	 *
301bbe31b70SEd Maste 	 * This method does not look between files.
302bbe31b70SEd Maste 	 */
303bbe31b70SEd Maste 	char peek();
304bbe31b70SEd Maste 	/**
305bbe31b70SEd Maste 	 * If a /include/ token is encountered, then look up the corresponding
306bbe31b70SEd Maste 	 * input file, push it onto the input stack, and continue.
307bbe31b70SEd Maste 	 */
308bbe31b70SEd Maste 	void handle_include();
309bbe31b70SEd Maste 	/**
310bbe31b70SEd Maste 	 * The base directory for this file.
311bbe31b70SEd Maste 	 */
312bbe31b70SEd Maste 	const std::string dir;
313bbe31b70SEd Maste 	/**
314bbe31b70SEd Maste 	 * The file where dependencies should be output.
315bbe31b70SEd Maste 	 */
316bbe31b70SEd Maste 	FILE *depfile;
317bbe31b70SEd Maste 	public:
318bbe31b70SEd Maste 	/**
319bbe31b70SEd Maste 	 * Construct a new text input buffer with the specified buffer as the start
320bbe31b70SEd Maste 	 * of parsing and the specified set of input paths for handling new
321bbe31b70SEd Maste 	 * inclusions.
322bbe31b70SEd Maste 	 */
text_input_buffer(std::unique_ptr<input_buffer> && b,std::unordered_set<std::string> && d,std::vector<std::string> && i,const std::string directory,FILE * deps)323bbe31b70SEd Maste 	text_input_buffer(std::unique_ptr<input_buffer> &&b,
324bbe31b70SEd Maste 	                  std::unordered_set<std::string> &&d,
325bbe31b70SEd Maste 	                  std::vector<std::string> &&i,
326bbe31b70SEd Maste 	                  const std::string directory,
327bbe31b70SEd Maste 	                  FILE *deps)
328bbe31b70SEd Maste 		: defines(d), include_paths(i), dir(directory), depfile(deps)
329bbe31b70SEd Maste 	{
330bbe31b70SEd Maste 		input_stack.push(std::move(b));
331bbe31b70SEd Maste 	}
332bbe31b70SEd Maste 	/**
333bbe31b70SEd Maste 	 * Skips all characters in the input until the specified character is
334bbe31b70SEd Maste 	 * encountered.
335bbe31b70SEd Maste 	 */
336bbe31b70SEd Maste 	void skip_to(char);
337bbe31b70SEd Maste 	/**
338bbe31b70SEd Maste 	 * Parse an expression.  If `stopAtParen` is set, then only parse a number
339bbe31b70SEd Maste 	 * or a parenthetical expression, otherwise assume that either is the
340bbe31b70SEd Maste 	 * left-hand side of a binary expression and try to parse the right-hand
341bbe31b70SEd Maste 	 * side.
342bbe31b70SEd Maste 	 */
343bbe31b70SEd Maste 	expression_ptr parse_expression(bool stopAtParen=false);
344bbe31b70SEd Maste 	/**
345bbe31b70SEd Maste 	 * Parse a binary expression, having already parsed the right-hand side.
346bbe31b70SEd Maste 	 */
347bbe31b70SEd Maste 	expression_ptr parse_binary_expression(expression_ptr lhs);
348bbe31b70SEd Maste 	/**
349bbe31b70SEd Maste 	 * Return whether all input has been consumed.
350bbe31b70SEd Maste 	 */
finished()351bbe31b70SEd Maste 	bool finished()
352bbe31b70SEd Maste 	{
353bbe31b70SEd Maste 		return input_stack.empty() ||
354bbe31b70SEd Maste 			((input_stack.size() == 1) && input_stack.top()->finished());
355bbe31b70SEd Maste 	}
356bbe31b70SEd Maste 	/**
357bbe31b70SEd Maste 	 * Dereferencing operator.  Returns the current character in the top input buffer.
358bbe31b70SEd Maste 	 */
operator *()359bbe31b70SEd Maste 	inline char operator*()
360bbe31b70SEd Maste 	{
361bbe31b70SEd Maste 		if (input_stack.empty())
362bbe31b70SEd Maste 		{
363bbe31b70SEd Maste 			return 0;
364bbe31b70SEd Maste 		}
365bbe31b70SEd Maste 		return *(*input_stack.top());
366bbe31b70SEd Maste 	}
367bbe31b70SEd Maste 	/**
368bbe31b70SEd Maste 	 * Increments the cursor, iterating forward in the buffer.
369bbe31b70SEd Maste 	 */
operator ++()370bbe31b70SEd Maste 	inline text_input_buffer &operator++()
371bbe31b70SEd Maste 	{
372bbe31b70SEd Maste 		if (input_stack.empty())
373bbe31b70SEd Maste 		{
374bbe31b70SEd Maste 			return *this;
375bbe31b70SEd Maste 		}
376bbe31b70SEd Maste 		cursor++;
377bbe31b70SEd Maste 		auto &top = *input_stack.top();
378bbe31b70SEd Maste 		++top;
379bbe31b70SEd Maste 		if (top.finished())
380bbe31b70SEd Maste 		{
381bbe31b70SEd Maste 			input_stack.pop();
382bbe31b70SEd Maste 		}
383bbe31b70SEd Maste 		return *this;
384bbe31b70SEd Maste 	}
385bbe31b70SEd Maste 	/**
386bbe31b70SEd Maste 	 * Consumes a character.  Moves the cursor one character forward if the
387bbe31b70SEd Maste 	 * next character matches the argument, returning true.  If the current
388bbe31b70SEd Maste 	 * character does not match the argument, returns false.
389bbe31b70SEd Maste 	 */
consume(char c)390bbe31b70SEd Maste 	inline bool consume(char c)
391bbe31b70SEd Maste 	{
392bbe31b70SEd Maste 		if (*(*this) == c)
393bbe31b70SEd Maste 		{
394bbe31b70SEd Maste 			++(*this);
395bbe31b70SEd Maste 			return true;
396bbe31b70SEd Maste 		}
397bbe31b70SEd Maste 		return false;
398bbe31b70SEd Maste 	}
399bbe31b70SEd Maste 	/**
400bbe31b70SEd Maste 	 * Consumes a string.  If the (null-terminated) string passed as the
401bbe31b70SEd Maste 	 * argument appears in the input, advances the cursor to the end and
402bbe31b70SEd Maste 	 * returns true.  Returns false if the string does not appear at the
403bbe31b70SEd Maste 	 * current point in the input.
404bbe31b70SEd Maste 	 *
405bbe31b70SEd Maste 	 * This method does not scan between files.
406bbe31b70SEd Maste 	 */
consume(const char * str)407bbe31b70SEd Maste 	bool consume(const char *str)
408bbe31b70SEd Maste 	{
409bbe31b70SEd Maste 		if (input_stack.empty())
410bbe31b70SEd Maste 		{
411bbe31b70SEd Maste 			return false;
412bbe31b70SEd Maste 		}
413bbe31b70SEd Maste 		return input_stack.top()->consume(str);
414bbe31b70SEd Maste 	}
415bbe31b70SEd Maste 	/**
416bbe31b70SEd Maste 	 * Reads an integer in base 8, 10, or 16.  Returns true and advances
417bbe31b70SEd Maste 	 * the cursor to the end of the integer if the cursor points to an
418bbe31b70SEd Maste 	 * integer, returns false and does not move the cursor otherwise.
419bbe31b70SEd Maste 	 *
420bbe31b70SEd Maste 	 * The parsed value is returned via the argument.
421bbe31b70SEd Maste 	 *
422bbe31b70SEd Maste 	 * This method does not scan between files.
423bbe31b70SEd Maste 	 */
consume_integer(unsigned long long & outInt)424bbe31b70SEd Maste 	bool consume_integer(unsigned long long &outInt)
425bbe31b70SEd Maste 	{
426bbe31b70SEd Maste 		if (input_stack.empty())
427bbe31b70SEd Maste 		{
428bbe31b70SEd Maste 			return false;
429bbe31b70SEd Maste 		}
430bbe31b70SEd Maste 		return input_stack.top()->consume_integer(outInt);
431bbe31b70SEd Maste 	}
432bbe31b70SEd Maste 	/**
433bbe31b70SEd Maste 	 * Reads an arithmetic expression (containing any of the normal C
434bbe31b70SEd Maste 	 * operators), evaluates it, and returns the result.
435bbe31b70SEd Maste 	 */
436bbe31b70SEd Maste 	bool consume_integer_expression(unsigned long long &outInt);
437bbe31b70SEd Maste 	/**
438bbe31b70SEd Maste 	 * Consumes two hex digits and return the resulting byte via the first
439bbe31b70SEd Maste 	 * argument.  If the next two characters are hex digits, returns true
440bbe31b70SEd Maste 	 * and advances the cursor.  If not, then returns false and leaves the
441bbe31b70SEd Maste 	 * cursor in place.
442bbe31b70SEd Maste 	 *
443bbe31b70SEd Maste 	 * This method does not scan between files.
444bbe31b70SEd Maste 	 */
consume_hex_byte(uint8_t & outByte)445bbe31b70SEd Maste 	bool consume_hex_byte(uint8_t &outByte)
446bbe31b70SEd Maste 	{
447bbe31b70SEd Maste 		if (input_stack.empty())
448bbe31b70SEd Maste 		{
449bbe31b70SEd Maste 			return false;
450bbe31b70SEd Maste 		}
451bbe31b70SEd Maste 		return input_stack.top()->consume_hex_byte(outByte);
452bbe31b70SEd Maste 	}
453bbe31b70SEd Maste 	/**
454bbe31b70SEd Maste 	 * Returns the longest string in the input buffer starting at the
455bbe31b70SEd Maste 	 * current cursor and composed entirely of characters that are valid in
456bbe31b70SEd Maste 	 * node names.
457bbe31b70SEd Maste 	*/
458bbe31b70SEd Maste 	std::string parse_node_name();
459bbe31b70SEd Maste 	/**
460bbe31b70SEd Maste 	 * Returns the longest string in the input buffer starting at the
461bbe31b70SEd Maste 	 * current cursor and composed entirely of characters that are valid in
462bbe31b70SEd Maste 	 * property names.
463bbe31b70SEd Maste 	 */
464bbe31b70SEd Maste 	std::string parse_property_name();
465bbe31b70SEd Maste 	/**
466bbe31b70SEd Maste 	 * Parses either a node or a property name.  If is_property is true on
467bbe31b70SEd Maste 	 * entry, then only property names are parsed.  If it is false, then it
468bbe31b70SEd Maste 	 * will be set, on return, to indicate whether the parsed name is only
469bbe31b70SEd Maste 	 * valid as a property.
470bbe31b70SEd Maste 	 */
471bbe31b70SEd Maste 	std::string parse_node_or_property_name(bool &is_property);
472bbe31b70SEd Maste 	/**
473bbe31b70SEd Maste 	 * Parses up to a specified character and returns the intervening
474bbe31b70SEd Maste 	 * characters as a string.
475bbe31b70SEd Maste 	 */
476bbe31b70SEd Maste 	std::string parse_to(char);
477bbe31b70SEd Maste 	/**
478bbe31b70SEd Maste 	 * Advances the cursor to the start of the next token, skipping
479bbe31b70SEd Maste 	 * comments and whitespace.  If the cursor already points to the start
480bbe31b70SEd Maste 	 * of a token, then this function does nothing.
481bbe31b70SEd Maste 	 */
482bbe31b70SEd Maste 	text_input_buffer &next_token();
483bbe31b70SEd Maste 	/**
484bbe31b70SEd Maste 	 * Location in the source file.  This should never be interpreted by
485bbe31b70SEd Maste 	 * anything other than error reporting functions of this class.  It will
486bbe31b70SEd Maste 	 * eventually become something more complex than an `int`.
487bbe31b70SEd Maste 	 */
488bbe31b70SEd Maste 	class source_location
489bbe31b70SEd Maste 	{
490bbe31b70SEd Maste 		friend class text_input_buffer;
491bbe31b70SEd Maste 		/**
492bbe31b70SEd Maste 		 * The text buffer object that included `b`.
493bbe31b70SEd Maste 		 */
494bbe31b70SEd Maste 		text_input_buffer &buffer;
495bbe31b70SEd Maste 		/**
496bbe31b70SEd Maste 		 * The underlying buffer that contains this location.
497bbe31b70SEd Maste 		 */
498bbe31b70SEd Maste 		std::shared_ptr<input_buffer> b;
499bbe31b70SEd Maste 		/**
500bbe31b70SEd Maste 		 * The offset within the current buffer of the source location.
501bbe31b70SEd Maste 		 */
502bbe31b70SEd Maste 		int cursor;
source_location(text_input_buffer & buf)503bbe31b70SEd Maste 		source_location(text_input_buffer &buf)
504bbe31b70SEd Maste 			: buffer(buf),
505bbe31b70SEd Maste 			  b(buf.input_stack.empty() ? nullptr : buf.input_stack.top()),
506bbe31b70SEd Maste 			  cursor(b ? b->cursor : 0) {}
507bbe31b70SEd Maste 		public:
508bbe31b70SEd Maste 		/**
509bbe31b70SEd Maste 		 * Report an error at this location.
510bbe31b70SEd Maste 		 */
report_error(const char * msg)511bbe31b70SEd Maste 		void report_error(const char *msg)
512bbe31b70SEd Maste 		{
513bbe31b70SEd Maste 			if (b)
514bbe31b70SEd Maste 			{
515bbe31b70SEd Maste 				buffer.parse_error(msg, *b, cursor);
516bbe31b70SEd Maste 			}
517bbe31b70SEd Maste 			else
518bbe31b70SEd Maste 			{
519bbe31b70SEd Maste 				buffer.parse_error(msg);
520bbe31b70SEd Maste 			}
521bbe31b70SEd Maste 		}
522af0dd31fSDavid Chisnall 	};
523af0dd31fSDavid Chisnall 	/**
524bbe31b70SEd Maste 	 * Returns the current source location.
525af0dd31fSDavid Chisnall 	 */
location()526bbe31b70SEd Maste 	source_location location()
527af0dd31fSDavid Chisnall 	{
528bbe31b70SEd Maste 		return { *this };
529bbe31b70SEd Maste 	}
530af0dd31fSDavid Chisnall 	/**
531bbe31b70SEd Maste 	 * Prints a message indicating the location of a parse error.
532af0dd31fSDavid Chisnall 	 */
533bbe31b70SEd Maste 	void parse_error(const char *msg);
534ca84c67cSKyle Evans 	/**
535ca84c67cSKyle Evans 	 * Reads the contents of a binary file into `b`.  The file name is assumed
536ca84c67cSKyle Evans 	 * to be relative to one of the include paths.
537ca84c67cSKyle Evans 	 *
538ca84c67cSKyle Evans 	 * Returns true if the file exists and can be read, false otherwise.
539ca84c67cSKyle Evans 	 */
540ca84c67cSKyle Evans 	bool read_binary_file(const std::string &filename, byte_buffer &b);
541bbe31b70SEd Maste 	private:
542af0dd31fSDavid Chisnall 	/**
543bbe31b70SEd Maste 	 * Prints a message indicating the location of a parse error, given a
544bbe31b70SEd Maste 	 * specified location.  This is used when input has already moved beyond
545bbe31b70SEd Maste 	 * the location that caused the failure.
546af0dd31fSDavid Chisnall 	 */
547bbe31b70SEd Maste 	void parse_error(const char *msg, input_buffer &b, int loc);
548af0dd31fSDavid Chisnall };
549af0dd31fSDavid Chisnall 
550af0dd31fSDavid Chisnall } // namespace dtc
551af0dd31fSDavid Chisnall 
552af0dd31fSDavid Chisnall #endif // !_INPUT_BUFFER_HH_
553