svn commit: r292876 - head/usr.bin/dtc

David Chisnall theraven at FreeBSD.org
Tue Dec 29 16:29:44 UTC 2015


Author: theraven
Date: Tue Dec 29 16:29:42 2015
New Revision: 292876
URL: https://svnweb.freebsd.org/changeset/base/292876

Log:
  Improvements to BSD-licensed DTC.
  
  - Added an expression parser so that expressions from headers are now working
  - Fixed missing null terminators on cross references
  - Disabled exceptions / RTTI in the build for smaller binaries
  - Changed phandle order generation to be identical to GPL'd dtc

Modified:
  head/usr.bin/dtc/Makefile
  head/usr.bin/dtc/checking.cc
  head/usr.bin/dtc/checking.hh
  head/usr.bin/dtc/dtb.hh
  head/usr.bin/dtc/fdt.cc
  head/usr.bin/dtc/fdt.hh
  head/usr.bin/dtc/input_buffer.cc
  head/usr.bin/dtc/input_buffer.hh

Modified: head/usr.bin/dtc/Makefile
==============================================================================
--- head/usr.bin/dtc/Makefile	Tue Dec 29 16:11:43 2015	(r292875)
+++ head/usr.bin/dtc/Makefile	Tue Dec 29 16:29:42 2015	(r292876)
@@ -6,7 +6,7 @@ MAN=	dtc.1
 
 WARNS?=	3
 
-CXXFLAGS+=	-std=c++11
+CXXFLAGS+=	-std=c++11 -fno-rtti -fno-exceptions
 
 NO_SHARED?=NO
 

Modified: head/usr.bin/dtc/checking.cc
==============================================================================
--- head/usr.bin/dtc/checking.cc	Tue Dec 29 16:11:43 2015	(r292875)
+++ head/usr.bin/dtc/checking.cc	Tue Dec 29 16:29:42 2015	(r292876)
@@ -51,7 +51,7 @@ namespace
 	struct address_cells_checker : public checker
 	{
 		address_cells_checker(const char *name) : checker(name) {}
-		virtual bool check_node(device_tree *tree, const node_ptr &n)
+		virtual bool check_node(device_tree *, const node_ptr &n)
 		{
 			// If this has no children, it trivially meets the
 			// conditions.
@@ -151,7 +151,7 @@ property_checker::check_property(device_
 }
 
 bool
-property_size_checker::check(device_tree *tree, const node_ptr &n, property_ptr p)
+property_size_checker::check(device_tree *, const node_ptr &, property_ptr p)
 {
 	uint32_t psize = 0;
 	for (property::value_iterator i=p->begin(),e=p->end() ; i!=e ; ++i)

Modified: head/usr.bin/dtc/checking.hh
==============================================================================
--- head/usr.bin/dtc/checking.hh	Tue Dec 29 16:11:43 2015	(r292875)
+++ head/usr.bin/dtc/checking.hh	Tue Dec 29 16:29:42 2015	(r292876)
@@ -86,7 +86,7 @@ class checker
 	 * Method for checking that a node is valid.  The root class version
 	 * does nothing, subclasses should override this.
 	 */
-	virtual bool check_node(device_tree *tree, const node_ptr &n)
+	virtual bool check_node(device_tree *, const node_ptr &)
 	{
 		return true;
 	}
@@ -94,7 +94,7 @@ class checker
 	 * Method for checking that a property is valid.  The root class
 	 * version does nothing, subclasses should override this.
 	 */
-	virtual bool check_property(device_tree *tree, const node_ptr &n, property_ptr p)
+	virtual bool check_property(device_tree *, const node_ptr &, property_ptr )
 	{
 		return true;
 	}
@@ -160,7 +160,7 @@ struct property_type_checker <property_v
 {
 	property_type_checker(const char* name, string property_name) : 
 		property_checker(name, property_name) {}
-	virtual bool check(device_tree *tree, const node_ptr &n, property_ptr p)
+	virtual bool check(device_tree *, const node_ptr &, property_ptr p)
 	{
 		return p->begin() == p->end();
 	}
@@ -175,7 +175,7 @@ struct property_type_checker <property_v
 {
 	property_type_checker(const char* name, string property_name) : 
 		property_checker(name, property_name) {}
-	virtual bool check(device_tree *tree, const node_ptr &n, property_ptr p)
+	virtual bool check(device_tree *, const node_ptr &, property_ptr p)
 	{
 		return (p->begin() + 1 == p->end()) && p->begin()->is_string();
 	}
@@ -190,7 +190,7 @@ struct property_type_checker <property_v
 {
 	property_type_checker(const char* name, string property_name) : 
 		property_checker(name, property_name) {}
-	virtual bool check(device_tree *tree, const node_ptr &n, property_ptr p)
+	virtual bool check(device_tree *, const node_ptr &, property_ptr p)
 	{
 		for (property::value_iterator i=p->begin(),e=p->end() ; i!=e ;
 		     ++i)
@@ -213,7 +213,7 @@ struct property_type_checker <property_v
 {
 	property_type_checker(const char* name, string property_name) : 
 		property_checker(name, property_name) {}
-	virtual bool check(device_tree *tree, const node_ptr &n, property_ptr p)
+	virtual bool check(device_tree *tree, const node_ptr &, property_ptr p)
 	{
 		return (p->begin() + 1 == p->end()) && 
 			(tree->referenced_node(*p->begin()) != 0);

Modified: head/usr.bin/dtc/dtb.hh
==============================================================================
--- head/usr.bin/dtc/dtb.hh	Tue Dec 29 16:11:43 2015	(r292875)
+++ head/usr.bin/dtc/dtb.hh	Tue Dec 29 16:29:42 2015	(r292876)
@@ -186,11 +186,11 @@ class binary_writer : public output_writ
 	 *  The binary format does not support labels, so this method
 	 * does nothing.
 	 */
-	virtual void write_label(string name) {}
+	virtual void write_label(string) {}
 	/**
 	 * Comments are ignored by the binary writer.
 	 */
-	virtual void write_comment(string name) {}
+	virtual void write_comment(string) {}
 	virtual void write_string(string name);
 	virtual void write_data(uint8_t v);
 	virtual void write_data(uint32_t v);

Modified: head/usr.bin/dtc/fdt.cc
==============================================================================
--- head/usr.bin/dtc/fdt.cc	Tue Dec 29 16:11:43 2015	(r292875)
+++ head/usr.bin/dtc/fdt.cc	Tue Dec 29 16:29:42 2015	(r292876)
@@ -264,24 +264,6 @@ property::parse_string(input_buffer &inp
 void
 property::parse_cells(input_buffer &input, int cell_size)
 {
-	unsigned long long cell_max;
-	switch (cell_size)
-	{
-		case 8:
-			cell_max = UINT8_MAX;
-			break;
-		case 16:
-			cell_max = UINT16_MAX;
-			break;
-		case 32:
-			cell_max = UINT32_MAX;
-			break;
-		case 64:
-			cell_max = UINT64_MAX;
-			break;
-		default:
-			assert(0 && "Invalid cell size!");
-	}
 	assert(input[0] == '<');
 	++input;
 	property_value v;
@@ -327,19 +309,12 @@ property::parse_cells(input_buffer &inpu
 			//FIXME: We should support labels in the middle
 			//of these, but we don't.
 			unsigned long long val;
-			if (!input.consume_integer(val))
+			if (!input.consume_integer_expression(val))
 			{
 				input.parse_error("Expected numbers in array of cells");
 				valid = false;
 				return;
 			}
-			if (val > cell_max)
-			{
-				fprintf(stderr, "%lld > %lld\n", val, cell_max);
-				input.parse_error("Value out of range");
-				valid = false;
-				return;
-			}
 			switch (cell_size)
 			{
 				case 8:
@@ -685,6 +660,16 @@ node::parse_name(input_buffer &input, bo
 	return n;
 }
 
+void
+node::visit(std::function<void(node&)> fn)
+{
+	fn(*this);
+	for (auto &&c : children)
+	{
+		c->visit(fn);
+	}
+}
+
 node::node(input_buffer &structs, input_buffer &strings) : valid(true)
 {
 	const char *name_start = (const char*)structs;
@@ -742,7 +727,7 @@ node::node(input_buffer &structs, input_
 					valid = false;
 					return;
 				}
-				properties.push_back(prop);
+				props.push_back(prop);
 				break;
 			}
 				break;
@@ -806,7 +791,7 @@ node::node(input_buffer &input, string n
 			}
 			else
 			{
-				properties.push_back(p);
+				props.push_back(p);
 			}
 		}
 		else if (!is_property && input[0] == ('{'))
@@ -824,7 +809,7 @@ node::node(input_buffer &input, string n
 		}
 		else if (input.consume(';'))
 		{
-			properties.push_back(property_ptr(new property(child_name, child_label)));
+			props.push_back(property_ptr(new property(child_name, child_label)));
 		}
 		else
 		{
@@ -857,9 +842,9 @@ node::sort()
 {
 	std::sort(property_begin(), property_end(), cmp_properties);
 	std::sort(child_begin(), child_end(), cmp_children);
-	for (child_iterator i=child_begin(), e=child_end() ; i!=e ; ++i)
+	for (auto &c : child_nodes())
 	{
-		(*i)->sort();
+		c->sort();
 	}
 }
 
@@ -892,7 +877,7 @@ node::parse_dtb(input_buffer &structs, i
 property_ptr
 node::get_property(string key)
 {
-	for (auto &i : properties)
+	for (auto &i : props)
 	{
 		if (i->get_key() == key)
 		{
@@ -914,14 +899,14 @@ node::merge_node(node_ptr other)
 	// large numbers of properties, but for typical usage the
 	// entire vector will fit (easily) into cache, so iterating
 	// over it repeatedly isn't that expensive.
-	for (auto &p : other->properties)
+	for (auto &p : other->properties())
 	{
 		bool found = false;
-		for (auto i=property_begin(), e=property_end() ; i!=e ; ++i)
+		for (auto &mp : properties())
 		{
-			if ((*i)->get_key() == p->get_key())
+			if (mp->get_key() == p->get_key())
 			{
-				*i = p;
+				mp = p;
 				found = true;
 				break;
 			}
@@ -964,13 +949,13 @@ node::write(dtb::output_writer &writer, 
 	writer.write_comment(name);
 	writer.write_data(name_buffer);
 	writer.write_data((uint8_t)0);
-	for (auto i=property_begin(), e=property_end() ; i!=e ; ++i)
+	for (auto p : properties())
 	{
-		(*i)->write(writer, strings);
+		p->write(writer, strings);
 	}
-	for (child_iterator i=child_begin(), e=child_end() ; i!=e ; ++i)
+	for (auto &c : child_nodes())
 	{
-		(*i)->write(writer, strings);
+		c->write(writer, strings);
 	}
 	writer.write_token(dtb::FDT_END_NODE);
 }
@@ -999,13 +984,13 @@ node::write_dts(FILE *file, int indent)
 		unit_address.print(file);
 	}
 	fputs(" {\n\n", file);
-	for (auto i=property_begin(), e=property_end() ; i!=e ; ++i)
+	for (auto p : properties())
 	{
-		(*i)->write_dts(file, indent+1);
+		p->write_dts(file, indent+1);
 	}
-	for (child_iterator i=child_begin(), e=child_end() ; i!=e ; ++i)
+	for (auto &c : child_nodes())
 	{
-		(*i)->write_dts(file, indent+1);
+		c->write_dts(file, indent+1);
 	}
 	for (int i=0 ; i<indent ; i++)
 	{
@@ -1039,30 +1024,30 @@ device_tree::collect_names_recursive(nod
 			fprintf(stderr, ".  References to this label will not be resolved.");
 		}
 	}
-	for (node::child_iterator i=n->child_begin(), e=n->child_end() ; i!=e ; ++i)
+	for (auto &c : n->child_nodes())
 	{
-		collect_names_recursive(*i, path);
+		collect_names_recursive(c, path);
 	}
 	path.pop_back();
 	// Now we collect the phandles and properties that reference
 	// other nodes.
-	for (auto i=n->property_begin(), e=n->property_end() ; i!=e ; ++i)
+	for (auto &p : n->properties())
 	{
-		for (property::value_iterator p=(*i)->begin(),pe=(*i)->end() ; p!=pe ; ++p)
+		for (auto &v : *p)
 		{
-			if (p->is_phandle())
+			if (v.is_phandle())
 			{
-				phandles.push_back(&*p);
+				phandles.push_back(&v);
 			}
-			if (p->is_cross_reference())
+			if (v.is_cross_reference())
 			{
-				cross_references.push_back(&*p);
+				cross_references.push_back(&v);
 			}
 		}
-		if ((*i)->get_key() == string("phandle") ||
-		    (*i)->get_key() == string("linux,phandle"))
+		if (p->get_key() == string("phandle") ||
+		    p->get_key() == string("linux,phandle"))
 		{
-			if ((*i)->begin()->byte_data.size() != 4)
+			if (p->begin()->byte_data.size() != 4)
 			{
 				fprintf(stderr, "Invalid phandle value for node ");
 				n->name.dump();
@@ -1071,7 +1056,7 @@ device_tree::collect_names_recursive(nod
 			}
 			else
 			{
-				uint32_t phandle = (*i)->begin()->get_as_uint32();
+				uint32_t phandle = p->begin()->get_as_uint32();
 				used_phandles.insert(std::make_pair(phandle, n.get()));
 			}
 		}
@@ -1104,12 +1089,33 @@ device_tree::resolve_cross_references()
 			{
 				pv->byte_data.push_back('@');
 				p->second.push_to_buffer(pv->byte_data);
+				pv->byte_data.push_back(0);
 			}
 		}
 	}
-	uint32_t phandle = 1;
+	std::unordered_set<property_value*> phandle_set;
 	for (auto &i : phandles)
 	{
+		phandle_set.insert(i);
+	}
+	std::vector<property_value*> sorted_phandles;
+	root->visit([&](node &n) {
+		for (auto &p : n.properties())
+		{
+			for (auto &v : *p)
+			{
+				if (phandle_set.count(&v))
+				{
+					sorted_phandles.push_back(&v);
+				}
+			}
+		}
+	});
+	assert(sorted_phandles.size() == phandles.size());
+
+	uint32_t phandle = 1;
+	for (auto &i : sorted_phandles)
+	{
 		string target_name = i->string_data;
 		node *target = node_names[target_name];
 		if (target == 0)
@@ -1163,94 +1169,111 @@ device_tree::resolve_cross_references()
 	}
 }
 
-void
-device_tree::parse_file(input_buffer &input,
+bool
+device_tree::parse_include(input_buffer &input,
                         const std::string &dir,
                         std::vector<node_ptr> &roots,
                         FILE *depfile,
                         bool &read_header)
 {
-	input.next_token();
-	// Read the header
-	if (input.consume("/dts-v1/;"))
+	if (!input.consume("/include/"))
 	{
-		read_header = true;
+		return false;
 	}
-	input.next_token();
-	while(input.consume("/include/"))
+	bool reallyInclude = true;
+	if (input.consume("if "))
 	{
-		bool reallyInclude = true;
-		if (input.consume("if "))
-		{
-			input.next_token();
-			string name = string::parse_property_name(input);
-			// XXX: Error handling
-			if (defines.find(name) == defines.end())
-			{
-				reallyInclude = false;
-			}
-			input.consume('/');
-		}
 		input.next_token();
-		if (!input.consume('"'))
+		string name = string::parse_property_name(input);
+		// XXX: Error handling
+		if (defines.find(name) == defines.end())
 		{
-			input.parse_error("Expected quoted filename");
-			valid = false;
-			return;
+			reallyInclude = false;
 		}
-		int length = 0;
-		while (input[length] != '"') length++;
+		input.consume('/');
+	}
+	input.next_token();
+	if (!input.consume('"'))
+	{
+		input.parse_error("Expected quoted filename");
+		valid = false;
+		return false;
+	}
+	int length = 0;
+	while (input[length] != '"') length++;
 
-		std::string file((const char*)input, length);
-		std::string include_file = dir + '/' + file;
-		assert(input.consume(file.c_str()));
+	std::string file((const char*)input, length);
+	std::string include_file = dir + '/' + file;
+	input.consume(file.c_str());
+	if (!reallyInclude)
+	{
 		input.consume('"');
 		input.next_token();
-		if (!reallyInclude)
-		{
-			continue;
-		}
+		return true;
+	}
 
-		input_buffer *include_buffer = buffer_for_file(include_file.c_str());
+	input_buffer *include_buffer = buffer_for_file(include_file.c_str(), false);
 
-		if (include_buffer == 0)
+	if (include_buffer == 0)
+	{
+		for (auto i : include_paths)
 		{
-			for (auto i : include_paths)
+			include_file = i + '/' + file;
+			include_buffer = buffer_for_file(include_file.c_str());
+			if (include_buffer != 0)
 			{
-				include_file = i + '/' + file;
-				include_buffer = buffer_for_file(include_file.c_str());
-				if (include_buffer != 0)
-				{
-					break;
-				}
+				break;
 			}
 		}
-		if (depfile != 0)
-		{
-			putc(' ', depfile);
-			fputs(include_file.c_str(), depfile);
-		}
-		if (include_buffer == 0)
-		{
-			valid = false;
-			return;
-		}
-		parse_file(*include_buffer, dir, roots, depfile, read_header);
 	}
+	if (depfile != 0)
+	{
+		putc(' ', depfile);
+		fputs(include_file.c_str(), depfile);
+	}
+	if (include_buffer == 0)
+	{
+		input.parse_error("Unable to locate input file");
+		input.consume('"');
+		input.next_token();
+		valid = false;
+		return true;
+	}
+	input.consume('"');
+	input.next_token();
+	parse_file(*include_buffer, dir, roots, depfile, read_header);
+	return true;
+}
+
+void
+device_tree::parse_file(input_buffer &input,
+                        const std::string &dir,
+                        std::vector<node_ptr> &roots,
+                        FILE *depfile,
+                        bool &read_header)
+{
+	input.next_token();
+	// Read the header
+	if (input.consume("/dts-v1/;"))
+	{
+		read_header = true;
+	}
+	input.next_token();
 	input.next_token();
 	if (!read_header)
 	{
 		input.parse_error("Expected /dts-v1/; version string");
 	}
+	while(parse_include(input, dir, roots, depfile, read_header)) {}
 	// Read any memory reservations
 	while(input.consume("/memreserve/"))
 	{
 		unsigned long long start, len;
 		input.next_token();
 		// Read the start and length.
-		if (!(input.consume_integer(start) &&
+		if (!(input.consume_integer_expression(start) &&
 		    (input.next_token(),
-		    input.consume_integer(len))))
+		    input.consume_integer_expression(len))))
 		{
 			input.parse_error("Expected size on /memreserve/ node.");
 		}
@@ -1259,6 +1282,7 @@ device_tree::parse_file(input_buffer &in
 		reservations.push_back(reservation(start, len));
 	}
 	input.next_token();
+	while(parse_include(input, dir, roots, depfile, read_header)) {}
 	while (valid && !input.finished())
 	{
 		node_ptr n;
@@ -1287,11 +1311,12 @@ device_tree::parse_file(input_buffer &in
 			valid = false;
 		}
 		input.next_token();
+		while(parse_include(input, dir, roots, depfile, read_header)) {}
 	}
 }
 
 input_buffer*
-device_tree::buffer_for_file(const char *path)
+device_tree::buffer_for_file(const char *path, bool warn)
 {
 	if (string(path) == string("-"))
 	{
@@ -1306,7 +1331,10 @@ device_tree::buffer_for_file(const char 
 	int source = open(path, O_RDONLY);
 	if (source == -1)
 	{
-		fprintf(stderr, "Unable to open file '%s'.  %s\n", path, strerror(errno));
+		if (warn)
+		{
+			fprintf(stderr, "Unable to open file '%s'.  %s\n", path, strerror(errno));
+		}
 		return 0;
 	}
 	struct stat st;
@@ -1447,7 +1475,7 @@ device_tree::write_dts(int fd)
 }
 
 void
-device_tree::parse_dtb(const char *fn, FILE *depfile)
+device_tree::parse_dtb(const char *fn, FILE *)
 {
 	input_buffer *in = buffer_for_file(fn);
 	if (in == 0)

Modified: head/usr.bin/dtc/fdt.hh
==============================================================================
--- head/usr.bin/dtc/fdt.hh	Tue Dec 29 16:11:43 2015	(r292875)
+++ head/usr.bin/dtc/fdt.hh	Tue Dec 29 16:29:42 2015	(r292876)
@@ -36,6 +36,7 @@
 #include <unordered_set>
 #include <memory>
 #include <string>
+#include <functional>
 
 #include "util.hh"
 #include "string.hh"
@@ -402,11 +403,37 @@ class node
 	 * The type for the property vector.
 	 */
 	typedef std::vector<property_ptr> property_vector;
+	/**
+	 * Iterator type for child nodes.
+	 */
+	typedef std::vector<node_ptr>::iterator child_iterator;
 	private:
 	/**
+	 * Adaptor to use children in range-based for loops.
+	 */
+	struct child_range
+	{
+		child_range(node &nd) : n(nd) {}
+		child_iterator begin() { return n.child_begin(); }
+		child_iterator end() { return n.child_end(); }
+		private:
+		node &n;
+	};
+	/**
+	 * Adaptor to use properties in range-based for loops.
+	 */
+	struct property_range
+	{
+		property_range(node &nd) : n(nd) {}
+		property_vector::iterator begin() { return n.property_begin(); }
+		property_vector::iterator end() { return n.property_end(); }
+		private:
+		node &n;
+	};
+	/**
 	 * The properties contained within this node.
 	 */
-	property_vector properties;
+	property_vector props;
 	/**
 	 * The children of this node.
 	 */
@@ -458,10 +485,6 @@ class node
 	 */
 	void sort();
 	/**
-	 * Iterator type for child nodes.
-	 */
-	typedef std::vector<node_ptr>::iterator child_iterator;
-	/**
 	 * Returns an iterator for the first child of this node.
 	 */
 	inline child_iterator child_begin()
@@ -475,19 +498,27 @@ class node
 	{
 		return children.end();
 	}
+	inline child_range child_nodes()
+	{
+		return child_range(*this);
+	}
+	inline property_range properties()
+	{
+		return property_range(*this);
+	}
 	/**
 	 * Returns an iterator after the last property of this node.
 	 */
 	inline property_vector::iterator property_begin()
 	{
-		return properties.begin();
+		return props.begin();
 	}
 	/**
 	 * Returns an iterator for the first property of this node.
 	 */
 	inline property_vector::iterator property_end()
 	{
-		return properties.end();
+		return props.end();
 	}
 	/**
 	 * Factory method for constructing a new node.  Attempts to parse a
@@ -519,7 +550,7 @@ class node
 	 */
 	inline void add_property(property_ptr &p)
 	{
-		properties.push_back(p);
+		props.push_back(p);
 	}
 	/**
 	 * Merges a node into this one.  Any properties present in both are
@@ -539,6 +570,10 @@ class node
 	 * with this number of tabs.  
 	 */
 	void write_dts(FILE *file, int indent);
+	/**
+	 * Recursively visit this node and then its children.
+	 */
+	void visit(std::function<void(node&)>);
 };
 
 /**
@@ -683,6 +718,14 @@ class device_tree
 	 */
 	void resolve_cross_references();
 	/**
+	 * Parse a top-level include directive.
+	 */
+	bool parse_include(input_buffer &input,
+	                   const std::string &dir,
+	                   std::vector<node_ptr> &roots,
+	                   FILE *depfile,
+	                   bool &read_header);
+	/**
 	 * Parses a dts file in the given buffer and adds the roots to the parsed
 	 * set.  The `read_header` argument indicates whether the header has
 	 * already been read.  Some dts files place the header in an include,
@@ -698,7 +741,7 @@ class device_tree
 	 * object then keeps a reference to it, ensuring that it is not
 	 * deallocated until the device tree is destroyed.
 	 */
-	input_buffer *buffer_for_file(const char *path);
+	input_buffer *buffer_for_file(const char *path, bool warn=true);
 	/**
 	 * Template function that writes a dtb blob using the specified writer.
 	 * The writer defines the output format (assembly, blob).

Modified: head/usr.bin/dtc/input_buffer.cc
==============================================================================
--- head/usr.bin/dtc/input_buffer.cc	Tue Dec 29 16:11:43 2015	(r292875)
+++ head/usr.bin/dtc/input_buffer.cc	Tue Dec 29 16:29:42 2015	(r292876)
@@ -37,6 +37,10 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <functional>
+#ifndef NDEBUG
+#include <iostream>
+#endif
 
 
 #include <sys/stat.h>
@@ -49,7 +53,6 @@
 
 namespace dtc
 {
-
 void
 input_buffer::skip_spaces()
 {
@@ -131,6 +134,563 @@ input_buffer::consume_integer(unsigned l
 	return true;
 }
 
+namespace {
+
+/**
+ * Convenience typedef for the type that we use for all values.
+ */
+typedef unsigned long long valty;
+
+/**
+ * Expression tree currently being parsed.
+ */
+struct expression
+{
+	/**
+	 * Evaluate this node, taking into account operator precedence.
+	 */
+	virtual valty operator()() = 0;
+	/**
+	 * Returns the precedence of this node.  Lower values indicate higher
+	 * precedence.
+	 */
+	virtual int precedence() = 0;
+	virtual ~expression() {}
+#ifndef NDEBUG
+	/**
+	 * Dumps this expression to `std::cerr`, appending a newline if `nl` is
+	 * `true`.
+	 */
+	void dump(bool nl=false)
+	{
+		if (this == nullptr)
+		{
+			std::cerr << "{nullptr}\n";
+			return;
+		}
+		dump_impl();
+		if (nl)
+		{
+			std::cerr << '\n';
+		}
+	}
+	private:
+	/**
+	 * Method that sublcasses override to implement the behaviour of `dump()`.
+	 */
+	virtual void dump_impl() = 0;
+#endif
+};
+
+/**
+ * Expression wrapping a single integer.  Leaf nodes in the expression tree.
+ */
+class terminal_expr : public expression
+{
+	/**
+	 * The value that this wraps.
+	 */
+	valty val;
+	/**
+	 * Evaluate.  Trivially returns the value that this class wraps.
+	 */
+	valty operator()() override
+	{
+		return val;
+	}
+	int precedence() override
+	{
+		return 0;
+	}
+	public:
+	/**
+	 * Constructor.
+	 */
+	terminal_expr(valty v) : val(v) {}
+#ifndef NDEBUG
+	void dump_impl() override { std::cerr << val; }
+#endif
+};
+
+/**
+ * Parenthetical expression.  Exists to make the contents opaque.
+ */
+struct paren_expression : public expression
+{
+	/**
+	 * The expression within the parentheses.
+	 */
+	expression_ptr subexpr;
+	/**
+	 * Constructor.  Takes the child expression as the only argument.
+	 */
+	paren_expression(expression_ptr p) : subexpr(std::move(p)) {}
+	int precedence() override
+	{
+		return 0;
+	}
+	/**
+	 * Evaluate - just forwards to the underlying expression.
+	 */
+	valty operator()() override
+	{
+		return (*subexpr)();
+	}
+#ifndef NDEBUG
+	void dump_impl() override
+	{
+		std::cerr << " (";
+		subexpr->dump();
+		std::cerr << ") ";
+	}
+#endif
+};
+
+/**
+ * Template class for unary operators.  The `OpChar` template parameter is
+ * solely for debugging and makes it easy to print the expression.  The `Op`
+ * template parameter is a function object that implements the operator that
+ * this class provides.  Most of these are provided by the `<functional>`
+ * header.
+ */
+template<char OpChar, class Op>
+class unary_operator : public expression
+{
+	/**
+	 * The subexpression for this unary operator.
+	 */
+	expression_ptr subexpr;
+	valty operator()() override
+	{
+		Op op;
+		return op((*subexpr)());
+	}
+	/**
+	 * All unary operators have the same precedence.  They are all evaluated
+	 * before binary expressions, but after parentheses.
+	 */
+	int precedence() override
+	{
+		return 3;
+	}
+	public:
+	unary_operator(expression_ptr p) : subexpr(std::move(p)) {}
+#ifndef NDEBUG
+	void dump_impl() override
+	{
+		std::cerr << OpChar;
+		subexpr->dump();
+	}
+#endif
+};
+
+/**
+ * Abstract base class for binary operators.  Allows the tree to be modified
+ * without knowing what the operations actually are.
+ */
+struct binary_operator_base : public expression
+{
+	/**
+	 * The left side of the expression.
+	 */
+	expression_ptr lhs;
+	/**
+	 * The right side of the expression.
+	 */
+	expression_ptr rhs;
+	/**
+	 * Insert a node somewhere down the path of left children, until it would
+	 * be preempting something that should execute first.
+	 */
+	void insert_left(binary_operator_base *new_left)
+	{
+		if (lhs->precedence() < new_left->precedence())
+		{
+			new_left->rhs = std::move(lhs);
+			lhs.reset(new_left);
+		}
+		else
+		{
+			static_cast<binary_operator_base*>(lhs.get())->insert_left(new_left);
+		}
+	}
+};
+
+/**
+ * Template class for binary operators.  The precedence and the operation are
+ * provided as template parameters.
+ */
+template<int Precedence, class Op>
+struct binary_operator : public binary_operator_base
+{
+	valty operator()() override
+	{
+		Op op;
+		return op((*lhs)(), (*rhs)());
+	}
+	int precedence() override
+	{
+		return Precedence;
+	}
+#ifdef NDEBUG
+	/**
+	 * Constructor.  Takes the name of the operator as an argument, for
+	 * debugging.  Only stores it in debug mode.
+	 */
+	binary_operator(const char *) {}
+#else
+	const char *opName;
+	binary_operator(const char *o) : opName(o) {}
+	void dump_impl() override
+	{
+		lhs->dump();
+		std::cerr << opName;
+		rhs->dump();
+	}
+#endif
+};
+
+/**
+ * Ternary conditional operators (`cond ? true : false`) are a special case -
+ * there are no other ternary operators.
+ */
+class ternary_conditional_operator : public expression
+{
+	/**
+	 * The condition for the clause.
+	 */
+	expression_ptr cond;
+	/**
+	 * The expression that this evaluates to if the condition is true.
+	 */
+	expression_ptr lhs;
+	/**
+	 * The expression that this evaluates to if the condition is false.
+	 */
+	expression_ptr rhs;
+	valty operator()() override
+	{
+		return (*cond)() ? (*lhs)() : (*rhs)();
+	}
+	int precedence() override
+	{
+		// The actual precedence of a ternary conditional operator is 15, but
+		// its associativity is the opposite way around to the other operators,
+		// so we fudge it slightly.
+		return 3;
+	}
+#ifndef NDEBUG
+	void dump_impl() override
+	{
+		cond->dump();
+		std::cerr << " ? ";
+		lhs->dump();
+		std::cerr << " : ";
+		rhs->dump();
+	}
+#endif
+	public:
+	ternary_conditional_operator(expression_ptr c,
+	                             expression_ptr l,
+	                             expression_ptr r) :
+		cond(std::move(c)), lhs(std::move(l)), rhs(std::move(r)) {}
+};
+
+template<typename T>
+struct lshift
+{
+	constexpr T operator()(const T &lhs, const T &rhs) const
+	{
+		return lhs << rhs;
+	}
+};
+template<typename T>
+struct rshift
+{
+	constexpr T operator()(const T &lhs, const T &rhs) const

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***


More information about the svn-src-head mailing list