Skip to content

Commit

Permalink
Merge pull request #1731 from riganti/property-directive-parser
Browse files Browse the repository at this point in the history
Improvements to `BindingParser` and `PropertyDeclarationDirectiveCompiler`
  • Loading branch information
exyi authored Dec 8, 2023
2 parents 7187fd0 + 85d2ff4 commit 13f3cf4
Show file tree
Hide file tree
Showing 6 changed files with 219 additions and 84 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ private IEnumerable<object> InstantiateAttributes(DothtmlDirectiveNode dothtmlDi

if (attributeInstance is null)
{
dothtmlDirective.AddError($"Could not create insstance of the attribute {attribute.attributeType}.");
dothtmlDirective.AddError($"Could not create instance of the attribute {attribute.attributeType}.");
continue;
}

Expand All @@ -85,11 +85,18 @@ private IEnumerable<object> InstantiateAttributes(DothtmlDirectiveNode dothtmlDi

if (reflectedProperty is null)
{
dothtmlDirective.AddError($"Could not find property {property.name} insstance of the attribute {attribute.attributeType}.");
dothtmlDirective.AddError($"Could not find property {property.name} instance of the attribute {attribute.attributeType}.");
continue;
}

reflectedProperty.SetValue(attributeInstance, property.value);
try
{
reflectedProperty.SetValue(attributeInstance, property.value);
}
catch (Exception ex)
{
dothtmlDirective.AddError($"Cannot assign {reflectedProperty.Name} of attribute {attribute.attributeType.FullName}: {ex.Message}");
}
}
yield return attributeInstance;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,44 +53,55 @@ protected override IAbstractPropertyDeclarationDirective Resolve(DothtmlDirectiv

var attributeSyntaxes = (declaration?.Attributes ?? new List<BindingParserNode>());
var resolvedAttributes = ProcessPropertyDirectiveAttributeReference(directiveNode, attributeSyntaxes)
.Select(a => TreeBuilder.BuildPropertyDeclarationAttributeReference(directiveNode, a.name, a.type, a.initializer, imports))
.Select(a => TreeBuilder.BuildPropertyDeclarationAttributeReference(directiveNode, a.Name, a.Type, a.Initializer, imports))
.ToList();

return TreeBuilder.BuildPropertyDeclarationDirective(directiveNode, type, name, declaration?.Initializer, resolvedAttributes, valueSyntaxRoot, imports);
}

private List<(TypeReferenceBindingParserNode type, IdentifierNameBindingParserNode name, LiteralExpressionBindingParserNode initializer)> ProcessPropertyDirectiveAttributeReference(DothtmlDirectiveNode directiveNode, List<BindingParserNode> attributeReferences)
private List<AttributeInfo> ProcessPropertyDirectiveAttributeReference(DothtmlDirectiveNode directiveNode, List<BindingParserNode> attributeReferences)
{
var result = new List<(TypeReferenceBindingParserNode, IdentifierNameBindingParserNode, LiteralExpressionBindingParserNode)>();
foreach (var attributeReference in attributeReferences)
return attributeReferences.Select(attr => GetAttributeInfo(attr, directiveNode)).ToList();
}

private AttributeInfo GetAttributeInfo(BindingParserNode attributeReference, DothtmlDirectiveNode directiveNode)
{
if (attributeReference is not BinaryOperatorBindingParserNode { Operator: BindingTokenType.AssignOperator } assignment)
{
directiveNode.AddError("Property attributes must be in the form Attribute.Property = value.");
//No idea what is that, lets make it a type and move on

var typeRef = new ActualTypeReferenceBindingParserNode(attributeReference);
typeRef.TransferTokens(attributeReference);

return new AttributeInfo(
typeRef,
new SimpleNameBindingParserNode("") { StartPosition = attributeReference.EndPosition },
new LiteralExpressionBindingParserNode("") { StartPosition = attributeReference.EndPosition });
}

var attributePropertyReference = assignment.FirstExpression as MemberAccessBindingParserNode;
var initializer = assignment.SecondExpression as LiteralExpressionBindingParserNode;
var attributeTypeReference = attributePropertyReference?.TargetExpression;
var attributePropertyNameReference = attributePropertyReference?.MemberNameExpression;


if (attributeTypeReference is null || attributePropertyNameReference is null)
{
if (attributeReference is not BinaryOperatorBindingParserNode { Operator: BindingTokenType.AssignOperator } assignment)
{
directiveNode.AddError("Property attributes must be in the form Attribute.Property = value.");
continue;
}

var attributePropertyReference = assignment.FirstExpression as MemberAccessBindingParserNode;
var attributeTypeReference = attributePropertyReference?.TargetExpression;
var attributePropertyNameReference = attributePropertyReference?.MemberNameExpression;
var initializer = assignment.SecondExpression as LiteralExpressionBindingParserNode;

if (attributeTypeReference is null || attributePropertyNameReference is null)
{
directiveNode.AddError("Property attributes must be in the form Attribute.Property = value.");
continue;
}
if (initializer == null)
{
directiveNode.AddError($"Value for property {attributeTypeReference.ToDisplayString()} of attribute {attributePropertyNameReference.ToDisplayString()} is missing or not a constant.");
continue;
}

var type = new ActualTypeReferenceBindingParserNode(attributeTypeReference);
type.TransferTokens(attributeTypeReference);
result.Add(new (type, attributePropertyNameReference, initializer));
directiveNode.AddError("Property attributes must be in the form Attribute.Property = value.");
//Name is probably mising or type is incomplete
attributeTypeReference = attributeTypeReference ?? new SimpleNameBindingParserNode("");
attributePropertyNameReference = attributePropertyNameReference ?? new SimpleNameBindingParserNode("") { StartPosition = attributeTypeReference.EndPosition };
}
return result;
if (initializer == null)
{
directiveNode.AddError($"Value for property {attributeTypeReference.ToDisplayString()} of attribute {attributePropertyNameReference.ToDisplayString()} is missing or not a constant.");
initializer = new LiteralExpressionBindingParserNode("") { StartPosition = attributePropertyNameReference.EndPosition };
}

var type = new ActualTypeReferenceBindingParserNode(attributeTypeReference);
type.TransferTokens(attributeTypeReference);
return new AttributeInfo(type, attributePropertyNameReference, initializer);
}

protected override ImmutableList<DotvvmProperty> CreateArtefact(IReadOnlyList<IAbstractPropertyDeclarationDirective> directives)
Expand All @@ -108,6 +119,8 @@ protected override ImmutableList<DotvvmProperty> CreateArtefact(IReadOnlyList<IA

protected abstract bool HasPropertyType(IAbstractPropertyDeclarationDirective directive);
protected abstract DotvvmProperty TryCreateDotvvmPropertyFromDirective(IAbstractPropertyDeclarationDirective propertyDeclarationDirective);

private record AttributeInfo(ActualTypeReferenceBindingParserNode Type, IdentifierNameBindingParserNode Name, LiteralExpressionBindingParserNode Initializer);
}

public class ResolvedPropertyDeclarationDirectiveCompiler : PropertyDeclarationDirectiveCompiler
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,11 @@ namespace DotVVM.Framework.Compilation.Parser.Binding.Parser
public class AssemblyNameBindingParserNode : BindingParserNode
{
public string Name { get; }
public AssemblyNameBindingParserNode(List<BindingToken> tokens)
{
Tokens = tokens;
Name = string.Concat(tokens.Select(t => t.Text));
}

public AssemblyNameBindingParserNode(string name)
: this(new List<BindingToken>() { new BindingToken(name, BindingTokenType.Identifier, 0, 0, 0, 0)})
{ }
{
Name = name;
}

public override IEnumerable<BindingParserNode> EnumerateChildNodes()
=> Enumerable.Empty<BindingParserNode>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ public BindingParserNode ReadArrayInitializerValue()
SkipWhiteSpace();
}

if(PeekType() == BindingTokenType.CloseArrayBrace)
if (PeekType() == BindingTokenType.CloseArrayBrace)
{
Read();
SkipWhiteSpace();
Expand Down Expand Up @@ -107,7 +107,7 @@ public BindingParserNode ReadPropertyDirectiveValue()
Read();
SkipWhiteSpace();

var attributes = ReadArguments();
var attributes = ReadArguments(OnEnd);

foreach (var attribute in attributes)
{
Expand All @@ -131,7 +131,7 @@ public BindingParserNode ReadDirectiveTypeName()
Read();
var assemblyName = ReadAssemblyName();

return new AssemblyQualifiedNameBindingParserNode(typeName, assemblyName);
return CreateNode(new AssemblyQualifiedNameBindingParserNode(typeName, assemblyName), startIndex);
}
else if (Peek() is BindingToken token)
{
Expand All @@ -144,13 +144,20 @@ public AssemblyNameBindingParserNode ReadAssemblyName()
{
// almost anything can be an assembly name, so we just read everything until the end
SkipWhiteSpace();
var tokens = this.Tokens.Skip(CurrentIndex).ToList();
CurrentIndex = Tokens.Count;

var startIndex = CurrentIndex;

var tokens = Tokens.Skip(CurrentIndex).ToList();

while (tokens.Count > 0 && tokens[tokens.Count - 1].Type == BindingTokenType.WhiteSpace)
tokens.RemoveAt(tokens.Count - 1);

var node = new AssemblyNameBindingParserNode(tokens);
CurrentIndex += tokens.Count;
var name = string.Concat(tokens.Select(t => t.Text));

var node = CreateNode(new AssemblyNameBindingParserNode(name), startIndex);
CurrentIndex = Tokens.Count;

if (node.Name.Length == 0)
{
node.NodeErrors.Add("Assembly name cannot be empty.");
Expand Down Expand Up @@ -742,29 +749,36 @@ private BindingParserNode ReadFunctionCall(int startIndex, BindingParserNode exp
{
// function call
Read();
var arguments = ReadArguments();
SkipWhiteSpace();
var arguments = ReadArguments(() => PeekType() == BindingTokenType.CloseParenthesis);
var error = IsCurrentTokenIncorrect(BindingTokenType.CloseParenthesis);
Read();
SkipWhiteSpace();
expression = CreateNode(new FunctionCallBindingParserNode(expression, arguments), startIndex, error ? "The ')' was expected." : null);
return expression;
}

private List<BindingParserNode> ReadArguments()
private List<BindingParserNode> ReadArguments(Func<bool> isEndToken)
{
var arguments = new List<BindingParserNode>();
int previousInnerIndex = -1;
while (Peek() is BindingToken operatorToken && operatorToken.Type != BindingTokenType.CloseParenthesis && previousInnerIndex != CurrentIndex)
var previousInnerIndex = -1;
while (previousInnerIndex != CurrentIndex && !OnEnd() && !isEndToken())
{
previousInnerIndex = CurrentIndex;
if (arguments.Count > 0)

arguments.Add(ReadExpression());

SkipWhiteSpace();

if (PeekType() == BindingTokenType.Comma)
{
Read();
SkipWhiteSpace();
if (IsCurrentTokenIncorrect(BindingTokenType.Comma))
arguments.Add(CreateNode(new LiteralExpressionBindingParserNode(null), CurrentIndex, "The ',' was expected"));
else Read();
}
arguments.Add(ReadExpression());
else if (!isEndToken())
{
arguments.Add(CreateNode(new SimpleNameBindingParserNode(""), CurrentIndex, "The ',' was expected"));
}
}

return arguments;
Expand Down Expand Up @@ -1116,7 +1130,7 @@ private static bool ParseNumberLiteralSuffix(ref string text, ref string? error,
{
error = null;
if (method(text, styles, CultureInfo.InvariantCulture, out var result)) return result;
error = $"could not parse { text } using { method.GetMethodInfo()?.DeclaringType?.FullName + "." + method.GetMethodInfo()?.Name }";
error = $"could not parse {text} using {method.GetMethodInfo()?.DeclaringType?.FullName + "." + method.GetMethodInfo()?.Name}";
return null;
}

Expand Down
Loading

0 comments on commit 13f3cf4

Please sign in to comment.