29 changed files with 377 additions and 429 deletions
@ -0,0 +1,24 @@ |
|||
// Copyright (c) The Perspex Project. All rights reserved.
|
|||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|||
|
|||
using System; |
|||
using Perspex.Markup.Binding.Parsers; |
|||
|
|||
namespace Perspex.Markup.Binding |
|||
{ |
|||
public class ExpressionParseException : Exception |
|||
{ |
|||
internal ExpressionParseException(int column, string message) |
|||
: base(message) |
|||
{ |
|||
Column = column; |
|||
} |
|||
|
|||
internal ExpressionParseException(Reader r, string message) |
|||
: this(r.Position, message) |
|||
{ |
|||
} |
|||
|
|||
public int Column { get; } |
|||
} |
|||
} |
|||
@ -0,0 +1,63 @@ |
|||
// Copyright (c) The Perspex Project. All rights reserved.
|
|||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
|
|||
namespace Perspex.Markup.Binding.Parsers |
|||
{ |
|||
internal static class ArgumentListParser |
|||
{ |
|||
public static IList<object> Parse(Reader r, char open, char close) |
|||
{ |
|||
if (r.Peek == open) |
|||
{ |
|||
var result = new List<object>(); |
|||
|
|||
r.Take(); |
|||
|
|||
while (!r.End && r.Peek != close) |
|||
{ |
|||
var literal = LiteralParser.Parse(r); |
|||
|
|||
if (literal != null) |
|||
{ |
|||
result.Add(literal); |
|||
} |
|||
else |
|||
{ |
|||
throw new ExpressionParseException(r, "Expected integer."); |
|||
} |
|||
|
|||
r.SkipWhitespace(); |
|||
|
|||
if (r.End) |
|||
{ |
|||
throw new ExpressionParseException(r, "Expected ','."); |
|||
} |
|||
if (r.Peek != close) |
|||
{ |
|||
if (r.Take() != ',') |
|||
{ |
|||
throw new ExpressionParseException(r, "Expected ','."); |
|||
} |
|||
|
|||
r.SkipWhitespace(); |
|||
} |
|||
} |
|||
|
|||
if (!r.End) |
|||
{ |
|||
r.Take(); |
|||
return result; |
|||
} |
|||
else |
|||
{ |
|||
throw new ExpressionParseException(r, "Expected ']'."); |
|||
} |
|||
} |
|||
|
|||
return null; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,152 @@ |
|||
// Copyright (c) The Perspex Project. All rights reserved.
|
|||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
|
|||
namespace Perspex.Markup.Binding.Parsers |
|||
{ |
|||
internal static class ExpressionParser |
|||
{ |
|||
public static ExpressionNode Parse(Reader r) |
|||
{ |
|||
var nodes = new List<ExpressionNode>(); |
|||
var state = State.Start; |
|||
|
|||
while (!r.End && state != State.End) |
|||
{ |
|||
switch (state) |
|||
{ |
|||
case State.Start: |
|||
state = ParseStart(r, nodes); |
|||
break; |
|||
|
|||
case State.AfterMember: |
|||
state = ParseAfterMember(r, nodes); |
|||
break; |
|||
|
|||
case State.BeforeMember: |
|||
state = ParseBeforeMember(r, nodes); |
|||
break; |
|||
|
|||
default: |
|||
state = State.End; |
|||
break; |
|||
} |
|||
} |
|||
|
|||
for (int n = 0; n < nodes.Count - 1; ++n) |
|||
{ |
|||
nodes[n].Next = nodes[n + 1]; |
|||
} |
|||
|
|||
return nodes[0]; |
|||
} |
|||
|
|||
private static State ParseStart(Reader r, IList<ExpressionNode> nodes) |
|||
{ |
|||
if (ParseNot(r)) |
|||
{ |
|||
nodes.Add(new LogicalNotNode()); |
|||
return State.Start; |
|||
} |
|||
else |
|||
{ |
|||
var identifier = IdentifierParser.Parse(r); |
|||
|
|||
if (identifier != null) |
|||
{ |
|||
nodes.Add(new PropertyAccessorNode(identifier)); |
|||
return State.AfterMember; |
|||
} |
|||
} |
|||
|
|||
return State.End; |
|||
} |
|||
|
|||
private static State ParseAfterMember(Reader r, IList<ExpressionNode> nodes) |
|||
{ |
|||
if (ParseMemberAccessor(r)) |
|||
{ |
|||
return State.BeforeMember; |
|||
} |
|||
else |
|||
{ |
|||
var args = ArgumentListParser.Parse(r, '[', ']'); |
|||
|
|||
if (args != null) |
|||
{ |
|||
if (args.Count == 0) |
|||
{ |
|||
throw new ExpressionParseException(r, "Indexer may not be empty."); |
|||
} |
|||
|
|||
nodes.Add(new IndexerNode(args)); |
|||
return State.AfterMember; |
|||
} |
|||
} |
|||
|
|||
return State.End; |
|||
} |
|||
|
|||
private static State ParseBeforeMember(Reader r, IList<ExpressionNode> nodes) |
|||
{ |
|||
var identifier = IdentifierParser.Parse(r); |
|||
|
|||
if (identifier != null) |
|||
{ |
|||
nodes.Add(new PropertyAccessorNode(identifier)); |
|||
return State.AfterMember; |
|||
} |
|||
|
|||
return State.End; |
|||
} |
|||
|
|||
private static bool ParseNot(Reader r) |
|||
{ |
|||
if (!r.End && r.Peek == '!') |
|||
{ |
|||
r.Take(); |
|||
return true; |
|||
} |
|||
else |
|||
{ |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
private static bool ParseMemberAccessor(Reader r) |
|||
{ |
|||
if (!r.End && r.Peek == '.') |
|||
{ |
|||
r.Take(); |
|||
return true; |
|||
} |
|||
else |
|||
{ |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
private static bool ParseIndexer(Reader r) |
|||
{ |
|||
if (!r.End && r.Peek == '[') |
|||
{ |
|||
r.Take(); |
|||
return true; |
|||
} |
|||
else |
|||
{ |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
private enum State |
|||
{ |
|||
Start, |
|||
AfterMember, |
|||
BeforeMember, |
|||
End, |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,51 @@ |
|||
// Copyright (c) The Perspex Project. All rights reserved.
|
|||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|||
|
|||
using System.Globalization; |
|||
using System.Text; |
|||
|
|||
namespace Perspex.Markup.Binding.Parsers |
|||
{ |
|||
internal static class IdentifierParser |
|||
{ |
|||
public static string Parse(Reader r) |
|||
{ |
|||
if (IsValidIdentifierStart(r.Peek)) |
|||
{ |
|||
var result = new StringBuilder(); |
|||
|
|||
while (!r.End && IsValidIdentifierChar(r.Peek)) |
|||
{ |
|||
result.Append(r.Take()); |
|||
} |
|||
|
|||
return result.ToString(); |
|||
} |
|||
else |
|||
{ |
|||
return null; |
|||
} |
|||
} |
|||
|
|||
private static bool IsValidIdentifierStart(char c) |
|||
{ |
|||
return char.IsLetter(c) || c == '_'; |
|||
} |
|||
|
|||
private static bool IsValidIdentifierChar(char c) |
|||
{ |
|||
if (IsValidIdentifierStart(c)) |
|||
{ |
|||
return true; |
|||
} |
|||
else |
|||
{ |
|||
var cat = CharUnicodeInfo.GetUnicodeCategory(c); |
|||
return cat == UnicodeCategory.NonSpacingMark || |
|||
cat == UnicodeCategory.SpacingCombiningMark || |
|||
cat == UnicodeCategory.ConnectorPunctuation || |
|||
cat == UnicodeCategory.Format; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
// Copyright (c) The Perspex Project. All rights reserved.
|
|||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|||
|
|||
using System.Text; |
|||
|
|||
namespace Perspex.Markup.Binding.Parsers |
|||
{ |
|||
internal static class LiteralParser |
|||
{ |
|||
public static object Parse(Reader r) |
|||
{ |
|||
if (char.IsDigit(r.Peek)) |
|||
{ |
|||
StringBuilder result = new StringBuilder(); |
|||
|
|||
while (!r.End) |
|||
{ |
|||
if (char.IsDigit(r.Peek)) |
|||
{ |
|||
result.Append(r.Take()); |
|||
} |
|||
else |
|||
{ |
|||
break; |
|||
} |
|||
} |
|||
|
|||
return int.Parse(result.ToString()); |
|||
} |
|||
|
|||
return null; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,31 @@ |
|||
// Copyright (c) The Perspex Project. All rights reserved.
|
|||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|||
|
|||
using System; |
|||
|
|||
namespace Perspex.Markup.Binding.Parsers |
|||
{ |
|||
internal class Reader |
|||
{ |
|||
private string _s; |
|||
private int _i; |
|||
|
|||
public Reader(string s) |
|||
{ |
|||
_s = s; |
|||
} |
|||
|
|||
public bool End => _i == _s.Length; |
|||
public char Peek => _s[_i]; |
|||
public int Position => _i; |
|||
public char Take() => _s[_i++]; |
|||
|
|||
public void SkipWhitespace() |
|||
{ |
|||
while (!End && char.IsWhiteSpace(Peek)) |
|||
{ |
|||
Take(); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,18 +0,0 @@ |
|||
// Copyright (c) The Perspex Project. All rights reserved.
|
|||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|||
|
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
|
|||
namespace Perspex.Markup.Parsers.CSharp |
|||
{ |
|||
internal class BracketedArgumentListSyntax |
|||
{ |
|||
public BracketedArgumentListSyntax(IEnumerable<ExpressionSyntax> arguments) |
|||
{ |
|||
Arguments = arguments.ToList(); |
|||
} |
|||
|
|||
public IList<ExpressionSyntax> Arguments { get; } |
|||
} |
|||
} |
|||
@ -1,17 +0,0 @@ |
|||
// Copyright (c) The Perspex Project. All rights reserved.
|
|||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|||
|
|||
using System; |
|||
using Perspex.Markup.Parsers.CSharp.Grammar; |
|||
using Sprache; |
|||
|
|||
namespace Perspex.Markup.Parsers.CSharp |
|||
{ |
|||
public class CSharpSyntaxTree |
|||
{ |
|||
internal static ExpressionStatementSyntax ParseExpression(string expression) |
|||
{ |
|||
return ExpressionGrammar.ExpressionStatement().Parse(expression); |
|||
} |
|||
} |
|||
} |
|||
@ -1,19 +0,0 @@ |
|||
// Copyright (c) The Perspex Project. All rights reserved.
|
|||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|||
|
|||
namespace Perspex.Markup.Parsers.CSharp |
|||
{ |
|||
internal class ElementAccessExpressionSyntax : ExpressionSyntax |
|||
{ |
|||
public ElementAccessExpressionSyntax( |
|||
ExpressionSyntax expression, |
|||
BracketedArgumentListSyntax argumentList) |
|||
{ |
|||
Expression = expression; |
|||
ArgumentList = argumentList; |
|||
} |
|||
|
|||
public ExpressionSyntax Expression { get; } |
|||
public BracketedArgumentListSyntax ArgumentList { get; } |
|||
} |
|||
} |
|||
@ -1,15 +0,0 @@ |
|||
// Copyright (c) The Perspex Project. All rights reserved.
|
|||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|||
|
|||
namespace Perspex.Markup.Parsers.CSharp |
|||
{ |
|||
internal class ExpressionStatementSyntax : SyntaxNode |
|||
{ |
|||
public ExpressionStatementSyntax(ExpressionSyntax expression) |
|||
{ |
|||
Expression = expression; |
|||
} |
|||
|
|||
public ExpressionSyntax Expression { get; } |
|||
} |
|||
} |
|||
@ -1,9 +0,0 @@ |
|||
// Copyright (c) The Perspex Project. All rights reserved.
|
|||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|||
|
|||
namespace Perspex.Markup.Parsers.CSharp |
|||
{ |
|||
internal class ExpressionSyntax : SyntaxNode |
|||
{ |
|||
} |
|||
} |
|||
@ -1,64 +0,0 @@ |
|||
// Copyright (c) The Perspex Project. All rights reserved.
|
|||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|||
|
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using Sprache; |
|||
|
|||
namespace Perspex.Markup.Parsers.CSharp.Grammar |
|||
{ |
|||
internal class ExpressionGrammar |
|||
{ |
|||
public static Parser<ExpressionStatementSyntax> ExpressionStatement() |
|||
{ |
|||
return from expression in Expression().End() |
|||
select new ExpressionStatementSyntax(expression); |
|||
} |
|||
|
|||
public static Parser<ExpressionSyntax> Expression() |
|||
{ |
|||
return LiteralGrammar.Literal() |
|||
.Or<ExpressionSyntax>(PrefixUnary()) |
|||
.Or<ExpressionSyntax>(MemberAccess()) |
|||
.Or<ExpressionSyntax>(ElementAccess()) |
|||
.Or<ExpressionSyntax>(IdentifierGrammar.Identifier()); |
|||
} |
|||
|
|||
public static Parser<MemberAccessExpressionSyntax> MemberAccess() |
|||
{ |
|||
return from identifier in IdentifierGrammar.Identifier() |
|||
from dot in Parse.Char('.') |
|||
from expression in Expression() |
|||
select new MemberAccessExpressionSyntax(expression, identifier); |
|||
} |
|||
|
|||
public static Parser<ElementAccessExpressionSyntax> ElementAccess() |
|||
{ |
|||
return from expression in IdentifierGrammar.Identifier() |
|||
from arguments in BracketedArgumentList('[', ']') |
|||
select new ElementAccessExpressionSyntax(expression, arguments); |
|||
} |
|||
|
|||
public static Parser<BracketedArgumentListSyntax> BracketedArgumentList( |
|||
char openBracket, |
|||
char closeBracket) |
|||
{ |
|||
return from open in Parse.Char(openBracket) |
|||
from arguments in Arguments() |
|||
from close in Parse.Char(closeBracket) |
|||
select new BracketedArgumentListSyntax(arguments); |
|||
} |
|||
|
|||
public static Parser<IEnumerable<ExpressionSyntax>> Arguments() |
|||
{ |
|||
return Expression().DelimitedBy(Parse.Char(',').Token()); |
|||
} |
|||
|
|||
public static Parser<PrefixUnaryExpressionSyntax> PrefixUnary() |
|||
{ |
|||
return from bang in Parse.Char('!') |
|||
from operand in Expression() |
|||
select new PrefixUnaryExpressionSyntax(operand, SyntaxKind.LogicalNotExpression); |
|||
} |
|||
} |
|||
} |
|||
@ -1,41 +0,0 @@ |
|||
using System.Globalization; |
|||
using System.Linq; |
|||
using Sprache; |
|||
|
|||
namespace Perspex.Markup.Parsers.CSharp.Grammar |
|||
{ |
|||
internal class IdentifierGrammar |
|||
{ |
|||
private static readonly Parser<char> CombiningCharacter = Parse.Char( |
|||
c => |
|||
{ |
|||
var cat = CharUnicodeInfo.GetUnicodeCategory(c); |
|||
return cat == UnicodeCategory.NonSpacingMark || |
|||
cat == UnicodeCategory.SpacingCombiningMark; |
|||
}, |
|||
"Connecting Character"); |
|||
|
|||
private static readonly Parser<char> ConnectingCharacter = Parse.Char( |
|||
c => CharUnicodeInfo.GetUnicodeCategory(c) == UnicodeCategory.ConnectorPunctuation, |
|||
"Connecting Character"); |
|||
|
|||
private static readonly Parser<char> FormattingCharacter = Parse.Char( |
|||
c => CharUnicodeInfo.GetUnicodeCategory(c) == UnicodeCategory.Format, |
|||
"Connecting Character"); |
|||
|
|||
private static readonly Parser<char> IdentifierStart = Parse.Letter.Or(Parse.Char('_')); |
|||
|
|||
private static readonly Parser<char> IdentifierChar = Parse |
|||
.LetterOrDigit |
|||
.Or(ConnectingCharacter) |
|||
.Or(CombiningCharacter) |
|||
.Or(FormattingCharacter); |
|||
|
|||
public static Parser<IdentifierSyntax> Identifier() |
|||
{ |
|||
return from start in IdentifierStart.Once().Text() |
|||
from @char in IdentifierChar.Many().Text() |
|||
select new IdentifierSyntax(start + @char); |
|||
} |
|||
} |
|||
} |
|||
@ -1,20 +0,0 @@ |
|||
using System.Globalization; |
|||
using System.Linq; |
|||
using Sprache; |
|||
|
|||
namespace Perspex.Markup.Parsers.CSharp.Grammar |
|||
{ |
|||
internal class LiteralGrammar |
|||
{ |
|||
public static Parser<LiteralExpressionSyntax> Literal() |
|||
{ |
|||
return Integer(); |
|||
} |
|||
|
|||
public static Parser<LiteralExpressionSyntax> Integer() |
|||
{ |
|||
return from number in Parse.Number |
|||
select new LiteralExpressionSyntax(int.Parse(number), number); |
|||
} |
|||
} |
|||
} |
|||
@ -1,17 +0,0 @@ |
|||
// Copyright (c) The Perspex Project. All rights reserved.
|
|||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|||
|
|||
using System; |
|||
|
|||
namespace Perspex.Markup.Parsers.CSharp |
|||
{ |
|||
internal class IdentifierSyntax : ExpressionSyntax |
|||
{ |
|||
public IdentifierSyntax(string name) |
|||
{ |
|||
Name = name; |
|||
} |
|||
|
|||
public String Name { get; } |
|||
} |
|||
} |
|||
@ -1,17 +0,0 @@ |
|||
// Copyright (c) The Perspex Project. All rights reserved.
|
|||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|||
|
|||
namespace Perspex.Markup.Parsers.CSharp |
|||
{ |
|||
internal class LiteralExpressionSyntax : ExpressionSyntax |
|||
{ |
|||
public LiteralExpressionSyntax(object value, string valueText) |
|||
{ |
|||
Value = value; |
|||
ValueText = valueText; |
|||
} |
|||
|
|||
public object Value { get; } |
|||
public string ValueText { get; } |
|||
} |
|||
} |
|||
@ -1,17 +0,0 @@ |
|||
// Copyright (c) The Perspex Project. All rights reserved.
|
|||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|||
|
|||
namespace Perspex.Markup.Parsers.CSharp |
|||
{ |
|||
internal class MemberAccessExpressionSyntax : ExpressionSyntax |
|||
{ |
|||
public MemberAccessExpressionSyntax(ExpressionSyntax expression, IdentifierSyntax member) |
|||
{ |
|||
Expression = expression; |
|||
Member = member; |
|||
} |
|||
|
|||
public ExpressionSyntax Expression { get; } |
|||
public IdentifierSyntax Member { get; } |
|||
} |
|||
} |
|||
@ -1,23 +0,0 @@ |
|||
// Copyright (c) The Perspex Project. All rights reserved.
|
|||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|||
|
|||
namespace Perspex.Markup.Parsers.CSharp |
|||
{ |
|||
internal class PrefixUnaryExpressionSyntax : ExpressionSyntax |
|||
{ |
|||
private SyntaxKind _kind; |
|||
|
|||
public PrefixUnaryExpressionSyntax(ExpressionSyntax operand, SyntaxKind kind) |
|||
{ |
|||
Operand = operand; |
|||
_kind = kind; |
|||
} |
|||
|
|||
public ExpressionSyntax Operand { get; } |
|||
|
|||
public SyntaxKind Kind() |
|||
{ |
|||
return _kind; |
|||
} |
|||
} |
|||
} |
|||
@ -1,10 +0,0 @@ |
|||
// Copyright (c) The Perspex Project. All rights reserved.
|
|||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|||
|
|||
namespace Perspex.Markup.Parsers.CSharp |
|||
{ |
|||
internal enum SyntaxKind |
|||
{ |
|||
LogicalNotExpression, |
|||
} |
|||
} |
|||
@ -1,9 +0,0 @@ |
|||
// Copyright (c) The Perspex Project. All rights reserved.
|
|||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|||
|
|||
namespace Perspex.Markup.Parsers.CSharp |
|||
{ |
|||
internal class SyntaxNode |
|||
{ |
|||
} |
|||
} |
|||
@ -1,15 +0,0 @@ |
|||
// Copyright (c) The Perspex Project. All rights reserved.
|
|||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
|||
|
|||
namespace Perspex.Markup.Parsers.CSharp |
|||
{ |
|||
internal class SyntaxToken |
|||
{ |
|||
public SyntaxToken(string valueText) |
|||
{ |
|||
ValueText = valueText; |
|||
} |
|||
|
|||
public string ValueText { get; } |
|||
} |
|||
} |
|||
Loading…
Reference in new issue