Skip to content

[WIP] Support ListLiteralExpression '[]' in PowerShell - #5761

Closed
Dongbo Wang (daxian-dbw) wants to merge 4 commits into
PowerShell:masterfrom
daxian-dbw:literalList
Closed

Dongbo Wang (daxian-dbw) wants to merge 4 commits into
PowerShell:masterfrom
daxian-dbw:literalList

Conversation

@daxian-dbw

@daxian-dbw Dongbo Wang (daxian-dbw) commented Dec 29, 2017 •

Copy link
Copy Markdown
Member

PR Summary

An attempt to address #5643

This design adds the ListLiteralExpression '[]' to PowerShell, which is similar to the ArrayLiteralExpression. They are different in mainly two ways:

  • ListLiteralExpression always returns a List<object>
  • When there is only one element expression, ListLiteralExpression unwraps the value of the expression while ArrayLiteralExpression directly uses the value as the first element of the returned array.

Please review with #5762 as a comparison, where list is supported as a ListExpression '@[]', similar to ArrayExpression '@()'.

Example

# an empty list
> $l = []
> $l.GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     List`1                                   System.Object
# one element list, the element is a type
> $l = [[string]]
> $l.Count
1
> $l[0]

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     String                                   System.Object
# multi-element list
> $l = [$pid,2,$home,4]
> $l.Count
4
# unwrap the only expression element in a list literal expression, like '@()'
> $l = [(dir)]
> $l.GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     List`1                                   System.Object

> $l.Count
30

# as a comparison, see the behavior of ArrayLiteralExpression
> $a = ,(dir)
> $a.Count
1

PR Checklist

Note: Please mark anything not applicable to this PR NA.

@daxian-dbw Dongbo Wang (daxian-dbw) changed the title [WIP] Support list literal expression in PowerShell [WIP] Support list literal expression '[]' in PowerShell Dec 29, 2017
@daxian-dbw Dongbo Wang (daxian-dbw) changed the title [WIP] Support list literal expression '[]' in PowerShell [WIP] Support ListLiteralExpression '[]' in PowerShell Dec 29, 2017
@markekraus

Mark Kraus (markekraus) commented Dec 29, 2017 •

Copy link
Copy Markdown
Contributor

why [] and not @[]? seeing just [] is confusing compared to types. also [()] is weird... I'd expect something similar to @():

Nevermind.. missed the other PR.

@daxian-dbw

Copy link
Copy Markdown
Member Author

Mark Kraus (@markekraus) My first design attempt is to support @[], behaving like @(), see PR #5762. However, unlike character ')' that always starts a new token, the character ']' doesn't force to start a new token and therefore can be included in GenericToken, meaning command names, arguments, function names and etc. It makes impossible to determine where '@[]' ends and I cannot find an elegant solution without breaking changes.

We need more discussions on the design, and that is why I submitted 2 PRs for the list support: #5762 to add @[], and this one to add [].

@lzybkr Jason Shirk (lzybkr) left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is wip, I just did a quick skim.

return new NewArrayInitInstruction<T>(elementCount);
}

protected internal override Instruction NewListInit(int elementCount)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we should add this to the interpreter - this way it stays as close as possible to original and/or the one used for dynamic when now JIT is available.

It is possible to add new instructions without touching the interpreter it that's how you want to do it - see PowerShellLoopExpression for an example.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The NewListInitInstruction is a minor optimization which can be removed.
But I need to implement LightCompiler.CompileListInitExpression because it's currently not implemented. Are you suggesting to not use ListInitExpression in our compiler generated code?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see the method is now implemented: http://source.dot.net/#System.Linq.Expressions/System/Linq/Expressions/Interpreter/LightCompiler.cs,a988c37e1da7a799

I wonder if there is a supported way of invoking the interpreter now? We'd have to make sure they have any of our fixes first, but it'd be nice to not ship two copies of basically the same thing.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great, I can use their implementation then.

}

/// <summary/>
public interface ICustomAstVisitor3 : ICustomAstVisitor2

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think numbered interfaces made sense when the release cycle spanned years, but I'm worried we'll end up with ICustomAstVistor9 before too long.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One (maybe not awesome) idea is to avoid the problem for this feature is to add a bool flag to ArrayLiteralAst - because they should be structurally the same.

The downside to this approach is that some analysis tools that aren't aware of list literals will analyze the ast as an array, e.g. they may incorrectly give warnings about performance.

@daxian-dbw Dongbo Wang (daxian-dbw) Dec 29, 2017 •

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought about reusing the ArrayLiteralAst, but didn't go that direction for two reasons:

  1. An explicit ListLiteralAst is more discoverable.
  2. ArrayLiteralAst can appear on the left side of an assignment. I didn't plan to make [] also assignable. (but I don't object to the idea to make it capable of the same).

I would like to see more discussion on this before updating the code.

{
Expression values = Compile(listLiteralAst.Elements[0]);

if (values.Type == typeof(List<object>)) { return values; }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand this. Does this mean the following would be true?

$x = [1]
$y = [[1]]
$x.Count -eq $y.Count -and $x[0] -eq $y[0]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reading more of the implementation, I'm confused now. Is this supposed to emulate ArrayExpressionAst or ArrayLiteralAst - it seems like the former, but it's named like the later.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a change I made that is different from ArrayLiteral -- when there is only one expression element in the ListLiteral, unwrap the value of the expression element like @() does.
This is because I think it may be better for [(dir)] to return a list containing all files than it to return a list containing an object array.
It's just one of my thoughts, not necessarily a right one 😄

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You shouldn't call it a literal if you can't guarantee the number of objects in the result.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. I will remove that part of logic.


do {
if (commaToken != null) { SkipToken(); }
V3SkipNewlines();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's probably time to get rid of V3SkipNewlines - I was overly cautious introducing that method and it hasn't been used.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will replace V3SkipNewlines with SkipNewlines

}
else if (listValues.Count > 0)
{
extent = ExtentOf(lBracket, listValues[listValues.Count-1].Extent);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This excludes a trailing comma. The simple approach to tracking the end is to just have a variable endExtent and keep updating it with each token that you consume or ast you add to listValues.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update to use a endExtent variable.

@daxian-dbw

Dongbo Wang (daxian-dbw) commented Jan 8, 2018 •

Copy link
Copy Markdown
Member Author

Per the discussions in #5643, it's commonly agreed that we want ListExpressionAst which behaves similar to @() instead of a ListLiteralAst. Therefore, I will close this PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants