public Ast.Resource Parse(TextReader input) { var ps = new FtlParserStream(input); ps.SkipBlankLines(); var entries = new List <Ast.Entry>(); Ast.Comment lastComment = null; while (ps.Current != Eof) { var entry = GetEntryOrJunk(ps); int blankLines = ps.SkipBlankLines(); // Regular Comments require special logic. Comments may be attached to // Messages or Terms if they are followed immediately by them. However // they should parse as standalone when they're followed by Junk. // Consequently, we only attach Comments once we know that the Message // or the Term parsed successfully. if (entry is Ast.Comment comment && blankLines == 0 && ps.Current != Eof) { // Stash the comment and decide what to do with it in the next pass. lastComment = comment; continue; } if (lastComment != null) { if (entry is Ast.MessageTermBase mt) { mt.Comment = lastComment; if (_withSpans) { mt.Span.Start = lastComment.Span.Start; } } else { entries.Add(lastComment); } // In either case, the stashed comment has been dealt with; clear it. lastComment = null; } // No special logic for other types of entries. entries.Add(entry); } var res = new Ast.Resource(entries); if (_withSpans) { res.AddSpan(Position.Start, ps.GetPosition()); } return(res); }
public void Serialize(TextWriter writer, Ast.Resource resource) { if (resource == null) { throw new ArgumentNullException(nameof(resource)); } if (writer == null) { throw new ArgumentNullException(nameof(resource)); } State state = 0; var indentingWriter = new IndentingWriter(writer); foreach (var entry in resource.Body) { if (this._withJunk || !(entry is Ast.Junk)) { SerializeEntry(indentingWriter, entry, state); state |= State.HasEntries; } } }