// Simplified Lua AST based roughly on http://lua-users.org/wiki/MetaLuaAbstractSyntaxTree,
// https://www.lua.org/manual/5.3/manual.html#9 and the TS AST implementation
// We can elide a lot of nodes especially tokens and keywords
// because we don't create the AST from text
import * as ts from "typescript";
import { LuaLibFeature } from "./LuaLib";
import { castArray } from "./utils";
export enum SyntaxKind {
File,
Block,
// Statements
DoStatement,
VariableDeclarationStatement,
AssignmentStatement,
IfStatement,
WhileStatement,
RepeatStatement,
ForStatement,
ForInStatement,
GotoStatement,
LabelStatement,
ReturnStatement,
BreakStatement,
ContinueStatement, // Luau only.
ExpressionStatement,
// Expression
StringLiteral,
NumericLiteral,
NilKeyword,
DotsKeyword,
ArgKeyword,
TrueKeyword,
FalseKeyword,
FunctionExpression,
TableFieldExpression,
TableExpression,
UnaryExpression,
BinaryExpression,
CallExpression,
MethodCallExpression,
Identifier,
TableIndexExpression,
ParenthesizedExpression,
ConditionalExpression, // Luau only
// Operators
// Arithmetic
AdditionOperator, // Maybe use abbreviations for those add, sub, mul ...
SubtractionOperator,
MultiplicationOperator,
DivisionOperator,
FloorDivisionOperator,
ModuloOperator,
PowerOperator,
NegationOperator, // Unary minus
// Concat
ConcatOperator,
// Length
LengthOperator, // Unary
// Relational Ops
EqualityOperator,
InequalityOperator,
LessThanOperator,
LessEqualOperator,
// Syntax Sugar `x > y` <=> `not (y <= x)`
// but we should probably use them to make the output code more readable
GreaterThanOperator,
GreaterEqualOperator, // Syntax Sugar `x >= y` <=> `not (y < x)`
// Logical
AndOperator,
OrOperator,
NotOperator, // Unary
// Bitwise
BitwiseAndOperator,
BitwiseOrOperator,
BitwiseExclusiveOrOperator,
BitwiseRightShiftOperator,
BitwiseLeftShiftOperator,
BitwiseNotOperator, // Unary
}
// TODO maybe name this PrefixUnary? not sure it makes sense to do so, because all unary ops in Lua are prefix
export type UnaryBitwiseOperator = SyntaxKind.BitwiseNotOperator;
export type UnaryOperator =
| SyntaxKind.NegationOperator
| SyntaxKind.LengthOperator
| SyntaxKind.NotOperator
| UnaryBitwiseOperator;
export type BinaryBitwiseOperator =
| SyntaxKind.BitwiseAndOperator
| SyntaxKind.BitwiseOrOperator
| SyntaxKind.BitwiseExclusiveOrOperator
| SyntaxKind.BitwiseRightShiftOperator
| SyntaxKind.BitwiseLeftShiftOperator;
export type BinaryOperator =
| SyntaxKind.AdditionOperator
| SyntaxKind.SubtractionOperator
| SyntaxKind.MultiplicationOperator
| SyntaxKind.DivisionOperator
| SyntaxKind.FloorDivisionOperator
| SyntaxKind.ModuloOperator
| SyntaxKind.PowerOperator
| SyntaxKind.ConcatOperator
| SyntaxKind.EqualityOperator
| SyntaxKind.InequalityOperator
| SyntaxKind.LessThanOperator
| SyntaxKind.LessEqualOperator
| SyntaxKind.GreaterThanOperator
| SyntaxKind.GreaterEqualOperator
| SyntaxKind.AndOperator
| SyntaxKind.OrOperator
| BinaryBitwiseOperator;
export type Operator = UnaryOperator | BinaryOperator;
export type SymbolId = number & { _symbolIdBrand: any };
export enum NodeFlags {
None = 0,
Inline = 1 << 0, // Keep function body on same line
Declaration = 1 << 1, // Prefer declaration syntax `function foo()` over assignment syntax `foo = function()`
TableUnpackCall = 1 << 2, // This is a table.unpack call
}
export interface TextRange {
line?: number;
column?: number;
}
export interface Node extends TextRange {
kind: SyntaxKind;
flags: NodeFlags;
}
export function createNode(kind: SyntaxKind, tsOriginal?: ts.Node): Node {
if (tsOriginal === undefined) {
return { kind, flags: NodeFlags.None };
}
const sourcePosition = getSourcePosition(tsOriginal);
if (sourcePosition) {
return { kind, line: sourcePosition.line, column: sourcePosition.column, flags: NodeFlags.None };
} else {
return { kind, flags: NodeFlags.None };
}
}
export function cloneNode(node: T): T {
return { ...node };
}
export function setNodePosition(node: T, position: TextRange): T {
node.line = position.line;
node.column = position.column;
return node;
}
export function setNodeOriginal(node: T, tsOriginal: ts.Node): T;
export function setNodeOriginal(node: T | undefined, tsOriginal: ts.Node): T | undefined;
export function setNodeOriginal(node: T | undefined, tsOriginal: ts.Node): T | undefined {
if (node === undefined) {
return undefined;
}
const sourcePosition = getSourcePosition(tsOriginal);
if (sourcePosition) {
setNodePosition(node, sourcePosition);
}
return node;
}
function getSourcePosition(sourceNode: ts.Node): TextRange | undefined {
const parseTreeNode = ts.getParseTreeNode(sourceNode) ?? sourceNode;
const sourceFile = parseTreeNode.getSourceFile();
if (sourceFile !== undefined && parseTreeNode.pos >= 0) {
const { line, character } = ts.getLineAndCharacterOfPosition(
sourceFile,
parseTreeNode.pos + parseTreeNode.getLeadingTriviaWidth()
);
return { line, column: character };
}
}
export function getOriginalPos(node: Node): TextRange {
return { line: node.line, column: node.column };
}
export function setNodeFlags(node: T, flags: NodeFlags): T {
node.flags = flags;
return node;
}
export interface File extends Node {
kind: SyntaxKind.File;
statements: Statement[];
luaLibFeatures: Set;
trivia: string;
}
export function isFile(node: Node): node is File {
return node.kind === SyntaxKind.File;
}
export function createFile(
statements: Statement[],
luaLibFeatures: Set,
trivia: string,
tsOriginal?: ts.Node
): File {
const file = createNode(SyntaxKind.File, tsOriginal) as File;
file.statements = statements;
file.luaLibFeatures = luaLibFeatures;
file.trivia = trivia;
return file;
}
export interface Block extends Node {
kind: SyntaxKind.Block;
statements: Statement[];
}
export function isBlock(node: Node): node is Block {
return node.kind === SyntaxKind.Block;
}
export function createBlock(statements: Statement[], tsOriginal?: ts.Node): Block {
const block = createNode(SyntaxKind.Block, tsOriginal) as Block;
block.statements = statements;
return block;
}
export interface Statement extends Node {
_statementBrand: any;
leadingComments?: Array;
trailingComments?: Array;
}
export interface DoStatement extends Statement {
kind: SyntaxKind.DoStatement;
statements: Statement[];
}
export function isDoStatement(node: Node): node is DoStatement {
return node.kind === SyntaxKind.DoStatement;
}
export function createDoStatement(statements: Statement[], tsOriginal?: ts.Node): DoStatement {
const statement = createNode(SyntaxKind.DoStatement, tsOriginal) as DoStatement;
statement.statements = statements;
return statement;
}
// `local test1, test2 = 12, 42` or `local test1, test2`
export interface VariableDeclarationStatement extends Statement {
kind: SyntaxKind.VariableDeclarationStatement;
left: Identifier[];
right?: Expression[];
}
export function isVariableDeclarationStatement(node: Node): node is VariableDeclarationStatement {
return node.kind === SyntaxKind.VariableDeclarationStatement;
}
export function createVariableDeclarationStatement(
left: Identifier | Identifier[],
right?: Expression | Expression[],
tsOriginal?: ts.Node
): VariableDeclarationStatement {
const statement = createNode(SyntaxKind.VariableDeclarationStatement, tsOriginal) as VariableDeclarationStatement;
statement.left = castArray(left);
if (right) statement.right = castArray(right);
return statement;
}
// `test1, test2 = 12, 42`
export interface AssignmentStatement extends Statement {
kind: SyntaxKind.AssignmentStatement;
left: AssignmentLeftHandSideExpression[];
right: Expression[];
}
export function isAssignmentStatement(node: Node): node is AssignmentStatement {
return node.kind === SyntaxKind.AssignmentStatement;
}
export function createAssignmentStatement(
left: AssignmentLeftHandSideExpression | AssignmentLeftHandSideExpression[],
right?: Expression | Expression[],
tsOriginal?: ts.Node
): AssignmentStatement {
const statement = createNode(SyntaxKind.AssignmentStatement, tsOriginal) as AssignmentStatement;
statement.left = castArray(left);
statement.right = right ? castArray(right) : [];
return statement;
}
export interface IfStatement extends Statement {
kind: SyntaxKind.IfStatement;
condition: Expression;
ifBlock: Block;
elseBlock?: Block | IfStatement;
}
export function isIfStatement(node: Node): node is IfStatement {
return node.kind === SyntaxKind.IfStatement;
}
export function createIfStatement(
condition: Expression,
ifBlock: Block,
elseBlock?: Block | IfStatement,
tsOriginal?: ts.Node
): IfStatement {
const statement = createNode(SyntaxKind.IfStatement, tsOriginal) as IfStatement;
statement.condition = condition;
statement.ifBlock = ifBlock;
statement.elseBlock = elseBlock;
return statement;
}
export interface IterationStatement extends Statement {
body: Block;
}
export function isIterationStatement(node: Node): node is IterationStatement {
return (
node.kind === SyntaxKind.WhileStatement ||
node.kind === SyntaxKind.RepeatStatement ||
node.kind === SyntaxKind.ForStatement ||
node.kind === SyntaxKind.ForInStatement
);
}
export interface WhileStatement extends IterationStatement {
kind: SyntaxKind.WhileStatement;
condition: Expression;
}
export function isWhileStatement(node: Node): node is WhileStatement {
return node.kind === SyntaxKind.WhileStatement;
}
export function createWhileStatement(body: Block, condition: Expression, tsOriginal?: ts.Node): WhileStatement {
const statement = createNode(SyntaxKind.WhileStatement, tsOriginal) as WhileStatement;
statement.body = body;
statement.condition = condition;
return statement;
}
export interface RepeatStatement extends IterationStatement {
kind: SyntaxKind.RepeatStatement;
condition: Expression;
}
export function isRepeatStatement(node: Node): node is RepeatStatement {
return node.kind === SyntaxKind.RepeatStatement;
}
export function createRepeatStatement(body: Block, condition: Expression, tsOriginal?: ts.Node): RepeatStatement {
const statement = createNode(SyntaxKind.RepeatStatement, tsOriginal) as RepeatStatement;
statement.body = body;
statement.condition = condition;
return statement;
}
// TODO maybe rename to ForNumericStatement
export interface ForStatement extends IterationStatement {
kind: SyntaxKind.ForStatement;
controlVariable: Identifier;
controlVariableInitializer: Expression;
limitExpression: Expression;
stepExpression?: Expression;
}
export function isForStatement(node: Node): node is ForStatement {
return node.kind === SyntaxKind.ForStatement;
}
export function createForStatement(
body: Block,
controlVariable: Identifier,
controlVariableInitializer: Expression,
limitExpression: Expression,
stepExpression?: Expression,
tsOriginal?: ts.Node
): ForStatement {
const statement = createNode(SyntaxKind.ForStatement, tsOriginal) as ForStatement;
statement.body = body;
statement.controlVariable = controlVariable;
statement.controlVariableInitializer = controlVariableInitializer;
statement.limitExpression = limitExpression;
statement.stepExpression = stepExpression;
return statement;
}
export interface ForInStatement extends IterationStatement {
kind: SyntaxKind.ForInStatement;
names: Identifier[];
expressions: Expression[];
}
export function isForInStatement(node: Node): node is ForInStatement {
return node.kind === SyntaxKind.ForInStatement;
}
export function createForInStatement(
body: Block,
names: Identifier[],
expressions: Expression[],
tsOriginal?: ts.Node
): ForInStatement {
const statement = createNode(SyntaxKind.ForInStatement, tsOriginal) as ForInStatement;
statement.body = body;
statement.names = names;
statement.expressions = expressions;
return statement;
}
export interface GotoStatement extends Statement {
kind: SyntaxKind.GotoStatement;
label: string; // or identifier ?
}
export function isGotoStatement(node: Node): node is GotoStatement {
return node.kind === SyntaxKind.GotoStatement;
}
export function createGotoStatement(label: string, tsOriginal?: ts.Node): GotoStatement {
const statement = createNode(SyntaxKind.GotoStatement, tsOriginal) as GotoStatement;
statement.label = label;
return statement;
}
export interface LabelStatement extends Statement {
kind: SyntaxKind.LabelStatement;
name: string; // or identifier ?
}
export function isLabelStatement(node: Node): node is LabelStatement {
return node.kind === SyntaxKind.LabelStatement;
}
export function createLabelStatement(name: string, tsOriginal?: ts.Node): LabelStatement {
const statement = createNode(SyntaxKind.LabelStatement, tsOriginal) as LabelStatement;
statement.name = name;
return statement;
}
export interface ReturnStatement extends Statement {
kind: SyntaxKind.ReturnStatement;
expressions: Expression[];
}
export function isReturnStatement(node: Node): node is ReturnStatement {
return node.kind === SyntaxKind.ReturnStatement;
}
export function createReturnStatement(expressions: Expression[], tsOriginal?: ts.Node): ReturnStatement {
const statement = createNode(SyntaxKind.ReturnStatement, tsOriginal) as ReturnStatement;
statement.expressions = expressions;
return statement;
}
export interface BreakStatement extends Statement {
kind: SyntaxKind.BreakStatement;
}
export function isBreakStatement(node: Node): node is BreakStatement {
return node.kind === SyntaxKind.BreakStatement;
}
export function createBreakStatement(tsOriginal?: ts.Node): BreakStatement {
return createNode(SyntaxKind.BreakStatement, tsOriginal) as BreakStatement;
}
export interface ContinueStatement extends Statement {
kind: SyntaxKind.ContinueStatement;
}
export function isContinueStatement(node: Node): node is ContinueStatement {
return node.kind === SyntaxKind.ContinueStatement;
}
export function createContinueStatement(tsOriginal?: ts.Node): ContinueStatement {
return createNode(SyntaxKind.ContinueStatement, tsOriginal) as ContinueStatement;
}
export interface ExpressionStatement extends Statement {
kind: SyntaxKind.ExpressionStatement;
expression: Expression;
}
export function isExpressionStatement(node: Node): node is ExpressionStatement {
return node.kind === SyntaxKind.ExpressionStatement;
}
export function createExpressionStatement(expressions: Expression, tsOriginal?: ts.Node): ExpressionStatement {
const statement = createNode(SyntaxKind.ExpressionStatement, tsOriginal) as ExpressionStatement;
statement.expression = expressions;
return statement;
}
export interface Expression extends Node {
_expressionBrand: any;
}
// Expressions
// TODO maybe create subexport interface for Literals/PrimaryExpressions
export interface NilLiteral extends Expression {
kind: SyntaxKind.NilKeyword;
}
export function isNilLiteral(node: Node): node is NilLiteral {
return node.kind === SyntaxKind.NilKeyword;
}
export function createNilLiteral(tsOriginal?: ts.Node): NilLiteral {
return createNode(SyntaxKind.NilKeyword, tsOriginal) as NilLiteral;
}
export interface BooleanLiteral extends Expression {
kind: SyntaxKind.TrueKeyword | SyntaxKind.FalseKeyword;
}
export function isBooleanLiteral(node: Node): node is BooleanLiteral {
return node.kind === SyntaxKind.TrueKeyword || node.kind === SyntaxKind.FalseKeyword;
}
export function createBooleanLiteral(value: boolean, tsOriginal?: ts.Node): BooleanLiteral {
return createNode(value ? SyntaxKind.TrueKeyword : SyntaxKind.FalseKeyword, tsOriginal) as BooleanLiteral;
}
// TODO Call this DotsLiteral or DotsKeyword?
export interface DotsLiteral extends Expression {
kind: SyntaxKind.DotsKeyword;
}
export function isDotsLiteral(node: Node): node is DotsLiteral {
return node.kind === SyntaxKind.DotsKeyword;
}
export function createDotsLiteral(tsOriginal?: ts.Node): DotsLiteral {
return createNode(SyntaxKind.DotsKeyword, tsOriginal) as DotsLiteral;
}
export interface ArgLiteral extends Expression {
kind: SyntaxKind.ArgKeyword;
}
export function isArgLiteral(node: Node): node is ArgLiteral {
return node.kind === SyntaxKind.ArgKeyword;
}
export function createArgLiteral(tsOriginal?: ts.Node): ArgLiteral {
return createNode(SyntaxKind.ArgKeyword, tsOriginal) as ArgLiteral;
}
// StringLiteral / NumberLiteral
// TODO TS uses the export interface "LiteralLikeNode" with a "text: string" member
// but since we don't parse from text I think we can simplify by just having a value member
// TODO NumericLiteral or NumberLiteral?
export interface NumericLiteral extends Expression {
kind: SyntaxKind.NumericLiteral;
value: number;
}
export function isNumericLiteral(node: Node): node is NumericLiteral {
return node.kind === SyntaxKind.NumericLiteral;
}
export function createNumericLiteral(value: number, tsOriginal?: ts.Node): NumericLiteral {
const expression = createNode(SyntaxKind.NumericLiteral, tsOriginal) as NumericLiteral;
expression.value = value;
return expression;
}
export interface StringLiteral extends Expression {
kind: SyntaxKind.StringLiteral;
value: string;
}
export function isStringLiteral(node: Node): node is StringLiteral {
return node.kind === SyntaxKind.StringLiteral;
}
export function createStringLiteral(value: string, tsOriginal?: ts.Node): StringLiteral {
const expression = createNode(SyntaxKind.StringLiteral, tsOriginal) as StringLiteral;
expression.value = value;
return expression;
}
export function isLiteral(
node: Node
): node is NilLiteral | DotsLiteral | ArgLiteral | BooleanLiteral | NumericLiteral | StringLiteral {
return (
isNilLiteral(node) ||
isDotsLiteral(node) ||
isArgLiteral(node) ||
isBooleanLiteral(node) ||
isNumericLiteral(node) ||
isStringLiteral(node)
);
}
export interface FunctionExpression extends Expression {
kind: SyntaxKind.FunctionExpression;
params?: Identifier[];
dots?: DotsLiteral;
body: Block;
}
export function isFunctionExpression(node: Node): node is FunctionExpression {
return node.kind === SyntaxKind.FunctionExpression;
}
export function createFunctionExpression(
body: Block,
params?: Identifier[],
dots?: DotsLiteral,
flags = NodeFlags.None,
tsOriginal?: ts.Node
): FunctionExpression {
const expression = createNode(SyntaxKind.FunctionExpression, tsOriginal) as FunctionExpression;
expression.body = body;
expression.params = params;
expression.dots = dots;
expression.flags = flags;
return expression;
}
export interface TableFieldExpression extends Expression {
kind: SyntaxKind.TableFieldExpression;
value: Expression;
key?: Expression;
}
export function isTableFieldExpression(node: Node): node is TableFieldExpression {
return node.kind === SyntaxKind.TableFieldExpression;
}
export function createTableFieldExpression(
value: Expression,
key?: Expression,
tsOriginal?: ts.Node
): TableFieldExpression {
const expression = createNode(SyntaxKind.TableFieldExpression, tsOriginal) as TableFieldExpression;
expression.value = value;
expression.key = key;
return expression;
}
export interface TableExpression extends Expression {
kind: SyntaxKind.TableExpression;
fields: TableFieldExpression[];
}
export function isTableExpression(node: Node): node is TableExpression {
return node.kind === SyntaxKind.TableExpression;
}
export function createTableExpression(fields: TableFieldExpression[] = [], tsOriginal?: ts.Node): TableExpression {
const expression = createNode(SyntaxKind.TableExpression, tsOriginal) as TableExpression;
expression.fields = fields;
return expression;
}
export interface UnaryExpression extends Expression {
kind: SyntaxKind.UnaryExpression;
operand: Expression;
operator: UnaryOperator;
}
export function isUnaryExpression(node: Node): node is UnaryExpression {
return node.kind === SyntaxKind.UnaryExpression;
}
export function createUnaryExpression(
operand: Expression,
operator: UnaryOperator,
tsOriginal?: ts.Node
): UnaryExpression {
const expression = createNode(SyntaxKind.UnaryExpression, tsOriginal) as UnaryExpression;
expression.operand = operand;
expression.operator = operator;
return expression;
}
export interface BinaryExpression extends Expression {
kind: SyntaxKind.BinaryExpression;
operator: BinaryOperator;
left: Expression;
right: Expression;
}
export function isBinaryExpression(node: Node): node is BinaryExpression {
return node.kind === SyntaxKind.BinaryExpression;
}
export function createBinaryExpression(
left: Expression,
right: Expression,
operator: BinaryOperator,
tsOriginal?: ts.Node
): BinaryExpression {
const expression = createNode(SyntaxKind.BinaryExpression, tsOriginal) as BinaryExpression;
expression.left = left;
expression.right = right;
expression.operator = operator;
return expression;
}
export interface CallExpression extends Expression {
kind: SyntaxKind.CallExpression;
expression: Expression;
params: Expression[];
}
export function isCallExpression(node: Node): node is CallExpression {
return node.kind === SyntaxKind.CallExpression;
}
export function createCallExpression(
expression: Expression,
params: Expression[],
tsOriginal?: ts.Node
): CallExpression {
const callExpression = createNode(SyntaxKind.CallExpression, tsOriginal) as CallExpression;
callExpression.expression = expression;
callExpression.params = params;
return callExpression;
}
export interface MethodCallExpression extends Expression {
kind: SyntaxKind.MethodCallExpression;
prefixExpression: Expression;
name: Identifier;
params: Expression[];
}
export function isMethodCallExpression(node: Node): node is MethodCallExpression {
return node.kind === SyntaxKind.MethodCallExpression;
}
export function createMethodCallExpression(
prefixExpression: Expression,
name: Identifier,
params: Expression[],
tsOriginal?: ts.Node
): MethodCallExpression {
const callExpression = createNode(SyntaxKind.MethodCallExpression, tsOriginal) as MethodCallExpression;
callExpression.prefixExpression = prefixExpression;
callExpression.name = name;
callExpression.params = params;
return callExpression;
}
export interface Identifier extends Expression {
kind: SyntaxKind.Identifier;
exportable: boolean;
text: string;
originalName?: string;
symbolId?: SymbolId;
}
export function isIdentifier(node: Node): node is Identifier {
return node.kind === SyntaxKind.Identifier;
}
export function createIdentifier(
text: string,
tsOriginal?: ts.Node,
symbolId?: SymbolId,
originalName?: string
): Identifier {
const expression = createNode(SyntaxKind.Identifier, tsOriginal) as Identifier;
expression.exportable = true;
expression.text = text;
expression.symbolId = symbolId;
expression.originalName = originalName;
return expression;
}
export function cloneIdentifier(identifier: Identifier, tsOriginal?: ts.Node): Identifier {
return createIdentifier(identifier.text, tsOriginal, identifier.symbolId, identifier.originalName);
}
export function createAnonymousIdentifier(tsOriginal?: ts.Node): Identifier {
const expression = createNode(SyntaxKind.Identifier, tsOriginal) as Identifier;
expression.exportable = false;
expression.text = "____";
return expression;
}
export interface TableIndexExpression extends Expression {
kind: SyntaxKind.TableIndexExpression;
table: Expression;
index: Expression;
}
export function isTableIndexExpression(node: Node): node is TableIndexExpression {
return node.kind === SyntaxKind.TableIndexExpression;
}
export function createTableIndexExpression(
table: Expression,
index: Expression,
tsOriginal?: ts.Node
): TableIndexExpression {
const expression = createNode(SyntaxKind.TableIndexExpression, tsOriginal) as TableIndexExpression;
expression.table = table;
expression.index = index;
return expression;
}
export type AssignmentLeftHandSideExpression = Identifier | TableIndexExpression;
export function isAssignmentLeftHandSideExpression(node: Node): node is AssignmentLeftHandSideExpression {
return isIdentifier(node) || isTableIndexExpression(node);
}
export type FunctionDefinition = (VariableDeclarationStatement | AssignmentStatement) & {
right: [FunctionExpression];
};
export function isFunctionDefinition(
statement: VariableDeclarationStatement | AssignmentStatement
): statement is FunctionDefinition {
return statement.left.length === 1 && statement.right?.length === 1 && isFunctionExpression(statement.right[0]);
}
export type InlineFunctionExpression = FunctionExpression & {
body: { statements: [ReturnStatement & { expressions: Expression[] }] };
};
export function isInlineFunctionExpression(expression: FunctionExpression): expression is InlineFunctionExpression {
return (
expression.body.statements?.length === 1 &&
isReturnStatement(expression.body.statements[0]) &&
expression.body.statements[0].expressions !== undefined &&
(expression.flags & NodeFlags.Inline) !== 0
);
}
export type ParenthesizedExpression = Expression & {
expression: Expression;
};
export function isParenthesizedExpression(node: Node): node is ParenthesizedExpression {
return node.kind === SyntaxKind.ParenthesizedExpression;
}
export function createParenthesizedExpression(expression: Expression, tsOriginal?: ts.Node): ParenthesizedExpression {
const parenthesizedExpression = createNode(
SyntaxKind.ParenthesizedExpression,
tsOriginal
) as ParenthesizedExpression;
parenthesizedExpression.expression = expression;
return parenthesizedExpression;
}
export type ConditionalExpression = Expression & {
condition: Expression;
whenTrue: Expression;
whenFalse: Expression;
};
export function isConditionalExpression(node: Node): node is ConditionalExpression {
return node.kind === SyntaxKind.ConditionalExpression;
}
export function createConditionalExpression(
condition: Expression,
whenTrue: Expression,
whenFalse: Expression,
tsOriginal?: ts.Node
): ConditionalExpression {
const conditionalExpression = createNode(SyntaxKind.ConditionalExpression, tsOriginal) as ConditionalExpression;
conditionalExpression.condition = condition;
conditionalExpression.whenTrue = whenTrue;
conditionalExpression.whenFalse = whenFalse;
return conditionalExpression;
}