Group of options but allow only one?

Oct 3, 2012 at 11:55 PM

How can I parse a group of items that can be in any order, but only allow 1 of each rule?

With the sql grammar in code for create table there is the 'null/not null' rule. A new rule for something such as 'character set utf8' would be easy to add if it always comes either before or after the 'null/not null' rule. How to handle when it can be in either place though?

 

I tried modifying the sql grammar code slightly like this:

 

createTableStmt.Rule = CREATE + TABLE + Id + "(" + fieldDefList + ")" + constraintListOpt;

fieldDefList.Rule = MakePlusRule(fieldDefList, comma, fieldDef);
fieldDef.Rule = Id + typeName + typeParamsOpt + typeOptList;
typeParamsOpt.Rule = "(" + number + ")" | "(" + number + comma + number + ")" | Empty;

typeOptList.Rule = MakeStarRule(typeOptList, typeOpt);
typeOpt.Rule = nullSpecOpt | charsetOpt;
nullSpecOpt.Rule = NULL | NOT + NULL;
charsetOpt.Rule = CHARACTER + SET + charset_type;

 

But then something like this parses successfully allowing two nullSpecOpt rules:

 

create table test (
 charCol VARCHAR(10) NOT NULL CHARACTER SET utf8 NOT NULL
)

 

What is the right way to only allow 1 of each rule in a list and in any order?

Oct 4, 2012 at 4:32 AM

I would use a simple Set object, in AstNode creation, rather than trying too use the parser explicitly. This is a semantic check more than a gramatical one.

That said, there are circumstances where checks like this are easily done in the parser; but twisting the parser around to achieve this just complicates matters unnecessarily, in my experience.

Coordinator
Oct 4, 2012 at 5:19 PM

yes, things like these - items in any order but 0 or 1 of any kind - these should be handled after parsing, not expressed in grammar rules. In fact, it known that context-free grammars and BNF are really bad at describing/handling "counting". The easiest way to handle this is to define the grammar rule as "list of items" without restriction on repetition, then hook to Reduced event, and verify in code that there's no more than one instance of any item type in the list

Roman

Oct 18, 2012 at 11:56 PM

I found I could identify duplicates in the Reduced event. There's probably a better way but this works:

var something = new NonTerminal("something");
something.Reduced += something_Reduced;

 

void something_Reduced(object sender, ReducedEventArgs e)
{
	List<string> items = new List<string>();
	foreach(var child in e.ResultNode.ChildNodes) {
		if(items.Contains(child.ToString()))
			e.Context.AddParserError("Duplicate option " + ((child.Term is NonTerminal && !String.IsNullOrEmpty(child.Term.ErrorAlias)) ? child.Term.ErrorAlias : child.ToString());
		else
			items.Add(child.ToString());
	}
}