Implement rudimentary decorator support

This change introduces decorator support for CocoJS and the corresponding
IL/AST changes to store them on definition nodes.  Nothing consumes these
at the moment, however, I am looking at leveraging this to indicate that
certain program fragments are "code" and should be serialized specially
(in support of Functions-as-lambdas).
This commit is contained in:
joeduffy 2017-04-18 16:53:26 -07:00
parent 4989e70425
commit 847d74c9f6
5 changed files with 63 additions and 8 deletions

View file

@ -10,10 +10,19 @@ import * as statements from "./statements";
// A definition is something that possibly exported for external usage.
export interface Definition extends Node {
name: Identifier; // a required name, unique amongst definitions with a common parent.
description?: string; // an optional informative description.
name: Identifier; // a required name, unique amongst definitions with a common parent.
description?: string; // an optional informative description.
attributes?: Attribute[]; // an optional list of metadata attributes.
}
// An attribute is a simple decorator token that acts as a metadata annotation.
export interface Attribute extends Node {
kind: AttributeKind;
decorator: Token;
}
export const attributeKind = "Attribute";
export type AttributeKind = "Attribute";
/* Modules */
// A module contains members, including variables, functions, and/or classes.

View file

@ -24,6 +24,7 @@ export type NodeKind =
ModuleTokenKind |
TypeTokenKind |
definitions.AttributeKind |
definitions.ModuleKind |
definitions.ClassKind |
definitions.ExportKind |

View file

@ -1912,6 +1912,12 @@ export class Transformer {
return notYetImplemented(node);
}
private getDecoratorSymbol(decorator: ts.Decorator): ts.Symbol {
contract.assert(decorator.expression.kind === ts.SyntaxKind.Identifier,
"Only simple @decorator annotations are currently supported");
return this.checker().getSymbolAtLocation(decorator.expression);
}
private async transformParameterDeclaration(
node: ts.ParameterDeclaration): Promise<VariableDeclaration<ast.LocalVariable>> {
// Validate that we're dealing with the supported subset.
@ -1919,6 +1925,22 @@ export class Transformer {
this.diagnostics.push(this.dctx.newRestParamsNotSupportedError(node.dotDotDotToken));
}
// Pluck out any decorators and store them in the metadata as attributes.
let attributes: ast.Attribute[] | undefined;
if (node.decorators) {
attributes = [];
for (let decorator of node.decorators) {
let sym: ts.Symbol = this.getDecoratorSymbol(decorator);
attributes.push({
kind: ast.attributeKind,
decorator: {
kind: ast.tokenKind,
tok: await this.resolveTokenFromSymbol(sym),
},
});
}
}
// TODO[pulumi/coconut#43]: parameters can be any binding name, including destructuring patterns. For now,
// however, we only support the identifier forms.
let name: ast.Identifier = this.transformBindingIdentifier(node.name);
@ -1930,9 +1952,10 @@ export class Transformer {
node: node,
tok: name.ident,
variable: {
kind: ast.localVariableKind,
name: name,
type: await this.resolveTypeTokenFromTypeLike(node),
kind: ast.localVariableKind,
name: name,
type: await this.resolveTypeTokenFromTypeLike(node),
attributes: attributes,
},
initializer: initializer,
};

View file

@ -85,12 +85,23 @@ class TypeToken(Node):
class Definition(Node):
"""A definition is something that is possibly exported for external usage."""
def __init__(self, kind, name, description=None, loc=None):
def __init__(self, kind, name, description=None, attributes=None, loc=None):
assert isinstance(name, Identifier)
assert description is None or isinstance(description, basestring)
assert (attributes is None or
(isinstance(attributes, list) and
all(isinstance(attribute, Attribute) for attribute in attributes)))
super(Definition, self).__init__(kind, loc)
self.name = name
self.description = description
self.attributes = attributes
class Attribute(Node):
"""An attribute is a simple decorator token that acts as a metadata annotation."""
def __init__(self, kind, decorator, loc=None):
assert isinstance(decorator, Token)
super(Attribute, self).__init__(kind, loc)
self.decorator = decorator
# ...Modules

View file

@ -18,14 +18,25 @@ type Definition interface {
type DefinitionNode struct {
NodeValue
Name *Identifier `json:"name"`
Description *string `json:"description,omitempty"`
Name *Identifier `json:"name"` // a required name, unique amongst siblings.
Description *string `json:"description,omitempty"` // an optional informative description.
Attributes *[]Attribute `json:"attributes,omitempty"` // an optional list of metadata decorators.
}
func (node *DefinitionNode) definition() {}
func (node *DefinitionNode) GetName() *Identifier { return node.Name }
func (node *DefinitionNode) GetDescription() *string { return node.Description }
// Attribute is a simple decorator token that acts as a metadata annotation.
type Attribute struct {
NodeValue
Decorator *Token `json:"decorator"`
}
var _ Node = (*Attribute)(nil)
const AttributeKind NodeKind = "Attribute"
/* Modules */
// Module contains members, including variables, functions, and/or classes.