Ast.StringLiteral _GetString(FtlParserStream ps) { var val = new StringBuilder(); ps.ExpectChar('"'); int ch; while ((ch = ps.TakeChar(x => x != '"' && x != '\r' && x != '\n')) != Eof) { if (ch == '\\') { GetEscapeSequence(ps, new int[] { '{', '\\', '"' }, val); } else { val.Append((char)ch); } } if (ps.CurrentIs('\r') || ps.CurrentIs('\n')) { throw new ParseException("E0020"); } ps.Next(); return(new Ast.StringLiteral(val.ToString())); }
Ast.Variant _GetVariant(FtlParserStream ps, bool hasDefault) { bool defaultIndex = false; if (ps.CurrentIs('*')) { if (hasDefault) { throw new ParseException("E0015"); } ps.Next(); defaultIndex = true; hasDefault = true; } ps.ExpectChar('['); var key = GetVariantKey(ps); ps.ExpectChar(']'); if (ps.IsPeekValueStart()) { ps.SkipIndent(); var value = GetValue(ps); return(new Ast.Variant(key, value, defaultIndex)); } throw new ParseException("E0012"); }
public Ast.BaseComment _GetComment(FtlParserStream ps) { // 0 - comment // 1 - group comment // 2 - resource comment int level = -1; var content = new StringBuilder(); while (true) { int i = -1; while (ps.CurrentIs('#') && (i < (level == -1 ? 2 : level))) { ps.Next(); i++; } if (level == -1) { level = i; } if (!ps.IsPeekNewLine()) { ps.ExpectChar(' '); int ch; while ((ch = ps.TakeChar(x => x != '\r' && x != '\n')) != Eof) { content.Append((char)ch); } } if (ps.IsPeekNextLineComment(level)) { content.Append('\n'); ps.SkipNewLine(); } else { break; } } var text = content.ToString(); switch (level) { case 0: return(new Ast.Comment(text)); case 1: return(new Ast.GroupComment(text)); case 2: return(new Ast.ResourceComment(text)); } throw new InvalidOperationException($"Unknown level value '{level}'"); }
public Ast.Entry GetEntry(FtlParserStream ps) { if (ps.CurrentIs('#')) { return(GetComment(ps)); } if (ps.CurrentIs('-')) { return(GetTerm(ps)); } if (ps.IsIdentifierStart()) { return(GetMessage(ps)); } throw new ParseException("E0002"); }
Ast.Expression GetArgVal(FtlParserStream ps) { if (ps.IsNumberStart()) { return(GetNumber(ps)); } else if (ps.CurrentIs('"')) { return(GetString(ps)); } throw new ParseException("E0012"); }
Ast.NumberLiteral _GetNumber(FtlParserStream ps) { var num = new StringBuilder(); if (ps.CurrentIs('-')) { num.Append('-'); ps.Next(); } num.Append(GetDigits(ps)); if (ps.CurrentIs('.')) { num.Append('.'); ps.Next(); num.Append(GetDigits(ps)); } return(new Ast.NumberLiteral(num.ToString())); }
Ast.SyntaxNode _GetValue(FtlParserStream ps) { if (ps.CurrentIs('{')) { ps.Peek(); ps.PeekInlineWs(); if (ps.IsPeekNextLineVariantStart()) { return(GetVariantList(ps)); } } return(GetPattern(ps)); }
/// <summary> /// Parse the first Message or Term in `source`. /// /// Skip all encountered comments and start parsing at the first Message or /// Term start. Return Junk if the parsing is not successful. /// /// Preceding comments are ignored unless they contain syntax errors /// themselves, in which case Junk for the invalid comment is returned. /// </summary> public Ast.Entry ParseEntry(TextReader source) { var ps = new FtlParserStream(source); ps.SkipBlankLines(); while (ps.CurrentIs('#')) { var skipped = GetEntryOrJunk(ps); if (skipped is Ast.Junk) { // Don't skip Junk comments. return(skipped); } ps.SkipBlankLines(); } return(GetEntryOrJunk(ps)); }
Ast.SyntaxNode _GetSelectorExpression(FtlParserStream ps) { if (ps.CurrentIs('{')) { return(GetPlaceable(ps)); } var literal = GetLiteral(ps); var mtReference = literal as Ast.MessageTermReference; if (mtReference == null) { return(literal); } var ch = ps.Current; if (ch == '.') { ps.Next(); var attr = GetIdentifier(ps); return(new Ast.AttributeExpression(mtReference, attr)); } if (ch == '[') { ps.Next(); if (mtReference is Ast.MessageReference) { throw new ParseException("E0024"); } var key = GetVariantKey(ps); ps.ExpectChar(']'); return(new Ast.VariantExpression(literal, key)); } if (ch == '(') { ps.Next(); if (!s_fnName.IsMatch(mtReference.Id.Name)) { throw new ParseException("E0008"); } var args = GetCallArgs(ps); ps.ExpectChar(')'); var func = new Ast.Function(mtReference.Id.Name); if (_withSpans) { func.AddSpan(mtReference.Span.Start, mtReference.Span.End); } return(new Ast.CallExpression(func, args.Positional, args.Named)); } return(literal); }
Ast.SyntaxNode _GetExpression(FtlParserStream ps) { ps.SkipInlineWs(); var selector = GetSelectorExpression(ps); ps.SkipInlineWs(); if (ps.CurrentIs('-')) { ps.Peek(); if (!ps.CurrentPeekIs('>')) { ps.ResetPeek(); return(selector); } if (selector is Ast.MessageReference) { throw new ParseException("E0016"); } if (selector is Ast.AttributeExpression ae && ae.Ref is Ast.MessageReference) { throw new ParseException("E0018"); } if (selector is Ast.VariantExpression) { throw new ParseException("E0017"); } ps.Next(); ps.Next(); ps.SkipInlineWs(); var variants = GetVariants(ps); if (variants.Count == 0) { throw new ParseException("E0011"); } // VariantLists are only allowed in other VariantLists. if (variants.Any(v => v.Value is Ast.VariantList)) { throw new ParseException("E0023"); } ps.ExpectIndent(); return(new Ast.SelectExpression(selector, variants)); } else if (selector is Ast.AttributeExpression ae && ae.Ref is Ast.TermReference) { throw new ParseException("E0019"); } return(selector); }