diff --git a/packages/adl/compiler/binder.ts b/packages/adl/compiler/binder.ts index e8a7d5862..fb14ce4d6 100644 --- a/packages/adl/compiler/binder.ts +++ b/packages/adl/compiler/binder.ts @@ -10,6 +10,8 @@ import { SourceLocation, Sym, Declaration, + OperationStatementNode, + ScopeNode, } from "./types.js"; export class SymbolTable extends Map { @@ -50,8 +52,7 @@ export function createBinder(): Binder { let parentNode: Node; // Node where locals go. - let scope: Node; - + let scope: ScopeNode; return { bindSourceFile, }; @@ -63,6 +64,7 @@ export function createBinder(): Binder { ) { currentFile = sourceFile; bindNode(sourceFile.ast); + reportDuplicateSymbols(currentFile.symbols); // everything is global if (globalScope) { @@ -80,13 +82,16 @@ export function createBinder(): Binder { switch (node.kind) { case SyntaxKind.ModelStatement: - bindModelStatement(node); + bindModelStatement(node); break; case SyntaxKind.NamespaceStatement: - bindNamespaceStatement(node); + bindNamespaceStatement(node); + break; + case SyntaxKind.OperationStatement: + bindOperationStatement(node); break; case SyntaxKind.TemplateParameterDeclaration: - bindTemplateParameterDeclaration(node); + bindTemplateParameterDeclaration(node); } const prevParent = parentNode; @@ -106,25 +111,33 @@ export function createBinder(): Binder { parentNode = prevParent; } + function getContainingSymbolTable() { + return scope ? scope.locals! : currentFile.symbols; + } + function bindTemplateParameterDeclaration(node: TemplateParameterDeclarationNode) { - (scope).locals!.set(node.sv, { - kind: "type", - node: node, - name: node.sv, - }); + declareSymbol(getContainingSymbolTable(), node); } function bindModelStatement(node: ModelStatementNode) { - declareSymbol(currentFile.symbols, node); - // initialize locals for type parameters. + declareSymbol(getContainingSymbolTable(), node); + + // Initialize locals for type parameters node.locals = new SymbolTable(); } function bindNamespaceStatement(statement: NamespaceStatementNode) { - declareSymbol(currentFile.symbols, statement); + declareSymbol(getContainingSymbolTable(), statement); + + // Initialize locals for namespace members + statement.locals = new SymbolTable(); } - function reportDuplicateSymbols(globalSymbols: SymbolTable) { + function bindOperationStatement(statement: OperationStatementNode) { + declareSymbol(getContainingSymbolTable(), statement); + } + + function reportDuplicateSymbols(symbols: SymbolTable) { let reported = new Set(); let messages = new Array(); @@ -132,10 +145,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 @@ -145,6 +165,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")); } @@ -158,10 +179,12 @@ 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; } diff --git a/packages/adl/compiler/checker.ts b/packages/adl/compiler/checker.ts index ef6bd9f15..7108c66f9 100644 --- a/packages/adl/compiler/checker.ts +++ b/packages/adl/compiler/checker.ts @@ -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, @@ -34,6 +35,8 @@ import { DecoratorSymbol, TypeSymbol, SymbolLinks, + MemberExpressionNode, + Sym, } from "./types.js"; /** @@ -85,7 +88,7 @@ export function createChecker(program: Program) { checkProgram, getLiteralType, getTypeName, - checkNamespaceProperty, + checkOperation, }; function getTypeForNode(node: Node): Type { @@ -98,8 +101,8 @@ export function createChecker(program: Program) { return checkModelProperty(node); case SyntaxKind.NamespaceStatement: return checkNamespace(node); - case SyntaxKind.NamespaceProperty: - return checkNamespaceProperty(node); + case SyntaxKind.OperationStatement: + return checkOperation(node); case SyntaxKind.NumericLiteral: return checkNumericLiteral(node); case SyntaxKind.BooleanLiteral: @@ -140,9 +143,16 @@ export function createChecker(program: Program) { return "(unnamed type)"; } + function getNamespaceString(type: ModelType | NamespaceType | OperationType): string | undefined { + return type.namespace + ? `${getNamespaceString(type.namespace) || ""}${type.namespace.name}.` + : undefined; + } + function getModelName(model: ModelType) { + let modelName: string | undefined = model.name; if ((model.node).assignment) { - return model.name; + // Just use the existing modelName } else if (model.templateArguments && model.templateArguments.length > 0) { // template instantiation const args = model.templateArguments.map(getTypeName); @@ -155,6 +165,8 @@ export function createChecker(program: Program) { // regular old model. return model.name || "(anonymous model)"; } + + return (getNamespaceString(model) || "") + modelName; } function checkTemplateParameterDeclaration(node: TemplateParameterDeclarationNode): Type { @@ -327,16 +339,28 @@ 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 ? 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)); + 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; + } } const links = getSymbolLinks(node.symbol!); @@ -345,13 +369,29 @@ export function createChecker(program: Program) { return type; } - function checkNamespaceProperty(prop: NamespacePropertyNode): NamespaceProperty { + function getParentNamespaceType( + node: ModelStatementNode | NamespaceStatementNode | OperationStatementNode + ) { + switch (node.kind) { + case SyntaxKind.ModelStatement: + case SyntaxKind.NamespaceStatement: + case SyntaxKind.OperationStatement: + return node.parent && node.parent.kind === SyntaxKind.NamespaceStatement + ? (getTypeForNode(node.parent) as NamespaceType) + : undefined; + default: + return undefined; + } + } + + function checkOperation(node: OperationStatementNode): OperationType { return createType({ - kind: "NamespaceProperty", - name: prop.id.sv, - node: prop, - parameters: getTypeForNode(prop.parameters), - returnType: getTypeForNode(prop.returnType), + kind: "Operation", + name: node.id.sv, + namespace: getParentNamespaceType(node), + node: node, + parameters: getTypeForNode(node.parameters), + returnType: getTypeForNode(node.returnType), }); } @@ -384,13 +424,75 @@ export function createChecker(program: Program) { return s.id; } - function resolveIdentifier(node: IdentifierNode): DecoratorSymbol | TypeSymbol { + function getMemberExpressionPath(node: MemberExpressionNode): string { + // Recursively build the rest of the path, back to front + const pathBefore = + node.base.kind === SyntaxKind.MemberExpression + ? getMemberExpressionPath(node.base) + : node.base.sv; + + return pathBefore + "." + node.id.sv; + } + + function checkMemberExpression(node: MemberExpressionNode) { + const binding = resolveMember(node); + if (binding) { + if (binding.kind === "decorator") { + return {}; + } else { + return getTypeForNode(binding.node); + } + } else { + throwDiagnostic(`Cannot resolve identifier '${getMemberExpressionPath(node)}'`, node); + } + } + + function resolveMember(node: MemberExpressionNode): any { + let result: Sym | undefined = undefined; + + // Navigate down the member expression and then resolve on the way + // back up because the 'base' pointers are stored in reverse + if (node.base.kind === SyntaxKind.MemberExpression) { + result = resolveMember(node.base); + } else { + // The last 'base' in the chain will be an identifier, so + // resolve it first and then resolve all remaining member + // expressions with respect to its scope + result = resolveIdentifier(node.base); + } + + if (result) { + // Previous segment was resolved, was it a namespace? + if (result.kind === "type") { + if (result.node.kind === SyntaxKind.NamespaceStatement) { + return resolveIdentifierInScope(node.id, result.node); + } else { + throwDiagnostic( + `Cannot resolve '${node.id.sv}' in non-namespace node ${result.node.kind}`, + node + ); + } + } else { + throwDiagnostic(`Unexpectedly resolved '${node.id.sv}' to a decorator symbol`, node); + } + } else { + // Let checkMemberExpression report on the inability to + // resolve the member expression + return undefined; + } + } + + function resolveIdentifierInScope(node: IdentifierNode, scope: { locals?: SymbolTable }) { + return (scope).locals.get(node.sv); + } + + function resolveIdentifier(node: IdentifierNode) { let scope: Node | undefined = node.parent; let binding; while (scope) { if ("locals" in scope) { - binding = (scope).locals.get(node.sv); + binding = resolveIdentifierInScope(node, scope); if (binding) break; } scope = scope.parent; diff --git a/packages/adl/compiler/parser.ts b/packages/adl/compiler/parser.ts index 6be79f9b9..3b8da5f3d 100644 --- a/packages/adl/compiler/parser.ts +++ b/packages/adl/compiler/parser.ts @@ -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.NamespaceStatementNode { const pos = tokenPos(); @@ -87,15 +89,11 @@ export function parse(code: string | Types.SourceFile) { } parseExpected(Token.OpenBrace); - const properties: Array = []; - do { - if (token() == Token.CloseBrace) { - break; - } - const memberDecorators = parseDecoratorList(); - properties.push(parseNamespaceProperty(memberDecorators)); - } while (parseOptional(Token.Comma) || parseOptional(Token.Semicolon)); + const statements: Array = []; + while (token() !== Token.CloseBrace) { + statements.push(parseStatement()); + } 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.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 ); @@ -734,7 +733,7 @@ export function visitChildren(node: Types.Node, cb: NodeCb): 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) || @@ -743,10 +742,7 @@ export function visitChildren(node: Types.Node, cb: NodeCb): 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); diff --git a/packages/adl/compiler/program.ts b/packages/adl/compiler/program.ts index a095389a2..4676b302c 100644 --- a/packages/adl/compiler/program.ts +++ b/packages/adl/compiler/program.ts @@ -11,7 +11,7 @@ import { ADLScriptNode, DecoratorExpressionNode, IdentifierNode, - Namespace, + NamespaceType, LiteralType, ModelStatementNode, ModelType, @@ -32,7 +32,7 @@ export interface Program { checker?: ReturnType; 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; - interfaces: Array; + interfaces: Array; } 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); } diff --git a/packages/adl/compiler/types.ts b/packages/adl/compiler/types.ts index 305f1ab66..5667ef661 100644 --- a/packages/adl/compiler/types.ts +++ b/packages/adl/compiler/types.ts @@ -14,8 +14,8 @@ export type Type = | ModelType | ModelTypeProperty | TemplateParameterType - | Namespace - | NamespaceProperty + | NamespaceType + | OperationType | StringLiteralType | NumericLiteralType | BooleanLiteralType @@ -26,6 +26,7 @@ export type Type = export interface ModelType extends BaseType { kind: "Model"; name: string; + namespace?: NamespaceType; properties: Map; baseModels: Array; templateArguments?: Array; @@ -44,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; - parameters?: ModelType; + models: Map; + operations: Map; + namespaces: Map; } export type LiteralType = StringLiteralType | NumericLiteralType | BooleanLiteralType; @@ -139,7 +143,7 @@ export enum SyntaxKind { DecoratorExpression, MemberExpression, NamespaceStatement, - NamespaceProperty, + OperationStatement, ModelStatement, ModelExpression, ModelProperty, @@ -164,7 +168,7 @@ export type Node = | ADLScriptNode | TemplateParameterDeclarationNode | ModelPropertyNode - | NamespacePropertyNode + | OperationStatementNode | NamedImportNode | ModelPropertyNode | ModelSpreadPropertyNode @@ -179,7 +183,11 @@ 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 @@ -187,7 +195,13 @@ export interface DeclarationNode { export type Declaration = | ModelStatementNode - | NamespaceStatementNode; + | NamespaceStatementNode + | OperationStatementNode + | TemplateParameterDeclarationNode; + +export type ScopeNode = + | NamespaceStatementNode + | ModelStatementNode export interface ImportStatementNode extends BaseNode { kind: SyntaxKind.ImportStatement; @@ -238,17 +252,19 @@ export interface MemberExpressionNode extends BaseNode { export interface NamespaceStatementNode extends BaseNode, DeclarationNode { kind: SyntaxKind.NamespaceStatement; id: IdentifierNode; - parameters?: ModelExpressionNode; - properties: Array; + statements: Array; + locals?: SymbolTable; decorators: Array; } -export interface NamespacePropertyNode extends BaseNode { - kind: SyntaxKind.NamespaceProperty; +export interface OperationStatementNode extends BaseNode { + kind: SyntaxKind.OperationStatement; id: IdentifierNode; parameters: ModelExpressionNode; returnType: Expression; decorators: Array; + + symbol: TypeSymbol; } @@ -326,7 +342,8 @@ export interface TypeReferenceNode extends BaseNode { export interface TemplateParameterDeclarationNode extends BaseNode { kind: SyntaxKind.TemplateParameterDeclaration; - sv: string; + id: IdentifierNode; + symbol: TypeSymbol; } /** diff --git a/packages/adl/language.grammar b/packages/adl/language.grammar index 83f462b4f..32d8f8662 100644 --- a/packages/adl/language.grammar +++ b/packages/adl/language.grammar @@ -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 diff --git a/packages/adl/language.md b/packages/adl/language.md index 8ff774fa9..b730a9a8b 100644 --- a/packages/adl/language.md +++ b/packages/adl/language.md @@ -160,6 +160,7 @@    *[ImportStatement](#ImportStatement)*    *[ModelStatement](#ModelStatement)*    *[NamespaceStatement](#NamespaceStatement)* +   *[OperationStatement](#OperationStatement)*    `` ; ``   *ImportStatement* **:** @@ -206,19 +207,10 @@    `` ... `` *[ReferenceExpression](#ReferenceExpression)*   *NamespaceStatement* **:** -   *[DecoratorList](#DecoratorList)*opt `` namespace `` *[Identifier](#Identifier)* `` { `` *[NamespaceBody](#NamespaceBody)*opt `` } `` +   *[DecoratorList](#DecoratorList)*opt `` namespace `` *[IdentifierOrMemberExpression](#IdentifierOrMemberExpression)* `` { `` *[StatementList](#StatementList)*opt `` } `` -  *NamespaceBody* **:** -   *[NamespacePropertyList](#NamespacePropertyList)* `` , ``opt -   *[NamespacePropertyList](#NamespacePropertyList)* `` ; ``opt - -  *NamespacePropertyList* **:** -   *[NamespaceProperty](#NamespaceProperty)* -   *[NamespacePropertyList](#NamespacePropertyList)* `` , `` *[NamespaceProperty](#NamespaceProperty)* -   *[NamespacePropertyList](#NamespacePropertyList)* `` ; `` *[NamespaceProperty](#NamespaceProperty)* - -  *NamespaceProperty* **:** -   *[DecoratorList](#DecoratorList)*opt `` op `` *[Identifier](#Identifier)* `` ( `` *[ModelPropertyList](#ModelPropertyList)*opt `` ) `` `` : `` *[Expression](#Expression)* +  *OperationStatement* **:** +   *[DecoratorList](#DecoratorList)*opt `` op `` *[Identifier](#Identifier)* `` ( `` *[ModelPropertyList](#ModelPropertyList)*opt `` ) `` `` : `` *[Expression](#Expression)* `` ; ``   *Expression* **:**    *[UnionExpressionOrHigher](#UnionExpressionOrHigher)* diff --git a/packages/adl/lib/decorators.ts b/packages/adl/lib/decorators.ts index 1aa6cf92c..eb8f56a4a 100644 --- a/packages/adl/lib/decorators.ts +++ b/packages/adl/lib/decorators.ts @@ -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(); @@ -178,7 +178,7 @@ function mapFilterOut( const listProperties = new Set(); 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(); // 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(); for (const t of getTags(namespace)) { diff --git a/packages/adl/lib/rest.ts b/packages/adl/lib/rest.ts index 230a1c11a..defc3c3a4 100644 --- a/packages/adl/lib/rest.ts +++ b/packages/adl/lib/rest.ts @@ -75,7 +75,7 @@ interface OperationRoute { const operationRoutes = new Map(); function setOperationRoute(entity: Type, verb: OperationRoute) { - if (entity.kind === "NamespaceProperty") { + if (entity.kind === "Operation") { if (!operationRoutes.has(entity)) { operationRoutes.set(entity, verb); } else { diff --git a/packages/adl/test/test-parser.ts b/packages/adl/test/test-parser.ts index 0c6bc37b7..7dcd68422 100644 --- a/packages/adl/test/test-parser.ts +++ b/packages/adl/test/test-parser.ts @@ -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): {}; } }", ]); });