Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improvements to BindingParser and PropertyDeclarationDirectiveCompiler #1731

Merged
merged 6 commits into from
Dec 8, 2023
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -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,16 @@ 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();

var startIndex = CurrentIndex;

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

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

var node = new AssemblyNameBindingParserNode(tokens);
var node = CreateNode(new AssemblyNameBindingParserNode(tokens), startIndex);
if (node.Name.Length == 0)
{
node.NodeErrors.Add("Assembly name cannot be empty.");
Expand Down Expand Up @@ -742,29 +745,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 +1126,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
Loading