From d43a79fab5bf2c1d4a8e9ba9a501407eb36aad8f Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 1 Oct 2015 21:35:35 +0200 Subject: [PATCH] Wrote binding expression parser by hand. Sprache couldn't cope with it as it requires a left-recursive grammar. --- .../Perspex.Markup/Binding/ExpressionNode.cs | 7 +- .../Binding/ExpressionNodeBuilder.cs | 85 +--------- .../Binding/ExpressionParseException.cs | 24 +++ ...{ElementAccessorNode.cs => IndexerNode.cs} | 5 +- .../Perspex.Markup/Binding/LogicalNotNode.cs | 5 - .../Binding/Parsers/ArgumentListParser.cs | 63 ++++++++ .../Binding/Parsers/ExpressionParser.cs | 152 ++++++++++++++++++ .../Binding/Parsers/IdentifierParser.cs | 51 ++++++ .../Binding/Parsers/LiteralParser.cs | 34 ++++ .../Perspex.Markup/Binding/Parsers/Reader.cs | 31 ++++ .../Binding/PropertyAccessorNode.cs | 3 +- .../CSharp/BracketedArgumentListSyntax.cs | 18 --- .../Parsers/CSharp/CSharpSyntaxTree.cs | 17 -- .../CSharp/ElementAccessExpressionSyntax.cs | 19 --- .../CSharp/ExpressionStatementSyntax.cs | 15 -- .../Parsers/CSharp/ExpressionSyntax.cs | 9 -- .../CSharp/Grammar/ExpressionGrammar.cs | 64 -------- .../CSharp/Grammar/IdentifierGrammar.cs | 41 ----- .../Parsers/CSharp/Grammar/LiteralGrammar.cs | 20 --- .../Parsers/CSharp/IdentifierSyntax.cs | 17 -- .../Parsers/CSharp/LiteralExpressionSyntax.cs | 17 -- .../CSharp/MemberAccessExpressionSyntax.cs | 17 -- .../CSharp/PrefixUnaryExpressionSyntax.cs | 23 --- .../Parsers/CSharp/SyntaxKind.cs | 10 -- .../Parsers/CSharp/SyntaxNode.cs | 9 -- .../Parsers/CSharp/SyntaxToken.cs | 15 -- .../Perspex.Markup/Perspex.Markup.csproj | 27 +--- src/Markup/Perspex.Markup/packages.config | 1 - .../Binding/ExpressionNodeBuilderTests.cs | 7 +- 29 files changed, 377 insertions(+), 429 deletions(-) create mode 100644 src/Markup/Perspex.Markup/Binding/ExpressionParseException.cs rename src/Markup/Perspex.Markup/Binding/{ElementAccessorNode.cs => IndexerNode.cs} (95%) create mode 100644 src/Markup/Perspex.Markup/Binding/Parsers/ArgumentListParser.cs create mode 100644 src/Markup/Perspex.Markup/Binding/Parsers/ExpressionParser.cs create mode 100644 src/Markup/Perspex.Markup/Binding/Parsers/IdentifierParser.cs create mode 100644 src/Markup/Perspex.Markup/Binding/Parsers/LiteralParser.cs create mode 100644 src/Markup/Perspex.Markup/Binding/Parsers/Reader.cs delete mode 100644 src/Markup/Perspex.Markup/Parsers/CSharp/BracketedArgumentListSyntax.cs delete mode 100644 src/Markup/Perspex.Markup/Parsers/CSharp/CSharpSyntaxTree.cs delete mode 100644 src/Markup/Perspex.Markup/Parsers/CSharp/ElementAccessExpressionSyntax.cs delete mode 100644 src/Markup/Perspex.Markup/Parsers/CSharp/ExpressionStatementSyntax.cs delete mode 100644 src/Markup/Perspex.Markup/Parsers/CSharp/ExpressionSyntax.cs delete mode 100644 src/Markup/Perspex.Markup/Parsers/CSharp/Grammar/ExpressionGrammar.cs delete mode 100644 src/Markup/Perspex.Markup/Parsers/CSharp/Grammar/IdentifierGrammar.cs delete mode 100644 src/Markup/Perspex.Markup/Parsers/CSharp/Grammar/LiteralGrammar.cs delete mode 100644 src/Markup/Perspex.Markup/Parsers/CSharp/IdentifierSyntax.cs delete mode 100644 src/Markup/Perspex.Markup/Parsers/CSharp/LiteralExpressionSyntax.cs delete mode 100644 src/Markup/Perspex.Markup/Parsers/CSharp/MemberAccessExpressionSyntax.cs delete mode 100644 src/Markup/Perspex.Markup/Parsers/CSharp/PrefixUnaryExpressionSyntax.cs delete mode 100644 src/Markup/Perspex.Markup/Parsers/CSharp/SyntaxKind.cs delete mode 100644 src/Markup/Perspex.Markup/Parsers/CSharp/SyntaxNode.cs delete mode 100644 src/Markup/Perspex.Markup/Parsers/CSharp/SyntaxToken.cs diff --git a/src/Markup/Perspex.Markup/Binding/ExpressionNode.cs b/src/Markup/Perspex.Markup/Binding/ExpressionNode.cs index 4bb4356c30..e5dc9f2810 100644 --- a/src/Markup/Perspex.Markup/Binding/ExpressionNode.cs +++ b/src/Markup/Perspex.Markup/Binding/ExpressionNode.cs @@ -14,12 +14,7 @@ namespace Perspex.Markup.Binding private ExpressionValue _value = ExpressionValue.None; - public ExpressionNode(ExpressionNode next) - { - Next = next; - } - - public ExpressionNode Next { get; } + public ExpressionNode Next { get; set; } public object Target { diff --git a/src/Markup/Perspex.Markup/Binding/ExpressionNodeBuilder.cs b/src/Markup/Perspex.Markup/Binding/ExpressionNodeBuilder.cs index 4667bfbf13..953b1728b0 100644 --- a/src/Markup/Perspex.Markup/Binding/ExpressionNodeBuilder.cs +++ b/src/Markup/Perspex.Markup/Binding/ExpressionNodeBuilder.cs @@ -2,12 +2,11 @@ // Licensed under the MIT license. See licence.md file in the project root for full license information. using System; -using System.Collections.Generic; -using Perspex.Markup.Parsers.CSharp; +using Perspex.Markup.Binding.Parsers; namespace Perspex.Markup.Binding { - internal class ExpressionNodeBuilder + internal static class ExpressionNodeBuilder { public static ExpressionNode Build(string expression) { @@ -16,85 +15,15 @@ namespace Perspex.Markup.Binding throw new ArgumentException("'expression' may not be empty."); } - var syntax = CSharpSyntaxTree.ParseExpression(expression); + var reader = new Reader(expression); + var node = ExpressionParser.Parse(reader); - if (syntax != null) + if (!reader.End) { - return Build(expression, syntax, null); + throw new ExpressionParseException(reader, "Expected end of expression."); } - else - { - throw new Exception($"Invalid expression: {expression}"); - } - } - - private static ExpressionNode Build(string expression, SyntaxNode syntax, ExpressionNode next) - { - var expressionStatement = syntax as ExpressionStatementSyntax; - var identifier = syntax as IdentifierSyntax; - var memberAccess = syntax as MemberAccessExpressionSyntax; - var unaryExpression = syntax as PrefixUnaryExpressionSyntax; - var elementAccess = syntax as ElementAccessExpressionSyntax; - if (expressionStatement != null) - { - return Build(expression, expressionStatement.Expression, next); - } - else if (identifier != null) - { - next = new PropertyAccessorNode(next, identifier.Name); - } - else if (memberAccess != null) - { - next = Build(expression, memberAccess.Expression, next); - next = new PropertyAccessorNode(next, memberAccess.Member.Name); - } - else if (unaryExpression != null && unaryExpression.Kind() == SyntaxKind.LogicalNotExpression) - { - next = Build(expression, unaryExpression.Operand, next); - next = new LogicalNotNode(next); - } - else if (elementAccess != null) - { - next = Build(expression, elementAccess, next); - next = Build(expression, elementAccess.Expression, next); - } - else - { - throw new Exception($"Invalid expression: {expression}"); - } - - return next; - } - - private static ExpressionNode Build(string expression, ElementAccessExpressionSyntax syntax, ExpressionNode next) - { - var argList = syntax.ArgumentList as BracketedArgumentListSyntax; - - if (argList != null) - { - var args = new List(); - - foreach (var arg in argList.Arguments) - { - var literal = arg as LiteralExpressionSyntax; - - if (literal != null) - { - args.Add(literal.Value); - } - else - { - throw new Exception($"Invalid expression: {expression}"); - } - } - - return new ElementAccessorNode(next, args); - } - else - { - throw new Exception($"Invalid expression: {expression}"); - } + return node; } } } diff --git a/src/Markup/Perspex.Markup/Binding/ExpressionParseException.cs b/src/Markup/Perspex.Markup/Binding/ExpressionParseException.cs new file mode 100644 index 0000000000..06505e1de8 --- /dev/null +++ b/src/Markup/Perspex.Markup/Binding/ExpressionParseException.cs @@ -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; } + } +} diff --git a/src/Markup/Perspex.Markup/Binding/ElementAccessorNode.cs b/src/Markup/Perspex.Markup/Binding/IndexerNode.cs similarity index 95% rename from src/Markup/Perspex.Markup/Binding/ElementAccessorNode.cs rename to src/Markup/Perspex.Markup/Binding/IndexerNode.cs index d8d4e9979c..953852a6db 100644 --- a/src/Markup/Perspex.Markup/Binding/ElementAccessorNode.cs +++ b/src/Markup/Perspex.Markup/Binding/IndexerNode.cs @@ -10,12 +10,11 @@ using System.Reflection; namespace Perspex.Markup.Binding { - internal class ElementAccessorNode : ExpressionNode + internal class IndexerNode : ExpressionNode { private int[] _intArgs; - public ElementAccessorNode(ExpressionNode next, IList arguments) - : base(next) + public IndexerNode(IList arguments) { Arguments = arguments; diff --git a/src/Markup/Perspex.Markup/Binding/LogicalNotNode.cs b/src/Markup/Perspex.Markup/Binding/LogicalNotNode.cs index 6ea29deffa..d20972c639 100644 --- a/src/Markup/Perspex.Markup/Binding/LogicalNotNode.cs +++ b/src/Markup/Perspex.Markup/Binding/LogicalNotNode.cs @@ -9,11 +9,6 @@ namespace Perspex.Markup.Binding { internal class LogicalNotNode : ExpressionNode { - public LogicalNotNode(ExpressionNode next) - : base(next) - { - } - public override bool SetValue(object value) { throw new NotSupportedException("Cannot set a negated binding."); diff --git a/src/Markup/Perspex.Markup/Binding/Parsers/ArgumentListParser.cs b/src/Markup/Perspex.Markup/Binding/Parsers/ArgumentListParser.cs new file mode 100644 index 0000000000..9d53c09453 --- /dev/null +++ b/src/Markup/Perspex.Markup/Binding/Parsers/ArgumentListParser.cs @@ -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 Parse(Reader r, char open, char close) + { + if (r.Peek == open) + { + var result = new List(); + + 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; + } + } +} diff --git a/src/Markup/Perspex.Markup/Binding/Parsers/ExpressionParser.cs b/src/Markup/Perspex.Markup/Binding/Parsers/ExpressionParser.cs new file mode 100644 index 0000000000..c344a41cda --- /dev/null +++ b/src/Markup/Perspex.Markup/Binding/Parsers/ExpressionParser.cs @@ -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(); + 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 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 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 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, + } + } +} diff --git a/src/Markup/Perspex.Markup/Binding/Parsers/IdentifierParser.cs b/src/Markup/Perspex.Markup/Binding/Parsers/IdentifierParser.cs new file mode 100644 index 0000000000..6f3c760d74 --- /dev/null +++ b/src/Markup/Perspex.Markup/Binding/Parsers/IdentifierParser.cs @@ -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; + } + } + } +} diff --git a/src/Markup/Perspex.Markup/Binding/Parsers/LiteralParser.cs b/src/Markup/Perspex.Markup/Binding/Parsers/LiteralParser.cs new file mode 100644 index 0000000000..08056f4aab --- /dev/null +++ b/src/Markup/Perspex.Markup/Binding/Parsers/LiteralParser.cs @@ -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; + } + } +} diff --git a/src/Markup/Perspex.Markup/Binding/Parsers/Reader.cs b/src/Markup/Perspex.Markup/Binding/Parsers/Reader.cs new file mode 100644 index 0000000000..b69d299ebb --- /dev/null +++ b/src/Markup/Perspex.Markup/Binding/Parsers/Reader.cs @@ -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(); + } + } + } +} diff --git a/src/Markup/Perspex.Markup/Binding/PropertyAccessorNode.cs b/src/Markup/Perspex.Markup/Binding/PropertyAccessorNode.cs index 0a4b4b737e..d304577a32 100644 --- a/src/Markup/Perspex.Markup/Binding/PropertyAccessorNode.cs +++ b/src/Markup/Perspex.Markup/Binding/PropertyAccessorNode.cs @@ -11,8 +11,7 @@ namespace Perspex.Markup.Binding { private PropertyInfo _propertyInfo; - public PropertyAccessorNode(ExpressionNode next, string propertyName) - : base(next) + public PropertyAccessorNode(string propertyName) { PropertyName = propertyName; } diff --git a/src/Markup/Perspex.Markup/Parsers/CSharp/BracketedArgumentListSyntax.cs b/src/Markup/Perspex.Markup/Parsers/CSharp/BracketedArgumentListSyntax.cs deleted file mode 100644 index 52a53313be..0000000000 --- a/src/Markup/Perspex.Markup/Parsers/CSharp/BracketedArgumentListSyntax.cs +++ /dev/null @@ -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 arguments) - { - Arguments = arguments.ToList(); - } - - public IList Arguments { get; } - } -} diff --git a/src/Markup/Perspex.Markup/Parsers/CSharp/CSharpSyntaxTree.cs b/src/Markup/Perspex.Markup/Parsers/CSharp/CSharpSyntaxTree.cs deleted file mode 100644 index 19a6f8ac07..0000000000 --- a/src/Markup/Perspex.Markup/Parsers/CSharp/CSharpSyntaxTree.cs +++ /dev/null @@ -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); - } - } -} diff --git a/src/Markup/Perspex.Markup/Parsers/CSharp/ElementAccessExpressionSyntax.cs b/src/Markup/Perspex.Markup/Parsers/CSharp/ElementAccessExpressionSyntax.cs deleted file mode 100644 index 6568e6f1a7..0000000000 --- a/src/Markup/Perspex.Markup/Parsers/CSharp/ElementAccessExpressionSyntax.cs +++ /dev/null @@ -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; } - } -} diff --git a/src/Markup/Perspex.Markup/Parsers/CSharp/ExpressionStatementSyntax.cs b/src/Markup/Perspex.Markup/Parsers/CSharp/ExpressionStatementSyntax.cs deleted file mode 100644 index 6c653eaea5..0000000000 --- a/src/Markup/Perspex.Markup/Parsers/CSharp/ExpressionStatementSyntax.cs +++ /dev/null @@ -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; } - } -} diff --git a/src/Markup/Perspex.Markup/Parsers/CSharp/ExpressionSyntax.cs b/src/Markup/Perspex.Markup/Parsers/CSharp/ExpressionSyntax.cs deleted file mode 100644 index 403bc7b6b6..0000000000 --- a/src/Markup/Perspex.Markup/Parsers/CSharp/ExpressionSyntax.cs +++ /dev/null @@ -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 - { - } -} diff --git a/src/Markup/Perspex.Markup/Parsers/CSharp/Grammar/ExpressionGrammar.cs b/src/Markup/Perspex.Markup/Parsers/CSharp/Grammar/ExpressionGrammar.cs deleted file mode 100644 index 71f847d312..0000000000 --- a/src/Markup/Perspex.Markup/Parsers/CSharp/Grammar/ExpressionGrammar.cs +++ /dev/null @@ -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 ExpressionStatement() - { - return from expression in Expression().End() - select new ExpressionStatementSyntax(expression); - } - - public static Parser Expression() - { - return LiteralGrammar.Literal() - .Or(PrefixUnary()) - .Or(MemberAccess()) - .Or(ElementAccess()) - .Or(IdentifierGrammar.Identifier()); - } - - public static Parser MemberAccess() - { - return from identifier in IdentifierGrammar.Identifier() - from dot in Parse.Char('.') - from expression in Expression() - select new MemberAccessExpressionSyntax(expression, identifier); - } - - public static Parser ElementAccess() - { - return from expression in IdentifierGrammar.Identifier() - from arguments in BracketedArgumentList('[', ']') - select new ElementAccessExpressionSyntax(expression, arguments); - } - - public static Parser 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> Arguments() - { - return Expression().DelimitedBy(Parse.Char(',').Token()); - } - - public static Parser PrefixUnary() - { - return from bang in Parse.Char('!') - from operand in Expression() - select new PrefixUnaryExpressionSyntax(operand, SyntaxKind.LogicalNotExpression); - } - } -} diff --git a/src/Markup/Perspex.Markup/Parsers/CSharp/Grammar/IdentifierGrammar.cs b/src/Markup/Perspex.Markup/Parsers/CSharp/Grammar/IdentifierGrammar.cs deleted file mode 100644 index 015a3f55c9..0000000000 --- a/src/Markup/Perspex.Markup/Parsers/CSharp/Grammar/IdentifierGrammar.cs +++ /dev/null @@ -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 CombiningCharacter = Parse.Char( - c => - { - var cat = CharUnicodeInfo.GetUnicodeCategory(c); - return cat == UnicodeCategory.NonSpacingMark || - cat == UnicodeCategory.SpacingCombiningMark; - }, - "Connecting Character"); - - private static readonly Parser ConnectingCharacter = Parse.Char( - c => CharUnicodeInfo.GetUnicodeCategory(c) == UnicodeCategory.ConnectorPunctuation, - "Connecting Character"); - - private static readonly Parser FormattingCharacter = Parse.Char( - c => CharUnicodeInfo.GetUnicodeCategory(c) == UnicodeCategory.Format, - "Connecting Character"); - - private static readonly Parser IdentifierStart = Parse.Letter.Or(Parse.Char('_')); - - private static readonly Parser IdentifierChar = Parse - .LetterOrDigit - .Or(ConnectingCharacter) - .Or(CombiningCharacter) - .Or(FormattingCharacter); - - public static Parser Identifier() - { - return from start in IdentifierStart.Once().Text() - from @char in IdentifierChar.Many().Text() - select new IdentifierSyntax(start + @char); - } - } -} diff --git a/src/Markup/Perspex.Markup/Parsers/CSharp/Grammar/LiteralGrammar.cs b/src/Markup/Perspex.Markup/Parsers/CSharp/Grammar/LiteralGrammar.cs deleted file mode 100644 index 9cc4ea5705..0000000000 --- a/src/Markup/Perspex.Markup/Parsers/CSharp/Grammar/LiteralGrammar.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System.Globalization; -using System.Linq; -using Sprache; - -namespace Perspex.Markup.Parsers.CSharp.Grammar -{ - internal class LiteralGrammar - { - public static Parser Literal() - { - return Integer(); - } - - public static Parser Integer() - { - return from number in Parse.Number - select new LiteralExpressionSyntax(int.Parse(number), number); - } - } -} diff --git a/src/Markup/Perspex.Markup/Parsers/CSharp/IdentifierSyntax.cs b/src/Markup/Perspex.Markup/Parsers/CSharp/IdentifierSyntax.cs deleted file mode 100644 index dc676652d7..0000000000 --- a/src/Markup/Perspex.Markup/Parsers/CSharp/IdentifierSyntax.cs +++ /dev/null @@ -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; } - } -} diff --git a/src/Markup/Perspex.Markup/Parsers/CSharp/LiteralExpressionSyntax.cs b/src/Markup/Perspex.Markup/Parsers/CSharp/LiteralExpressionSyntax.cs deleted file mode 100644 index bc741e8a77..0000000000 --- a/src/Markup/Perspex.Markup/Parsers/CSharp/LiteralExpressionSyntax.cs +++ /dev/null @@ -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; } - } -} diff --git a/src/Markup/Perspex.Markup/Parsers/CSharp/MemberAccessExpressionSyntax.cs b/src/Markup/Perspex.Markup/Parsers/CSharp/MemberAccessExpressionSyntax.cs deleted file mode 100644 index 3b019613af..0000000000 --- a/src/Markup/Perspex.Markup/Parsers/CSharp/MemberAccessExpressionSyntax.cs +++ /dev/null @@ -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; } - } -} diff --git a/src/Markup/Perspex.Markup/Parsers/CSharp/PrefixUnaryExpressionSyntax.cs b/src/Markup/Perspex.Markup/Parsers/CSharp/PrefixUnaryExpressionSyntax.cs deleted file mode 100644 index f0e292ba33..0000000000 --- a/src/Markup/Perspex.Markup/Parsers/CSharp/PrefixUnaryExpressionSyntax.cs +++ /dev/null @@ -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; - } - } -} diff --git a/src/Markup/Perspex.Markup/Parsers/CSharp/SyntaxKind.cs b/src/Markup/Perspex.Markup/Parsers/CSharp/SyntaxKind.cs deleted file mode 100644 index 121bc48b9f..0000000000 --- a/src/Markup/Perspex.Markup/Parsers/CSharp/SyntaxKind.cs +++ /dev/null @@ -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, - } -} diff --git a/src/Markup/Perspex.Markup/Parsers/CSharp/SyntaxNode.cs b/src/Markup/Perspex.Markup/Parsers/CSharp/SyntaxNode.cs deleted file mode 100644 index ea9e71d870..0000000000 --- a/src/Markup/Perspex.Markup/Parsers/CSharp/SyntaxNode.cs +++ /dev/null @@ -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 - { - } -} diff --git a/src/Markup/Perspex.Markup/Parsers/CSharp/SyntaxToken.cs b/src/Markup/Perspex.Markup/Parsers/CSharp/SyntaxToken.cs deleted file mode 100644 index e66aa4bde0..0000000000 --- a/src/Markup/Perspex.Markup/Parsers/CSharp/SyntaxToken.cs +++ /dev/null @@ -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; } - } -} diff --git a/src/Markup/Perspex.Markup/Perspex.Markup.csproj b/src/Markup/Perspex.Markup/Perspex.Markup.csproj index d9665eb28f..aee9048774 100644 --- a/src/Markup/Perspex.Markup/Perspex.Markup.csproj +++ b/src/Markup/Perspex.Markup/Perspex.Markup.csproj @@ -35,34 +35,21 @@ + - + + + + + + - - - - - - - - - - - - - - - - - ..\..\..\packages\Sprache.2.0.0.47\lib\portable-net4+netcore45+win8+wp8+sl5+MonoAndroid1+MonoTouch1\Sprache.dll - True - ..\..\..\packages\Rx-Core.2.2.5\lib\portable-windows8+net45+wp8\System.Reactive.Core.dll True diff --git a/src/Markup/Perspex.Markup/packages.config b/src/Markup/Perspex.Markup/packages.config index 2c1d2743e3..5bc4ae393b 100644 --- a/src/Markup/Perspex.Markup/packages.config +++ b/src/Markup/Perspex.Markup/packages.config @@ -5,5 +5,4 @@ - \ No newline at end of file diff --git a/tests/Perspex.Markup.UnitTests/Binding/ExpressionNodeBuilderTests.cs b/tests/Perspex.Markup.UnitTests/Binding/ExpressionNodeBuilderTests.cs index 5c9d4e3956..623d6a6365 100644 --- a/tests/Perspex.Markup.UnitTests/Binding/ExpressionNodeBuilderTests.cs +++ b/tests/Perspex.Markup.UnitTests/Binding/ExpressionNodeBuilderTests.cs @@ -63,6 +63,7 @@ namespace Perspex.Markup.UnitTests.Binding Assert.Equal(2, result.Count); AssertIsProperty(result[0], "Foo"); AssertIsIndexer(result[1], 15); + Assert.IsType(result[1]); } [Fact] @@ -90,7 +91,7 @@ namespace Perspex.Markup.UnitTests.Binding { var result = ToList(ExpressionNodeBuilder.Build("Foo[15][16]")); - Assert.Equal(2, result.Count); + Assert.Equal(3, result.Count); AssertIsProperty(result[0], "Foo"); AssertIsIndexer(result[1], 15); AssertIsIndexer(result[2], 16); @@ -118,9 +119,9 @@ namespace Perspex.Markup.UnitTests.Binding private void AssertIsIndexer(ExpressionNode node, params object[] args) { - Assert.IsType(node); + Assert.IsType(node); - var e = (ElementAccessorNode)node; + var e = (IndexerNode)node; Assert.Equal(e.Arguments.ToArray(), args.ToArray()); }