static WoodyRule getWoodyRuleFromSource(L<S> lines, int lineIndex) {
  S s = lines.get(lineIndex);
  Explain e = explain(s, ctxParsingRules(), "rule");
  if (e == null)
    fail("Bad rule: " + s);
    
  new WoodyRule rule;
  rule.nameInFile = e.sub(0).string();
  rule.lineInFile = lineIndex;
  rule.condition = e.sub(1).string();
  pcall {
    rule.parsedCondition = ctxParseExpression(e.sub(1));
  }
  
  int opening = indexOfTrim(lines, "{", lineIndex);
  if (opening >= 0) {
    for (int i = lineIndex+1; i < opening; i++) {
      L<S> tok2 = javaTok(lines.get(i));
      if (eqGet(tok2, 1, "[") && eqGet(tok2, 5, "]")
        && eqGet(tok2, 7, "="))
        rule.properties.put(tok2.get(3), unquoteCtx(trim(join(subList(tok2, 9)))));
    }
    int closing = indexOfClosingCurlyBracket(lines, opening);
    rule.body = lines(subList(lines, opening+1, closing));
  }
  ret rule;
}