in src/Migrations/AddXHPChildrenDeclarationMethodMigration.hack [273:380]
private static function convertChildrenExpression(
Node $in,
): FunctionCallExpression {
if ($in is XHPChildrenParenthesizedList) {
$count = $in->getXhpChildren()->getCount();
invariant($count >= 1, 'Got empty XHP children parenthesized list');
if ($count === 1) {
return self::convertChildrenExpression(
C\onlyx($in->getXhpChildren()->getChildrenOfItems()),
);
}
$children = Vec\map(
$in->getXhpChildren()->getChildrenOfItems(),
$node ==> self::convertChildrenExpression($node),
);
return self::makeCall('sequence', null, $children);
}
if ($in is PostfixUnaryExpression) {
$op = $in->getOperator();
if ($op is PlusToken) {
$fun = 'atLeastOneOf';
} else if ($op is QuestionToken) {
$fun = 'optional';
} else if ($op is StarToken) {
$fun = 'anyNumberOf';
} else {
invariant_violation(
"Got an XHP postfix unary expression with unexpected operator '%s'",
$in->getOperator()->getText(),
);
}
return self::makeCall(
$fun,
null,
vec[self::convertChildrenExpression($in->getOperand())],
);
}
if ($in is BinaryExpression) {
// foo | bar | baz
invariant(
$in->getOperator() is BarToken,
"Got an XHP child binary expression with unexpected operator '%s'",
$in->getOperator()->getText(),
);
// `foo | bar | baz` is `((foo | bar) | baz)` in the AST; flatten them out
// for readability
$next = $in;
$parts = vec[];
while ($next is BinaryExpression && $next->getOperator() is BarToken) {
$parts[] = $next->getRightOperand();
$next = $next->getLeftOperand();
}
$parts[] = $next;
$parts = Vec\reverse($parts)
|> Vec\map($$, $part ==> self::convertChildrenExpression($part));
return self::makeCall('anyOf', null, $parts);
}
if ($in is NameExpression || $in is EmptyToken || $in is NameToken) {
$name = $in is NameExpression ? $in->getWrappedNode() : $in;
if (
$name is NameToken || $name is EmptyToken || $name is XHPClassNameToken
) {
$name = $name->getText();
if ($name === 'pcdata' || $name === 'empty' || $name === 'any') {
return self::makeCall($name);
}
// Class name
return self::makeCall(
'ofType',
new TypeArguments(
new LessThanToken(null, null),
new NodeList(vec[
new ListItem(
new SimpleTypeSpecifier(new NameToken(null, null, $name)),
null,
),
]),
new GreaterThanToken(null, null),
),
);
}
if ($name is XHPCategoryNameToken) {
return self::makeCall('category', null, vec[new LiteralExpression(
new SingleQuotedStringLiteralToken(
null,
null,
"'".$name->getText()."'",
),
)]);
}
invariant_violation(
'Unhandled XHP child name type: %s',
\get_class($name),
);
}
invariant_violation(
'Unhandled XHP children expression: %s (%s)',
\get_class($in),
$in->getCode(),
);
}