Merge pull request #324 from bterlson/new-checker
Refactor checker, implemented nested namespaces, fix bugs
This commit is contained in:
Коммит
338ff4a187
|
@ -1,7 +1,6 @@
|
|||
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,
|
||||
|
@ -10,6 +9,9 @@ import {
|
|||
TemplateParameterDeclarationNode,
|
||||
SourceLocation,
|
||||
Sym,
|
||||
Declaration,
|
||||
OperationStatementNode,
|
||||
ScopeNode,
|
||||
} from "./types.js";
|
||||
|
||||
export class SymbolTable extends Map<string, Sym> {
|
||||
|
@ -49,9 +51,10 @@ export function createBinder(): Binder {
|
|||
let currentFile: ADLSourceFile;
|
||||
let parentNode: Node;
|
||||
|
||||
// Node where locals go.
|
||||
let scope: Node;
|
||||
let currentNamespace: NamespaceStatementNode | undefined;
|
||||
|
||||
// Node where locals go.
|
||||
let scope: ScopeNode;
|
||||
return {
|
||||
bindSourceFile,
|
||||
};
|
||||
|
@ -63,6 +66,7 @@ export function createBinder(): Binder {
|
|||
) {
|
||||
currentFile = sourceFile;
|
||||
bindNode(sourceFile.ast);
|
||||
reportDuplicateSymbols(currentFile.symbols);
|
||||
|
||||
// everything is global
|
||||
if (globalScope) {
|
||||
|
@ -80,13 +84,16 @@ export function createBinder(): Binder {
|
|||
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.ModelStatement:
|
||||
bindModelStatement(<any>node);
|
||||
bindModelStatement(node);
|
||||
break;
|
||||
case SyntaxKind.NamespaceStatement:
|
||||
bindInterfaceStatement(<any>node);
|
||||
bindNamespaceStatement(node);
|
||||
break;
|
||||
case SyntaxKind.OperationStatement:
|
||||
bindOperationStatement(node);
|
||||
break;
|
||||
case SyntaxKind.TemplateParameterDeclaration:
|
||||
bindTemplateParameterDeclaration(<any>node);
|
||||
bindTemplateParameterDeclaration(node);
|
||||
}
|
||||
|
||||
const prevParent = parentNode;
|
||||
|
@ -95,9 +102,16 @@ export function createBinder(): Binder {
|
|||
|
||||
if (hasScope(node)) {
|
||||
const prevScope = scope;
|
||||
const prevNamespace = currentNamespace;
|
||||
scope = node;
|
||||
if (node.kind === SyntaxKind.NamespaceStatement) {
|
||||
currentNamespace = node;
|
||||
}
|
||||
|
||||
visitChildren(node, bindNode);
|
||||
|
||||
scope = prevScope;
|
||||
currentNamespace = prevNamespace;
|
||||
} else {
|
||||
visitChildren(node, bindNode);
|
||||
}
|
||||
|
@ -106,34 +120,42 @@ export function createBinder(): Binder {
|
|||
parentNode = prevParent;
|
||||
}
|
||||
|
||||
function getContainingSymbolTable() {
|
||||
return scope ? scope.locals! : currentFile.symbols;
|
||||
}
|
||||
|
||||
function bindTemplateParameterDeclaration(node: TemplateParameterDeclarationNode) {
|
||||
(<ModelStatementNode>scope).locals!.set(node.sv, {
|
||||
kind: "type",
|
||||
node: node,
|
||||
name: node.sv,
|
||||
});
|
||||
declareSymbol(getContainingSymbolTable(), node);
|
||||
}
|
||||
|
||||
function bindModelStatement(node: ModelStatementNode) {
|
||||
currentFile.symbols.set(node.id.sv, {
|
||||
kind: "type",
|
||||
node: node,
|
||||
name: node.id.sv,
|
||||
});
|
||||
declareSymbol(getContainingSymbolTable(), node);
|
||||
|
||||
// initialize locals for type parameters.
|
||||
// Initialize locals for type parameters
|
||||
node.locals = new SymbolTable();
|
||||
}
|
||||
|
||||
function bindInterfaceStatement(statement: NamespaceStatementNode) {
|
||||
currentFile.symbols.set(statement.id.sv, {
|
||||
kind: "type",
|
||||
node: statement,
|
||||
name: statement.id.sv,
|
||||
});
|
||||
function bindNamespaceStatement(statement: NamespaceStatementNode) {
|
||||
declareSymbol(getContainingSymbolTable(), statement);
|
||||
|
||||
// Initialize locals for namespace members
|
||||
statement.locals = new SymbolTable();
|
||||
}
|
||||
|
||||
function reportDuplicateSymbols(globalSymbols: SymbolTable) {
|
||||
function bindOperationStatement(statement: OperationStatementNode) {
|
||||
declareSymbol(getContainingSymbolTable(), statement);
|
||||
}
|
||||
|
||||
function declareSymbol(table: SymbolTable, node: Declaration) {
|
||||
const symbol = createTypeSymbol(node, node.id.sv);
|
||||
node.symbol = symbol;
|
||||
if (currentNamespace && node.kind !== SyntaxKind.TemplateParameterDeclaration) {
|
||||
node.namespaceSymbol = currentNamespace.symbol;
|
||||
}
|
||||
table.set(node.id.sv, symbol);
|
||||
}
|
||||
|
||||
function reportDuplicateSymbols(symbols: SymbolTable) {
|
||||
let reported = new Set<Sym>();
|
||||
let messages = new Array<string>();
|
||||
|
||||
|
@ -141,10 +163,17 @@ export function createBinder(): Binder {
|
|||
report(symbol);
|
||||
}
|
||||
|
||||
for (const symbol of globalSymbols.duplicates) {
|
||||
for (const symbol of symbols.duplicates) {
|
||||
report(symbol);
|
||||
}
|
||||
|
||||
// Check symbols that have their own scopes
|
||||
for (const [_, symbol] of symbols) {
|
||||
if (symbol.kind === "type" && hasScope(symbol.node) && symbol.node.locals) {
|
||||
reportDuplicateSymbols(symbol.node.locals);
|
||||
}
|
||||
}
|
||||
|
||||
if (messages.length > 0) {
|
||||
// TODO: We're now reporting all duplicates up to the binding of the first file
|
||||
// that introduced one, but still bailing the compilation rather than
|
||||
|
@ -154,6 +183,7 @@ 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"));
|
||||
}
|
||||
|
||||
|
@ -167,11 +197,21 @@ export function createBinder(): Binder {
|
|||
}
|
||||
}
|
||||
|
||||
function hasScope(node: Node) {
|
||||
function hasScope(node: Node): node is ScopeNode {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.ModelStatement:
|
||||
return true;
|
||||
case SyntaxKind.NamespaceStatement:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function createTypeSymbol(node: Node, name: string): TypeSymbol {
|
||||
return {
|
||||
kind: "type",
|
||||
node,
|
||||
name,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { SymbolTable } from "./binder.js";
|
||||
import { throwDiagnostic } from "./diagnostics.js";
|
||||
import { ADLSourceFile, Program } from "./program.js";
|
||||
import {
|
||||
|
@ -5,10 +6,10 @@ import {
|
|||
BooleanLiteralNode,
|
||||
BooleanLiteralType,
|
||||
IdentifierNode,
|
||||
NamespacePropertyNode,
|
||||
OperationStatementNode,
|
||||
NamespaceStatementNode,
|
||||
Namespace,
|
||||
NamespaceProperty,
|
||||
NamespaceType,
|
||||
OperationType,
|
||||
IntersectionExpressionNode,
|
||||
LiteralNode,
|
||||
LiteralType,
|
||||
|
@ -23,7 +24,7 @@ import {
|
|||
StringLiteralNode,
|
||||
StringLiteralType,
|
||||
SyntaxKind,
|
||||
TemplateApplicationNode,
|
||||
TypeReferenceNode,
|
||||
TemplateParameterDeclarationNode,
|
||||
TupleExpressionNode,
|
||||
TupleType,
|
||||
|
@ -31,6 +32,11 @@ import {
|
|||
UnionExpressionNode,
|
||||
UnionType,
|
||||
ReferenceExpression,
|
||||
DecoratorSymbol,
|
||||
TypeSymbol,
|
||||
SymbolLinks,
|
||||
MemberExpressionNode,
|
||||
Sym,
|
||||
} from "./types.js";
|
||||
|
||||
/**
|
||||
|
@ -72,6 +78,8 @@ export class MultiKeyMap<T> {
|
|||
export function createChecker(program: Program) {
|
||||
let templateInstantiation: Array<Type> = [];
|
||||
let instantiatingTemplate: Node | undefined;
|
||||
let currentSymbolId = 0;
|
||||
const symbolLinks = new Map<number, SymbolLinks>();
|
||||
|
||||
const seq = 0;
|
||||
|
||||
|
@ -80,13 +88,10 @@ export function createChecker(program: Program) {
|
|||
checkProgram,
|
||||
getLiteralType,
|
||||
getTypeName,
|
||||
checkNamespaceProperty,
|
||||
checkOperation,
|
||||
};
|
||||
|
||||
function getTypeForNode(node: Node): Type {
|
||||
const cached = program.typeCache.get([node, ...templateInstantiation.values()]);
|
||||
if (cached) return cached;
|
||||
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.ModelExpression:
|
||||
return checkModel(node);
|
||||
|
@ -96,11 +101,8 @@ export function createChecker(program: Program) {
|
|||
return checkModelProperty(node);
|
||||
case SyntaxKind.NamespaceStatement:
|
||||
return checkNamespace(node);
|
||||
case SyntaxKind.NamespaceProperty:
|
||||
return checkNamespaceProperty(node);
|
||||
case SyntaxKind.Identifier:
|
||||
// decorator bindings presently return an empty binding
|
||||
return <any>checkIdentifier(node);
|
||||
case SyntaxKind.OperationStatement:
|
||||
return checkOperation(node);
|
||||
case SyntaxKind.NumericLiteral:
|
||||
return checkNumericLiteral(node);
|
||||
case SyntaxKind.BooleanLiteral:
|
||||
|
@ -115,8 +117,8 @@ export function createChecker(program: Program) {
|
|||
return checkUnionExpression(node);
|
||||
case SyntaxKind.IntersectionExpression:
|
||||
return checkIntersectionExpression(node);
|
||||
case SyntaxKind.TemplateApplication:
|
||||
return checkTemplateApplication(node);
|
||||
case SyntaxKind.TypeReference:
|
||||
return checkTypeReference(node);
|
||||
case SyntaxKind.TemplateParameterDeclaration:
|
||||
return checkTemplateParameterDeclaration(node);
|
||||
}
|
||||
|
@ -141,20 +143,27 @@ export function createChecker(program: Program) {
|
|||
return "(unnamed type)";
|
||||
}
|
||||
|
||||
function getNamespaceString(type: NamespaceType | undefined): string {
|
||||
if (!type) return "";
|
||||
const parent = type.namespace;
|
||||
|
||||
return parent ? `${getNamespaceString(parent)}.${type.name}` : type.name;
|
||||
}
|
||||
|
||||
function getModelName(model: ModelType) {
|
||||
if ((<ModelStatementNode>model.node).assignment) {
|
||||
return model.name;
|
||||
} else if (model.templateArguments && model.templateArguments.length > 0) {
|
||||
const nsName = getNamespaceString(model.namespace);
|
||||
const modelName = (nsName ? nsName + "." : "") + (model.name || "(anonymous model)");
|
||||
if (model.templateArguments && model.templateArguments.length > 0) {
|
||||
// template instantiation
|
||||
const args = model.templateArguments.map(getTypeName);
|
||||
return `${model.name}<${args.join(", ")}>`;
|
||||
return `${modelName}<${args.join(", ")}>`;
|
||||
} else if ((<ModelStatementNode>model.node).templateParameters?.length > 0) {
|
||||
// template
|
||||
const params = (<ModelStatementNode>model.node).templateParameters.map((t) => t.sv);
|
||||
const params = (<ModelStatementNode>model.node).templateParameters.map((t) => t.id.sv);
|
||||
return `${model.name}<${params.join(", ")}>`;
|
||||
} else {
|
||||
// regular old model.
|
||||
return model.name || "(anonymous model)";
|
||||
return modelName;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -172,11 +181,65 @@ export function createChecker(program: Program) {
|
|||
});
|
||||
}
|
||||
|
||||
function checkTemplateApplication(node: TemplateApplicationNode): Type {
|
||||
function checkTypeReference(node: TypeReferenceNode): Type {
|
||||
const sym = resolveTypeReference(node);
|
||||
if (sym.kind === "decorator") {
|
||||
throwDiagnostic("Can't put a decorator in a type", node);
|
||||
}
|
||||
|
||||
const symbolLinks = getSymbolLinks(sym);
|
||||
const args = node.arguments.map(getTypeForNode);
|
||||
const targetType = getTypeForNode(node.target);
|
||||
// todo: check proper target
|
||||
return instantiateTemplate(<ModelStatementNode>targetType.node, args);
|
||||
|
||||
if (sym.node.kind === SyntaxKind.ModelStatement && !sym.node.assignment) {
|
||||
// model statement, possibly templated
|
||||
if (sym.node.templateParameters.length === 0) {
|
||||
if (args.length > 0) {
|
||||
throwDiagnostic("Can't pass template arguments to model that is not templated", node);
|
||||
}
|
||||
|
||||
if (symbolLinks.declaredType) {
|
||||
return symbolLinks.declaredType;
|
||||
}
|
||||
|
||||
return checkModelStatement(sym.node);
|
||||
} else {
|
||||
// model is templated, lets instantiate.
|
||||
|
||||
if (!symbolLinks.declaredType) {
|
||||
// we haven't checked the declared type yet, so do so.
|
||||
checkModelStatement(sym.node);
|
||||
}
|
||||
if (sym.node.templateParameters!.length > node.arguments.length) {
|
||||
throwDiagnostic("Too few template arguments provided.", node);
|
||||
}
|
||||
|
||||
if (sym.node.templateParameters!.length < node.arguments.length) {
|
||||
throwDiagnostic("Too many template arguments provided.", node);
|
||||
}
|
||||
|
||||
return instantiateTemplate(sym.node, args);
|
||||
}
|
||||
}
|
||||
// some other kind of reference
|
||||
|
||||
if (args.length > 0) {
|
||||
throwDiagnostic("Can't pass template arguments to non-templated type", node);
|
||||
}
|
||||
|
||||
if (sym.node.kind === SyntaxKind.TemplateParameterDeclaration) {
|
||||
const type = checkTemplateParameterDeclaration(sym.node);
|
||||
// TODO: could cache this probably.
|
||||
return type;
|
||||
}
|
||||
// types for non-templated types
|
||||
if (symbolLinks.type) {
|
||||
return symbolLinks.type;
|
||||
}
|
||||
|
||||
const type = getTypeForNode(sym.node);
|
||||
symbolLinks.type = type;
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -185,15 +248,14 @@ export function createChecker(program: Program) {
|
|||
* parameters to access type type arguments.
|
||||
*
|
||||
* This will fall over if the same template is ever being instantiated
|
||||
* twice at the same time.
|
||||
* twice at the same time, or if template parameters from more than one template
|
||||
* are ever in scope at once.
|
||||
*/
|
||||
function instantiateTemplate(templateNode: ModelStatementNode, args: Array<Type>): ModelType {
|
||||
if (templateNode.templateParameters!.length < args.length) {
|
||||
throwDiagnostic("Too few template arguments provided.", templateNode);
|
||||
}
|
||||
|
||||
if (templateNode.templateParameters!.length > args.length) {
|
||||
throwDiagnostic("Too many template arguments provided.", templateNode);
|
||||
const symbolLinks = getSymbolLinks(templateNode.symbol!);
|
||||
const cached = symbolLinks.instantiations!.get(args) as ModelType;
|
||||
if (cached) {
|
||||
return cached;
|
||||
}
|
||||
|
||||
const oldTis = templateInstantiation;
|
||||
|
@ -202,6 +264,9 @@ export function createChecker(program: Program) {
|
|||
instantiatingTemplate = templateNode;
|
||||
// this cast is invalid once we support templatized `model =`.
|
||||
const type = <ModelType>getTypeForNode(templateNode);
|
||||
|
||||
symbolLinks.instantiations!.set(args, type);
|
||||
|
||||
type.templateNode = templateNode;
|
||||
templateInstantiation = oldTis;
|
||||
instantiatingTemplate = oldTemplate;
|
||||
|
@ -242,13 +307,10 @@ export function createChecker(program: Program) {
|
|||
);
|
||||
}
|
||||
|
||||
const newPropType = createType(
|
||||
{
|
||||
const newPropType = createType({
|
||||
...prop,
|
||||
sourceProperty: prop,
|
||||
},
|
||||
true
|
||||
);
|
||||
});
|
||||
|
||||
properties.set(prop.name, newPropType);
|
||||
}
|
||||
|
@ -274,28 +336,56 @@ export function createChecker(program: Program) {
|
|||
}
|
||||
|
||||
function checkNamespace(node: NamespaceStatementNode) {
|
||||
const type: Namespace = createType({
|
||||
const type: NamespaceType = createType({
|
||||
kind: "Namespace",
|
||||
name: node.id.sv,
|
||||
namespace: getParentNamespaceType(node),
|
||||
node: node,
|
||||
properties: new Map(),
|
||||
parameters: node.parameters ? <ModelType>getTypeForNode(node.parameters) : undefined,
|
||||
models: new Map(),
|
||||
operations: new Map(),
|
||||
namespaces: new Map(),
|
||||
});
|
||||
|
||||
for (const prop of node.properties) {
|
||||
type.properties.set(prop.id.sv, checkNamespaceProperty(prop));
|
||||
const links = getSymbolLinks(node.symbol!);
|
||||
links.type = type;
|
||||
|
||||
for (const statement of node.statements.map(getTypeForNode)) {
|
||||
switch (statement.kind) {
|
||||
case "Model":
|
||||
type.models.set(statement.name, statement as ModelType);
|
||||
break;
|
||||
case "Operation":
|
||||
type.operations.set(statement.name, statement as OperationType);
|
||||
break;
|
||||
case "Namespace":
|
||||
type.namespaces.set(statement.name, statement as NamespaceType);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
function checkNamespaceProperty(prop: NamespacePropertyNode): NamespaceProperty {
|
||||
function getParentNamespaceType(
|
||||
node: ModelStatementNode | NamespaceStatementNode | OperationStatementNode
|
||||
): NamespaceType | undefined {
|
||||
if (!node.namespaceSymbol) return undefined;
|
||||
|
||||
const symbolLinks = getSymbolLinks(node.namespaceSymbol);
|
||||
if (!symbolLinks.type) {
|
||||
throw new Error("Parent namespace isn't typed yet, please file a bug.");
|
||||
}
|
||||
return symbolLinks.type as NamespaceType;
|
||||
}
|
||||
|
||||
function checkOperation(node: OperationStatementNode): OperationType {
|
||||
return createType({
|
||||
kind: "NamespaceProperty",
|
||||
name: prop.id.sv,
|
||||
node: prop,
|
||||
parameters: <ModelType>getTypeForNode(prop.parameters),
|
||||
returnType: getTypeForNode(prop.returnType),
|
||||
kind: "Operation",
|
||||
name: node.id.sv,
|
||||
namespace: getParentNamespaceType(node),
|
||||
node: node,
|
||||
parameters: <ModelType>getTypeForNode(node.parameters),
|
||||
returnType: getTypeForNode(node.returnType),
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -307,13 +397,29 @@ export function createChecker(program: Program) {
|
|||
});
|
||||
}
|
||||
|
||||
function checkIdentifier(node: IdentifierNode) {
|
||||
const binding = resolveIdentifier(node);
|
||||
if (binding.kind === "decorator") {
|
||||
return {};
|
||||
} else {
|
||||
return getTypeForNode(binding.node);
|
||||
function getSymbolLinks(s: TypeSymbol): SymbolLinks {
|
||||
const id = getSymbolId(s);
|
||||
|
||||
if (symbolLinks.has(id)) {
|
||||
return symbolLinks.get(id)!;
|
||||
}
|
||||
|
||||
const links = {};
|
||||
symbolLinks.set(id, links);
|
||||
|
||||
return links;
|
||||
}
|
||||
|
||||
function getSymbolId(s: TypeSymbol) {
|
||||
if (s.id === undefined) {
|
||||
s.id = currentSymbolId++;
|
||||
}
|
||||
|
||||
return s.id;
|
||||
}
|
||||
|
||||
function resolveIdentifierInScope(node: IdentifierNode, scope: { locals?: SymbolTable }) {
|
||||
return (<any>scope).locals.get(node.sv);
|
||||
}
|
||||
|
||||
function resolveIdentifier(node: IdentifierNode) {
|
||||
|
@ -322,7 +428,7 @@ export function createChecker(program: Program) {
|
|||
|
||||
while (scope) {
|
||||
if ("locals" in scope) {
|
||||
binding = (<any>scope).locals.get(node.sv);
|
||||
binding = resolveIdentifierInScope(node, scope);
|
||||
if (binding) break;
|
||||
}
|
||||
scope = scope.parent;
|
||||
|
@ -339,6 +445,36 @@ export function createChecker(program: Program) {
|
|||
return binding;
|
||||
}
|
||||
|
||||
function resolveTypeReference(node: ReferenceExpression): DecoratorSymbol | TypeSymbol {
|
||||
if (node.kind === SyntaxKind.TypeReference) {
|
||||
return resolveTypeReference(node.target);
|
||||
}
|
||||
|
||||
if (node.kind === SyntaxKind.MemberExpression) {
|
||||
const base = resolveTypeReference(node.base);
|
||||
if (base.kind === "type" && base.node.kind === SyntaxKind.NamespaceStatement) {
|
||||
const symbol = resolveIdentifierInScope(node.id, base.node);
|
||||
if (!symbol) {
|
||||
throwDiagnostic(`Namespace doesn't have member ${node.id.sv}`, node);
|
||||
}
|
||||
return symbol;
|
||||
} else if (base.kind === "decorator") {
|
||||
throwDiagnostic(`Cannot resolve '${node.id.sv}' in decorator`, node);
|
||||
} else {
|
||||
throwDiagnostic(
|
||||
`Cannot resolve '${node.id.sv}' in non-namespace node ${base.node.kind}`,
|
||||
node
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (node.kind === SyntaxKind.Identifier) {
|
||||
return resolveIdentifier(node);
|
||||
}
|
||||
|
||||
throw new Error("Unknown type reference kind");
|
||||
}
|
||||
|
||||
function checkStringLiteral(str: StringLiteralNode): StringLiteralType {
|
||||
return getLiteralType(str);
|
||||
}
|
||||
|
@ -364,12 +500,69 @@ export function createChecker(program: Program) {
|
|||
}
|
||||
|
||||
function checkModel(node: ModelExpressionNode | ModelStatementNode) {
|
||||
if (node.kind === SyntaxKind.ModelStatement) {
|
||||
if (node.properties) {
|
||||
const properties = new Map();
|
||||
const baseModels =
|
||||
node.kind === SyntaxKind.ModelExpression ? [] : checkClassHeritage(node.heritage);
|
||||
return checkModelStatement(node);
|
||||
} else {
|
||||
return checkModelEquals(node);
|
||||
}
|
||||
} else {
|
||||
return checkModelExpression(node);
|
||||
}
|
||||
}
|
||||
|
||||
for (const prop of node.properties) {
|
||||
function checkModelStatement(node: ModelStatementNode) {
|
||||
const links = getSymbolLinks(node.symbol!);
|
||||
const instantiatingThisTemplate = instantiatingTemplate === node;
|
||||
|
||||
if (links.declaredType && !instantiatingThisTemplate) {
|
||||
// we're not instantiating this model and we've already checked it
|
||||
return links.declaredType;
|
||||
}
|
||||
|
||||
const baseModels = checkClassHeritage(node.heritage);
|
||||
const properties = checkModelProperties(node);
|
||||
const type: ModelType = {
|
||||
kind: "Model",
|
||||
name: node.id.sv,
|
||||
node: node,
|
||||
properties,
|
||||
baseModels: baseModels,
|
||||
namespace: getParentNamespaceType(node),
|
||||
};
|
||||
|
||||
if (
|
||||
(instantiatingThisTemplate &&
|
||||
templateInstantiation.every((t) => t.kind !== "TemplateParameter")) ||
|
||||
node.templateParameters.length === 0
|
||||
) {
|
||||
createType(type);
|
||||
}
|
||||
|
||||
if (!instantiatingThisTemplate) {
|
||||
links.declaredType = type;
|
||||
links.instantiations = new MultiKeyMap();
|
||||
}
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
function checkModelExpression(node: ModelExpressionNode) {
|
||||
const properties = checkModelProperties(node);
|
||||
const type: ModelType = createType({
|
||||
kind: "Model",
|
||||
name: "",
|
||||
node: node,
|
||||
properties,
|
||||
baseModels: [],
|
||||
});
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
function checkModelProperties(node: ModelExpressionNode | ModelStatementNode) {
|
||||
const properties = new Map();
|
||||
for (const prop of node.properties!) {
|
||||
if ("id" in prop) {
|
||||
const propType = <ModelTypeProperty>getTypeForNode(prop);
|
||||
properties.set(propType.name, propType);
|
||||
|
@ -387,14 +580,10 @@ export function createChecker(program: Program) {
|
|||
}
|
||||
}
|
||||
|
||||
return createType({
|
||||
kind: "Model",
|
||||
name: node.kind === SyntaxKind.ModelStatement ? node.id.sv : "",
|
||||
node: node,
|
||||
properties,
|
||||
baseModels: baseModels,
|
||||
});
|
||||
} else {
|
||||
return properties;
|
||||
}
|
||||
|
||||
function checkModelEquals(node: ModelStatementNode) {
|
||||
// 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.
|
||||
|
@ -406,6 +595,7 @@ export function createChecker(program: Program) {
|
|||
node: node,
|
||||
name: (<ModelStatementNode>node).id.sv,
|
||||
assignmentType,
|
||||
namespace: getParentNamespaceType(node),
|
||||
});
|
||||
|
||||
return type;
|
||||
|
@ -413,17 +603,11 @@ export function createChecker(program: Program) {
|
|||
|
||||
return assignmentType;
|
||||
}
|
||||
}
|
||||
|
||||
function checkClassHeritage(heritage: ReferenceExpression[]): ModelType[] {
|
||||
return heritage.flatMap((heritageRef) => {
|
||||
return heritage.map((heritageRef) => {
|
||||
const heritageType = getTypeForNode(heritageRef);
|
||||
|
||||
if (heritageType.kind === "TemplateParameter") {
|
||||
// don't need to track heritage for template parameters.
|
||||
return [];
|
||||
}
|
||||
|
||||
if (heritageType.kind !== "Model") {
|
||||
throwDiagnostic("Models must extend other models.", heritageRef);
|
||||
}
|
||||
|
@ -443,13 +627,10 @@ export function createChecker(program: Program) {
|
|||
|
||||
// copy each property
|
||||
for (const prop of walkPropertiesInherited(targetType)) {
|
||||
const newProp = createType(
|
||||
{
|
||||
const newProp = createType({
|
||||
...prop,
|
||||
sourceProperty: prop,
|
||||
},
|
||||
true
|
||||
);
|
||||
});
|
||||
props.push(newProp);
|
||||
}
|
||||
}
|
||||
|
@ -490,18 +671,12 @@ export function createChecker(program: Program) {
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
// the types here aren't ideal and could probably be refactored.
|
||||
function createType<T extends Type>(typeDef: T, skipCache = false): T {
|
||||
if (!skipCache) {
|
||||
(<any>typeDef).seq = program.typeCache.set([typeDef.node, ...templateInstantiation], typeDef);
|
||||
}
|
||||
function createType<T extends Type>(typeDef: T): T {
|
||||
(<any>typeDef).templateArguments = templateInstantiation;
|
||||
|
||||
// only run decorators on fully instantiated types.
|
||||
if (templateInstantiation.every((i) => i.kind !== "TemplateParameter")) {
|
||||
program.executeDecorators(typeDef);
|
||||
}
|
||||
|
||||
return typeDef;
|
||||
}
|
||||
|
||||
|
|
|
@ -38,7 +38,9 @@ export function parse(code: string | Types.SourceFile) {
|
|||
case Token.ModelKeyword:
|
||||
return parseModelStatement(decorators);
|
||||
case Token.NamespaceKeyword:
|
||||
return parseInterfaceStatement(decorators);
|
||||
return parseNamespaceStatement(decorators);
|
||||
case Token.OpKeyword:
|
||||
return parseOperationStatement(decorators);
|
||||
case Token.Semicolon:
|
||||
if (decorators.length > 0) {
|
||||
error("Cannot decorate an empty statement");
|
||||
|
@ -63,7 +65,7 @@ export function parse(code: string | Types.SourceFile) {
|
|||
return decorators;
|
||||
}
|
||||
|
||||
function parseInterfaceStatement(
|
||||
function parseNamespaceStatement(
|
||||
decorators: Array<Types.DecoratorExpressionNode>
|
||||
): Types.NamespaceStatementNode {
|
||||
const pos = tokenPos();
|
||||
|
@ -87,15 +89,11 @@ export function parse(code: string | Types.SourceFile) {
|
|||
}
|
||||
|
||||
parseExpected(Token.OpenBrace);
|
||||
const properties: Array<Types.NamespacePropertyNode> = [];
|
||||
|
||||
do {
|
||||
if (token() == Token.CloseBrace) {
|
||||
break;
|
||||
const statements: Array<Types.Statement> = [];
|
||||
while (token() !== Token.CloseBrace) {
|
||||
statements.push(parseStatement());
|
||||
}
|
||||
const memberDecorators = parseDecoratorList();
|
||||
properties.push(parseNamespaceProperty(memberDecorators));
|
||||
} while (parseOptional(Token.Comma) || parseOptional(Token.Semicolon));
|
||||
|
||||
parseExpected(Token.CloseBrace);
|
||||
|
||||
|
@ -105,15 +103,15 @@ export function parse(code: string | Types.SourceFile) {
|
|||
decorators,
|
||||
id,
|
||||
parameters,
|
||||
properties,
|
||||
statements,
|
||||
},
|
||||
pos
|
||||
);
|
||||
}
|
||||
|
||||
function parseNamespaceProperty(
|
||||
function parseOperationStatement(
|
||||
decorators: Array<Types.DecoratorExpressionNode>
|
||||
): Types.NamespacePropertyNode {
|
||||
): Types.OperationStatementNode {
|
||||
const pos = tokenPos();
|
||||
parseExpected(Token.OpKeyword);
|
||||
const id = parseIdentifier();
|
||||
|
@ -137,9 +135,10 @@ export function parse(code: string | Types.SourceFile) {
|
|||
parseExpected(Token.Colon);
|
||||
const returnType = parseExpression();
|
||||
|
||||
parseExpected(Token.Semicolon);
|
||||
return finishNode(
|
||||
{
|
||||
kind: Types.SyntaxKind.NamespaceProperty,
|
||||
kind: Types.SyntaxKind.OperationStatement,
|
||||
id,
|
||||
parameters,
|
||||
returnType,
|
||||
|
@ -218,7 +217,7 @@ export function parse(code: string | Types.SourceFile) {
|
|||
const param = finishNode(
|
||||
{
|
||||
kind: Types.SyntaxKind.TemplateParameterDeclaration,
|
||||
sv: id.sv,
|
||||
id: id,
|
||||
} as const,
|
||||
pos
|
||||
);
|
||||
|
@ -379,20 +378,18 @@ export function parse(code: string | Types.SourceFile) {
|
|||
|
||||
function parseReferenceExpression(): Types.ReferenceExpression {
|
||||
const pos = tokenPos();
|
||||
const expr = parseIdentifierOrMemberExpression();
|
||||
const target = parseIdentifierOrMemberExpression();
|
||||
|
||||
if (token() !== Token.LessThan) {
|
||||
return expr;
|
||||
}
|
||||
|
||||
parseExpected(Token.LessThan);
|
||||
const args = parseExpressionList();
|
||||
let args: Types.Expression[] = [];
|
||||
if (parseOptional(Token.LessThan)) {
|
||||
args = parseExpressionList();
|
||||
parseExpected(Token.GreaterThan);
|
||||
}
|
||||
|
||||
return finishNode(
|
||||
{
|
||||
kind: Types.SyntaxKind.TemplateApplication,
|
||||
target: expr,
|
||||
kind: Types.SyntaxKind.TypeReference,
|
||||
target,
|
||||
arguments: args,
|
||||
},
|
||||
pos
|
||||
|
@ -738,7 +735,7 @@ export function visitChildren<T>(node: Types.Node, cb: NodeCb<T>): T | undefined
|
|||
return visitNode(cb, node.target) || visitEach(cb, node.arguments);
|
||||
case Types.SyntaxKind.ImportStatement:
|
||||
return visitNode(cb, node.id) || visitEach(cb, node.as);
|
||||
case Types.SyntaxKind.NamespaceProperty:
|
||||
case Types.SyntaxKind.OperationStatement:
|
||||
return (
|
||||
visitEach(cb, node.decorators) ||
|
||||
visitNode(cb, node.id) ||
|
||||
|
@ -747,10 +744,7 @@ export function visitChildren<T>(node: Types.Node, cb: NodeCb<T>): T | undefined
|
|||
);
|
||||
case Types.SyntaxKind.NamespaceStatement:
|
||||
return (
|
||||
visitEach(cb, node.decorators) ||
|
||||
visitNode(cb, node.id) ||
|
||||
visitNode(cb, node.parameters) ||
|
||||
visitEach(cb, node.properties)
|
||||
visitEach(cb, node.decorators) || visitNode(cb, node.id) || visitEach(cb, node.statements)
|
||||
);
|
||||
case Types.SyntaxKind.IntersectionExpression:
|
||||
return visitEach(cb, node.options);
|
||||
|
@ -773,7 +767,7 @@ export function visitChildren<T>(node: Types.Node, cb: NodeCb<T>): T | undefined
|
|||
);
|
||||
case Types.SyntaxKind.NamedImport:
|
||||
return visitNode(cb, node.id);
|
||||
case Types.SyntaxKind.TemplateApplication:
|
||||
case Types.SyntaxKind.TypeReference:
|
||||
return visitNode(cb, node.target) || visitEach(cb, node.arguments);
|
||||
case Types.SyntaxKind.TupleExpression:
|
||||
return visitEach(cb, node.values);
|
||||
|
|
|
@ -11,7 +11,7 @@ import {
|
|||
ADLScriptNode,
|
||||
DecoratorExpressionNode,
|
||||
IdentifierNode,
|
||||
Namespace,
|
||||
NamespaceType,
|
||||
LiteralType,
|
||||
ModelStatementNode,
|
||||
ModelType,
|
||||
|
@ -32,7 +32,7 @@ export interface Program {
|
|||
checker?: ReturnType<typeof createChecker>;
|
||||
evalAdlScript(adlScript: string, filePath?: string): void;
|
||||
onBuild(cb: (program: Program) => void): void;
|
||||
executeInterfaceDecorators(type: Namespace): void;
|
||||
executeNamespaceDecorators(type: NamespaceType): void;
|
||||
executeModelDecorators(type: ModelType): void;
|
||||
executeDecorators(type: Type): void;
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ export interface ADLSourceFile extends SourceFile {
|
|||
ast: ADLScriptNode;
|
||||
symbols: SymbolTable;
|
||||
models: Array<ModelType>;
|
||||
interfaces: Array<Namespace>;
|
||||
interfaces: Array<NamespaceType>;
|
||||
}
|
||||
|
||||
export async function compile(rootDir: string, options?: CompilerOptions) {
|
||||
|
@ -54,7 +54,7 @@ export async function compile(rootDir: string, options?: CompilerOptions) {
|
|||
typeCache: new MultiKeyMap(),
|
||||
literalTypes: new Map(),
|
||||
evalAdlScript,
|
||||
executeInterfaceDecorators,
|
||||
executeNamespaceDecorators,
|
||||
executeModelDecorators,
|
||||
executeDecorators,
|
||||
onBuild(cb) {
|
||||
|
@ -79,14 +79,22 @@ export async function compile(rootDir: string, options?: CompilerOptions) {
|
|||
* does type checking.
|
||||
*/
|
||||
|
||||
function executeInterfaceDecorators(type: Namespace) {
|
||||
function executeNamespaceDecorators(type: NamespaceType) {
|
||||
const stmt = type.node;
|
||||
|
||||
for (const dec of stmt.decorators) {
|
||||
executeDecorator(dec, program, type);
|
||||
}
|
||||
|
||||
for (const [name, propType] of type.properties) {
|
||||
for (const [_, modelType] of type.models) {
|
||||
executeModelDecorators(modelType);
|
||||
}
|
||||
|
||||
for (const [_, namespaceType] of type.namespaces) {
|
||||
executeNamespaceDecorators(namespaceType);
|
||||
}
|
||||
|
||||
for (const [_, propType] of type.operations) {
|
||||
for (const dec of propType.node.decorators) {
|
||||
executeDecorator(dec, program, propType);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { SymbolTable } from "./binder";
|
||||
import { MultiKeyMap } from "./checker";
|
||||
|
||||
/**
|
||||
* Type System types
|
||||
|
@ -13,8 +14,8 @@ export type Type =
|
|||
| ModelType
|
||||
| ModelTypeProperty
|
||||
| TemplateParameterType
|
||||
| Namespace
|
||||
| NamespaceProperty
|
||||
| NamespaceType
|
||||
| OperationType
|
||||
| StringLiteralType
|
||||
| NumericLiteralType
|
||||
| BooleanLiteralType
|
||||
|
@ -25,6 +26,7 @@ export type Type =
|
|||
export interface ModelType extends BaseType {
|
||||
kind: "Model";
|
||||
name: string;
|
||||
namespace?: NamespaceType;
|
||||
properties: Map<string, ModelTypeProperty>;
|
||||
baseModels: Array<ModelType>;
|
||||
templateArguments?: Array<Type>;
|
||||
|
@ -43,20 +45,23 @@ export interface ModelTypeProperty {
|
|||
optional: boolean;
|
||||
}
|
||||
|
||||
export interface NamespaceProperty {
|
||||
kind: "NamespaceProperty";
|
||||
node: NamespacePropertyNode;
|
||||
export interface OperationType {
|
||||
kind: "Operation";
|
||||
node: OperationStatementNode;
|
||||
name: string;
|
||||
namespace?: NamespaceType;
|
||||
parameters?: ModelType;
|
||||
returnType: Type;
|
||||
}
|
||||
|
||||
export interface Namespace extends BaseType {
|
||||
export interface NamespaceType extends BaseType {
|
||||
kind: "Namespace";
|
||||
name: string;
|
||||
namespace?: NamespaceType;
|
||||
node: NamespaceStatementNode;
|
||||
properties: Map<string, NamespaceProperty>;
|
||||
parameters?: ModelType;
|
||||
models: Map<string, ModelType>;
|
||||
operations: Map<string, OperationType>;
|
||||
namespaces: Map<string, NamespaceType>;
|
||||
}
|
||||
|
||||
export type LiteralType = StringLiteralType | NumericLiteralType | BooleanLiteralType;
|
||||
|
@ -114,6 +119,16 @@ export interface TypeSymbol {
|
|||
kind: "type";
|
||||
node: Node;
|
||||
name: string;
|
||||
id?: number;
|
||||
}
|
||||
|
||||
export interface SymbolLinks {
|
||||
type?: Type;
|
||||
|
||||
// for types which can be instantiated, we split `type` into declaredType and
|
||||
// a map of instantiations.
|
||||
declaredType?: Type;
|
||||
instantiations?: MultiKeyMap<Type>;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -127,7 +142,7 @@ export enum SyntaxKind {
|
|||
DecoratorExpression,
|
||||
MemberExpression,
|
||||
NamespaceStatement,
|
||||
NamespaceProperty,
|
||||
OperationStatement,
|
||||
ModelStatement,
|
||||
ModelExpression,
|
||||
ModelProperty,
|
||||
|
@ -139,7 +154,7 @@ export enum SyntaxKind {
|
|||
StringLiteral,
|
||||
NumericLiteral,
|
||||
BooleanLiteral,
|
||||
TemplateApplication,
|
||||
TypeReference,
|
||||
TemplateParameterDeclaration,
|
||||
}
|
||||
|
||||
|
@ -152,7 +167,7 @@ export type Node =
|
|||
| ADLScriptNode
|
||||
| TemplateParameterDeclarationNode
|
||||
| ModelPropertyNode
|
||||
| NamespacePropertyNode
|
||||
| OperationStatementNode
|
||||
| NamedImportNode
|
||||
| ModelPropertyNode
|
||||
| ModelSpreadPropertyNode
|
||||
|
@ -166,7 +181,24 @@ export interface ADLScriptNode extends BaseNode {
|
|||
file: SourceFile;
|
||||
}
|
||||
|
||||
export type Statement = ImportStatementNode | ModelStatementNode | NamespaceStatementNode;
|
||||
export type Statement =
|
||||
| ImportStatementNode
|
||||
| ModelStatementNode
|
||||
| NamespaceStatementNode
|
||||
| OperationStatementNode;
|
||||
|
||||
export interface DeclarationNode {
|
||||
symbol?: TypeSymbol; // tracks the symbol assigned to this declaration
|
||||
namespaceSymbol?: TypeSymbol; // tracks the namespace this declaration is in
|
||||
}
|
||||
|
||||
export type Declaration =
|
||||
| ModelStatementNode
|
||||
| NamespaceStatementNode
|
||||
| OperationStatementNode
|
||||
| TemplateParameterDeclarationNode;
|
||||
|
||||
export type ScopeNode = NamespaceStatementNode | ModelStatementNode;
|
||||
|
||||
export interface ImportStatementNode extends BaseNode {
|
||||
kind: SyntaxKind.ImportStatement;
|
||||
|
@ -197,13 +229,13 @@ export type Expression =
|
|||
| TupleExpressionNode
|
||||
| UnionExpressionNode
|
||||
| IntersectionExpressionNode
|
||||
| TemplateApplicationNode
|
||||
| TypeReferenceNode
|
||||
| IdentifierNode
|
||||
| StringLiteralNode
|
||||
| NumericLiteralNode
|
||||
| BooleanLiteralNode;
|
||||
|
||||
export type ReferenceExpression = TemplateApplicationNode | MemberExpressionNode | IdentifierNode;
|
||||
export type ReferenceExpression = TypeReferenceNode | MemberExpressionNode | IdentifierNode;
|
||||
|
||||
export interface MemberExpressionNode extends BaseNode {
|
||||
kind: SyntaxKind.MemberExpression;
|
||||
|
@ -211,23 +243,23 @@ export interface MemberExpressionNode extends BaseNode {
|
|||
base: MemberExpressionNode | IdentifierNode;
|
||||
}
|
||||
|
||||
export interface NamespaceStatementNode extends BaseNode {
|
||||
export interface NamespaceStatementNode extends BaseNode, DeclarationNode {
|
||||
kind: SyntaxKind.NamespaceStatement;
|
||||
id: IdentifierNode;
|
||||
parameters?: ModelExpressionNode;
|
||||
properties: Array<NamespacePropertyNode>;
|
||||
statements: Array<Statement>;
|
||||
locals?: SymbolTable;
|
||||
decorators: Array<DecoratorExpressionNode>;
|
||||
}
|
||||
|
||||
export interface NamespacePropertyNode extends BaseNode {
|
||||
kind: SyntaxKind.NamespaceProperty;
|
||||
export interface OperationStatementNode extends BaseNode, DeclarationNode {
|
||||
kind: SyntaxKind.OperationStatement;
|
||||
id: IdentifierNode;
|
||||
parameters: ModelExpressionNode;
|
||||
returnType: Expression;
|
||||
decorators: Array<DecoratorExpressionNode>;
|
||||
}
|
||||
|
||||
export interface ModelStatementNode extends BaseNode {
|
||||
export interface ModelStatementNode extends BaseNode, DeclarationNode {
|
||||
kind: SyntaxKind.ModelStatement;
|
||||
id: IdentifierNode;
|
||||
properties?: Array<ModelPropertyNode | ModelSpreadPropertyNode>;
|
||||
|
@ -293,15 +325,16 @@ export interface IntersectionExpressionNode extends BaseNode {
|
|||
options: Array<Expression>;
|
||||
}
|
||||
|
||||
export interface TemplateApplicationNode extends BaseNode {
|
||||
kind: SyntaxKind.TemplateApplication;
|
||||
target: Expression;
|
||||
export interface TypeReferenceNode extends BaseNode {
|
||||
kind: SyntaxKind.TypeReference;
|
||||
target: ReferenceExpression;
|
||||
arguments: Array<Expression>;
|
||||
}
|
||||
|
||||
export interface TemplateParameterDeclarationNode extends BaseNode {
|
||||
kind: SyntaxKind.TemplateParameterDeclaration;
|
||||
sv: string;
|
||||
id: IdentifierNode;
|
||||
symbol?: TypeSymbol;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -174,6 +174,7 @@ Statement :
|
|||
ImportStatement
|
||||
ModelStatement
|
||||
NamespaceStatement
|
||||
OperationStatement
|
||||
`;`
|
||||
|
||||
ImportStatement :
|
||||
|
@ -220,19 +221,10 @@ ModelSpreadProperty :
|
|||
`...` ReferenceExpression
|
||||
|
||||
NamespaceStatement:
|
||||
DecoratorList? `namespace` Identifier `{` NamespaceBody? `}`
|
||||
DecoratorList? `namespace` IdentifierOrMemberExpression `{` StatementList? `}`
|
||||
|
||||
NamespaceBody :
|
||||
NamespacePropertyList `,`?
|
||||
NamespacePropertyList `;`?
|
||||
|
||||
NamespacePropertyList :
|
||||
NamespaceProperty
|
||||
NamespacePropertyList `,` NamespaceProperty
|
||||
NamespacePropertyList `;` NamespaceProperty
|
||||
|
||||
NamespaceProperty :
|
||||
DecoratorList? `op` Identifier `(` ModelPropertyList? `)` `:` Expression
|
||||
OperationStatement :
|
||||
DecoratorList? `op` Identifier `(` ModelPropertyList? `)` `:` Expression `;`
|
||||
|
||||
Expression :
|
||||
UnionExpressionOrHigher
|
||||
|
|
|
@ -160,6 +160,7 @@
|
|||
   <a name="Statement-zi_5hwi0"></a>*[ImportStatement](#ImportStatement)*
|
||||
   <a name="Statement-ngbc4m7o"></a>*[ModelStatement](#ModelStatement)*
|
||||
   <a name="Statement-_ljtj5og"></a>*[NamespaceStatement](#NamespaceStatement)*
|
||||
   <a name="Statement-qlyu8ssa"></a>*[OperationStatement](#OperationStatement)*
|
||||
   <a name="Statement-sg2sawim"></a>`` ; ``
|
||||
|
||||
  <a name="ImportStatement"></a>*ImportStatement* **:**
|
||||
|
@ -206,19 +207,10 @@
|
|||
   <a name="ModelSpreadProperty-r9kkoml-"></a>`` ... `` *[ReferenceExpression](#ReferenceExpression)*
|
||||
|
||||
  <a name="NamespaceStatement"></a>*NamespaceStatement* **:**
|
||||
   <a name="NamespaceStatement-lllfloam"></a>*[DecoratorList](#DecoratorList)*<sub>opt</sub> `` namespace `` *[Identifier](#Identifier)* `` { `` *[NamespaceBody](#NamespaceBody)*<sub>opt</sub> `` } ``
|
||||
   <a name="NamespaceStatement-lrsdvje0"></a>*[DecoratorList](#DecoratorList)*<sub>opt</sub> `` namespace `` *[IdentifierOrMemberExpression](#IdentifierOrMemberExpression)* `` { `` *[StatementList](#StatementList)*<sub>opt</sub> `` } ``
|
||||
|
||||
  <a name="NamespaceBody"></a>*NamespaceBody* **:**
|
||||
   <a name="NamespaceBody-ihomezph"></a>*[NamespacePropertyList](#NamespacePropertyList)* `` , ``<sub>opt</sub>
|
||||
   <a name="NamespaceBody-srhr2gdj"></a>*[NamespacePropertyList](#NamespacePropertyList)* `` ; ``<sub>opt</sub>
|
||||
|
||||
  <a name="NamespacePropertyList"></a>*NamespacePropertyList* **:**
|
||||
   <a name="NamespacePropertyList-feggpj5t"></a>*[NamespaceProperty](#NamespaceProperty)*
|
||||
   <a name="NamespacePropertyList-8g_wmadx"></a>*[NamespacePropertyList](#NamespacePropertyList)* `` , `` *[NamespaceProperty](#NamespaceProperty)*
|
||||
   <a name="NamespacePropertyList-isnpt3gn"></a>*[NamespacePropertyList](#NamespacePropertyList)* `` ; `` *[NamespaceProperty](#NamespaceProperty)*
|
||||
|
||||
  <a name="NamespaceProperty"></a>*NamespaceProperty* **:**
|
||||
   <a name="NamespaceProperty-vbdf9viv"></a>*[DecoratorList](#DecoratorList)*<sub>opt</sub> `` op `` *[Identifier](#Identifier)* `` ( `` *[ModelPropertyList](#ModelPropertyList)*<sub>opt</sub> `` ) `` `` : `` *[Expression](#Expression)*
|
||||
  <a name="OperationStatement"></a>*OperationStatement* **:**
|
||||
   <a name="OperationStatement-fic4qgph"></a>*[DecoratorList](#DecoratorList)*<sub>opt</sub> `` op `` *[Identifier](#Identifier)* `` ( `` *[ModelPropertyList](#ModelPropertyList)*<sub>opt</sub> `` ) `` `` : `` *[Expression](#Expression)* `` ; ``
|
||||
|
||||
  <a name="Expression"></a>*Expression* **:**
|
||||
   <a name="Expression-otzlm2nv"></a>*[UnionExpressionOrHigher](#UnionExpressionOrHigher)*
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { Program } from "../compiler/program";
|
||||
import { ModelTypeProperty, Namespace, Type } from "../compiler/types";
|
||||
import { ModelTypeProperty, NamespaceType, Type } from "../compiler/types";
|
||||
|
||||
const docs = new Map<Type, string>();
|
||||
|
||||
|
@ -178,7 +178,7 @@ function mapFilterOut(
|
|||
const listProperties = new Set<Type>();
|
||||
|
||||
export function list(program: Program, target: Type) {
|
||||
if (target.kind === "NamespaceProperty" || target.kind === "ModelProperty") {
|
||||
if (target.kind === "Operation" || target.kind === "ModelProperty") {
|
||||
listProperties.add(target);
|
||||
} else {
|
||||
throw new Error("The @list decorator can only be applied to interface or model properties.");
|
||||
|
@ -195,7 +195,7 @@ const tagProperties = new Map<Type, string[]>();
|
|||
// Set a tag on an operation or namespace. There can be multiple tags on either an
|
||||
// operation or namespace.
|
||||
export function tag(program: Program, target: Type, tag: string) {
|
||||
if (target.kind === "NamespaceProperty" || target.kind === "Namespace") {
|
||||
if (target.kind === "Operation" || target.kind === "Namespace") {
|
||||
const tags = tagProperties.get(target);
|
||||
if (tags) {
|
||||
tags.push(tag);
|
||||
|
@ -215,7 +215,7 @@ export function getTags(target: Type): string[] {
|
|||
// Merge the tags for a operation with the tags that are on the namespace it resides within.
|
||||
//
|
||||
// TODO: (JC) We'll need to update this for nested namespaces
|
||||
export function getAllTags(namespace: Namespace, target: Type): string[] | undefined {
|
||||
export function getAllTags(namespace: NamespaceType, target: Type): string[] | undefined {
|
||||
const tags = new Set<string>();
|
||||
|
||||
for (const t of getTags(namespace)) {
|
||||
|
|
|
@ -75,7 +75,7 @@ interface OperationRoute {
|
|||
const operationRoutes = new Map<Type, OperationRoute>();
|
||||
|
||||
function setOperationRoute(entity: Type, verb: OperationRoute) {
|
||||
if (entity.kind === "NamespaceProperty") {
|
||||
if (entity.kind === "Operation") {
|
||||
if (!operationRoutes.has(entity)) {
|
||||
operationRoutes.set(entity, verb);
|
||||
} else {
|
||||
|
|
|
@ -110,7 +110,7 @@ describe("syntax", () => {
|
|||
});
|
||||
|
||||
describe("tuple model expressions", () => {
|
||||
parseEach(['namespace A { op b(param: [number, string]): [1, "hi"] }']);
|
||||
parseEach(['namespace A { op b(param: [number, string]): [1, "hi"]; }']);
|
||||
});
|
||||
|
||||
describe("array expressions", () => {
|
||||
|
@ -136,13 +136,12 @@ describe("syntax", () => {
|
|||
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): {} }",
|
||||
"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 }",
|
||||
"namespace Store { namespace Read { op read(): int32; } namespace Write { op write(v: int32): {}; } }",
|
||||
]);
|
||||
});
|
||||
|
||||
|
@ -203,7 +202,7 @@ function dumpAST(astNode: any) {
|
|||
const replacer = function (this: any, key: string, value: any) {
|
||||
return key == "kind" ? SyntaxKind[value] : value;
|
||||
};
|
||||
console.log(JSON.stringify(astNode, replacer, 4));
|
||||
//console.log(JSON.stringify(astNode, replacer, 4));
|
||||
}
|
||||
|
||||
function shorten(code: string) {
|
||||
|
|
Загрузка…
Ссылка в новой задаче