Question

the following works just fine for what I'm trying to do, but it's obviously very repetitive. It should match the following examples:

  • #id.class1.class2 attr="asdsa"
  • .class1.class2 attr="asdsad"
  • attr="asds"

It's tempting to use

id:idShortcut? classes:classShortcut* attrs:fullAttribute* 

But I don't want it to match if all three components are absent. How do I specify a rule of 3 optional components, but at least one must be present?

attributes = id:idShortcut classes:classShortcut* attrs:fullAttribute* 
{ 
  var ret = [['id', id]];
  for(var i = 0; i < classes.length; ++i ) {
    ret.push(['class', classes[i]]);
  }

  for(var i = 0; i < attrs.length; ++i ) {
    ret.push(attrs[i]);
  }

  return ret;
}
/ classes:classShortcut+ attrs:fullAttribute* { 

  // TODO: how to dry this with the above?

  var ret = [];
  for(var i = 0; i < classes.length; ++i ) {
    ret.push(['class', classes[i]]);
  }

  for(var i = 0; i < attrs.length; ++i ) {
    ret.push(attrs[i]);
  }

  return ret;
}
/ attrs:fullAttribute+ { 
  var ret = [];
  for(var i = 0; i < attrs.length; ++i ) {
    ret.push(attrs[i]);
  }
  return ret;
}
Was it helpful?

Solution 2

OK, not sure why this was even a struggle for me, but this refactored nicely as follows (and could probably be improved beyond that:

attributes 
  = a:( attributesAtLeastID
    / attributesAtLeastClass
    / attributesAtFullAttr ) 
{
  var id = a[0];
  var classes = a[1];
  var attrs = a[2];
  var ret = [];

  if(id) { ret.push(['id', id]); }

  for(var i = 0; i < classes.length; ++i ) {
    ret.push(['class', classes[i]]);
  }

  for(var i = 0; i < attrs.length; ++i ) {
    ret.push(attrs[i]);
  }
}

attributesAtLeastID = id:idShortcut classes:classShortcut* attrs:fullAttribute* { return [id, classes, attrs]; }
attributesAtLeastClass = classes:classShortcut+ attrs:fullAttribute* { return [null, classes, attrs]; }
attributesAtFullAttr = attrs:fullAttribute+ { return [null, [], attrs]; }

OTHER TIPS

This is what And predicates are useful for

Attributes only matches if either id is not undefined, or classes is not an empty array, or attrs is not an empty array.

attributes = 
    id:idShortcut? classes:classShortcut* attrs:fullAttribute* &{return id !== undefined || classes.length > 0 || attrs.length > 0;} 
    {
        var ret = [];
        if (id) {
            ret.push(['id', id]);
        }
        for (var x=0; x < classes.length;x++) {
            ret.push(['class', classes[x]);
        }
        for (var y=0; y < attrs.length;y++) {
            ret.push([attrs[y]);
        }
        return ret;
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top