/// <summary> /// Will check that all rule conditions have been met. /// Will also remove the rules if they are done. /// </summary> /// <param name="line"></param> /// <returns></returns> private bool CheckUnfinishedRule(LineInfo line) { if (_unfinishedRule != null) { // rule is done, remove it. // we can exit extra lines may have rules that are done too. if (!_unfinishedRule.IsMultiLine(line, false)) _unfinishedRule = null; else return true; } bool res = false; foreach (LineInfo subLine in _extraLines) { if (subLine.CheckUnfinishedRule(line)) res = true; } return res; }
/// <summary> /// Read next line from file /// </summary> /// <returns>true if line could be read; false if EOF.</returns> protected bool ReadLine() { string line = _reader.ReadLine(); string trimmedLine = (line != null) ? line.Trim(' ', '\t') : string.Empty; while (line != null && (trimmedLine == string.Empty || (trimmedLine.Length > 0 && trimmedLine[0] == '-' && trimmedLine[1] == '/'))) { ++_lineNo; line = _reader.ReadLine(); trimmedLine = (line != null) ? line.Trim(new char[] { ' ', '\t' }) : string.Empty; } if (line == null) return false; ++_lineNo; _prevLine = _currentLine; _currentLine = new LineInfo(_lineNo, line); return true; }
/// <summary> /// Append another line /// </summary> /// <param name="line"></param> public void Append(LineInfo line) { SetParsedData(_data + line.Data); _extraLines.AddLast(line); if (CheckUnfinishedRule(this)) _appendNextLine = true; else _appendNextLine = false; }
/// <summary> /// check if current line is a multi line /// </summary> /// <param name="prevLine">previous line</param> /// <param name="line">current line</param> protected void CheckMultiLine(LineInfo prevLine, LineInfo line) { if (prevLine != null && prevLine.UnfinishedRule != null) { if (prevLine.UnfinishedRule.IsMultiLine(line, true)) { _logger.Trace(line.LineNumber + ": " + prevLine.UnfinishedRule.GetType().Name + " says that the next line should be appended."); line.AppendNextLine = true; return; } } foreach (Rule rule in _rules) { if (rule.IsMultiLine(line, false)) { _logger.Trace(line.LineNumber + ": " + rule.GetType().Name + " says that the next line should be appended."); line.AppendNextLine = true; } } }
/// <summary> /// PreParse goes through the text add handles indentation /// and all multi line cases. /// </summary> /// <param name="reader">Reader containing the text</param> protected void PreParse(TextReader reader) { if (reader == null) throw new ArgumentNullException("reader"); // Read first line to be able to assign it to the mother. if (!ReadLine()) throw new CodeGeneratorException(1, "No data."); if (_currentLine.Intendation != 0) throw new CodeGeneratorException(1, "Invalid indentation, should be 0."); _currentLine.Parent = _mother; CheckIntendation(_currentLine); CheckMultiLine(_prevLine, _currentLine); while (ReadLine()) { if (_currentLine.UnparsedData.Length == 0) continue; CheckIntendation(_currentLine); CheckMultiLine(_prevLine, _currentLine); if (_prevLine.AppendNextLine) { _prevLine.Append(_currentLine); if (_currentLine.AppendNextLine) _prevLine.AppendNextLine = true; _currentLine = _prevLine; continue; } HandlePlacement(); } }
/// <summary> /// Parse a node /// todo: improve doc /// </summary> /// <param name="theLine"></param> /// <param name="prototypes"></param> /// <param name="parent"></param> /// <param name="textNode"></param> protected static void ParseNode(LineInfo theLine, NodeList prototypes, Node parent, TextNode textNode) { Node curNode = null; int offset = 0; // parse each part of a line while (offset <= theLine.Data.Length - 1) { Node node = prototypes.GetPrototype(GetWord(theLine.Data, offset), curNode == null) ?? textNode; node = node.Parse(prototypes, curNode, theLine, ref offset); // first node on line, set it as current if (curNode == null) { curNode = node; curNode.LineInfo = theLine; parent.Children.AddLast(node); } else curNode.AddModifier(node); // append attributes etc. } foreach (LineInfo child in theLine.Children) ParseNode(child, prototypes, curNode, textNode); }
/// <summary> /// Check indentation /// </summary> /// <param name="line">fills line with intend info</param> protected static void CheckIntendation(LineInfo line) { int ws, intendation; CheckIntendation(line, out ws, out intendation); if (ws == -1) throw new CodeGeneratorException(line.LineNumber, line.Data, "Failed to find indentation on line #" + line.LineNumber); line.Set(ws, intendation); }
/// <summary> /// Check and validate indentation /// </summary> /// <param name="line">line to check</param> /// <param name="ws">number of white spaces</param> /// <param name="intendation">number of indentations (2 white spaces = 1 intend, 1 tab = 1 intend)</param> protected static void CheckIntendation(LineInfo line, out int ws, out int intendation) { intendation = 0; ws = -1; char prevUnusedCh = line.UnparsedData[0]; if (prevUnusedCh == '\t') { ++intendation; prevUnusedCh = char.MinValue; } else if (prevUnusedCh != ' ') { ws = 0; return; } for (int i = 1; i < line.UnparsedData.Length; ++i) { char ch = line.UnparsedData[i]; if (ch == ' ') { if (prevUnusedCh == '\t') { ++intendation; prevUnusedCh = ' '; continue; } if (prevUnusedCh == ' ') { prevUnusedCh = char.MinValue; ++intendation; continue; } prevUnusedCh = ' '; } else if (ch == '\t') { if (prevUnusedCh == ' ') throw new CodeGeneratorException(line.LineNumber, line.Data, "Invalid intendation sequence: One space + one tab. Should either be one tab or two spaces."); if (prevUnusedCh == char.MinValue) { ++intendation; prevUnusedCh = char.MinValue; continue; } } else { if (prevUnusedCh != char.MinValue) throw new CodeGeneratorException(line.LineNumber, line.Data, "Invalid intendation at char " + i + ", expected a space."); if (i == 1 && !char.IsWhiteSpace(line.UnparsedData[0])) ws = 0; else ws = i; return; } } }
/// <summary> /// Print line information to the console /// </summary> /// <param name="line"></param> public void PrintNode(LineInfo line) { _logger.Debug(Spaces(line.Intendation) + line.Data); foreach (LineInfo info in line.Children) PrintNode(info); }
/// <summary> /// Parse a file and convert into to our own template object code. /// </summary> /// <param name="reader">A <see cref="TextReader"/> containing our template</param> /// <exception cref="CodeGeneratorException">If something is incorrect in the template.</exception> public void Parse(TextReader reader) { _lineNo = -1; _reader = reader; _mother = new LineInfo(-1, string.Empty); _prevLine = null; _currentLine = null; PreParse(reader); NodeList prototypes = new NodeList(); prototypes.Add(new AttributeNode(null)); prototypes.Add(new TagNode(null)); prototypes.Add(new IdNode(null)); prototypes.Add(new SilentCodeNode(null)); prototypes.Add(new ClassNode(null)); prototypes.Add(new DisplayCodeNode(null)); prototypes.Add(new DocTypeTag(null, null)); prototypes.Add(new PartialNode(null)); TextNode textNode = new TextNode(null, "prototype"); _parentNode = new TextNode(null, string.Empty); foreach (LineInfo info in _mother.Children) ParseNode(info, prototypes, _parentNode, textNode); }