private static IEnumerable<ParsedThing> parseFile(string path) { string[] lines = File.ReadAllLines(path); for (int i = 0; i < lines.Length; i++) { string line; if (isLineBlank(line = lines[i].Trim())) continue; Action throwParseError = delegate() { throw new GurpenatorException("syntax problem (" + path + ":" + (i + 1) + ")"); }; Match match = null; // hack for local recursion (for DRY) Func<ParsedThing> parseThing = null; parseThing = delegate() { string name = match.Groups[1].Value.Trim(); string subPropertyName = match.Groups[2].Success ? match.Groups[2].Value.Trim() : null; string declarationOperator = match.Groups[3].Value; string formula = match.Groups[4].Value.Trim(); string comment = match.Groups[5].Success ? match.Groups[5].Value.Trim() : null; ParsedThing thing = new ParsedThing(name, subPropertyName, declarationOperator, formula, comment, path, i + 1); if (reservedWords.Contains(name)) throw thing.createError("name '" + name + "' is reserved"); bool hasOpenBrace = match.Groups[6].Success; if (!hasOpenBrace) return thing; i++; int firstLineIndex = i; for (; i < lines.Length; i++) { if (isLineBlank(line = lines[i].Trim())) continue; if (line == "}") return thing; if (i == firstLineIndex && (match = commentRegex.Match(line)).Success) { // the first line can be the comment if (thing.comment != null) throwParseError(); // two comments? thing.comment = match.Groups[1].Value; continue; } if ((match = thingStartLineRegex.Match(line)).Success) { thing.subThings.Add(parseThing()); continue; } throwParseError(); } throw thing.createError("syntax problem. '{' missing '}'"); }; if ((match = thingStartLineRegex.Match(line)).Success) { yield return parseThing(); continue; } throwParseError(); } }
private static GurpsProperty createParsedThing(ParsedThing parsedThing) { if (parsedThing.subPropertyName != null) throw parsedThing.createError("No '.' allowed in names"); switch (parsedThing.declarationOperator) { case ":": if (skillFormulaRegex.IsMatch(parsedThing.formula)) { SkillDifficulty difficulty = AbstractSkill.difficultyFromChar(parsedThing.formula[parsedThing.formula.Length - 1]); Formula formula = FormulaParser.parseFormula(parsedThing.formula.Remove(parsedThing.formula.Length - 1), parsedThing); return new Skill(parsedThing, difficulty, formula); } else { Formula costFormula = FormulaParser.parseFormula(parsedThing.formula, parsedThing); if (costFormula.usedNames().Contains("level")) return new IntAdvantage(parsedThing, costFormula); else return new BooleanAdvantage(parsedThing, costFormula); } case ":=": { SkillDifficulty difficulty = SkillDifficulty.Unspecified; string formulaText = parsedThing.formula; if (skillFormulaRegex.IsMatch(parsedThing.formula)) { difficulty = AbstractSkill.difficultyFromChar(parsedThing.formula[parsedThing.formula.Length - 1]); formulaText = parsedThing.formula.Remove(parsedThing.formula.Length - 1); } Formula formula = FormulaParser.parseFormula(formulaText, parsedThing); if (!(formula is Formula.Identifier)) throw parsedThing.createError("expected name of skill. got '" + formulaText + "'"); return new InheritedSkill(parsedThing, difficulty, ((Formula.Identifier)formula).token); } case "=": { Formula formula = FormulaParser.parseFormula(parsedThing.formula, parsedThing); return new AttributeFunction(parsedThing, formula); } default: throw parsedThing.createError("expected ':', '=', or ':='. Got '" + parsedThing.declarationOperator + "'"); } }