mirror of
				https://github.com/YosysHQ/yosys
				synced 2025-10-31 03:32:29 +00:00 
			
		
		
		
	partial rebase of PeterCrozier's enum work onto current master
I tried to keep only the enum-related changes, and minimize the diff. (The
original commit also had a lot of work done to get typedefs working, but yosys
has diverged quite a bit since the 2018-03-09 commit, with a new typedef
implementation.) I did not include the import related changes either.
Original commit:
"Initial implementation of enum, typedef, import.  Still a WIP."
881833aa73
			
			
This commit is contained in:
		
							parent
							
								
									2bda51ac34
								
							
						
					
					
						commit
						16ea4ea61a
					
				
					 5 changed files with 207 additions and 17 deletions
				
			
		|  | @ -88,6 +88,8 @@ std::string AST::type2str(AstNodeType type) | |||
| 	X(AST_LIVE) | ||||
| 	X(AST_FAIR) | ||||
| 	X(AST_COVER) | ||||
| 	X(AST_ENUM) | ||||
| 	X(AST_ENUM_ITEM) | ||||
| 	X(AST_FCALL) | ||||
| 	X(AST_TO_BITS) | ||||
| 	X(AST_TO_SIGNED) | ||||
|  | @ -202,6 +204,7 @@ AstNode::AstNode(AstNodeType type, AstNode *child1, AstNode *child2, AstNode *ch | |||
| 	is_logic = false; | ||||
| 	is_signed = false; | ||||
| 	is_string = false; | ||||
| 	is_enum = false; | ||||
| 	is_wand = false; | ||||
| 	is_wor = false; | ||||
| 	is_unsized = false; | ||||
|  | @ -321,6 +324,9 @@ void AstNode::dumpAst(FILE *f, std::string indent) const | |||
| 			fprintf(f, " %d", v); | ||||
| 		fprintf(f, " ]"); | ||||
| 	} | ||||
| 	if (is_enum) { | ||||
| 		fprintf(f, " type=enum"); | ||||
| 	} | ||||
| 	fprintf(f, "\n"); | ||||
| 
 | ||||
| 	for (auto &it : attributes) { | ||||
|  | @ -1174,7 +1180,15 @@ void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump | |||
| 			for (auto n : design->verilog_packages){ | ||||
| 				for (auto o : n->children) { | ||||
| 					AstNode *cloned_node = o->clone(); | ||||
| 					log("cloned node %s\n", type2str(cloned_node->type).c_str()); | ||||
| 					if (cloned_node->type == AST_ENUM){ | ||||
| 						for (auto e : cloned_node->children){ | ||||
| 							log_assert(e->type == AST_ENUM_ITEM); | ||||
| 							e->str = n->str + std::string("::") + e->str.substr(1); | ||||
| 						} | ||||
| 					} else { | ||||
| 						cloned_node->str = n->str + std::string("::") + cloned_node->str.substr(1); | ||||
| 					} | ||||
| 					(*it)->children.push_back(cloned_node); | ||||
| 				} | ||||
| 			} | ||||
|  | @ -1203,12 +1217,15 @@ void AST::process(RTLIL::Design *design, AstNode *ast, bool dump_ast1, bool dump | |||
| 
 | ||||
| 			design->add(process_module(*it, defer)); | ||||
| 		} | ||||
| 		else if ((*it)->type == AST_PACKAGE) | ||||
| 		else if ((*it)->type == AST_PACKAGE) { | ||||
| 			design->verilog_packages.push_back((*it)->clone()); | ||||
| 		else | ||||
| 		} | ||||
| 		else { | ||||
| 			// must be global definition
 | ||||
| 			design->verilog_globals.push_back((*it)->clone()); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // AstModule destructor
 | ||||
| AstModule::~AstModule() | ||||
|  |  | |||
|  | @ -68,6 +68,8 @@ namespace AST | |||
| 		AST_LIVE, | ||||
| 		AST_FAIR, | ||||
| 		AST_COVER, | ||||
| 		AST_ENUM, | ||||
| 		AST_ENUM_ITEM, | ||||
| 
 | ||||
| 		AST_FCALL, | ||||
| 		AST_TO_BITS, | ||||
|  | @ -181,6 +183,8 @@ namespace AST | |||
| 		int port_id, range_left, range_right; | ||||
| 		uint32_t integer; | ||||
| 		double realvalue; | ||||
| 		// set for IDs typed to an enumeration, not used
 | ||||
| 		bool is_enum; | ||||
| 
 | ||||
| 		// if this is a multirange memory then this vector contains offset and length of each dimension
 | ||||
| 		std::vector<int> multirange_dimensions; | ||||
|  | @ -285,6 +289,9 @@ namespace AST | |||
| 		int isConst() const; // return '1' for AST_CONSTANT and '2' for AST_REALVALUE
 | ||||
| 		double asReal(bool is_signed); | ||||
| 		RTLIL::Const realAsConst(int width); | ||||
| 
 | ||||
| 		// helpers for enum
 | ||||
| 		void allocateDefaultEnumValues(); | ||||
| 	}; | ||||
| 
 | ||||
| 	// process an AST tree (ast must point to an AST_DESIGN node) and generate RTLIL code
 | ||||
|  |  | |||
|  | @ -595,6 +595,9 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun | |||
| 
 | ||||
| 	switch (type) | ||||
| 	{ | ||||
| 	case AST_NONE: | ||||
| 		// unallocated enum, ignore
 | ||||
| 		break; | ||||
| 	case AST_CONSTANT: | ||||
| 		width_hint = max(width_hint, int(bits.size())); | ||||
| 		if (!is_signed) | ||||
|  | @ -612,7 +615,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun | |||
| 			id_ast = current_scope.at(str); | ||||
| 		if (!id_ast) | ||||
| 			log_file_error(filename, linenum, "Failed to resolve identifier %s for width detection!\n", str.c_str()); | ||||
| 		if (id_ast->type == AST_PARAMETER || id_ast->type == AST_LOCALPARAM) { | ||||
| 		if (id_ast->type == AST_PARAMETER || id_ast->type == AST_LOCALPARAM || id_ast->type == AST_ENUM_ITEM) { | ||||
| 			if (id_ast->children.size() > 1 && id_ast->children[1]->range_valid) { | ||||
| 				this_width = id_ast->children[1]->range_left - id_ast->children[1]->range_right + 1; | ||||
| 			} else | ||||
|  | @ -861,6 +864,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) | |||
| 	case AST_GENIF: | ||||
| 	case AST_GENCASE: | ||||
| 	case AST_PACKAGE: | ||||
| 	case AST_ENUM: | ||||
| 	case AST_MODPORT: | ||||
| 	case AST_MODPORTMEMBER: | ||||
| 	case AST_TYPEDEF: | ||||
|  | @ -1022,7 +1026,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) | |||
| 				else | ||||
| 					log_file_error(filename, linenum, "Identifier `%s' is implicitly declared and `default_nettype is set to none.\n", str.c_str()); | ||||
| 			} | ||||
| 			else if (id2ast->type == AST_PARAMETER || id2ast->type == AST_LOCALPARAM) { | ||||
| 			else if (id2ast->type == AST_PARAMETER || id2ast->type == AST_LOCALPARAM || id2ast->type == AST_ENUM_ITEM) { | ||||
| 				if (id2ast->children[0]->type != AST_CONSTANT) | ||||
| 					log_file_error(filename, linenum, "Parameter %s does not evaluate to constant value!\n", str.c_str()); | ||||
| 				chunk = RTLIL::Const(id2ast->children[0]->bits); | ||||
|  |  | |||
|  | @ -318,13 +318,13 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, | |||
| 	} | ||||
| 
 | ||||
| 	// activate const folding if this is anything that must be evaluated statically (ranges, parameters, attributes, etc.)
 | ||||
| 	if (type == AST_WIRE || type == AST_PARAMETER || type == AST_LOCALPARAM || type == AST_DEFPARAM || type == AST_PARASET || type == AST_RANGE || type == AST_PREFIX || type == AST_TYPEDEF) | ||||
| 	if (type == AST_WIRE || type == AST_PARAMETER || type == AST_LOCALPARAM || type == AST_ENUM_ITEM || type == AST_DEFPARAM || type == AST_PARASET || type == AST_RANGE || type == AST_PREFIX || type == AST_TYPEDEF) | ||||
| 		const_fold = true; | ||||
| 	if (type == AST_IDENTIFIER && current_scope.count(str) > 0 && (current_scope[str]->type == AST_PARAMETER || current_scope[str]->type == AST_LOCALPARAM)) | ||||
| 	if (type == AST_IDENTIFIER && current_scope.count(str) > 0 && (current_scope[str]->type == AST_PARAMETER || current_scope[str]->type == AST_LOCALPARAM || current_scope[str]->type == AST_ENUM_ITEM)) | ||||
| 		const_fold = true; | ||||
| 
 | ||||
| 	// in certain cases a function must be evaluated constant. this is what in_param controls.
 | ||||
| 	if (type == AST_PARAMETER || type == AST_LOCALPARAM || type == AST_DEFPARAM || type == AST_PARASET || type == AST_PREFIX) | ||||
| 	if (type == AST_PARAMETER || type == AST_LOCALPARAM || type == AST_LOCALPARAM || type == AST_ENUM_ITEM || type == AST_DEFPARAM || type == AST_PARASET || type == AST_PREFIX) | ||||
| 		in_param = true; | ||||
| 
 | ||||
| 	std::map<std::string, AstNode*> backup_scope; | ||||
|  | @ -405,12 +405,23 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, | |||
| 				} | ||||
| 				this_wire_scope[node->str] = node; | ||||
| 			} | ||||
| 			// these nodes appear at the top level in a module and can define names
 | ||||
| 			if (node->type == AST_PARAMETER || node->type == AST_LOCALPARAM || node->type == AST_WIRE || node->type == AST_AUTOWIRE || node->type == AST_GENVAR || | ||||
| 					node->type == AST_MEMORY || node->type == AST_FUNCTION || node->type == AST_TASK || node->type == AST_DPI_FUNCTION || node->type == AST_CELL || | ||||
| 					node->type == AST_TYPEDEF) { | ||||
| 				backup_scope[node->str] = current_scope[node->str]; | ||||
| 				current_scope[node->str] = node; | ||||
| 			} | ||||
| 			if (node->type == AST_ENUM) { | ||||
| 				for (auto enode : node->children) { | ||||
| 					log_assert(enode->type==AST_ENUM_ITEM); | ||||
| 					if (current_scope.count(enode->str) == 0) { | ||||
| 						current_scope[enode->str] = enode; | ||||
| 					} | ||||
| 					//	while (enode->simplify(true, false, false, 1, -1, false, true))
 | ||||
| 					//		did_something = true;
 | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		for (size_t i = 0; i < children.size(); i++) { | ||||
| 			AstNode *node = children[i]; | ||||
|  | @ -492,8 +503,21 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, | |||
| 		} | ||||
| 		break; | ||||
| 
 | ||||
| 	case AST_ENUM: | ||||
| 		// log("\nENUM %d child %d\n", basic_prep, children[0]->basic_prep);
 | ||||
| 		if (!basic_prep) { | ||||
| 			for (auto item_node : children) { | ||||
| 				while (!item_node->basic_prep && item_node->simplify(false, false, false, stage, -1, false, true) == true) | ||||
| 					did_something = true; | ||||
| 			} | ||||
| 			// allocate values (called more than once)
 | ||||
| 			allocateDefaultEnumValues(); | ||||
| 		} | ||||
| 		break; | ||||
| 
 | ||||
| 	case AST_PARAMETER: | ||||
| 	case AST_LOCALPARAM: | ||||
| 	case AST_ENUM_ITEM: | ||||
| 		while (!children[0]->basic_prep && children[0]->simplify(false, false, false, stage, -1, false, true) == true) | ||||
| 			did_something = true; | ||||
| 		children[0]->detectSignWidth(width_hint, sign_hint); | ||||
|  | @ -1005,7 +1029,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, | |||
| 	} | ||||
| 
 | ||||
| 	// trim/extend parameters
 | ||||
| 	if (type == AST_PARAMETER || type == AST_LOCALPARAM) { | ||||
| 	if (type == AST_PARAMETER || type == AST_LOCALPARAM || type == AST_ENUM_ITEM) { | ||||
| 		if (children.size() > 1 && children[1]->type == AST_RANGE) { | ||||
| 			if (!children[1]->range_valid) | ||||
| 				log_file_error(filename, linenum, "Non-constant width range on parameter decl.\n"); | ||||
|  | @ -1046,10 +1070,32 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, | |||
| 	if (type == AST_IDENTIFIER) { | ||||
| 		if (current_scope.count(str) == 0) { | ||||
| 			for (auto node : current_ast_mod->children) { | ||||
| 				if ((node->type == AST_PARAMETER || node->type == AST_LOCALPARAM || node->type == AST_WIRE || node->type == AST_AUTOWIRE || node->type == AST_GENVAR || | ||||
| 						node->type == AST_MEMORY || node->type == AST_FUNCTION || node->type == AST_TASK || node->type == AST_DPI_FUNCTION) && str == node->str) { | ||||
| 				//log("looking at mod scope child %s\n", type2str(node->type).c_str());
 | ||||
| 				switch (node->type) { | ||||
| 				case AST_PARAMETER: | ||||
| 				case AST_LOCALPARAM: | ||||
| 				case AST_WIRE: | ||||
| 				case AST_AUTOWIRE: | ||||
| 				case AST_GENVAR: | ||||
| 				case AST_MEMORY: | ||||
| 				case AST_FUNCTION: | ||||
| 				case AST_TASK: | ||||
| 				case AST_DPI_FUNCTION: | ||||
| 					//log("found child %s, %s\n", type2str(node->type).c_str(), node->str.c_str());
 | ||||
| 					log("add %s, type %s to scope\n", str.c_str(), type2str(node->type).c_str()); | ||||
| 					current_scope[node->str] = node; | ||||
| 					break; | ||||
| 				case AST_ENUM: | ||||
| 					for (auto enum_node : node->children) { | ||||
| 						log_assert(enum_node->type==AST_ENUM_ITEM); | ||||
| 						if (str == enum_node->str) { | ||||
| 							log("\nadding enum %s to scope\n", str.c_str()); | ||||
| 							current_scope[str] = enum_node; | ||||
| 						} | ||||
| 					} | ||||
| 					break; | ||||
| 				default: | ||||
| 					break; | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
|  | @ -2482,7 +2528,7 @@ skip_dynamic_range_lvalue_expansion:; | |||
| 		} | ||||
| 
 | ||||
| 		for (auto child : decl->children) | ||||
| 			if (child->type == AST_WIRE || child->type == AST_MEMORY || child->type == AST_PARAMETER || child->type == AST_LOCALPARAM) | ||||
| 			if (child->type == AST_WIRE || child->type == AST_MEMORY || child->type == AST_PARAMETER || child->type == AST_LOCALPARAM || child->type == AST_ENUM_ITEM) | ||||
| 			{ | ||||
| 				AstNode *wire = nullptr; | ||||
| 
 | ||||
|  | @ -2588,7 +2634,7 @@ replace_fcall_later:; | |||
| 		switch (type) | ||||
| 		{ | ||||
| 		case AST_IDENTIFIER: | ||||
| 			if (current_scope.count(str) > 0 && (current_scope[str]->type == AST_PARAMETER || current_scope[str]->type == AST_LOCALPARAM)) { | ||||
| 			if (current_scope.count(str) > 0 && (current_scope[str]->type == AST_PARAMETER || current_scope[str]->type == AST_LOCALPARAM || current_scope[str]->type == AST_ENUM_ITEM)) { | ||||
| 				if (current_scope[str]->children[0]->type == AST_CONSTANT) { | ||||
| 					if (children.size() != 0 && children[0]->type == AST_RANGE && children[0]->range_valid) { | ||||
| 						std::vector<RTLIL::State> data; | ||||
|  | @ -3025,7 +3071,7 @@ void AstNode::expand_genblock(std::string index_var, std::string prefix, std::ma | |||
| 	for (size_t i = 0; i < children.size(); i++) { | ||||
| 		AstNode *child = children[i]; | ||||
| 		if (child->type == AST_WIRE || child->type == AST_MEMORY || child->type == AST_PARAMETER || child->type == AST_LOCALPARAM || | ||||
| 				child->type == AST_FUNCTION || child->type == AST_TASK || child->type == AST_CELL || child->type == AST_TYPEDEF) { | ||||
| 				child->type == AST_FUNCTION || child->type == AST_TASK || child->type == AST_CELL || child->type == AST_TYPEDEF || child->type == AST_ENUM_ITEM) { | ||||
| 			if (backup_name_map.size() == 0) | ||||
| 				backup_name_map = name_map; | ||||
| 			std::string new_name = prefix[0] == '\\' ? prefix.substr(1) : prefix; | ||||
|  | @ -3782,4 +3828,31 @@ AstNode *AstNode::eval_const_function(AstNode *fcall) | |||
| 	return AstNode::mkconst_bits(variables.at(str).val.bits, variables.at(str).is_signed); | ||||
| } | ||||
| 
 | ||||
| void AstNode::allocateDefaultEnumValues() | ||||
| { | ||||
| 	log_assert(type==AST_ENUM); | ||||
| 	int last_enum_int = -1; | ||||
| 	for (auto node : children) { | ||||
| 		log_assert(node->type==AST_ENUM_ITEM); | ||||
| 		for (size_t i = 0; i < node->children.size(); i++) { | ||||
| 			switch (node->children[i]->type) { | ||||
| 			case AST_NONE: | ||||
| 				// replace with auto-incremented constant
 | ||||
| 				delete node->children[i]; | ||||
| 				node->children[i] = AstNode::mkconst_int(++last_enum_int, true); | ||||
| 				break; | ||||
| 			case AST_CONSTANT: | ||||
| 				// explicit constant (or folded expression)
 | ||||
| 				// TODO: can't extend 'x or 'z item
 | ||||
| 				last_enum_int = node->children[i]->integer; | ||||
| 				break; | ||||
| 			default: | ||||
| 				// ignore ranges
 | ||||
| 				break; | ||||
| 			} | ||||
| 			// TODO: range check
 | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| YOSYS_NAMESPACE_END | ||||
|  |  | |||
|  | @ -108,6 +108,20 @@ struct specify_rise_fall { | |||
| 	specify_triple fall; | ||||
| }; | ||||
| 
 | ||||
| static AstNode *makeRange(int msb = 31, int lsb = 0, bool isSigned = true) | ||||
| { | ||||
| 	auto range = new AstNode(AST_RANGE); | ||||
| 	range->children.push_back(AstNode::mkconst_int(msb, true)); | ||||
| 	range->children.push_back(AstNode::mkconst_int(lsb, true)); | ||||
| 	range->is_signed = isSigned; | ||||
| 	return range; | ||||
| } | ||||
| 
 | ||||
| static void addRange(AstNode *parent, int msb = 31, int lsb = 0, bool isSigned = true) | ||||
| { | ||||
| 	auto range = makeRange(msb, lsb, isSigned); | ||||
| 	parent->children.push_back(range); | ||||
| } | ||||
| %} | ||||
| 
 | ||||
| %define api.prefix {frontend_verilog_yy} | ||||
|  | @ -157,6 +171,7 @@ struct specify_rise_fall { | |||
| %type <ast> range range_or_multirange  non_opt_range non_opt_multirange range_or_signed_int | ||||
| %type <ast> wire_type expr basic_expr concat_list rvalue lvalue lvalue_concat_list | ||||
| %type <string> opt_label opt_sva_label tok_prim_wrapper hierarchical_id hierarchical_type_id | ||||
| %type <ast> opt_enum_init | ||||
| %type <boolean> opt_signed opt_property unique_case_attr always_comb_or_latch always_or_always_ff | ||||
| %type <al> attr case_attr | ||||
| 
 | ||||
|  | @ -428,7 +443,9 @@ package: | |||
| 	}; | ||||
| 
 | ||||
| package_body: | ||||
| 	package_body package_body_stmt |; | ||||
| 	package_body package_body_stmt | ||||
| 	| // optional | ||||
| 	; | ||||
| 
 | ||||
| package_body_stmt: | ||||
| 	typedef_decl | | ||||
|  | @ -604,6 +621,7 @@ module_body: | |||
| 
 | ||||
| module_body_stmt: | ||||
| 	task_func_decl | specify_block | param_decl | localparam_decl | typedef_decl | defparam_decl | specparam_declaration | wire_decl | assign_stmt | cell_stmt | | ||||
| 	enum_decl | | ||||
| 	always_stmt | TOK_GENERATE module_gen_body TOK_ENDGENERATE | defattr | assert_property | checker_decl | ignored_specify_block; | ||||
| 
 | ||||
| checker_decl: | ||||
|  | @ -1224,6 +1242,77 @@ single_defparam_decl: | |||
| 		ast_stack.back()->children.push_back(node); | ||||
| 	}; | ||||
| 
 | ||||
| enum_type: TOK_ENUM { | ||||
| 		// create parent node for the enum | ||||
| 		astbuf2 = new AstNode(AST_ENUM); | ||||
| 		ast_stack.back()->children.push_back(astbuf2); | ||||
| 		// create the template for the names | ||||
| 		astbuf1 = new AstNode(AST_ENUM_ITEM); | ||||
| 		astbuf1->children.push_back(AstNode::mkconst_int(0, true)); | ||||
| 	 } param_signed enum_base_type '{' enum_name_list '}' {  // create template for the enum vars | ||||
| 								auto tnode = astbuf1->clone(); | ||||
| 								delete astbuf1; | ||||
| 								astbuf1 = tnode; | ||||
| 								tnode->type = AST_WIRE; | ||||
| 								// drop constant but keep any range | ||||
| 								delete tnode->children[0]; | ||||
| 								tnode->children.erase(tnode->children.begin()); } | ||||
| 	 ; | ||||
| 
 | ||||
| enum_base_type: int_vec param_range | ||||
| 	| int_atom | ||||
| 	| /* nothing */		{ addRange(astbuf1); } | ||||
| 	; | ||||
| 
 | ||||
| int_atom: TOK_INTEGER		{ addRange(astbuf1); }		// probably should do byte, range [7:0] here | ||||
| 	; | ||||
| 
 | ||||
| int_vec: TOK_REG		{ astbuf1->is_reg = true; }	// lexer returns this for logic|bit too | ||||
| 	; | ||||
| 
 | ||||
| enum_name_list: | ||||
| 	enum_name_decl | ||||
| 	| enum_name_list ',' enum_name_decl | ||||
| 	; | ||||
| 
 | ||||
| enum_name_decl: | ||||
| 	TOK_ID opt_enum_init { | ||||
| 		// put in fn | ||||
| 		log_assert(astbuf1); | ||||
| 		log_assert(astbuf2); | ||||
| 		auto node = astbuf1->clone(); | ||||
| 		node->str = *$1; | ||||
| 		delete $1; | ||||
| 		delete node->children[0]; | ||||
| 		node->children[0] = $2 ?: new AstNode(AST_NONE); | ||||
| 		astbuf2->children.push_back(node); | ||||
| 	} | ||||
| 	; | ||||
| 
 | ||||
| opt_enum_init: | ||||
| 	'=' basic_expr		{ $$ = $2; }	// TODO: restrict this | ||||
| 	| /* optional */	{ $$ = NULL; } | ||||
| 	; | ||||
| 
 | ||||
| enum_var_list: | ||||
| 	enum_var | ||||
| 	| enum_var_list ',' enum_var | ||||
| 	; | ||||
| 
 | ||||
| enum_var: TOK_ID { | ||||
| 		log_assert(astbuf1); | ||||
| 		log_assert(astbuf2); | ||||
| 		auto node = astbuf1->clone(); | ||||
| 		ast_stack.back()->children.push_back(node); | ||||
| 		node->str = *$1; | ||||
| 		delete $1; | ||||
| 		node->is_enum = true; | ||||
| 	} | ||||
| 	; | ||||
| 
 | ||||
| enum_decl: enum_type enum_var_list ';'			{ delete astbuf1; } | ||||
| 	; | ||||
| 
 | ||||
| wire_decl: | ||||
| 	attr wire_type range { | ||||
| 		albuf = $1; | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue