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:
parent
4989e70425
commit
847d74c9f6
5 changed files with 63 additions and 8 deletions
|
@ -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.
|
||||
|
|
|
@ -24,6 +24,7 @@ export type NodeKind =
|
|||
ModuleTokenKind |
|
||||
TypeTokenKind |
|
||||
|
||||
definitions.AttributeKind |
|
||||
definitions.ModuleKind |
|
||||
definitions.ClassKind |
|
||||
definitions.ExportKind |
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in a new issue