I want to eliminate "Unnamed0"

Aug 13, 2012 at 10:43 PM

I have this tree output which contains an unexpected "Unnamed0" node. The unwanted node shows when the optional node (named Metric) following it is used:

Program
  Row
    Action
      pressure (Keyword)
      50 (Action.Value)
      % (Key symbol)
    Telemetry
      velocity (Keyword)
      >= (Key symbol)
      50 (Telemetry.Value)
  Row
    Action
      velocity (Keyword)
      40.2 (Action.Value)
    Telemetry
      distance (Keyword)
      >= (Key symbol)
      Unnamed0
        50 (Telemetry.Value)
      m (Keyword)

Here is some of my code for creating the rules. In this code (I believe) it's the NumberLiteral that doesn't work correctly:

private BnfExpression CreateTerminal(IEnumerable<TelemetryDefinitionModel> telemetryDefinitions, bool isAction = false)
{
	var um = ServiceRegistry.Get<UnitsManager>();
	BnfExpression combined = null;
	var actionOp = ToTerm("at");
	var prefix = isAction ? "Action" : "Telemetry";
	var smallOp = new NonTerminal(prefix + ".Operator");
	smallOp.Rule = ToTerm("==") | "!=";
	var bigOp = new NonTerminal(prefix + ".Operator"); 
	bigOp.Rule = smallOp | "<" | "<=" | ">" | ">=";
	MarkTransient(smallOp, bigOp);
	foreach (var definition in telemetryDefinitions)
	{
		NonTerminal metricOp = null;
		if(definition.Metric != PhysicalType.Unitless && um != null)
		{
			metricOp = new NonTerminal(prefix + ".Metric");
			var pt = um.GetCurrentUnit(definition.Metric);
			metricOp.Rule = ToTerm(pt.Abbreviations.First());
			foreach(var a in pt.Abbreviations.Skip(1))
				metricOp.Rule |= ToTerm(a);
			MarkTransient(metricOp);
		}

		var t = new NonTerminal(prefix);
		var action = ToTerm("[") + definition.Name + ToTerm("]");
		if(definition.Type == TelemetryType.String)
		{
			var op = isAction ? actionOp : (BnfTerm)smallOp;
			var tail = new StringLiteral(prefix + ".Value", "\"", StringOptions.AllowsDoubledQuote);
			t.Rule = action + op + tail;
		}
		else if(definition.Type == TelemetryType.Real)
		{
			var op = isAction ? actionOp : (BnfTerm)bigOp;
			var tail = new NumberLiteral(prefix + ".Value", NumberOptions.AllowStartEndDot);
			t.Rule = action + op + tail;
		}
		else throw new NotImplementedException("definition.Type is out of range");

		if (metricOp != null)
		{
			var optionalMetric = new NonTerminal("OptionalMetric");
			optionalMetric.Rule = metricOp | Empty;
			t.Rule += optionalMetric;
			MarkTransient(optionalMetric);
		}

		if (combined == null) combined = t;
		else combined |= t;
	}
	return combined;
}

Any help would be appreciated.

Coordinator
Aug 14, 2012 at 2:08 AM
Edited Aug 14, 2012 at 2:11 AM

I suspect it's coming from this operator:

 t.Rule += optionalMetric;

I don't see anywhere else it might be created. It is an "automatic" non-terminal Irony generates from expressions.

Set a breakpoint, inspect the Rule value after this assignment

If you want to "append" an element to the rule, you'd have to go inside it - it is an OR list of "+" lists; so you have to get the last (the only?) OR list, and append the element to it. Roman

Coordinator
Aug 14, 2012 at 2:33 AM

ah, found one more place where it can be:

metricOp.Rule |= ToTerm(a);

Aug 14, 2012 at 3:40 PM

So you're saying that "t.Rule = a + b + c" is not the same as "t.Rule = a + b; t.Rule += c" ? That seems strange. Surely the "+" operator is evaluated from left to right. I guess you're implying that these things are adjusted inside the getter/setter for t.Rule?

I removed the "|= ToTerm(a)". That made no difference in behavior. I also rearranged the code so that optionalMetric is created at the top. It's then appended inside the "if/else if" statement as part of t.Rule. Unfortunately, that made no improvement either.

Aug 14, 2012 at 4:00 PM
Edited Aug 14, 2012 at 4:01 PM

I tracked down the problem. Alas, I actually cut out the line of code that had the problem when I posted the code above. I had a third "else if":

else if (definition.Type == TelemetryType.Integer)
{
	var op = isAction ? actionOp : (BnfTerm)bigOp;
	BnfExpression tail = new NumberLiteral(prefix + ".Value2", NumberOptions.IntOnly);
	if (definition.Enumerations != null && definition.Enumerations.Length > 0)
	{
	    tail = definition.Enumerations.Aggregate(tail, (current, e) => current | ToTerm(e));
	}
	t.Rule = action + op + tail + optionalMetric;
}

Unfortunately, the problem occurs in the use of the "BnfExpression" type. If I change that to "var" like I had in my other two cases (and ditch the inner "if") the problem goes away. That seems very strange. Of course I can't change it to var because then I can't use it in the Aggregate. I suppose I will build my aggregate separately. It would be nice if the pipe operator supported null on either side.

Aug 14, 2012 at 4:06 PM
Edited Aug 14, 2012 at 4:16 PM

It works when constructed like this:

 

else if (definition.Type == TelemetryType.Integer)
{
	var op = isAction ? actionOp : (BnfTerm)bigOp;
	var tailRule = new NonTerminal("Tail");
	MarkTransient(tailRule);
	tailRule.Rule = new NumberLiteral(prefix + ".Value", NumberOptions.IntOnly);
	if (definition.Enumerations != null && definition.Enumerations.Length > 0)
	{
		tailRule.Rule = definition.Enumerations.Aggregate(tailRule.Rule, (current, e) => current | ToTerm(e));
	}
	t.Rule = action + op + tailRule + optionalMetric;
}

 

Aug 14, 2012 at 8:41 PM
brantheman wrote:

So you're saying that "t.Rule = a + b + c" is not the same as "t.Rule = a + b; t.Rule += c" ? That seems strange.

Yes, it has different behaviour. As workaround I used several overloading for rule construction function for 2-3-4 parameters, but later I had changed visibility of BnfExpression.Data field and worked directly with it.

Thanks for MarkTransient idea.