Prettier (#316)
* Setup prettier with config borrowed from azure-sdk-for-js * Run prettier to format existing code * Turn format on save on so VS Code with prettier will auto-format * Add rush check-format / rush format to check formatting / run prettier on whole repo * Add check-format to PR validation
This commit is contained in:
Родитель
9094a8a7c8
Коммит
dbe9ffd544
|
@ -1,35 +1,42 @@
|
|||
import { DiagnosticError, formatDiagnostic } from './diagnostics.js';
|
||||
import { visitChildren } from './parser.js';
|
||||
import { ADLSourceFile, Program } from './program.js';
|
||||
import { createSourceFile } from './scanner.js';
|
||||
import { NamespaceStatementNode, ModelStatementNode, Node, SyntaxKind, TemplateParameterDeclarationNode, SourceLocation, Sym } from './types.js';
|
||||
|
||||
import { DiagnosticError, formatDiagnostic } from "./diagnostics.js";
|
||||
import { visitChildren } from "./parser.js";
|
||||
import { ADLSourceFile, Program } from "./program.js";
|
||||
import { createSourceFile } from "./scanner.js";
|
||||
import {
|
||||
NamespaceStatementNode,
|
||||
ModelStatementNode,
|
||||
Node,
|
||||
SyntaxKind,
|
||||
TemplateParameterDeclarationNode,
|
||||
SourceLocation,
|
||||
Sym,
|
||||
} from "./types.js";
|
||||
|
||||
export class SymbolTable extends Map<string, Sym> {
|
||||
duplicates = new Set<Sym>();
|
||||
|
||||
// First set for a given key wins, but record all duplicates for diagnostics.
|
||||
set(key: string, value: Sym) {
|
||||
const existing = this.get(key);
|
||||
if (existing === undefined) {
|
||||
super.set(key, value);
|
||||
} else {
|
||||
this.duplicates.add(existing);
|
||||
this.duplicates.add(value);
|
||||
}
|
||||
return this;
|
||||
// First set for a given key wins, but record all duplicates for diagnostics.
|
||||
set(key: string, value: Sym) {
|
||||
const existing = this.get(key);
|
||||
if (existing === undefined) {
|
||||
super.set(key, value);
|
||||
} else {
|
||||
this.duplicates.add(existing);
|
||||
this.duplicates.add(value);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
export interface DecoratorSymbol {
|
||||
kind: 'decorator';
|
||||
kind: "decorator";
|
||||
path: string;
|
||||
name: string;
|
||||
value: (...args: Array<any>) => any;
|
||||
}
|
||||
|
||||
export interface TypeSymbol {
|
||||
kind: 'type';
|
||||
kind: "type";
|
||||
node: Node;
|
||||
name: string;
|
||||
}
|
||||
|
@ -49,7 +56,11 @@ export function createBinder(): Binder {
|
|||
bindSourceFile,
|
||||
};
|
||||
|
||||
function bindSourceFile(program: Program, sourceFile: ADLSourceFile, globalScope: boolean = false) {
|
||||
function bindSourceFile(
|
||||
program: Program,
|
||||
sourceFile: ADLSourceFile,
|
||||
globalScope: boolean = false
|
||||
) {
|
||||
currentFile = sourceFile;
|
||||
bindNode(sourceFile.ast);
|
||||
|
||||
|
@ -78,7 +89,6 @@ export function createBinder(): Binder {
|
|||
bindTemplateParameterDeclaration(<any>node);
|
||||
}
|
||||
|
||||
|
||||
const prevParent = parentNode;
|
||||
// set parent node when we walk into children
|
||||
parentNode = node;
|
||||
|
@ -98,7 +108,7 @@ export function createBinder(): Binder {
|
|||
|
||||
function bindTemplateParameterDeclaration(node: TemplateParameterDeclarationNode) {
|
||||
(<ModelStatementNode>scope).locals!.set(node.sv, {
|
||||
kind: 'type',
|
||||
kind: "type",
|
||||
node: node,
|
||||
name: node.sv,
|
||||
});
|
||||
|
@ -106,7 +116,7 @@ export function createBinder(): Binder {
|
|||
|
||||
function bindModelStatement(node: ModelStatementNode) {
|
||||
currentFile.symbols.set(node.id.sv, {
|
||||
kind: 'type',
|
||||
kind: "type",
|
||||
node: node,
|
||||
name: node.id.sv,
|
||||
});
|
||||
|
@ -115,11 +125,9 @@ export function createBinder(): Binder {
|
|||
node.locals = new SymbolTable();
|
||||
}
|
||||
|
||||
function bindInterfaceStatement(
|
||||
statement: NamespaceStatementNode
|
||||
) {
|
||||
function bindInterfaceStatement(statement: NamespaceStatementNode) {
|
||||
currentFile.symbols.set(statement.id.sv, {
|
||||
kind: 'type',
|
||||
kind: "type",
|
||||
node: statement,
|
||||
name: statement.id.sv,
|
||||
});
|
||||
|
@ -146,13 +154,13 @@ export function createBinder(): Binder {
|
|||
// That said, decorators are entered into the global symbol table before
|
||||
// any source file is bound and therefore this will include all duplicate
|
||||
// decorator implementations.
|
||||
throw new DiagnosticError(messages.join('\n'));
|
||||
throw new DiagnosticError(messages.join("\n"));
|
||||
}
|
||||
|
||||
function report(symbol: Sym) {
|
||||
if (!reported.has(symbol)) {
|
||||
reported.add(symbol);
|
||||
const message = formatDiagnostic('Duplicate name: ' + symbol.name, symbol);
|
||||
const message = formatDiagnostic("Duplicate name: " + symbol.name, symbol);
|
||||
messages.push(message);
|
||||
}
|
||||
}
|
||||
|
|
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
|
@ -1,5 +1,5 @@
|
|||
import { throwDiagnostic } from './diagnostics.js';
|
||||
import { ADLSourceFile, Program } from './program.js';
|
||||
import { throwDiagnostic } from "./diagnostics.js";
|
||||
import { ADLSourceFile, Program } from "./program.js";
|
||||
import {
|
||||
ArrayExpressionNode,
|
||||
BooleanLiteralNode,
|
||||
|
@ -30,8 +30,8 @@ import {
|
|||
Type,
|
||||
UnionExpressionNode,
|
||||
UnionType,
|
||||
ReferenceExpression
|
||||
} from './types.js';
|
||||
ReferenceExpression,
|
||||
} from "./types.js";
|
||||
|
||||
/**
|
||||
* A map keyed by a set of objects. Used as a type cache where the base type
|
||||
|
@ -55,7 +55,7 @@ export class MultiKeyMap<T> {
|
|||
}
|
||||
|
||||
compositeKeyFor(items: Array<object>) {
|
||||
return items.map(i => this.keyFor(i)).join(',');
|
||||
return items.map((i) => this.keyFor(i)).join(",");
|
||||
}
|
||||
|
||||
keyFor(item: object) {
|
||||
|
@ -80,7 +80,7 @@ export function createChecker(program: Program) {
|
|||
checkProgram,
|
||||
getLiteralType,
|
||||
getTypeName,
|
||||
checkNamespaceProperty
|
||||
checkNamespaceProperty,
|
||||
};
|
||||
|
||||
function getTypeForNode(node: Node): Type {
|
||||
|
@ -121,24 +121,24 @@ export function createChecker(program: Program) {
|
|||
return checkTemplateParameterDeclaration(node);
|
||||
}
|
||||
|
||||
throwDiagnostic('Cannot evaluate ' + SyntaxKind[node.kind], node);
|
||||
throwDiagnostic("Cannot evaluate " + SyntaxKind[node.kind], node);
|
||||
}
|
||||
|
||||
function getTypeName(type: Type): string {
|
||||
switch (type.kind) {
|
||||
case 'Model':
|
||||
case "Model":
|
||||
return getModelName(type);
|
||||
case 'Union':
|
||||
return type.options.map(getTypeName).join(' | ');
|
||||
case 'Array':
|
||||
return getTypeName(type.elementType) + '[]';
|
||||
case 'String':
|
||||
case 'Number':
|
||||
case 'Boolean':
|
||||
case "Union":
|
||||
return type.options.map(getTypeName).join(" | ");
|
||||
case "Array":
|
||||
return getTypeName(type.elementType) + "[]";
|
||||
case "String":
|
||||
case "Number":
|
||||
case "Boolean":
|
||||
return type.value.toString();
|
||||
}
|
||||
|
||||
return '(unnamed type)';
|
||||
return "(unnamed type)";
|
||||
}
|
||||
|
||||
function getModelName(model: ModelType) {
|
||||
|
@ -147,14 +147,14 @@ export function createChecker(program: Program) {
|
|||
} else if (model.templateArguments && model.templateArguments.length > 0) {
|
||||
// template instantiation
|
||||
const args = model.templateArguments.map(getTypeName);
|
||||
return `${model.name}<${args.join(', ')}>`;
|
||||
return `${model.name}<${args.join(", ")}>`;
|
||||
} else if ((<ModelStatementNode>model.node).templateParameters?.length > 0) {
|
||||
// template
|
||||
const params = (<ModelStatementNode>model.node).templateParameters.map(t => t.sv);
|
||||
return `${model.name}<${params.join(', ')}>`;
|
||||
const params = (<ModelStatementNode>model.node).templateParameters.map((t) => t.sv);
|
||||
return `${model.name}<${params.join(", ")}>`;
|
||||
} else {
|
||||
// regular old model.
|
||||
return model.name || '(anonymous model)';
|
||||
return model.name || "(anonymous model)";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -162,13 +162,13 @@ export function createChecker(program: Program) {
|
|||
const parentNode = <ModelStatementNode>node.parent!;
|
||||
|
||||
if (instantiatingTemplate === parentNode) {
|
||||
const index = parentNode.templateParameters.findIndex(v => v === node);
|
||||
const index = parentNode.templateParameters.findIndex((v) => v === node);
|
||||
return templateInstantiation[index];
|
||||
}
|
||||
|
||||
return createType({
|
||||
kind: 'TemplateParameter',
|
||||
node: node
|
||||
kind: "TemplateParameter",
|
||||
node: node,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -179,7 +179,6 @@ export function createChecker(program: Program) {
|
|||
return instantiateTemplate(<ModelStatementNode>targetType.node, args);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Builds a model type from a template and its template arguments.
|
||||
* Adds the template node to a set we can check when we bind template
|
||||
|
@ -190,11 +189,11 @@ export function createChecker(program: Program) {
|
|||
*/
|
||||
function instantiateTemplate(templateNode: ModelStatementNode, args: Array<Type>): ModelType {
|
||||
if (templateNode.templateParameters!.length < args.length) {
|
||||
throwDiagnostic('Too few template arguments provided.', templateNode);
|
||||
throwDiagnostic("Too few template arguments provided.", templateNode);
|
||||
}
|
||||
|
||||
if (templateNode.templateParameters!.length > args.length) {
|
||||
throwDiagnostic('Too many template arguments provided.', templateNode);
|
||||
throwDiagnostic("Too many template arguments provided.", templateNode);
|
||||
}
|
||||
|
||||
const oldTis = templateInstantiation;
|
||||
|
@ -211,14 +210,14 @@ export function createChecker(program: Program) {
|
|||
|
||||
function checkUnionExpression(node: UnionExpressionNode): UnionType {
|
||||
return createType({
|
||||
kind: 'Union',
|
||||
kind: "Union",
|
||||
node,
|
||||
options: node.options.map(getTypeForNode),
|
||||
});
|
||||
}
|
||||
|
||||
function allModelTypes(types: Array<Type>): types is Array<ModelType> {
|
||||
return types.every(t => t.kind === 'Model');
|
||||
return types.every((t) => t.kind === "Model");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -229,7 +228,7 @@ export function createChecker(program: Program) {
|
|||
function checkIntersectionExpression(node: IntersectionExpressionNode) {
|
||||
const optionTypes = node.options.map(getTypeForNode);
|
||||
if (!allModelTypes(optionTypes)) {
|
||||
throwDiagnostic('Cannot intersect non-model types (including union types).', node);
|
||||
throwDiagnostic("Cannot intersect non-model types (including union types).", node);
|
||||
}
|
||||
|
||||
const properties = new Map<string, ModelTypeProperty>();
|
||||
|
@ -237,25 +236,30 @@ export function createChecker(program: Program) {
|
|||
const allProps = walkPropertiesInherited(option);
|
||||
for (const prop of allProps) {
|
||||
if (properties.has(prop.name)) {
|
||||
throwDiagnostic(`Intersection contains duplicate property definitions for ${prop.name}`, node);
|
||||
throwDiagnostic(
|
||||
`Intersection contains duplicate property definitions for ${prop.name}`,
|
||||
node
|
||||
);
|
||||
}
|
||||
|
||||
const newPropType = createType({
|
||||
... prop,
|
||||
sourceProperty: prop
|
||||
}, true);
|
||||
const newPropType = createType(
|
||||
{
|
||||
...prop,
|
||||
sourceProperty: prop,
|
||||
},
|
||||
true
|
||||
);
|
||||
|
||||
properties.set(prop.name, newPropType);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const intersection = createType({
|
||||
kind: 'Model',
|
||||
kind: "Model",
|
||||
node,
|
||||
name: '',
|
||||
name: "",
|
||||
baseModels: [],
|
||||
properties: properties
|
||||
properties: properties,
|
||||
});
|
||||
|
||||
return intersection;
|
||||
|
@ -263,7 +267,7 @@ export function createChecker(program: Program) {
|
|||
|
||||
function checkArrayExpression(node: ArrayExpressionNode) {
|
||||
return createType({
|
||||
kind: 'Array',
|
||||
kind: "Array",
|
||||
node,
|
||||
elementType: getTypeForNode(node.elementType),
|
||||
});
|
||||
|
@ -271,11 +275,11 @@ export function createChecker(program: Program) {
|
|||
|
||||
function checkNamespace(node: NamespaceStatementNode) {
|
||||
const type: Namespace = createType({
|
||||
kind: 'Namespace',
|
||||
kind: "Namespace",
|
||||
name: node.id.sv,
|
||||
node: node,
|
||||
properties: new Map(),
|
||||
parameters: node.parameters ? <ModelType>getTypeForNode(node.parameters) : undefined
|
||||
parameters: node.parameters ? <ModelType>getTypeForNode(node.parameters) : undefined,
|
||||
});
|
||||
|
||||
for (const prop of node.properties) {
|
||||
|
@ -287,7 +291,7 @@ export function createChecker(program: Program) {
|
|||
|
||||
function checkNamespaceProperty(prop: NamespacePropertyNode): NamespaceProperty {
|
||||
return createType({
|
||||
kind: 'NamespaceProperty',
|
||||
kind: "NamespaceProperty",
|
||||
name: prop.id.sv,
|
||||
node: prop,
|
||||
parameters: <ModelType>getTypeForNode(prop.parameters),
|
||||
|
@ -295,10 +299,9 @@ export function createChecker(program: Program) {
|
|||
});
|
||||
}
|
||||
|
||||
|
||||
function checkTupleExpression(node: TupleExpressionNode): TupleType {
|
||||
return createType({
|
||||
kind: 'Tuple',
|
||||
kind: "Tuple",
|
||||
node: node,
|
||||
values: node.values.map((v) => getTypeForNode(v)),
|
||||
});
|
||||
|
@ -306,7 +309,7 @@ export function createChecker(program: Program) {
|
|||
|
||||
function checkIdentifier(node: IdentifierNode) {
|
||||
const binding = resolveIdentifier(node);
|
||||
if (binding.kind === 'decorator') {
|
||||
if (binding.kind === "decorator") {
|
||||
return {};
|
||||
} else {
|
||||
return getTypeForNode(binding.node);
|
||||
|
@ -318,7 +321,7 @@ export function createChecker(program: Program) {
|
|||
let binding;
|
||||
|
||||
while (scope) {
|
||||
if ('locals' in scope) {
|
||||
if ("locals" in scope) {
|
||||
binding = (<any>scope).locals.get(node.sv);
|
||||
if (binding) break;
|
||||
}
|
||||
|
@ -330,7 +333,7 @@ export function createChecker(program: Program) {
|
|||
}
|
||||
|
||||
if (!binding) {
|
||||
throwDiagnostic('Unknown identifier ' + node.sv, node);
|
||||
throwDiagnostic("Unknown identifier " + node.sv, node);
|
||||
}
|
||||
|
||||
return binding;
|
||||
|
@ -363,12 +366,11 @@ export function createChecker(program: Program) {
|
|||
function checkModel(node: ModelExpressionNode | ModelStatementNode) {
|
||||
if (node.properties) {
|
||||
const properties = new Map();
|
||||
const baseModels = node.kind === SyntaxKind.ModelExpression
|
||||
? []
|
||||
: checkClassHeritage(node.heritage);
|
||||
const baseModels =
|
||||
node.kind === SyntaxKind.ModelExpression ? [] : checkClassHeritage(node.heritage);
|
||||
|
||||
for (const prop of node.properties) {
|
||||
if ('id' in prop) {
|
||||
if ("id" in prop) {
|
||||
const propType = <ModelTypeProperty>getTypeForNode(prop);
|
||||
properties.set(propType.name, propType);
|
||||
} else {
|
||||
|
@ -386,25 +388,24 @@ export function createChecker(program: Program) {
|
|||
}
|
||||
|
||||
return createType({
|
||||
kind: 'Model',
|
||||
name: node.kind === SyntaxKind.ModelStatement ? node.id.sv : '',
|
||||
kind: "Model",
|
||||
name: node.kind === SyntaxKind.ModelStatement ? node.id.sv : "",
|
||||
node: node,
|
||||
properties,
|
||||
baseModels: baseModels
|
||||
baseModels: baseModels,
|
||||
});
|
||||
|
||||
} else {
|
||||
// model =
|
||||
// this will likely have to change, as right now `model =` is really just
|
||||
// alias and so disappears. That means you can't easily rename symbols.
|
||||
const assignmentType = getTypeForNode((<ModelStatementNode>node).assignment!);
|
||||
|
||||
if (assignmentType.kind === 'Model') {
|
||||
if (assignmentType.kind === "Model") {
|
||||
const type: ModelType = createType({
|
||||
... <ModelType>assignmentType,
|
||||
...(<ModelType>assignmentType),
|
||||
node: node,
|
||||
name: (<ModelStatementNode>node).id.sv,
|
||||
assignmentType
|
||||
assignmentType,
|
||||
});
|
||||
|
||||
return type;
|
||||
|
@ -415,7 +416,7 @@ export function createChecker(program: Program) {
|
|||
}
|
||||
|
||||
function checkClassHeritage(heritage: ReferenceExpression[]): ModelType[] {
|
||||
return heritage.flatMap(heritageRef => {
|
||||
return heritage.flatMap((heritageRef) => {
|
||||
const heritageType = getTypeForNode(heritageRef);
|
||||
|
||||
if (heritageType.kind === "TemplateParameter") {
|
||||
|
@ -435,18 +436,20 @@ export function createChecker(program: Program) {
|
|||
const props: ModelTypeProperty[] = [];
|
||||
const targetType = getTypeForNode(targetNode);
|
||||
|
||||
|
||||
if (targetType.kind != 'TemplateParameter') {
|
||||
if (targetType.kind !== 'Model') {
|
||||
throwDiagnostic('Cannot spread properties of non-model type.', targetNode);
|
||||
if (targetType.kind != "TemplateParameter") {
|
||||
if (targetType.kind !== "Model") {
|
||||
throwDiagnostic("Cannot spread properties of non-model type.", targetNode);
|
||||
}
|
||||
|
||||
// copy each property
|
||||
for (const prop of walkPropertiesInherited(targetType)) {
|
||||
const newProp = createType({
|
||||
... prop,
|
||||
sourceProperty: prop
|
||||
}, true)
|
||||
const newProp = createType(
|
||||
{
|
||||
...prop,
|
||||
sourceProperty: prop,
|
||||
},
|
||||
true
|
||||
);
|
||||
props.push(newProp);
|
||||
}
|
||||
}
|
||||
|
@ -467,11 +470,10 @@ export function createChecker(program: Program) {
|
|||
return props;
|
||||
}
|
||||
|
||||
|
||||
function checkModelProperty(prop: ModelPropertyNode): ModelTypeProperty {
|
||||
if (prop.id.kind === SyntaxKind.Identifier) {
|
||||
return createType({
|
||||
kind: 'ModelProperty',
|
||||
kind: "ModelProperty",
|
||||
name: prop.id.sv,
|
||||
node: prop,
|
||||
optional: prop.optional,
|
||||
|
@ -480,7 +482,7 @@ export function createChecker(program: Program) {
|
|||
} else {
|
||||
const name = prop.id.value;
|
||||
return createType({
|
||||
kind: 'ModelProperty',
|
||||
kind: "ModelProperty",
|
||||
name,
|
||||
node: prop,
|
||||
optional: prop.optional,
|
||||
|
@ -496,7 +498,7 @@ export function createChecker(program: Program) {
|
|||
(<any>typeDef).templateArguments = templateInstantiation;
|
||||
|
||||
// only run decorators on fully instantiated types.
|
||||
if (templateInstantiation.every(i => i.kind !== 'TemplateParameter')) {
|
||||
if (templateInstantiation.every((i) => i.kind !== "TemplateParameter")) {
|
||||
program.executeDecorators(typeDef);
|
||||
}
|
||||
|
||||
|
@ -514,13 +516,13 @@ export function createChecker(program: Program) {
|
|||
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.StringLiteral:
|
||||
type = { kind: 'String', node, value: node.value };
|
||||
type = { kind: "String", node, value: node.value };
|
||||
break;
|
||||
case SyntaxKind.NumericLiteral:
|
||||
type = { kind: 'Number', node, value: node.value };
|
||||
type = { kind: "Number", node, value: node.value };
|
||||
break;
|
||||
case SyntaxKind.BooleanLiteral:
|
||||
type = { kind: 'Boolean', node, value: node.value };
|
||||
type = { kind: "Boolean", node, value: node.value };
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -15,66 +15,61 @@ const adlVersion = getVersion();
|
|||
const args = yargs(process.argv.slice(2))
|
||||
.help()
|
||||
.strict()
|
||||
.command(
|
||||
"compile <path>",
|
||||
"Compile a directory of ADL files.",
|
||||
cmd => {
|
||||
return cmd
|
||||
.positional("path", {
|
||||
description:
|
||||
"The path to folder containing .adl files",
|
||||
type: "string"
|
||||
})
|
||||
.option("output-path", {
|
||||
type: "string",
|
||||
default: "./adl-output",
|
||||
describe: "The output path for generated artifacts. If it does not exist, it will be created."
|
||||
})
|
||||
.option("nostdlib", {
|
||||
type: "boolean",
|
||||
default: false,
|
||||
describe: "Don't load the ADL standard library."
|
||||
});
|
||||
}
|
||||
)
|
||||
.command("compile <path>", "Compile a directory of ADL files.", (cmd) => {
|
||||
return cmd
|
||||
.positional("path", {
|
||||
description: "The path to folder containing .adl files",
|
||||
type: "string",
|
||||
})
|
||||
.option("output-path", {
|
||||
type: "string",
|
||||
default: "./adl-output",
|
||||
describe:
|
||||
"The output path for generated artifacts. If it does not exist, it will be created.",
|
||||
})
|
||||
.option("nostdlib", {
|
||||
type: "boolean",
|
||||
default: false,
|
||||
describe: "Don't load the ADL standard library.",
|
||||
});
|
||||
})
|
||||
.command(
|
||||
"generate <path>",
|
||||
"Generate client and server code from a directory of ADL files.",
|
||||
cmd => {
|
||||
(cmd) => {
|
||||
return cmd
|
||||
.positional("path", {
|
||||
description:
|
||||
"The path to folder containing .adl files",
|
||||
type: "string"
|
||||
description: "The path to folder containing .adl files",
|
||||
type: "string",
|
||||
})
|
||||
.option("client", {
|
||||
type: "boolean",
|
||||
describe: "Generate a client library for the ADL definition"
|
||||
describe: "Generate a client library for the ADL definition",
|
||||
})
|
||||
.option("language", {
|
||||
type: "string",
|
||||
choices: ["typescript", "csharp", "python"],
|
||||
describe: "The language to use for code generation"
|
||||
describe: "The language to use for code generation",
|
||||
})
|
||||
.option("output-path", {
|
||||
type: "string",
|
||||
default: "./adl-output",
|
||||
describe: "The output path for generated artifacts. If it does not exist, it will be created."
|
||||
describe:
|
||||
"The output path for generated artifacts. If it does not exist, it will be created.",
|
||||
});
|
||||
}
|
||||
)
|
||||
.option("debug", {
|
||||
type: "boolean",
|
||||
description: "Output debug log messages."
|
||||
description: "Output debug log messages.",
|
||||
})
|
||||
.option("verbose", {
|
||||
alias: "v",
|
||||
type: "boolean",
|
||||
description: "Output verbose log messages."
|
||||
description: "Output verbose log messages.",
|
||||
})
|
||||
.version(adlVersion)
|
||||
.demandCommand(1, "You must use one of the supported commands.")
|
||||
.argv;
|
||||
.demandCommand(1, "You must use one of the supported commands.").argv;
|
||||
|
||||
async function compileInput(compilerOptions: CompilerOptions): Promise<boolean> {
|
||||
try {
|
||||
|
@ -101,17 +96,13 @@ async function getCompilerOptions(): Promise<CompilerOptions> {
|
|||
return {
|
||||
outputPath,
|
||||
swaggerOutputFile: path.resolve(args["output-path"], "openapi.json"),
|
||||
nostdlib: args["nostdlib"]
|
||||
nostdlib: args["nostdlib"],
|
||||
};
|
||||
}
|
||||
|
||||
function getVersion(): string {
|
||||
const packageJsonPath = new url.URL(`../../package.json`, import.meta.url);
|
||||
const packageJson = JSON.parse(
|
||||
readFileSync(
|
||||
url.fileURLToPath(packageJsonPath),
|
||||
"utf-8")
|
||||
);
|
||||
const packageJson = JSON.parse(readFileSync(url.fileURLToPath(packageJsonPath), "utf-8"));
|
||||
return packageJson.version;
|
||||
}
|
||||
|
||||
|
@ -123,7 +114,7 @@ async function main() {
|
|||
if (!(await compileInput(options))) {
|
||||
process.exit(1);
|
||||
}
|
||||
console.log(`Compilation completed successfully, output files are in ${options.outputPath}.`)
|
||||
console.log(`Compilation completed successfully, output files are in ${options.outputPath}.`);
|
||||
} else if (args._[0] === "generate") {
|
||||
const options = await getCompilerOptions();
|
||||
if (!(await compileInput(options))) {
|
||||
|
@ -132,28 +123,31 @@ async function main() {
|
|||
|
||||
if (args.client) {
|
||||
const clientPath = path.resolve(args["output-path"], "client");
|
||||
const autoRestBin =
|
||||
process.platform === "win32"
|
||||
? "autorest.cmd"
|
||||
: "autorest"
|
||||
const autoRestBin = process.platform === "win32" ? "autorest.cmd" : "autorest";
|
||||
const autoRestPath = new url.URL(`../../node_modules/.bin/${autoRestBin}`, import.meta.url);
|
||||
|
||||
// Execute AutoRest on the output file
|
||||
const result = spawnSync(url.fileURLToPath(autoRestPath), [
|
||||
`--${args.language}`,
|
||||
`--clear-output-folder=true`,
|
||||
`--output-folder=${clientPath}`,
|
||||
`--title=AdlClient`,
|
||||
`--input-file=${options.swaggerOutputFile}`
|
||||
], {
|
||||
stdio: 'inherit',
|
||||
shell: true
|
||||
});
|
||||
const result = spawnSync(
|
||||
url.fileURLToPath(autoRestPath),
|
||||
[
|
||||
`--${args.language}`,
|
||||
`--clear-output-folder=true`,
|
||||
`--output-folder=${clientPath}`,
|
||||
`--title=AdlClient`,
|
||||
`--input-file=${options.swaggerOutputFile}`,
|
||||
],
|
||||
{
|
||||
stdio: "inherit",
|
||||
shell: true,
|
||||
}
|
||||
);
|
||||
|
||||
if (result.status === 0) {
|
||||
console.log(`Generation completed successfully, output files are in ${options.outputPath}.`)
|
||||
console.log(
|
||||
`Generation completed successfully, output files are in ${options.outputPath}.`
|
||||
);
|
||||
} else {
|
||||
console.error("\nAn error occurred during compilation or client generation.")
|
||||
console.error("\nAn error occurred during compilation or client generation.");
|
||||
process.exit(result.status || 1);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,9 +3,9 @@ import { Message, Node, SourceLocation, SyntaxKind, Type, Sym } from "./types.js
|
|||
|
||||
/**
|
||||
* Represents an error in the code input that is fatal and bails the compilation.
|
||||
*
|
||||
*
|
||||
* This isn't meant to be kept long term, but we currently do this on all errors.
|
||||
*/
|
||||
*/
|
||||
export class DiagnosticError extends Error {
|
||||
constructor(message: string) {
|
||||
super(message);
|
||||
|
@ -26,19 +26,31 @@ export class ChainedError extends Error {
|
|||
|
||||
export type DiagnosticTarget = Node | Type | Sym | SourceLocation;
|
||||
|
||||
export type ErrorHandler = (message: Message | string, target: DiagnosticTarget, ...args: Array<string | number>) => void;
|
||||
export type ErrorHandler = (
|
||||
message: Message | string,
|
||||
target: DiagnosticTarget,
|
||||
...args: Array<string | number>
|
||||
) => void;
|
||||
|
||||
export const throwOnError: ErrorHandler = throwDiagnostic;
|
||||
|
||||
export function throwDiagnostic(message: Message | string, target: DiagnosticTarget, ...args: Array<string | number>): never {
|
||||
export function throwDiagnostic(
|
||||
message: Message | string,
|
||||
target: DiagnosticTarget,
|
||||
...args: Array<string | number>
|
||||
): never {
|
||||
throw new DiagnosticError(formatDiagnostic(message, target, ...args));
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Format a diagnostic into <file>:<line> - ADL<code> <category>: <text>.
|
||||
* Take extra care to preserve all info in thrown Error if this fails.
|
||||
*/
|
||||
export function formatDiagnostic(message: Message | string, target: DiagnosticTarget, ...args: Array<string | number>) {
|
||||
export function formatDiagnostic(
|
||||
message: Message | string,
|
||||
target: DiagnosticTarget,
|
||||
...args: Array<string | number>
|
||||
) {
|
||||
let location: SourceLocation;
|
||||
let locationError: Error | undefined;
|
||||
|
||||
|
@ -46,22 +58,24 @@ export function formatDiagnostic(message: Message | string, target: DiagnosticTa
|
|||
location = getSourceLocation(target);
|
||||
} catch (err) {
|
||||
locationError = err;
|
||||
location = {
|
||||
location = {
|
||||
file: createSourceFile("", "<unknown location>"),
|
||||
pos: 0,
|
||||
end: 0
|
||||
}
|
||||
end: 0,
|
||||
};
|
||||
}
|
||||
|
||||
if (typeof message === 'string') {
|
||||
if (typeof message === "string") {
|
||||
// Temporarily allow ad-hoc strings as error messages.
|
||||
message = { code: -1, text: message, category: 'error' }
|
||||
message = { code: -1, text: message, category: "error" };
|
||||
}
|
||||
|
||||
const [msg, formatError] = format(message.text, ...args);
|
||||
const code = message.code < 0 ? "" : ` ADL${message.code}`;
|
||||
const pos = location.file.getLineAndCharacterOfPosition(location.pos);
|
||||
const diagnostic = `${location.file.path}:${pos.line + 1}:${pos.character + 1} - ${message.category}${code}: ${msg}`;
|
||||
const diagnostic = `${location.file.path}:${pos.line + 1}:${pos.character + 1} - ${
|
||||
message.category
|
||||
}${code}: ${msg}`;
|
||||
|
||||
if (locationError || formatError) {
|
||||
throw new ChainedError(diagnostic, locationError, formatError);
|
||||
|
@ -101,8 +115,8 @@ function getSourceLocationOfNode(node: Node): SourceLocation {
|
|||
return {
|
||||
file: root.file,
|
||||
pos: node.pos,
|
||||
end: node.end
|
||||
}
|
||||
end: node.end,
|
||||
};
|
||||
}
|
||||
|
||||
export function dumpError(error: Error, writeLine: (s?: string) => void) {
|
||||
|
@ -133,4 +147,4 @@ function format(text: string, ...args: Array<string | number>): [string, Error?]
|
|||
|
||||
function isNotUndefined<T>(value: T | undefined): value is T {
|
||||
return value !== undefined;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,13 +2,30 @@ import { Message } from "./types";
|
|||
|
||||
export const messages: { [key: string]: Message } = {
|
||||
// Scanner errors
|
||||
DigitExpected: { code: 1100, category: 'error', text: 'Digit expected (0-9)' },
|
||||
HexDigitExpected: { code: 1101, category: 'error', text: 'Hex Digit expected (0-F,0-f)' },
|
||||
BinaryDigitExpected: { code: 1102, category: 'error', text: 'Binary Digit expected (0,1)' },
|
||||
UnexpectedEndOfFile: { code: 1103, category: 'error', text: 'Unexpected end of file while searching for \'{0}\'' },
|
||||
InvalidEscapeSequence: { code: 1104, category: 'error', text: 'Invalid escape sequence' },
|
||||
NoNewLineAtStartOfTripleQuotedString: { code: 1105, category: 'error', text: 'String content in triple quotes must begin on a new line' },
|
||||
NoNewLineAtEndOfTripleQuotedString: { code: 1106, category: 'error', text: 'Closing triple quotes must begin on a new line' },
|
||||
InconsistentTripleQuoteIndentation: { code: 1107, category: 'error', text: 'All lines in triple-quoted string lines must have the same indentation as closing triple quotes' },
|
||||
UnexpectedToken: { code: 1108, category: 'error', text: 'Unexpected token: \'{0}\'' },
|
||||
DigitExpected: { code: 1100, category: "error", text: "Digit expected (0-9)" },
|
||||
HexDigitExpected: { code: 1101, category: "error", text: "Hex Digit expected (0-F,0-f)" },
|
||||
BinaryDigitExpected: { code: 1102, category: "error", text: "Binary Digit expected (0,1)" },
|
||||
UnexpectedEndOfFile: {
|
||||
code: 1103,
|
||||
category: "error",
|
||||
text: "Unexpected end of file while searching for '{0}'",
|
||||
},
|
||||
InvalidEscapeSequence: { code: 1104, category: "error", text: "Invalid escape sequence" },
|
||||
NoNewLineAtStartOfTripleQuotedString: {
|
||||
code: 1105,
|
||||
category: "error",
|
||||
text: "String content in triple quotes must begin on a new line",
|
||||
},
|
||||
NoNewLineAtEndOfTripleQuotedString: {
|
||||
code: 1106,
|
||||
category: "error",
|
||||
text: "Closing triple quotes must begin on a new line",
|
||||
},
|
||||
InconsistentTripleQuoteIndentation: {
|
||||
code: 1107,
|
||||
category: "error",
|
||||
text:
|
||||
"All lines in triple-quoted string lines must have the same indentation as closing triple quotes",
|
||||
},
|
||||
UnexpectedToken: { code: 1108, category: "error", text: "Unexpected token: '{0}'" },
|
||||
};
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import { DiagnosticError, throwDiagnostic } from './diagnostics.js';
|
||||
import { createScanner, Token } from './scanner.js';
|
||||
import * as Types from './types.js';
|
||||
|
||||
import { DiagnosticError, throwDiagnostic } from "./diagnostics.js";
|
||||
import { createScanner, Token } from "./scanner.js";
|
||||
import * as Types from "./types.js";
|
||||
|
||||
export function parse(code: string | Types.SourceFile) {
|
||||
const scanner = createScanner(code);
|
||||
|
@ -14,7 +13,7 @@ export function parse(code: string | Types.SourceFile) {
|
|||
file: scanner.file,
|
||||
statements: [],
|
||||
pos: 0,
|
||||
end: 0
|
||||
end: 0,
|
||||
};
|
||||
|
||||
while (!scanner.eof()) {
|
||||
|
@ -26,7 +25,6 @@ export function parse(code: string | Types.SourceFile) {
|
|||
}
|
||||
|
||||
function parseStatement(): Types.Statement {
|
||||
// eslint-disable-next-line no-constant-condition
|
||||
while (true) {
|
||||
const decorators = parseDecoratorList();
|
||||
const tok = token();
|
||||
|
@ -34,7 +32,7 @@ export function parse(code: string | Types.SourceFile) {
|
|||
switch (tok) {
|
||||
case Token.ImportKeyword:
|
||||
if (decorators.length > 0) {
|
||||
error('Cannot decorate an import statement');
|
||||
error("Cannot decorate an import statement");
|
||||
}
|
||||
return parseImportStatement();
|
||||
case Token.ModelKeyword:
|
||||
|
@ -43,7 +41,7 @@ export function parse(code: string | Types.SourceFile) {
|
|||
return parseInterfaceStatement(decorators);
|
||||
case Token.Semicolon:
|
||||
if (decorators.length > 0) {
|
||||
error('Cannot decorate an empty statement');
|
||||
error("Cannot decorate an empty statement");
|
||||
}
|
||||
// no need to put empty statement nodes in the tree for now
|
||||
// since we aren't trying to emit ADL
|
||||
|
@ -78,14 +76,16 @@ export function parse(code: string | Types.SourceFile) {
|
|||
parseExpected(Token.OpenParen);
|
||||
const modelProps = parseModelPropertyList();
|
||||
parseExpected(Token.CloseParen);
|
||||
parameters = finishNode({
|
||||
kind: Types.SyntaxKind.ModelExpression,
|
||||
properties: modelProps,
|
||||
decorators: []
|
||||
}, modelPos);
|
||||
parameters = finishNode(
|
||||
{
|
||||
kind: Types.SyntaxKind.ModelExpression,
|
||||
properties: modelProps,
|
||||
decorators: [],
|
||||
},
|
||||
modelPos
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
parseExpected(Token.OpenBrace);
|
||||
const properties: Array<Types.NamespacePropertyNode> = [];
|
||||
|
||||
|
@ -99,43 +99,54 @@ export function parse(code: string | Types.SourceFile) {
|
|||
|
||||
parseExpected(Token.CloseBrace);
|
||||
|
||||
return finishNode({
|
||||
kind: Types.SyntaxKind.NamespaceStatement,
|
||||
decorators,
|
||||
id,
|
||||
parameters,
|
||||
properties
|
||||
}, pos);
|
||||
return finishNode(
|
||||
{
|
||||
kind: Types.SyntaxKind.NamespaceStatement,
|
||||
decorators,
|
||||
id,
|
||||
parameters,
|
||||
properties,
|
||||
},
|
||||
pos
|
||||
);
|
||||
}
|
||||
|
||||
function parseNamespaceProperty(decorators: Array<Types.DecoratorExpressionNode>): Types.NamespacePropertyNode {
|
||||
function parseNamespaceProperty(
|
||||
decorators: Array<Types.DecoratorExpressionNode>
|
||||
): Types.NamespacePropertyNode {
|
||||
const pos = tokenPos();
|
||||
parseExpected(Token.OpKeyword);
|
||||
const id = parseIdentifier();
|
||||
parseExpected(Token.OpenParen);
|
||||
const modelPos = tokenPos();
|
||||
let modelProps: Array<Types.ModelPropertyNode | Types.ModelSpreadPropertyNode>= [];
|
||||
let modelProps: Array<Types.ModelPropertyNode | Types.ModelSpreadPropertyNode> = [];
|
||||
|
||||
if (!parseOptional(Token.CloseParen)) {
|
||||
modelProps = parseModelPropertyList();
|
||||
parseExpected(Token.CloseParen);
|
||||
}
|
||||
const parameters: Types.ModelExpressionNode = finishNode({
|
||||
kind: Types.SyntaxKind.ModelExpression,
|
||||
properties: modelProps,
|
||||
decorators: []
|
||||
}, modelPos);
|
||||
const parameters: Types.ModelExpressionNode = finishNode(
|
||||
{
|
||||
kind: Types.SyntaxKind.ModelExpression,
|
||||
properties: modelProps,
|
||||
decorators: [],
|
||||
},
|
||||
modelPos
|
||||
);
|
||||
|
||||
parseExpected(Token.Colon);
|
||||
const returnType = parseExpression();
|
||||
|
||||
return finishNode({
|
||||
kind: Types.SyntaxKind.NamespaceProperty,
|
||||
id,
|
||||
parameters,
|
||||
returnType,
|
||||
decorators
|
||||
}, pos);
|
||||
return finishNode(
|
||||
{
|
||||
kind: Types.SyntaxKind.NamespaceProperty,
|
||||
id,
|
||||
parameters,
|
||||
returnType,
|
||||
decorators,
|
||||
},
|
||||
pos
|
||||
);
|
||||
}
|
||||
|
||||
function parseModelStatement(
|
||||
|
@ -152,40 +163,50 @@ export function parse(code: string | Types.SourceFile) {
|
|||
parseExpected(Token.GreaterThan);
|
||||
}
|
||||
|
||||
if (token() !== Token.Equals && token() !== Token.OpenBrace && token() !== Token.ExtendsKeyword) {
|
||||
throw error('Expected equals, open curly, or extends after model statement');
|
||||
if (
|
||||
token() !== Token.Equals &&
|
||||
token() !== Token.OpenBrace &&
|
||||
token() !== Token.ExtendsKeyword
|
||||
) {
|
||||
throw error("Expected equals, open curly, or extends after model statement");
|
||||
}
|
||||
|
||||
if (parseOptional(Token.Equals)) {
|
||||
const assignment = parseExpression();
|
||||
parseExpected(Token.Semicolon);
|
||||
return finishNode({
|
||||
kind: Types.SyntaxKind.ModelStatement,
|
||||
id,
|
||||
heritage: [],
|
||||
templateParameters,
|
||||
assignment,
|
||||
decorators,
|
||||
}, pos);
|
||||
return finishNode(
|
||||
{
|
||||
kind: Types.SyntaxKind.ModelStatement,
|
||||
id,
|
||||
heritage: [],
|
||||
templateParameters,
|
||||
assignment,
|
||||
decorators,
|
||||
},
|
||||
pos
|
||||
);
|
||||
} else {
|
||||
let heritage: Types.ReferenceExpression[] = [];
|
||||
|
||||
if (parseOptional(Token.ExtendsKeyword)) {
|
||||
heritage = parseReferenceExpressionList();
|
||||
}
|
||||
|
||||
|
||||
parseExpected(Token.OpenBrace);
|
||||
const properties = parseModelPropertyList();
|
||||
parseExpected(Token.CloseBrace);
|
||||
|
||||
return finishNode({
|
||||
kind: Types.SyntaxKind.ModelStatement,
|
||||
id,
|
||||
heritage,
|
||||
templateParameters,
|
||||
decorators,
|
||||
properties
|
||||
}, pos);
|
||||
return finishNode(
|
||||
{
|
||||
kind: Types.SyntaxKind.ModelStatement,
|
||||
id,
|
||||
heritage,
|
||||
templateParameters,
|
||||
decorators,
|
||||
properties,
|
||||
},
|
||||
pos
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -194,10 +215,13 @@ export function parse(code: string | Types.SourceFile) {
|
|||
do {
|
||||
const pos = tokenPos();
|
||||
const id = parseIdentifier();
|
||||
const param = finishNode({
|
||||
kind: Types.SyntaxKind.TemplateParameterDeclaration,
|
||||
sv: id.sv
|
||||
} as const, pos);
|
||||
const param = finishNode(
|
||||
{
|
||||
kind: Types.SyntaxKind.TemplateParameterDeclaration,
|
||||
sv: id.sv,
|
||||
} as const,
|
||||
pos
|
||||
);
|
||||
|
||||
params.push(param);
|
||||
} while (parseOptional(Token.Comma));
|
||||
|
@ -205,7 +229,9 @@ export function parse(code: string | Types.SourceFile) {
|
|||
return params;
|
||||
}
|
||||
|
||||
function parseModelPropertyList(): Array<Types.ModelPropertyNode | Types.ModelSpreadPropertyNode> {
|
||||
function parseModelPropertyList(): Array<
|
||||
Types.ModelPropertyNode | Types.ModelSpreadPropertyNode
|
||||
> {
|
||||
const properties: Array<Types.ModelPropertyNode | Types.ModelSpreadPropertyNode> = [];
|
||||
|
||||
do {
|
||||
|
@ -217,7 +243,7 @@ export function parse(code: string | Types.SourceFile) {
|
|||
|
||||
if (token() === Token.Elipsis) {
|
||||
if (memberDecorators.length > 0) {
|
||||
error('Cannot decorate a spread property');
|
||||
error("Cannot decorate a spread property");
|
||||
}
|
||||
properties.push(parseModelSpreadProperty());
|
||||
} else {
|
||||
|
@ -225,7 +251,6 @@ export function parse(code: string | Types.SourceFile) {
|
|||
}
|
||||
} while (parseOptional(Token.Comma) || parseOptional(Token.Semicolon));
|
||||
|
||||
|
||||
return properties;
|
||||
}
|
||||
|
||||
|
@ -236,13 +261,18 @@ export function parse(code: string | Types.SourceFile) {
|
|||
// This could be broadened to allow any type expression
|
||||
const target = parseReferenceExpression();
|
||||
|
||||
return finishNode({
|
||||
kind: Types.SyntaxKind.ModelSpreadProperty,
|
||||
target
|
||||
}, pos);
|
||||
return finishNode(
|
||||
{
|
||||
kind: Types.SyntaxKind.ModelSpreadProperty,
|
||||
target,
|
||||
},
|
||||
pos
|
||||
);
|
||||
}
|
||||
|
||||
function parseModelProperty(decorators: Array<Types.DecoratorExpressionNode>): Types.ModelPropertyNode {
|
||||
function parseModelProperty(
|
||||
decorators: Array<Types.DecoratorExpressionNode>
|
||||
): Types.ModelPropertyNode {
|
||||
const pos = tokenPos();
|
||||
let id: Types.IdentifierNode | Types.StringLiteralNode;
|
||||
switch (token()) {
|
||||
|
@ -260,13 +290,16 @@ export function parse(code: string | Types.SourceFile) {
|
|||
parseExpected(Token.Colon);
|
||||
const value = parseExpression();
|
||||
|
||||
return finishNode({
|
||||
kind: Types.SyntaxKind.ModelProperty,
|
||||
id,
|
||||
decorators,
|
||||
value,
|
||||
optional
|
||||
}, pos);
|
||||
return finishNode(
|
||||
{
|
||||
kind: Types.SyntaxKind.ModelProperty,
|
||||
id,
|
||||
decorators,
|
||||
value,
|
||||
optional,
|
||||
},
|
||||
pos
|
||||
);
|
||||
}
|
||||
|
||||
function parseExpression(): Types.Expression {
|
||||
|
@ -281,10 +314,13 @@ export function parse(code: string | Types.SourceFile) {
|
|||
return node;
|
||||
}
|
||||
|
||||
node = finishNode({
|
||||
kind: Types.SyntaxKind.UnionExpression,
|
||||
options: [node]
|
||||
}, pos);
|
||||
node = finishNode(
|
||||
{
|
||||
kind: Types.SyntaxKind.UnionExpression,
|
||||
options: [node],
|
||||
},
|
||||
pos
|
||||
);
|
||||
|
||||
while (parseOptional(Token.Bar)) {
|
||||
const expr = parseIntersectionExpressionOrHigher();
|
||||
|
@ -304,10 +340,13 @@ export function parse(code: string | Types.SourceFile) {
|
|||
return node;
|
||||
}
|
||||
|
||||
node = finishNode({
|
||||
kind: Types.SyntaxKind.IntersectionExpression,
|
||||
options: [node]
|
||||
}, pos);
|
||||
node = finishNode(
|
||||
{
|
||||
kind: Types.SyntaxKind.IntersectionExpression,
|
||||
options: [node],
|
||||
},
|
||||
pos
|
||||
);
|
||||
|
||||
while (parseOptional(Token.Ampersand)) {
|
||||
const expr = parseArrayExpressionOrHigher();
|
||||
|
@ -326,10 +365,13 @@ export function parse(code: string | Types.SourceFile) {
|
|||
while (parseOptional(Token.OpenBracket)) {
|
||||
parseExpected(Token.CloseBracket);
|
||||
|
||||
expr = finishNode({
|
||||
kind: Types.SyntaxKind.ArrayExpression,
|
||||
elementType: expr
|
||||
}, pos);
|
||||
expr = finishNode(
|
||||
{
|
||||
kind: Types.SyntaxKind.ArrayExpression,
|
||||
elementType: expr,
|
||||
},
|
||||
pos
|
||||
);
|
||||
}
|
||||
|
||||
return expr;
|
||||
|
@ -347,11 +389,14 @@ export function parse(code: string | Types.SourceFile) {
|
|||
const args = parseExpressionList();
|
||||
parseExpected(Token.GreaterThan);
|
||||
|
||||
return finishNode({
|
||||
kind: Types.SyntaxKind.TemplateApplication,
|
||||
target: expr,
|
||||
arguments: args,
|
||||
}, pos);
|
||||
return finishNode(
|
||||
{
|
||||
kind: Types.SyntaxKind.TemplateApplication,
|
||||
target: expr,
|
||||
arguments: args,
|
||||
},
|
||||
pos
|
||||
);
|
||||
}
|
||||
|
||||
function parseReferenceExpressionList(): Types.ReferenceExpression[] {
|
||||
|
@ -370,7 +415,7 @@ export function parse(code: string | Types.SourceFile) {
|
|||
const id = parseIdentifier();
|
||||
let as: Array<Types.NamedImportNode> = [];
|
||||
|
||||
if (token() === Token.Identifier && tokenValue() === 'as') {
|
||||
if (token() === Token.Identifier && tokenValue() === "as") {
|
||||
parseExpected(Token.Identifier);
|
||||
parseExpected(Token.OpenBrace);
|
||||
|
||||
|
@ -382,20 +427,29 @@ export function parse(code: string | Types.SourceFile) {
|
|||
}
|
||||
|
||||
parseExpected(Token.Semicolon);
|
||||
return finishNode({
|
||||
kind: Types.SyntaxKind.ImportStatement,
|
||||
as, id
|
||||
}, pos);
|
||||
return finishNode(
|
||||
{
|
||||
kind: Types.SyntaxKind.ImportStatement,
|
||||
as,
|
||||
id,
|
||||
},
|
||||
pos
|
||||
);
|
||||
}
|
||||
|
||||
function parseNamedImports(): Array<Types.NamedImportNode> {
|
||||
const names: Array<Types.NamedImportNode> = [];
|
||||
do {
|
||||
const pos = tokenPos();
|
||||
names.push(finishNode({
|
||||
kind: Types.SyntaxKind.NamedImport,
|
||||
id: parseIdentifier()
|
||||
}, pos));
|
||||
names.push(
|
||||
finishNode(
|
||||
{
|
||||
kind: Types.SyntaxKind.NamedImport,
|
||||
id: parseIdentifier(),
|
||||
},
|
||||
pos
|
||||
)
|
||||
);
|
||||
} while (parseOptional(Token.Comma));
|
||||
return names;
|
||||
}
|
||||
|
@ -416,11 +470,14 @@ export function parse(code: string | Types.SourceFile) {
|
|||
args = [parsePrimaryExpression()];
|
||||
}
|
||||
|
||||
return finishNode({
|
||||
kind: Types.SyntaxKind.DecoratorExpression,
|
||||
arguments: args,
|
||||
target
|
||||
}, pos);
|
||||
return finishNode(
|
||||
{
|
||||
kind: Types.SyntaxKind.DecoratorExpression,
|
||||
arguments: args,
|
||||
target,
|
||||
},
|
||||
pos
|
||||
);
|
||||
}
|
||||
|
||||
function parseExpressionList(): Array<Types.Expression> {
|
||||
|
@ -434,16 +491,18 @@ export function parse(code: string | Types.SourceFile) {
|
|||
}
|
||||
|
||||
function parseIdentifierOrMemberExpression(): Types.IdentifierNode | Types.MemberExpressionNode {
|
||||
|
||||
let base: Types.IdentifierNode | Types.MemberExpressionNode = parseIdentifier();
|
||||
|
||||
while (parseOptional(Token.Dot)) {
|
||||
const pos = tokenPos();
|
||||
base = finishNode({
|
||||
kind: Types.SyntaxKind.MemberExpression,
|
||||
base,
|
||||
id: parseIdentifier()
|
||||
}, pos);
|
||||
base = finishNode(
|
||||
{
|
||||
kind: Types.SyntaxKind.MemberExpression,
|
||||
base,
|
||||
id: parseIdentifier(),
|
||||
},
|
||||
pos
|
||||
);
|
||||
}
|
||||
|
||||
return base;
|
||||
|
@ -484,32 +543,43 @@ export function parse(code: string | Types.SourceFile) {
|
|||
parseExpected(Token.OpenBracket);
|
||||
const values = parseExpressionList();
|
||||
parseExpected(Token.CloseBracket);
|
||||
return finishNode({
|
||||
kind: Types.SyntaxKind.TupleExpression,
|
||||
values
|
||||
}, pos);
|
||||
return finishNode(
|
||||
{
|
||||
kind: Types.SyntaxKind.TupleExpression,
|
||||
values,
|
||||
},
|
||||
pos
|
||||
);
|
||||
}
|
||||
|
||||
function parseModelExpression(decorators: Array<Types.DecoratorExpressionNode>): Types.ModelExpressionNode {
|
||||
function parseModelExpression(
|
||||
decorators: Array<Types.DecoratorExpressionNode>
|
||||
): Types.ModelExpressionNode {
|
||||
const pos = tokenPos();
|
||||
parseExpected(Token.OpenBrace);
|
||||
const properties = parseModelPropertyList();
|
||||
parseExpected(Token.CloseBrace);
|
||||
return finishNode({
|
||||
kind: Types.SyntaxKind.ModelExpression,
|
||||
decorators,
|
||||
properties
|
||||
}, pos);
|
||||
return finishNode(
|
||||
{
|
||||
kind: Types.SyntaxKind.ModelExpression,
|
||||
decorators,
|
||||
properties,
|
||||
},
|
||||
pos
|
||||
);
|
||||
}
|
||||
|
||||
function parseStringLiteral(): Types.StringLiteralNode {
|
||||
const pos = tokenPos();
|
||||
const value = tokenValue();
|
||||
parseExpected(Token.StringLiteral);
|
||||
return finishNode({
|
||||
kind: Types.SyntaxKind.StringLiteral,
|
||||
value
|
||||
}, pos);
|
||||
return finishNode(
|
||||
{
|
||||
kind: Types.SyntaxKind.StringLiteral,
|
||||
value,
|
||||
},
|
||||
pos
|
||||
);
|
||||
}
|
||||
|
||||
function parseNumericLiteral(): Types.NumericLiteralNode {
|
||||
|
@ -518,21 +588,27 @@ export function parse(code: string | Types.SourceFile) {
|
|||
const value = Number(text);
|
||||
|
||||
parseExpected(Token.NumericLiteral);
|
||||
return finishNode({
|
||||
kind: Types.SyntaxKind.NumericLiteral,
|
||||
text,
|
||||
value
|
||||
}, pos);
|
||||
return finishNode(
|
||||
{
|
||||
kind: Types.SyntaxKind.NumericLiteral,
|
||||
text,
|
||||
value,
|
||||
},
|
||||
pos
|
||||
);
|
||||
}
|
||||
|
||||
function parseBooleanLiteral(): Types.BooleanLiteralNode {
|
||||
const pos = tokenPos();
|
||||
const token = parseExpectedOneOf(Token.TrueKeyword, Token.FalseKeyword);
|
||||
const value = token == Token.TrueKeyword;
|
||||
return finishNode({
|
||||
kind: Types.SyntaxKind.BooleanLiteral,
|
||||
value
|
||||
}, pos);
|
||||
return finishNode(
|
||||
{
|
||||
kind: Types.SyntaxKind.BooleanLiteral,
|
||||
value,
|
||||
},
|
||||
pos
|
||||
);
|
||||
}
|
||||
|
||||
function parseIdentifier(): Types.IdentifierNode {
|
||||
|
@ -546,10 +622,13 @@ export function parse(code: string | Types.SourceFile) {
|
|||
|
||||
nextToken();
|
||||
|
||||
return finishNode({
|
||||
kind: Types.SyntaxKind.Identifier,
|
||||
sv
|
||||
}, pos);
|
||||
return finishNode(
|
||||
{
|
||||
kind: Types.SyntaxKind.Identifier,
|
||||
sv,
|
||||
},
|
||||
pos
|
||||
);
|
||||
}
|
||||
|
||||
// utility functions
|
||||
|
@ -602,15 +681,15 @@ export function parse(code: string | Types.SourceFile) {
|
|||
return {
|
||||
...o,
|
||||
pos,
|
||||
end: tokenPos()
|
||||
end: tokenPos(),
|
||||
};
|
||||
}
|
||||
|
||||
function error(message: string) {
|
||||
throwDiagnostic(message, {
|
||||
throwDiagnostic(message, {
|
||||
file: scanner.file,
|
||||
pos: scanner.tokenPosition,
|
||||
end: scanner.position
|
||||
end: scanner.position,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -622,18 +701,18 @@ export function parse(code: string | Types.SourceFile) {
|
|||
}
|
||||
}
|
||||
|
||||
function parseExpectedOneOf<T extends Token[]>(... options: T): T[number] {
|
||||
function parseExpectedOneOf<T extends Token[]>(...options: T): T[number] {
|
||||
for (const tok of options) {
|
||||
if (token() === tok) {
|
||||
nextToken();
|
||||
return tok
|
||||
return tok;
|
||||
}
|
||||
}
|
||||
|
||||
// Intl isn't in standard library as it is stage 3, however it is supported in node >= 12
|
||||
const listfmt = new (Intl as any).ListFormat('en', { style: 'long', type: 'disjunction' });
|
||||
const textOptions = options.map(o => Token[o]);
|
||||
|
||||
const listfmt = new (Intl as any).ListFormat("en", { style: "long", type: "disjunction" });
|
||||
const textOptions = options.map((o) => Token[o]);
|
||||
|
||||
throw error(`expected ${listfmt.format(textOptions)}, got ${Token[token()]}`);
|
||||
}
|
||||
|
||||
|
@ -647,7 +726,7 @@ export function parse(code: string | Types.SourceFile) {
|
|||
}
|
||||
}
|
||||
|
||||
type NodeCb<T> = (c: Types.Node) => T;
|
||||
type NodeCb<T> = (c: Types.Node) => T;
|
||||
|
||||
export function visitChildren<T>(node: Types.Node, cb: NodeCb<T>): T | undefined {
|
||||
switch (node.kind) {
|
||||
|
@ -656,47 +735,46 @@ export function visitChildren<T>(node: Types.Node, cb: NodeCb<T>): T | undefined
|
|||
case Types.SyntaxKind.ArrayExpression:
|
||||
return visitNode(cb, node.elementType);
|
||||
case Types.SyntaxKind.DecoratorExpression:
|
||||
return visitNode(cb, node.target) ||
|
||||
visitEach(cb, node.arguments);
|
||||
return visitNode(cb, node.target) || visitEach(cb, node.arguments);
|
||||
case Types.SyntaxKind.ImportStatement:
|
||||
return visitNode(cb, node.id) ||
|
||||
visitEach(cb, node.as);
|
||||
return visitNode(cb, node.id) || visitEach(cb, node.as);
|
||||
case Types.SyntaxKind.NamespaceProperty:
|
||||
return visitEach(cb, node.decorators) ||
|
||||
return (
|
||||
visitEach(cb, node.decorators) ||
|
||||
visitNode(cb, node.id) ||
|
||||
visitNode(cb, node.parameters) ||
|
||||
visitNode(cb, node.returnType);
|
||||
visitNode(cb, node.returnType)
|
||||
);
|
||||
case Types.SyntaxKind.NamespaceStatement:
|
||||
return visitEach(cb, node.decorators) ||
|
||||
return (
|
||||
visitEach(cb, node.decorators) ||
|
||||
visitNode(cb, node.id) ||
|
||||
visitNode(cb, node.parameters) ||
|
||||
visitEach(cb, node.properties);
|
||||
visitEach(cb, node.properties)
|
||||
);
|
||||
case Types.SyntaxKind.IntersectionExpression:
|
||||
return visitEach(cb, node.options);
|
||||
case Types.SyntaxKind.MemberExpression:
|
||||
return visitNode(cb, node.base) ||
|
||||
visitNode(cb, node.id);
|
||||
return visitNode(cb, node.base) || visitNode(cb, node.id);
|
||||
case Types.SyntaxKind.ModelExpression:
|
||||
return visitEach(cb, node.decorators) ||
|
||||
visitEach(cb, node.properties);
|
||||
return visitEach(cb, node.decorators) || visitEach(cb, node.properties);
|
||||
case Types.SyntaxKind.ModelProperty:
|
||||
return visitEach(cb, node.decorators) ||
|
||||
visitNode(cb, node.id) ||
|
||||
visitNode(cb, node.value);
|
||||
return visitEach(cb, node.decorators) || visitNode(cb, node.id) || visitNode(cb, node.value);
|
||||
case Types.SyntaxKind.ModelSpreadProperty:
|
||||
return visitNode(cb, node.target);
|
||||
case Types.SyntaxKind.ModelStatement:
|
||||
return visitEach(cb, node.decorators) ||
|
||||
return (
|
||||
visitEach(cb, node.decorators) ||
|
||||
visitNode(cb, node.id) ||
|
||||
visitEach(cb, node.templateParameters) ||
|
||||
visitEach(cb, node.heritage) ||
|
||||
visitNode(cb, node.assignment) ||
|
||||
visitEach(cb, node.properties);
|
||||
visitEach(cb, node.properties)
|
||||
);
|
||||
case Types.SyntaxKind.NamedImport:
|
||||
return visitNode(cb, node.id);
|
||||
case Types.SyntaxKind.TemplateApplication:
|
||||
return visitNode(cb, node.target) ||
|
||||
visitEach(cb, node.arguments);
|
||||
return visitNode(cb, node.target) || visitEach(cb, node.arguments);
|
||||
case Types.SyntaxKind.TupleExpression:
|
||||
return visitEach(cb, node.values);
|
||||
case Types.SyntaxKind.UnionExpression:
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
import url from "url";
|
||||
import path from "path";
|
||||
import { readdir, readFile } from 'fs/promises';
|
||||
import { join } from 'path';
|
||||
import { createBinder, SymbolTable } from './binder.js';
|
||||
import { createChecker, MultiKeyMap } from './checker.js';
|
||||
import { CompilerOptions } from './options.js';
|
||||
import { parse } from './parser.js';
|
||||
import { resolvePath } from './util.js';
|
||||
import { readdir, readFile } from "fs/promises";
|
||||
import { join } from "path";
|
||||
import { createBinder, SymbolTable } from "./binder.js";
|
||||
import { createChecker, MultiKeyMap } from "./checker.js";
|
||||
import { CompilerOptions } from "./options.js";
|
||||
import { parse } from "./parser.js";
|
||||
import { resolvePath } from "./util.js";
|
||||
import {
|
||||
ADLScriptNode,
|
||||
DecoratorExpressionNode, IdentifierNode,
|
||||
DecoratorExpressionNode,
|
||||
IdentifierNode,
|
||||
Namespace,
|
||||
LiteralType,
|
||||
ModelStatementNode,
|
||||
|
@ -18,8 +19,7 @@ import {
|
|||
Type,
|
||||
SourceFile,
|
||||
DecoratorSymbol,
|
||||
|
||||
} from './types.js';
|
||||
} from "./types.js";
|
||||
import { createSourceFile } from "./scanner.js";
|
||||
import { throwDiagnostic } from "./diagnostics.js";
|
||||
|
||||
|
@ -68,9 +68,9 @@ export async function compile(rootDir: string, options?: CompilerOptions) {
|
|||
if (!options?.nostdlib) {
|
||||
await loadStandardLibrary(program);
|
||||
}
|
||||
|
||||
|
||||
await loadDirectory(program, rootDir);
|
||||
const checker = program.checker = createChecker(program);
|
||||
const checker = (program.checker = createChecker(program));
|
||||
program.checker.checkProgram(program);
|
||||
buildCbs.forEach((cb: any) => cb(program));
|
||||
|
||||
|
@ -104,7 +104,7 @@ export async function compile(rootDir: string, options?: CompilerOptions) {
|
|||
for (const [name, propType] of type.properties) {
|
||||
const propNode = propType.node;
|
||||
|
||||
if ('decorators' in propNode) {
|
||||
if ("decorators" in propNode) {
|
||||
for (const dec of propNode.decorators) {
|
||||
executeDecorator(dec, program, propType);
|
||||
}
|
||||
|
@ -123,13 +123,11 @@ export async function compile(rootDir: string, options?: CompilerOptions) {
|
|||
|
||||
function executeDecorator(dec: DecoratorExpressionNode, program: Program, type: Type) {
|
||||
if (dec.target.kind !== SyntaxKind.Identifier) {
|
||||
throwDiagnostic('Decorator must be identifier', dec);
|
||||
throwDiagnostic("Decorator must be identifier", dec);
|
||||
}
|
||||
|
||||
const decName = dec.target.sv;
|
||||
const args = dec.arguments.map((a) =>
|
||||
toJSON(checker.getTypeForNode(a))
|
||||
);
|
||||
const args = dec.arguments.map((a) => toJSON(checker.getTypeForNode(a)));
|
||||
const decBinding = <DecoratorSymbol>program.globalSymbols.get(decName);
|
||||
if (!decBinding) {
|
||||
throwDiagnostic(`Can't find decorator ${decName}`, dec);
|
||||
|
@ -139,10 +137,9 @@ export async function compile(rootDir: string, options?: CompilerOptions) {
|
|||
}
|
||||
|
||||
async function importDecorator(modulePath: string, name: string) {
|
||||
const resolvedPath =
|
||||
path.isAbsolute(modulePath)
|
||||
? modulePath
|
||||
: path.resolve(process.cwd(), modulePath);
|
||||
const resolvedPath = path.isAbsolute(modulePath)
|
||||
? modulePath
|
||||
: path.resolve(process.cwd(), modulePath);
|
||||
|
||||
const moduleUrl = url.pathToFileURL(resolvedPath);
|
||||
const module = await import(moduleUrl.href);
|
||||
|
@ -155,7 +152,7 @@ export async function compile(rootDir: string, options?: CompilerOptions) {
|
|||
* treated specially.
|
||||
*/
|
||||
function toJSON(type: Type): Type | string | number {
|
||||
if ('value' in type) {
|
||||
if ("value" in type) {
|
||||
return (<any>type).value;
|
||||
}
|
||||
|
||||
|
@ -184,9 +181,9 @@ export async function compile(rootDir: string, options?: CompilerOptions) {
|
|||
for (const entry of dir) {
|
||||
if (entry.isFile()) {
|
||||
const path = join(rootDir, entry.name);
|
||||
if (entry.name.endsWith('.js')) {
|
||||
if (entry.name.endsWith(".js")) {
|
||||
await loadJsFile(program, path);
|
||||
} else if (entry.name.endsWith('.adl')) {
|
||||
} else if (entry.name.endsWith(".adl")) {
|
||||
await loadAdlFile(program, path);
|
||||
}
|
||||
}
|
||||
|
@ -194,12 +191,12 @@ export async function compile(rootDir: string, options?: CompilerOptions) {
|
|||
}
|
||||
|
||||
async function loadAdlFile(program: Program, path: string) {
|
||||
const contents = await readFile(path, 'utf-8');
|
||||
const contents = await readFile(path, "utf-8");
|
||||
program.evalAdlScript(contents, path);
|
||||
}
|
||||
|
||||
async function loadJsFile(program: Program, path: string) {
|
||||
const contents = await readFile(path, 'utf-8');
|
||||
const contents = await readFile(path, "utf-8");
|
||||
|
||||
const exports = contents.match(/export function \w+/g);
|
||||
if (!exports) return;
|
||||
|
@ -209,14 +206,14 @@ export async function compile(rootDir: string, options?: CompilerOptions) {
|
|||
const name = match.match(/function (\w+)/)![1];
|
||||
const value = await importDecorator(path, name);
|
||||
|
||||
if (name === 'onBuild') {
|
||||
if (name === "onBuild") {
|
||||
program.onBuild(value);
|
||||
} else {
|
||||
program.globalSymbols.set(name, {
|
||||
kind: 'decorator',
|
||||
kind: "decorator",
|
||||
path,
|
||||
name,
|
||||
value
|
||||
value,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,16 @@
|
|||
import { CharacterCodes, isBinaryDigit, isDigit, isHexDigit, isIdentifierPart, isIdentifierStart, isLineBreak, isWhiteSpaceSingleLine } from './character-codes.js';
|
||||
import { throwOnError } from './diagnostics.js';
|
||||
import { messages } from './messages.js';
|
||||
import { Message, SourceFile } from './types.js';
|
||||
import {
|
||||
CharacterCodes,
|
||||
isBinaryDigit,
|
||||
isDigit,
|
||||
isHexDigit,
|
||||
isIdentifierPart,
|
||||
isIdentifierStart,
|
||||
isLineBreak,
|
||||
isWhiteSpaceSingleLine,
|
||||
} from "./character-codes.js";
|
||||
import { throwOnError } from "./diagnostics.js";
|
||||
import { messages } from "./messages.js";
|
||||
import { Message, SourceFile } from "./types.js";
|
||||
|
||||
// All conflict markers consist of the same character repeated seven times. If it is
|
||||
// a <<<<<<< or >>>>>>> marker then it is also followed by a space.
|
||||
|
@ -54,17 +63,17 @@ export enum Token {
|
|||
OpKeyword,
|
||||
ExtendsKeyword,
|
||||
TrueKeyword,
|
||||
FalseKeyword
|
||||
FalseKeyword,
|
||||
}
|
||||
|
||||
const keywords = new Map([
|
||||
['import', Token.ImportKeyword],
|
||||
['model', Token.ModelKeyword],
|
||||
['namespace', Token.NamespaceKeyword],
|
||||
['op', Token.OpKeyword],
|
||||
['extends', Token.ExtendsKeyword],
|
||||
['true', Token.TrueKeyword],
|
||||
['false', Token.FalseKeyword],
|
||||
["import", Token.ImportKeyword],
|
||||
["model", Token.ModelKeyword],
|
||||
["namespace", Token.NamespaceKeyword],
|
||||
["op", Token.OpKeyword],
|
||||
["extends", Token.ExtendsKeyword],
|
||||
["true", Token.TrueKeyword],
|
||||
["false", Token.FalseKeyword],
|
||||
]);
|
||||
|
||||
export interface Scanner {
|
||||
|
@ -105,7 +114,7 @@ const enum TokenFlags {
|
|||
}
|
||||
|
||||
export function createScanner(source: string | SourceFile, onError = throwOnError): Scanner {
|
||||
const file = typeof source === 'string' ? createSourceFile(source, '<anonymous file>') : source;
|
||||
const file = typeof source === "string" ? createSourceFile(source, "<anonymous file>") : source;
|
||||
const input = file.text;
|
||||
let position = 0;
|
||||
let token = Token.Unknown;
|
||||
|
@ -114,9 +123,15 @@ export function createScanner(source: string | SourceFile, onError = throwOnErro
|
|||
let tokenFlags = 0;
|
||||
|
||||
return {
|
||||
get position() { return position; },
|
||||
get token() { return token; },
|
||||
get tokenPosition() { return tokenPosition; },
|
||||
get position() {
|
||||
return position;
|
||||
},
|
||||
get token() {
|
||||
return token;
|
||||
},
|
||||
get tokenPosition() {
|
||||
return tokenPosition;
|
||||
},
|
||||
file,
|
||||
scan,
|
||||
eof,
|
||||
|
@ -130,7 +145,7 @@ export function createScanner(source: string | SourceFile, onError = throwOnErro
|
|||
|
||||
function next(t: Token, count = 1) {
|
||||
position += count;
|
||||
return token = t;
|
||||
return (token = t);
|
||||
}
|
||||
|
||||
function getTokenText() {
|
||||
|
@ -153,7 +168,7 @@ export function createScanner(source: string | SourceFile, onError = throwOnErro
|
|||
if (lookAhead(1) === CharacterCodes.lineFeed) {
|
||||
position++;
|
||||
}
|
||||
// fallthrough
|
||||
// fallthrough
|
||||
case CharacterCodes.lineFeed:
|
||||
case CharacterCodes.lineSeparator:
|
||||
case CharacterCodes.paragraphSeparator:
|
||||
|
@ -220,10 +235,9 @@ export function createScanner(source: string | SourceFile, onError = throwOnErro
|
|||
return next(Token.Ampersand);
|
||||
|
||||
case CharacterCodes.dot:
|
||||
return lookAhead(1) === CharacterCodes.dot &&
|
||||
lookAhead(2) === CharacterCodes.dot ?
|
||||
next(Token.Elipsis, 3) :
|
||||
next(Token.Dot);
|
||||
return lookAhead(1) === CharacterCodes.dot && lookAhead(2) === CharacterCodes.dot
|
||||
? next(Token.Elipsis, 3)
|
||||
: next(Token.Dot);
|
||||
|
||||
case CharacterCodes.slash:
|
||||
switch (lookAhead(1)) {
|
||||
|
@ -241,7 +255,7 @@ export function createScanner(source: string | SourceFile, onError = throwOnErro
|
|||
case CharacterCodes.b:
|
||||
return scanBinaryNumber();
|
||||
}
|
||||
// fallthrough
|
||||
// fallthrough
|
||||
case CharacterCodes._1:
|
||||
case CharacterCodes._2:
|
||||
case CharacterCodes._3:
|
||||
|
@ -254,35 +268,34 @@ export function createScanner(source: string | SourceFile, onError = throwOnErro
|
|||
return scanNumber();
|
||||
|
||||
case CharacterCodes.lessThan:
|
||||
return isConflictMarker() ?
|
||||
next(Token.ConflictMarker, mergeConflictMarkerLength) :
|
||||
next(Token.LessThan);
|
||||
return isConflictMarker()
|
||||
? next(Token.ConflictMarker, mergeConflictMarkerLength)
|
||||
: next(Token.LessThan);
|
||||
|
||||
case CharacterCodes.greaterThan:
|
||||
return isConflictMarker() ?
|
||||
next(Token.ConflictMarker, mergeConflictMarkerLength) :
|
||||
next(Token.GreaterThan);
|
||||
return isConflictMarker()
|
||||
? next(Token.ConflictMarker, mergeConflictMarkerLength)
|
||||
: next(Token.GreaterThan);
|
||||
|
||||
case CharacterCodes.equals:
|
||||
return isConflictMarker() ?
|
||||
next(Token.ConflictMarker, mergeConflictMarkerLength) :
|
||||
next(Token.Equals);
|
||||
return isConflictMarker()
|
||||
? next(Token.ConflictMarker, mergeConflictMarkerLength)
|
||||
: next(Token.Equals);
|
||||
|
||||
case CharacterCodes.bar:
|
||||
return isConflictMarker() ?
|
||||
next(Token.ConflictMarker, mergeConflictMarkerLength) :
|
||||
next(Token.Bar);
|
||||
return isConflictMarker()
|
||||
? next(Token.ConflictMarker, mergeConflictMarkerLength)
|
||||
: next(Token.Bar);
|
||||
|
||||
case CharacterCodes.doubleQuote:
|
||||
return scanString();
|
||||
|
||||
default:
|
||||
return isIdentifierStart(ch) ? scanIdentifier() : unknownToken();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return token = Token.EndOfFile;
|
||||
return (token = Token.EndOfFile);
|
||||
}
|
||||
|
||||
function unknownToken() {
|
||||
|
@ -295,13 +308,16 @@ export function createScanner(source: string | SourceFile, onError = throwOnErro
|
|||
// Conflict markers must be at the start of a line.
|
||||
const ch = input.charCodeAt(position);
|
||||
if (position === 0 || isLineBreak(input.charCodeAt(position - 1))) {
|
||||
if ((position + mergeConflictMarkerLength) < input.length) {
|
||||
if (position + mergeConflictMarkerLength < input.length) {
|
||||
for (let i = 0; i < mergeConflictMarkerLength; i++) {
|
||||
if (lookAhead(i) !== ch) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return ch === CharacterCodes.equals || lookAhead(mergeConflictMarkerLength) === CharacterCodes.space;
|
||||
return (
|
||||
ch === CharacterCodes.equals ||
|
||||
lookAhead(mergeConflictMarkerLength) === CharacterCodes.space
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -317,7 +333,7 @@ export function createScanner(source: string | SourceFile, onError = throwOnErro
|
|||
position++;
|
||||
} while (isWhiteSpaceSingleLine(input.charCodeAt(position)));
|
||||
|
||||
return token = Token.Whitespace;
|
||||
return (token = Token.Whitespace);
|
||||
}
|
||||
|
||||
function scanDigits() {
|
||||
|
@ -353,7 +369,7 @@ export function createScanner(source: string | SourceFile, onError = throwOnErro
|
|||
}
|
||||
}
|
||||
|
||||
return token = Token.NumericLiteral;
|
||||
return (token = Token.NumericLiteral);
|
||||
}
|
||||
|
||||
function scanHexNumber() {
|
||||
|
@ -363,8 +379,8 @@ export function createScanner(source: string | SourceFile, onError = throwOnErro
|
|||
}
|
||||
|
||||
position += 2;
|
||||
scanUntil(ch => !isHexDigit(ch), 'Hex Digit');
|
||||
return token = Token.NumericLiteral;
|
||||
scanUntil((ch) => !isHexDigit(ch), "Hex Digit");
|
||||
return (token = Token.NumericLiteral);
|
||||
}
|
||||
|
||||
function scanBinaryNumber() {
|
||||
|
@ -374,12 +390,15 @@ export function createScanner(source: string | SourceFile, onError = throwOnErro
|
|||
}
|
||||
|
||||
position += 2;
|
||||
scanUntil(ch => !isBinaryDigit(ch), 'Binary Digit');
|
||||
return token = Token.NumericLiteral;
|
||||
|
||||
scanUntil((ch) => !isBinaryDigit(ch), "Binary Digit");
|
||||
return (token = Token.NumericLiteral);
|
||||
}
|
||||
|
||||
function scanUntil(predicate: (char: number) => boolean, expectedClose?: string, consumeClose?: number) {
|
||||
function scanUntil(
|
||||
predicate: (char: number) => boolean,
|
||||
expectedClose?: string,
|
||||
consumeClose?: number
|
||||
) {
|
||||
let ch: number;
|
||||
|
||||
do {
|
||||
|
@ -402,12 +421,16 @@ export function createScanner(source: string | SourceFile, onError = throwOnErro
|
|||
|
||||
function scanSingleLineComment() {
|
||||
scanUntil(isLineBreak);
|
||||
return token = Token.SingleLineComment;
|
||||
return (token = Token.SingleLineComment);
|
||||
}
|
||||
|
||||
function scanMultiLineComment() {
|
||||
scanUntil(ch => ch === CharacterCodes.asterisk && lookAhead(1) === CharacterCodes.slash, '*/', 2);
|
||||
return token = Token.MultiLineComment;
|
||||
scanUntil(
|
||||
(ch) => ch === CharacterCodes.asterisk && lookAhead(1) === CharacterCodes.slash,
|
||||
"*/",
|
||||
2
|
||||
);
|
||||
return (token = Token.MultiLineComment);
|
||||
}
|
||||
|
||||
function scanString() {
|
||||
|
@ -415,8 +438,8 @@ export function createScanner(source: string | SourceFile, onError = throwOnErro
|
|||
let closing = '"';
|
||||
let isEscaping = false;
|
||||
|
||||
const tripleQuoted = lookAhead(1) === CharacterCodes.doubleQuote &&
|
||||
lookAhead(2) === CharacterCodes.doubleQuote;
|
||||
const tripleQuoted =
|
||||
lookAhead(1) === CharacterCodes.doubleQuote && lookAhead(2) === CharacterCodes.doubleQuote;
|
||||
|
||||
if (tripleQuoted) {
|
||||
tokenFlags |= TokenFlags.TripleQuoted;
|
||||
|
@ -426,36 +449,43 @@ export function createScanner(source: string | SourceFile, onError = throwOnErro
|
|||
|
||||
position += quoteLength;
|
||||
|
||||
scanUntil(ch => {
|
||||
if (isEscaping) {
|
||||
isEscaping = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (ch) {
|
||||
case CharacterCodes.carriageReturn:
|
||||
if (lookAhead(1) === CharacterCodes.lineFeed) {
|
||||
tokenFlags |= TokenFlags.HasCrlf;
|
||||
}
|
||||
scanUntil(
|
||||
(ch) => {
|
||||
if (isEscaping) {
|
||||
isEscaping = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
case CharacterCodes.backslash:
|
||||
isEscaping = true;
|
||||
tokenFlags |= TokenFlags.Escaped;
|
||||
return false;
|
||||
switch (ch) {
|
||||
case CharacterCodes.carriageReturn:
|
||||
if (lookAhead(1) === CharacterCodes.lineFeed) {
|
||||
tokenFlags |= TokenFlags.HasCrlf;
|
||||
}
|
||||
return false;
|
||||
|
||||
case CharacterCodes.doubleQuote:
|
||||
if (tripleQuoted) {
|
||||
return lookAhead(1) === CharacterCodes.doubleQuote && lookAhead(2) === CharacterCodes.doubleQuote;
|
||||
}
|
||||
return true;
|
||||
case CharacterCodes.backslash:
|
||||
isEscaping = true;
|
||||
tokenFlags |= TokenFlags.Escaped;
|
||||
return false;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}, closing, quoteLength);
|
||||
case CharacterCodes.doubleQuote:
|
||||
if (tripleQuoted) {
|
||||
return (
|
||||
lookAhead(1) === CharacterCodes.doubleQuote &&
|
||||
lookAhead(2) === CharacterCodes.doubleQuote
|
||||
);
|
||||
}
|
||||
return true;
|
||||
|
||||
return token = Token.StringLiteral;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
},
|
||||
closing,
|
||||
quoteLength
|
||||
);
|
||||
|
||||
return (token = Token.StringLiteral);
|
||||
}
|
||||
|
||||
function getTokenValue() {
|
||||
|
@ -464,18 +494,18 @@ export function createScanner(source: string | SourceFile, onError = throwOnErro
|
|||
}
|
||||
|
||||
if (token !== Token.StringLiteral) {
|
||||
return tokenValue = getTokenText();
|
||||
return (tokenValue = getTokenText());
|
||||
}
|
||||
|
||||
// strip quotes
|
||||
const quoteLength = (tokenFlags & TokenFlags.TripleQuoted) ? 3 : 1;
|
||||
const quoteLength = tokenFlags & TokenFlags.TripleQuoted ? 3 : 1;
|
||||
let value = input.substring(tokenPosition + quoteLength, position - quoteLength);
|
||||
|
||||
// Normalize CRLF to LF when interpreting value of multi-line string
|
||||
// literals. Matches JavaScript behavior and ensures program behavior does
|
||||
// not change due to line-ending conversion.
|
||||
if (tokenFlags & TokenFlags.HasCrlf) {
|
||||
value = value.replace(/\r\n/g, '\n');
|
||||
value = value.replace(/\r\n/g, "\n");
|
||||
}
|
||||
|
||||
if (tokenFlags & TokenFlags.TripleQuoted) {
|
||||
|
@ -486,7 +516,7 @@ export function createScanner(source: string | SourceFile, onError = throwOnErro
|
|||
value = unescapeString(value);
|
||||
}
|
||||
|
||||
return tokenValue = value;
|
||||
return (tokenValue = value);
|
||||
}
|
||||
|
||||
function unindentTripleQuoteString(text: string) {
|
||||
|
@ -523,8 +553,13 @@ export function createScanner(source: string | SourceFile, onError = throwOnErro
|
|||
return removeMatchingIndentation(text, start, end, indentation);
|
||||
}
|
||||
|
||||
function removeMatchingIndentation(text: string, start: number, end: number, indentation: string) {
|
||||
let result = '';
|
||||
function removeMatchingIndentation(
|
||||
text: string,
|
||||
start: number,
|
||||
end: number,
|
||||
indentation: string
|
||||
) {
|
||||
let result = "";
|
||||
let pos = start;
|
||||
|
||||
while (pos < end) {
|
||||
|
@ -563,7 +598,7 @@ export function createScanner(source: string | SourceFile, onError = throwOnErro
|
|||
}
|
||||
|
||||
function unescapeString(text: string) {
|
||||
let result = '';
|
||||
let result = "";
|
||||
let start = 0;
|
||||
let pos = 0;
|
||||
const end = text.length;
|
||||
|
@ -581,19 +616,19 @@ export function createScanner(source: string | SourceFile, onError = throwOnErro
|
|||
|
||||
switch (ch) {
|
||||
case CharacterCodes.r:
|
||||
result += '\r';
|
||||
result += "\r";
|
||||
break;
|
||||
case CharacterCodes.n:
|
||||
result += '\n';
|
||||
result += "\n";
|
||||
break;
|
||||
case CharacterCodes.t:
|
||||
result += '\t';
|
||||
result += "\t";
|
||||
break;
|
||||
case CharacterCodes.doubleQuote:
|
||||
result += '"';
|
||||
break;
|
||||
case CharacterCodes.backslash:
|
||||
result += '\\';
|
||||
result += "\\";
|
||||
break;
|
||||
default:
|
||||
error(messages.InvalidEscapeSequence);
|
||||
|
@ -610,8 +645,8 @@ export function createScanner(source: string | SourceFile, onError = throwOnErro
|
|||
}
|
||||
|
||||
function scanIdentifier() {
|
||||
scanUntil(ch => !isIdentifierPart(ch));
|
||||
return token = keywords.get(getTokenValue()) ?? Token.Identifier;
|
||||
scanUntil((ch) => !isIdentifierPart(ch));
|
||||
return (token = keywords.get(getTokenValue()) ?? Token.Identifier);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -626,7 +661,7 @@ export function createSourceFile(text: string, path: string): SourceFile {
|
|||
};
|
||||
|
||||
function getLineStarts() {
|
||||
return lineStarts = (lineStarts ?? scanLineStarts());
|
||||
return (lineStarts = lineStarts ?? scanLineStarts());
|
||||
}
|
||||
|
||||
function getLineAndCharacterOfPosition(position: number) {
|
||||
|
@ -663,7 +698,7 @@ export function createSourceFile(text: string, path: string): SourceFile {
|
|||
if (text.charCodeAt(pos) === CharacterCodes.lineFeed) {
|
||||
pos++;
|
||||
}
|
||||
// fallthrough
|
||||
// fallthrough
|
||||
case CharacterCodes.lineFeed:
|
||||
case CharacterCodes.lineSeparator:
|
||||
case CharacterCodes.paragraphSeparator:
|
||||
|
@ -701,4 +736,3 @@ export function createSourceFile(text: string, path: string): SourceFile {
|
|||
return ~low;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ export type Type =
|
|||
| UnionType;
|
||||
|
||||
export interface ModelType extends BaseType {
|
||||
kind: 'Model';
|
||||
kind: "Model";
|
||||
name: string;
|
||||
properties: Map<string, ModelTypeProperty>;
|
||||
baseModels: Array<ModelType>;
|
||||
|
@ -33,7 +33,7 @@ export interface ModelType extends BaseType {
|
|||
}
|
||||
|
||||
export interface ModelTypeProperty {
|
||||
kind: 'ModelProperty';
|
||||
kind: "ModelProperty";
|
||||
node: ModelPropertyNode | ModelSpreadPropertyNode;
|
||||
name: string;
|
||||
type: Type;
|
||||
|
@ -44,7 +44,7 @@ export interface ModelTypeProperty {
|
|||
}
|
||||
|
||||
export interface NamespaceProperty {
|
||||
kind: 'NamespaceProperty';
|
||||
kind: "NamespaceProperty";
|
||||
node: NamespacePropertyNode;
|
||||
name: string;
|
||||
parameters?: ModelType;
|
||||
|
@ -52,7 +52,7 @@ export interface NamespaceProperty {
|
|||
}
|
||||
|
||||
export interface Namespace extends BaseType {
|
||||
kind: 'Namespace';
|
||||
kind: "Namespace";
|
||||
name: string;
|
||||
node: NamespaceStatementNode;
|
||||
properties: Map<string, NamespaceProperty>;
|
||||
|
@ -62,56 +62,56 @@ export interface Namespace extends BaseType {
|
|||
export type LiteralType = StringLiteralType | NumericLiteralType | BooleanLiteralType;
|
||||
|
||||
export interface StringLiteralType extends BaseType {
|
||||
kind: 'String';
|
||||
kind: "String";
|
||||
node: StringLiteralNode;
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface NumericLiteralType extends BaseType {
|
||||
kind: 'Number';
|
||||
kind: "Number";
|
||||
node: NumericLiteralNode;
|
||||
value: number;
|
||||
}
|
||||
|
||||
export interface BooleanLiteralType extends BaseType {
|
||||
kind: 'Boolean';
|
||||
kind: "Boolean";
|
||||
node: BooleanLiteralNode;
|
||||
value: boolean;
|
||||
}
|
||||
|
||||
export interface ArrayType extends BaseType {
|
||||
kind: 'Array';
|
||||
kind: "Array";
|
||||
node: ArrayExpressionNode;
|
||||
elementType: Type;
|
||||
}
|
||||
|
||||
export interface TupleType extends BaseType {
|
||||
kind: 'Tuple';
|
||||
kind: "Tuple";
|
||||
node: TupleExpressionNode;
|
||||
values: Array<Type>;
|
||||
}
|
||||
|
||||
export interface UnionType extends BaseType {
|
||||
kind: 'Union';
|
||||
kind: "Union";
|
||||
options: Array<Type>;
|
||||
}
|
||||
|
||||
export interface TemplateParameterType extends BaseType {
|
||||
kind: 'TemplateParameter';
|
||||
kind: "TemplateParameter";
|
||||
}
|
||||
|
||||
// trying to avoid masking built-in Symbol
|
||||
export type Sym = DecoratorSymbol | TypeSymbol;
|
||||
|
||||
export interface DecoratorSymbol {
|
||||
kind: 'decorator';
|
||||
kind: "decorator";
|
||||
path: string;
|
||||
name: string;
|
||||
value: (...args: Array<any>) => any;
|
||||
}
|
||||
|
||||
export interface TypeSymbol {
|
||||
kind: 'type';
|
||||
kind: "type";
|
||||
node: Node;
|
||||
name: string;
|
||||
}
|
||||
|
@ -140,7 +140,7 @@ export enum SyntaxKind {
|
|||
NumericLiteral,
|
||||
BooleanLiteral,
|
||||
TemplateApplication,
|
||||
TemplateParameterDeclaration
|
||||
TemplateParameterDeclaration,
|
||||
}
|
||||
|
||||
export interface BaseNode extends TextRange {
|
||||
|
@ -148,8 +148,8 @@ export interface BaseNode extends TextRange {
|
|||
parent?: Node;
|
||||
}
|
||||
|
||||
export type Node =
|
||||
| ADLScriptNode
|
||||
export type Node =
|
||||
| ADLScriptNode
|
||||
| TemplateParameterDeclarationNode
|
||||
| ModelPropertyNode
|
||||
| NamespacePropertyNode
|
||||
|
@ -157,7 +157,7 @@ export type Node =
|
|||
| ModelPropertyNode
|
||||
| ModelSpreadPropertyNode
|
||||
| DecoratorExpressionNode
|
||||
| Statement
|
||||
| Statement
|
||||
| Expression;
|
||||
|
||||
export interface ADLScriptNode extends BaseNode {
|
||||
|
@ -166,10 +166,7 @@ export interface ADLScriptNode extends BaseNode {
|
|||
file: SourceFile;
|
||||
}
|
||||
|
||||
export type Statement =
|
||||
| ImportStatementNode
|
||||
| ModelStatementNode
|
||||
| NamespaceStatementNode;
|
||||
export type Statement = ImportStatementNode | ModelStatementNode | NamespaceStatementNode;
|
||||
|
||||
export interface ImportStatementNode extends BaseNode {
|
||||
kind: SyntaxKind.ImportStatement;
|
||||
|
@ -206,10 +203,7 @@ export type Expression =
|
|||
| NumericLiteralNode
|
||||
| BooleanLiteralNode;
|
||||
|
||||
export type ReferenceExpression =
|
||||
| TemplateApplicationNode
|
||||
| MemberExpressionNode
|
||||
| IdentifierNode;
|
||||
export type ReferenceExpression = TemplateApplicationNode | MemberExpressionNode | IdentifierNode;
|
||||
|
||||
export interface MemberExpressionNode extends BaseNode {
|
||||
kind: SyntaxKind.MemberExpression;
|
||||
|
@ -233,7 +227,6 @@ export interface NamespacePropertyNode extends BaseNode {
|
|||
decorators: Array<DecoratorExpressionNode>;
|
||||
}
|
||||
|
||||
|
||||
export interface ModelStatementNode extends BaseNode {
|
||||
kind: SyntaxKind.ModelStatement;
|
||||
id: IdentifierNode;
|
||||
|
@ -360,13 +353,13 @@ export interface SourceFile {
|
|||
}
|
||||
|
||||
export interface TextRange {
|
||||
/**
|
||||
/**
|
||||
* The starting position of the ranger measured in UTF-16 code units from the
|
||||
* start of the full string. Inclusive.
|
||||
*/
|
||||
pos: number;
|
||||
|
||||
/**
|
||||
/**
|
||||
* The ending position measured in UTF-16 code units from the start of the
|
||||
* full string. Exclusive.
|
||||
*/
|
||||
|
@ -380,5 +373,5 @@ export interface SourceLocation extends TextRange {
|
|||
export interface Message {
|
||||
code: number;
|
||||
text: string;
|
||||
category: 'error' | 'warning';
|
||||
category: "error" | "warning";
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import url from "url";
|
||||
|
||||
export function resolvePath(basePath: string, ...parts: string[]): string {
|
||||
const resolvedPath = new url.URL(parts.join(''), basePath);
|
||||
const resolvedPath = new url.URL(parts.join(""), basePath);
|
||||
return url.fileURLToPath(resolvedPath);
|
||||
}
|
||||
|
|
|
@ -38,9 +38,7 @@ export function getIntrinsicType(target: Type | undefined): string | undefined {
|
|||
return target.name;
|
||||
}
|
||||
|
||||
target =
|
||||
(target.assignmentType?.kind === "Model" && target.assignmentType)
|
||||
|| undefined;
|
||||
target = (target.assignmentType?.kind === "Model" && target.assignmentType) || undefined;
|
||||
} else if (target.kind === "ModelProperty") {
|
||||
return getIntrinsicType(target.type);
|
||||
} else {
|
||||
|
@ -151,11 +149,7 @@ export function getVisibility(target: Type): string | undefined {
|
|||
return visibilitySettings.get(target);
|
||||
}
|
||||
|
||||
export function withVisibility(
|
||||
program: Program,
|
||||
target: Type,
|
||||
...visibilities: string[]
|
||||
) {
|
||||
export function withVisibility(program: Program, target: Type, ...visibilities: string[]) {
|
||||
if (target.kind !== "Model") {
|
||||
throw new Error("The @withVisibility decorator can only be applied to models.");
|
||||
}
|
||||
|
|
|
@ -3,8 +3,8 @@ import { Type } from "../compiler/types";
|
|||
|
||||
const basePaths = new Map<Type, string>();
|
||||
|
||||
export function resource(program: Program, entity: Type, basePath = '') {
|
||||
if (entity.kind !== 'Namespace') return;
|
||||
export function resource(program: Program, entity: Type, basePath = "") {
|
||||
if (entity.kind !== "Namespace") return;
|
||||
basePaths.set(entity, basePath);
|
||||
}
|
||||
|
||||
|
@ -23,7 +23,7 @@ export function basePathForResource(resource: Type) {
|
|||
const headerFields = new Map<Type, string>();
|
||||
export function header(program: Program, entity: Type, headerName: string) {
|
||||
if (!headerName && entity.kind === "ModelProperty") {
|
||||
headerName = entity.name.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
|
||||
headerName = entity.name.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
|
||||
}
|
||||
headerFields.set(entity, headerName);
|
||||
}
|
||||
|
@ -93,28 +93,28 @@ export function getOperationRoute(entity: Type): OperationRoute | undefined {
|
|||
export function get(program: Program, entity: Type, subPath?: string) {
|
||||
setOperationRoute(entity, {
|
||||
verb: "get",
|
||||
subPath
|
||||
subPath,
|
||||
});
|
||||
}
|
||||
|
||||
export function put(program: Program, entity: Type, subPath?: string) {
|
||||
setOperationRoute(entity, {
|
||||
verb: "put",
|
||||
subPath
|
||||
subPath,
|
||||
});
|
||||
}
|
||||
|
||||
export function post(program: Program, entity: Type, subPath?: string) {
|
||||
setOperationRoute(entity, {
|
||||
verb: "post",
|
||||
subPath
|
||||
subPath,
|
||||
});
|
||||
}
|
||||
|
||||
export function patch(program: Program, entity: Type, subPath?: string) {
|
||||
setOperationRoute(entity, {
|
||||
verb: "patch",
|
||||
subPath
|
||||
subPath,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -122,6 +122,6 @@ export function patch(program: Program, entity: Type, subPath?: string) {
|
|||
export function _delete(program: Program, entity: Type, subPath?: string) {
|
||||
setOperationRoute(entity, {
|
||||
verb: "delete",
|
||||
subPath
|
||||
subPath,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -15,7 +15,9 @@
|
|||
"prepare": "npm run build",
|
||||
"test": "mocha 'dist/test/**/*.js'",
|
||||
"grammargen": "grammarkdown --newLine LF language.grammar",
|
||||
"regen-samples": "node scripts/regen-samples.js"
|
||||
"regen-samples": "node scripts/regen-samples.js",
|
||||
"check-format": "prettier --list-different --config ../../.prettierrc.json --ignore-path ../../.prettierignore \"**/*.ts\" \"*.{js,json}\"",
|
||||
"format": "prettier --write --config ../../.prettierrc.json --ignore-path ../../.prettierignore \"**/*.ts\" \"*.{js,json}\""
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
@ -46,7 +48,8 @@
|
|||
"mocha": "7.1.2",
|
||||
"ts-node": "^8.10.2",
|
||||
"typescript": "~3.9.5",
|
||||
"@types/yargs": "~15.0.12"
|
||||
"@types/yargs": "~15.0.12",
|
||||
"prettier": "~2.2.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"yargs": "~16.2.0",
|
||||
|
@ -55,4 +58,4 @@
|
|||
"autorest": "~3.0.6335"
|
||||
},
|
||||
"type": "module"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,20 +1,20 @@
|
|||
import * as assert from 'assert';
|
||||
import { parse } from '../compiler/parser.js';
|
||||
import { SyntaxKind } from '../compiler/types.js';
|
||||
import * as assert from "assert";
|
||||
import { parse } from "../compiler/parser.js";
|
||||
import { SyntaxKind } from "../compiler/types.js";
|
||||
|
||||
describe('syntax', () => {
|
||||
describe('import statements', () => {
|
||||
describe("syntax", () => {
|
||||
describe("import statements", () => {
|
||||
parseEach([
|
||||
'import x;',
|
||||
'import x as { one };',
|
||||
'import x as {};',
|
||||
'import x as { one, two };'
|
||||
"import x;",
|
||||
"import x as { one };",
|
||||
"import x as {};",
|
||||
"import x as { one, two };",
|
||||
]);
|
||||
});
|
||||
|
||||
describe('model statements', () => {
|
||||
describe("model statements", () => {
|
||||
parseEach([
|
||||
'model Car { };',
|
||||
"model Car { };",
|
||||
|
||||
`@foo()
|
||||
model Car { };`,
|
||||
|
@ -75,101 +75,80 @@ describe('syntax', () => {
|
|||
|
||||
'model Foo { "strKey": number, "😂😂😂": string }',
|
||||
|
||||
'model Foo<A, B> { }',
|
||||
"model Foo<A, B> { }",
|
||||
|
||||
'model Car { @foo @bar x: number }',
|
||||
"model Car { @foo @bar x: number }",
|
||||
|
||||
'model Car { ... A, ... B, c: number, ... D, e: string }',
|
||||
"model Car { ... A, ... B, c: number, ... D, e: string }",
|
||||
|
||||
'model Car { ... A.B, ... C<D> }'
|
||||
"model Car { ... A.B, ... C<D> }",
|
||||
]);
|
||||
});
|
||||
|
||||
describe('model extends statements', () => {
|
||||
describe("model extends statements", () => {
|
||||
parseEach([
|
||||
'model foo extends bar { }',
|
||||
'model foo extends bar, baz { }',
|
||||
'model foo extends bar.baz { }',
|
||||
'model foo extends bar<T> { }',
|
||||
'model foo<T> extends bar<T> { }',
|
||||
'model foo<T> extends bar.baz<T> { }'
|
||||
"model foo extends bar { }",
|
||||
"model foo extends bar, baz { }",
|
||||
"model foo extends bar.baz { }",
|
||||
"model foo extends bar<T> { }",
|
||||
"model foo<T> extends bar<T> { }",
|
||||
"model foo<T> extends bar.baz<T> { }",
|
||||
]);
|
||||
parseErrorEach([
|
||||
'model foo extends { }',
|
||||
'model foo extends = { }',
|
||||
'model foo extends bar = { }'
|
||||
"model foo extends { }",
|
||||
"model foo extends = { }",
|
||||
"model foo extends bar = { }",
|
||||
]);
|
||||
});
|
||||
|
||||
describe('model = statements', () => {
|
||||
describe("model = statements", () => {
|
||||
parseEach(["model x = y;", "model foo = bar | baz;", "model bar<a, b> = a | b;"]);
|
||||
});
|
||||
|
||||
describe("model expressions", () => {
|
||||
parseEach(['model Car { engine: { type: "v8" } }']);
|
||||
});
|
||||
|
||||
describe("tuple model expressions", () => {
|
||||
parseEach(['namespace A { op b(param: [number, string]): [1, "hi"] }']);
|
||||
});
|
||||
|
||||
describe("array expressions", () => {
|
||||
parseEach(["model A { foo: B[] }", "model A { foo: B[][] }"]);
|
||||
});
|
||||
|
||||
describe("union expressions", () => {
|
||||
parseEach(["model A { foo: B | C }", "model A { foo: B | C & D }"]);
|
||||
});
|
||||
|
||||
describe("template instantiations", () => {
|
||||
parseEach(["model A = Foo<number, string>;", "model B = Foo<number, string>[];"]);
|
||||
});
|
||||
|
||||
describe("intersection expressions", () => {
|
||||
parseEach(["model A { foo: B & C }"]);
|
||||
});
|
||||
|
||||
describe("parenthesized expressions", () => {
|
||||
parseEach(["model A = ((B | C) & D)[];"]);
|
||||
});
|
||||
|
||||
describe("namespace statements", () => {
|
||||
parseEach([
|
||||
'model x = y;',
|
||||
'model foo = bar | baz;',
|
||||
'model bar<a, b> = a | b;'
|
||||
"namespace Store {}",
|
||||
"namespace Store { op read(): int32 }",
|
||||
"namespace Store { op read(): int32, op write(v: int32): {} }",
|
||||
"namespace Store { op read(): int32; op write(v: int32): {} }",
|
||||
"@foo namespace Store { @dec op read():number, @dec op write(n: number): {} }",
|
||||
"@foo @bar namespace Store { @foo @bar op read(): number; }",
|
||||
"namespace Store(apiKey: string, otherArg: number) { }",
|
||||
"namespace Store(... apiKeys, x: string) { op foo(... A, b: string, ...C, d: number): void }",
|
||||
]);
|
||||
});
|
||||
|
||||
describe('model expressions', () => {
|
||||
describe("multiple statements", () => {
|
||||
parseEach([
|
||||
'model Car { engine: { type: "v8" } }'
|
||||
]);
|
||||
});
|
||||
|
||||
describe('tuple model expressions', () => {
|
||||
parseEach([
|
||||
'namespace A { op b(param: [number, string]): [1, "hi"] }'
|
||||
]);
|
||||
});
|
||||
|
||||
describe('array expressions', () => {
|
||||
parseEach([
|
||||
'model A { foo: B[] }',
|
||||
'model A { foo: B[][] }',
|
||||
]);
|
||||
});
|
||||
|
||||
describe('union expressions', () => {
|
||||
parseEach([
|
||||
'model A { foo: B | C }',
|
||||
'model A { foo: B | C & D }'
|
||||
]);
|
||||
});
|
||||
|
||||
describe('template instantiations', () => {
|
||||
parseEach([
|
||||
'model A = Foo<number, string>;',
|
||||
'model B = Foo<number, string>[];'
|
||||
]);
|
||||
});
|
||||
|
||||
|
||||
describe('intersection expressions', () => {
|
||||
parseEach([
|
||||
'model A { foo: B & C }'
|
||||
]);
|
||||
});
|
||||
|
||||
describe('parenthesized expressions', () => {
|
||||
parseEach([
|
||||
'model A = ((B | C) & D)[];'
|
||||
]);
|
||||
});
|
||||
|
||||
describe('namespace statements', () => {
|
||||
parseEach([
|
||||
'namespace Store {}',
|
||||
'namespace Store { op read(): int32 }',
|
||||
'namespace Store { op read(): int32, op write(v: int32): {} }',
|
||||
'namespace Store { op read(): int32; op write(v: int32): {} }',
|
||||
'@foo namespace Store { @dec op read():number, @dec op write(n: number): {} }',
|
||||
'@foo @bar namespace Store { @foo @bar op read(): number; }',
|
||||
'namespace Store(apiKey: string, otherArg: number) { }',
|
||||
'namespace Store(... apiKeys, x: string) { op foo(... A, b: string, ...C, d: number): void }'
|
||||
]);
|
||||
});
|
||||
|
||||
describe('multiple statements', () => {
|
||||
parseEach([`
|
||||
`
|
||||
model A { };
|
||||
model B { }
|
||||
model C = A;
|
||||
|
@ -182,11 +161,13 @@ describe('syntax', () => {
|
|||
}
|
||||
|
||||
|
||||
`]);
|
||||
`,
|
||||
]);
|
||||
});
|
||||
|
||||
describe('comments', () => {
|
||||
parseEach([`
|
||||
describe("comments", () => {
|
||||
parseEach([
|
||||
`
|
||||
// Comment
|
||||
model A { /* Another comment */
|
||||
/*
|
||||
|
@ -195,13 +176,14 @@ describe('syntax', () => {
|
|||
*/
|
||||
property /* 👀 */ : /* 👍 */ int32; // one more
|
||||
}
|
||||
`]);
|
||||
`,
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
function parseEach(cases: string[]) {
|
||||
for (const code of cases) {
|
||||
it('parses `' + shorten(code) + '`', () => {
|
||||
it("parses `" + shorten(code) + "`", () => {
|
||||
dumpAST(parse(code));
|
||||
});
|
||||
}
|
||||
|
@ -212,18 +194,18 @@ function parseErrorEach(cases: string[]) {
|
|||
it(`doesn't parse ${shorten(code)}`, () => {
|
||||
assert.throws(() => {
|
||||
parse(code);
|
||||
})
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function dumpAST(astNode: any) {
|
||||
const replacer = function(this: any, key: string, value: any) {
|
||||
return key == 'kind' ? SyntaxKind[value] : value;
|
||||
const replacer = function (this: any, key: string, value: any) {
|
||||
return key == "kind" ? SyntaxKind[value] : value;
|
||||
};
|
||||
console.log(JSON.stringify(astNode, replacer, 4));
|
||||
}
|
||||
|
||||
function shorten(code: string) {
|
||||
return code.replace(/\s+/g, ' ');
|
||||
return code.replace(/\s+/g, " ");
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { deepStrictEqual, strictEqual } from 'assert';
|
||||
import { readFile } from 'fs/promises';
|
||||
import { URL } from 'url';
|
||||
import { throwOnError } from '../compiler/diagnostics.js';
|
||||
import { createScanner, Token } from '../compiler/scanner.js';
|
||||
import { LineAndCharacter } from '../compiler/types.js';
|
||||
import { deepStrictEqual, strictEqual } from "assert";
|
||||
import { readFile } from "fs/promises";
|
||||
import { URL } from "url";
|
||||
import { throwOnError } from "../compiler/diagnostics.js";
|
||||
import { createScanner, Token } from "../compiler/scanner.js";
|
||||
import { LineAndCharacter } from "../compiler/types.js";
|
||||
|
||||
type TokenEntry = [Token, string?, number?, LineAndCharacter?];
|
||||
|
||||
|
@ -22,14 +22,17 @@ function tokens(text: string, onError = throwOnError): Array<TokenEntry> {
|
|||
} while (!scanner.eof());
|
||||
|
||||
// verify that the input matches the output
|
||||
const out = result.map(each => each[1]).join('');
|
||||
strictEqual(out, text, 'Input text should match parsed token values');
|
||||
const out = result.map((each) => each[1]).join("");
|
||||
strictEqual(out, text, "Input text should match parsed token values");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function verify(tokens: Array<TokenEntry>, expecting: Array<TokenEntry>) {
|
||||
for (const [index, [expectedToken, expectedText, expectedPosition, expectedLineAndCharacter]] of expecting.entries()) {
|
||||
for (const [
|
||||
index,
|
||||
[expectedToken, expectedText, expectedPosition, expectedLineAndCharacter],
|
||||
] of expecting.entries()) {
|
||||
const [token, text, position, lineAndCharacter] = tokens[index];
|
||||
strictEqual(Token[token], Token[expectedToken], `Token ${index} must match`);
|
||||
|
||||
|
@ -42,96 +45,95 @@ function verify(tokens: Array<TokenEntry>, expecting: Array<TokenEntry>) {
|
|||
}
|
||||
|
||||
if (expectedLineAndCharacter) {
|
||||
deepStrictEqual(lineAndCharacter, expectedLineAndCharacter, `Token ${index} line and character must match`);
|
||||
deepStrictEqual(
|
||||
lineAndCharacter,
|
||||
expectedLineAndCharacter,
|
||||
`Token ${index} line and character must match`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
describe('scanner', () => {
|
||||
describe("scanner", () => {
|
||||
/** verifies that we can scan tokens and get back some output. */
|
||||
it('smoketest', () => {
|
||||
const all = tokens('\tthis is a test');
|
||||
it("smoketest", () => {
|
||||
const all = tokens("\tthis is a test");
|
||||
verify(all, [
|
||||
[Token.Whitespace],
|
||||
[Token.Identifier, 'this'],
|
||||
[Token.Identifier, "this"],
|
||||
[Token.Whitespace],
|
||||
[Token.Identifier, 'is'],
|
||||
[Token.Identifier, "is"],
|
||||
[Token.Whitespace],
|
||||
[Token.Identifier, 'a'],
|
||||
[Token.Identifier, "a"],
|
||||
[Token.Whitespace],
|
||||
[Token.Identifier, 'test'],
|
||||
[Token.Identifier, "test"],
|
||||
]);
|
||||
});
|
||||
|
||||
it('scans objects', () => {
|
||||
const all = tokens('model Foo{x:y}');
|
||||
it("scans objects", () => {
|
||||
const all = tokens("model Foo{x:y}");
|
||||
|
||||
verify(all, [
|
||||
[Token.ModelKeyword],
|
||||
[Token.Whitespace],
|
||||
[Token.Identifier, 'Foo'],
|
||||
[Token.Identifier, "Foo"],
|
||||
[Token.OpenBrace],
|
||||
[Token.Identifier, 'x'],
|
||||
[Token.Identifier, "x"],
|
||||
[Token.Colon],
|
||||
[Token.Identifier, 'y'],
|
||||
[Token.CloseBrace]
|
||||
[Token.Identifier, "y"],
|
||||
[Token.CloseBrace],
|
||||
]);
|
||||
});
|
||||
|
||||
it('scans decorator expressions', () => {
|
||||
it("scans decorator expressions", () => {
|
||||
const all = tokens('@foo(1,"hello",foo)');
|
||||
|
||||
verify(all, [
|
||||
[Token.At],
|
||||
[Token.Identifier, 'foo'],
|
||||
[Token.Identifier, "foo"],
|
||||
[Token.OpenParen],
|
||||
[Token.NumericLiteral, '1'],
|
||||
[Token.NumericLiteral, "1"],
|
||||
[Token.Comma],
|
||||
[Token.StringLiteral, '"hello"'],
|
||||
[Token.Comma],
|
||||
[Token.Identifier],
|
||||
[Token.CloseParen]
|
||||
[Token.CloseParen],
|
||||
]);
|
||||
});
|
||||
|
||||
it('scans extends keyword', () => {
|
||||
const all = tokens('model foo extends bar{}');
|
||||
it("scans extends keyword", () => {
|
||||
const all = tokens("model foo extends bar{}");
|
||||
verify(all, [
|
||||
[Token.ModelKeyword],
|
||||
[Token.Whitespace],
|
||||
[Token.Identifier, 'foo'],
|
||||
[Token.Identifier, "foo"],
|
||||
[Token.Whitespace],
|
||||
[Token.ExtendsKeyword],
|
||||
[Token.Whitespace],
|
||||
[Token.Identifier, 'bar'],
|
||||
[Token.Identifier, "bar"],
|
||||
[Token.OpenBrace],
|
||||
[Token.CloseBrace]
|
||||
])
|
||||
});
|
||||
it('does not scan greater-than-equals as one operator', () => {
|
||||
const all = tokens('x>=y');
|
||||
verify(all, [
|
||||
[Token.Identifier],
|
||||
[Token.GreaterThan],
|
||||
[Token.Equals],
|
||||
[Token.Identifier]
|
||||
[Token.CloseBrace],
|
||||
]);
|
||||
});
|
||||
it("does not scan greater-than-equals as one operator", () => {
|
||||
const all = tokens("x>=y");
|
||||
verify(all, [[Token.Identifier], [Token.GreaterThan], [Token.Equals], [Token.Identifier]]);
|
||||
});
|
||||
|
||||
it('scans numeric literals', () => {
|
||||
const all = tokens('42 0xBEEF 0b1010 1.5e4 314.0e-2 1e+1000');
|
||||
it("scans numeric literals", () => {
|
||||
const all = tokens("42 0xBEEF 0b1010 1.5e4 314.0e-2 1e+1000");
|
||||
verify(all, [
|
||||
[Token.NumericLiteral, '42'],
|
||||
[Token.NumericLiteral, "42"],
|
||||
[Token.Whitespace],
|
||||
[Token.NumericLiteral, '0xBEEF'],
|
||||
[Token.NumericLiteral, "0xBEEF"],
|
||||
[Token.Whitespace],
|
||||
[Token.NumericLiteral, '0b1010'],
|
||||
[Token.NumericLiteral, "0b1010"],
|
||||
[Token.Whitespace],
|
||||
[Token.NumericLiteral, '1.5e4'],
|
||||
[Token.NumericLiteral, "1.5e4"],
|
||||
[Token.Whitespace],
|
||||
[Token.NumericLiteral, '314.0e-2'],
|
||||
[Token.NumericLiteral, "314.0e-2"],
|
||||
[Token.Whitespace],
|
||||
[Token.NumericLiteral, '1e+1000'],
|
||||
[Token.NumericLiteral, "1e+1000"],
|
||||
]);
|
||||
});
|
||||
|
||||
|
@ -143,19 +145,15 @@ describe('scanner', () => {
|
|||
strictEqual(scanner.getTokenValue(), expectedValue);
|
||||
}
|
||||
|
||||
it('scans strings single-line strings with escape sequences', () => {
|
||||
scanString(
|
||||
'"Hello world \\r\\n \\t \\" \\\\ !"',
|
||||
'Hello world \r\n \t " \\ !');
|
||||
it("scans strings single-line strings with escape sequences", () => {
|
||||
scanString('"Hello world \\r\\n \\t \\" \\\\ !"', 'Hello world \r\n \t " \\ !');
|
||||
});
|
||||
|
||||
it('scans multi-line strings', () => {
|
||||
scanString(
|
||||
'"More\r\nthan\r\none\r\nline"',
|
||||
'More\nthan\none\nline');
|
||||
it("scans multi-line strings", () => {
|
||||
scanString('"More\r\nthan\r\none\r\nline"', "More\nthan\none\nline");
|
||||
});
|
||||
|
||||
it('scans triple-quoted strings', () => {
|
||||
it("scans triple-quoted strings", () => {
|
||||
scanString(
|
||||
`"""
|
||||
This is a triple-quoted string
|
||||
|
@ -166,45 +164,48 @@ describe('scanner', () => {
|
|||
"""`,
|
||||
// NOTE: sloppy blank line formatting and trailing whitespace after open
|
||||
// quotes above is deliberately tolerated.
|
||||
'This is a triple-quoted string\n\n\n\nAnd this is another line');
|
||||
"This is a triple-quoted string\n\n\n\nAnd this is another line"
|
||||
);
|
||||
});
|
||||
|
||||
it('provides token position', () => {
|
||||
const all = tokens('a x\raa x\r\naaa x\naaaa x\u{2028}aaaaa x\u{2029}aaaaaa x');
|
||||
it("provides token position", () => {
|
||||
const all = tokens("a x\raa x\r\naaa x\naaaa x\u{2028}aaaaa x\u{2029}aaaaaa x");
|
||||
verify(all, [
|
||||
[Token.Identifier, 'a', 0, { line: 0, character: 0}],
|
||||
[Token.Whitespace, ' ', 1, { line: 0, character: 1}],
|
||||
[Token.Identifier, 'x', 2, { line: 0, character: 2}],
|
||||
[Token.NewLine, '\r', 3, { line: 0, character: 3}],
|
||||
[Token.Identifier, "a", 0, { line: 0, character: 0 }],
|
||||
[Token.Whitespace, " ", 1, { line: 0, character: 1 }],
|
||||
[Token.Identifier, "x", 2, { line: 0, character: 2 }],
|
||||
[Token.NewLine, "\r", 3, { line: 0, character: 3 }],
|
||||
|
||||
[Token.Identifier, 'aa', 4, { line: 1, character: 0 }],
|
||||
[Token.Whitespace, ' ', 6, { line: 1, character: 2 }],
|
||||
[Token.Identifier, 'x', 7, { line: 1, character: 3 }],
|
||||
[Token.NewLine, '\r\n', 8, { line: 1, character: 4 }],
|
||||
[Token.Identifier, "aa", 4, { line: 1, character: 0 }],
|
||||
[Token.Whitespace, " ", 6, { line: 1, character: 2 }],
|
||||
[Token.Identifier, "x", 7, { line: 1, character: 3 }],
|
||||
[Token.NewLine, "\r\n", 8, { line: 1, character: 4 }],
|
||||
|
||||
[Token.Identifier, 'aaa', 10, { line: 2, character: 0 }],
|
||||
[Token.Whitespace, ' ', 13, { line: 2, character: 3 }],
|
||||
[Token.Identifier, 'x', 14, { line: 2, character: 4 }],
|
||||
[Token.NewLine, '\n', 15, { line: 2, character: 5 }],
|
||||
[Token.Identifier, "aaa", 10, { line: 2, character: 0 }],
|
||||
[Token.Whitespace, " ", 13, { line: 2, character: 3 }],
|
||||
[Token.Identifier, "x", 14, { line: 2, character: 4 }],
|
||||
[Token.NewLine, "\n", 15, { line: 2, character: 5 }],
|
||||
|
||||
[Token.Identifier, 'aaaa', 16, { line: 3, character: 0 }],
|
||||
[Token.Whitespace, ' ', 20, { line: 3, character: 4 }],
|
||||
[Token.Identifier, 'x', 21, { line: 3, character: 5 }],
|
||||
[Token.NewLine, '\u{2028}', 22, { line: 3, character: 6 }],
|
||||
[Token.Identifier, "aaaa", 16, { line: 3, character: 0 }],
|
||||
[Token.Whitespace, " ", 20, { line: 3, character: 4 }],
|
||||
[Token.Identifier, "x", 21, { line: 3, character: 5 }],
|
||||
[Token.NewLine, "\u{2028}", 22, { line: 3, character: 6 }],
|
||||
|
||||
[Token.Identifier, 'aaaaa', 23, { line: 4, character: 0 }],
|
||||
[Token.Whitespace, ' ', 28, { line: 4, character: 5 }],
|
||||
[Token.Identifier, 'x', 29, { line: 4, character: 6 }],
|
||||
[Token.NewLine, '\u{2029}', 30, { line: 4, character: 7 }],
|
||||
[Token.Identifier, "aaaaa", 23, { line: 4, character: 0 }],
|
||||
[Token.Whitespace, " ", 28, { line: 4, character: 5 }],
|
||||
[Token.Identifier, "x", 29, { line: 4, character: 6 }],
|
||||
[Token.NewLine, "\u{2029}", 30, { line: 4, character: 7 }],
|
||||
|
||||
[Token.Identifier, 'aaaaaa', 31, { line: 5, character: 0 }],
|
||||
[Token.Whitespace, ' ', 37, { line: 5, character: 6 }],
|
||||
[Token.Identifier, 'x', 38, { line: 5, character: 7 }],
|
||||
[Token.Identifier, "aaaaaa", 31, { line: 5, character: 0 }],
|
||||
[Token.Whitespace, " ", 37, { line: 5, character: 6 }],
|
||||
[Token.Identifier, "x", 38, { line: 5, character: 7 }],
|
||||
]);
|
||||
});
|
||||
|
||||
it('scans this file', async () => {
|
||||
const text = await readFile(new URL(import.meta.url), 'utf-8');
|
||||
tokens(text, function() { /* ignore errors */});
|
||||
it("scans this file", async () => {
|
||||
const text = await readFile(new URL(import.meta.url), "utf-8");
|
||||
tokens(text, function () {
|
||||
/* ignore errors */
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -7,12 +7,5 @@
|
|||
"types": ["node", "mocha"]
|
||||
},
|
||||
"include": ["./**/*.ts"],
|
||||
"exclude": [
|
||||
"dist",
|
||||
"test/scenarios/**",
|
||||
"resources",
|
||||
"node_modules",
|
||||
"**/*.d.ts",
|
||||
"**/*.adl.ts"
|
||||
]
|
||||
"exclude": ["dist", "test/scenarios/**", "resources", "node_modules", "**/*.d.ts", "**/*.adl.ts"]
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче