781 lines
31 KiB
Plaintext
781 lines
31 KiB
Plaintext
"use strict";
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.Def = void 0;
|
|
var tslib_1 = require("tslib");
|
|
var Op = Object.prototype;
|
|
var objToStr = Op.toString;
|
|
var hasOwn = Op.hasOwnProperty;
|
|
var BaseType = /** @class */ (function () {
|
|
function BaseType() {
|
|
}
|
|
BaseType.prototype.assert = function (value, deep) {
|
|
if (!this.check(value, deep)) {
|
|
var str = shallowStringify(value);
|
|
throw new Error(str + " does not match type " + this);
|
|
}
|
|
return true;
|
|
};
|
|
BaseType.prototype.arrayOf = function () {
|
|
var elemType = this;
|
|
return new ArrayType(elemType);
|
|
};
|
|
return BaseType;
|
|
}());
|
|
var ArrayType = /** @class */ (function (_super) {
|
|
tslib_1.__extends(ArrayType, _super);
|
|
function ArrayType(elemType) {
|
|
var _this = _super.call(this) || this;
|
|
_this.elemType = elemType;
|
|
_this.kind = "ArrayType";
|
|
return _this;
|
|
}
|
|
ArrayType.prototype.toString = function () {
|
|
return "[" + this.elemType + "]";
|
|
};
|
|
ArrayType.prototype.check = function (value, deep) {
|
|
var _this = this;
|
|
return Array.isArray(value) && value.every(function (elem) { return _this.elemType.check(elem, deep); });
|
|
};
|
|
return ArrayType;
|
|
}(BaseType));
|
|
var IdentityType = /** @class */ (function (_super) {
|
|
tslib_1.__extends(IdentityType, _super);
|
|
function IdentityType(value) {
|
|
var _this = _super.call(this) || this;
|
|
_this.value = value;
|
|
_this.kind = "IdentityType";
|
|
return _this;
|
|
}
|
|
IdentityType.prototype.toString = function () {
|
|
return String(this.value);
|
|
};
|
|
IdentityType.prototype.check = function (value, deep) {
|
|
var result = value === this.value;
|
|
if (!result && typeof deep === "function") {
|
|
deep(this, value);
|
|
}
|
|
return result;
|
|
};
|
|
return IdentityType;
|
|
}(BaseType));
|
|
var ObjectType = /** @class */ (function (_super) {
|
|
tslib_1.__extends(ObjectType, _super);
|
|
function ObjectType(fields) {
|
|
var _this = _super.call(this) || this;
|
|
_this.fields = fields;
|
|
_this.kind = "ObjectType";
|
|
return _this;
|
|
}
|
|
ObjectType.prototype.toString = function () {
|
|
return "{ " + this.fields.join(", ") + " }";
|
|
};
|
|
ObjectType.prototype.check = function (value, deep) {
|
|
return (objToStr.call(value) === objToStr.call({}) &&
|
|
this.fields.every(function (field) {
|
|
return field.type.check(value[field.name], deep);
|
|
}));
|
|
};
|
|
return ObjectType;
|
|
}(BaseType));
|
|
var OrType = /** @class */ (function (_super) {
|
|
tslib_1.__extends(OrType, _super);
|
|
function OrType(types) {
|
|
var _this = _super.call(this) || this;
|
|
_this.types = types;
|
|
_this.kind = "OrType";
|
|
return _this;
|
|
}
|
|
OrType.prototype.toString = function () {
|
|
return this.types.join(" | ");
|
|
};
|
|
OrType.prototype.check = function (value, deep) {
|
|
return this.types.some(function (type) {
|
|
return type.check(value, deep);
|
|
});
|
|
};
|
|
return OrType;
|
|
}(BaseType));
|
|
var PredicateType = /** @class */ (function (_super) {
|
|
tslib_1.__extends(PredicateType, _super);
|
|
function PredicateType(name, predicate) {
|
|
var _this = _super.call(this) || this;
|
|
_this.name = name;
|
|
_this.predicate = predicate;
|
|
_this.kind = "PredicateType";
|
|
return _this;
|
|
}
|
|
PredicateType.prototype.toString = function () {
|
|
return this.name;
|
|
};
|
|
PredicateType.prototype.check = function (value, deep) {
|
|
var result = this.predicate(value, deep);
|
|
if (!result && typeof deep === "function") {
|
|
deep(this, value);
|
|
}
|
|
return result;
|
|
};
|
|
return PredicateType;
|
|
}(BaseType));
|
|
var Def = /** @class */ (function () {
|
|
function Def(type, typeName) {
|
|
this.type = type;
|
|
this.typeName = typeName;
|
|
this.baseNames = [];
|
|
this.ownFields = Object.create(null);
|
|
// Includes own typeName. Populated during finalization.
|
|
this.allSupertypes = Object.create(null);
|
|
// Linear inheritance hierarchy. Populated during finalization.
|
|
this.supertypeList = [];
|
|
// Includes inherited fields.
|
|
this.allFields = Object.create(null);
|
|
// Non-hidden keys of allFields.
|
|
this.fieldNames = [];
|
|
// This property will be overridden as true by individual Def instances
|
|
// when they are finalized.
|
|
this.finalized = false;
|
|
// False by default until .build(...) is called on an instance.
|
|
this.buildable = false;
|
|
this.buildParams = [];
|
|
}
|
|
Def.prototype.isSupertypeOf = function (that) {
|
|
if (that instanceof Def) {
|
|
if (this.finalized !== true ||
|
|
that.finalized !== true) {
|
|
throw new Error("");
|
|
}
|
|
return hasOwn.call(that.allSupertypes, this.typeName);
|
|
}
|
|
else {
|
|
throw new Error(that + " is not a Def");
|
|
}
|
|
};
|
|
Def.prototype.checkAllFields = function (value, deep) {
|
|
var allFields = this.allFields;
|
|
if (this.finalized !== true) {
|
|
throw new Error("" + this.typeName);
|
|
}
|
|
function checkFieldByName(name) {
|
|
var field = allFields[name];
|
|
var type = field.type;
|
|
var child = field.getValue(value);
|
|
return type.check(child, deep);
|
|
}
|
|
return value !== null &&
|
|
typeof value === "object" &&
|
|
Object.keys(allFields).every(checkFieldByName);
|
|
};
|
|
Def.prototype.bases = function () {
|
|
var supertypeNames = [];
|
|
for (var _i = 0; _i < arguments.length; _i++) {
|
|
supertypeNames[_i] = arguments[_i];
|
|
}
|
|
var bases = this.baseNames;
|
|
if (this.finalized) {
|
|
if (supertypeNames.length !== bases.length) {
|
|
throw new Error("");
|
|
}
|
|
for (var i = 0; i < supertypeNames.length; i++) {
|
|
if (supertypeNames[i] !== bases[i]) {
|
|
throw new Error("");
|
|
}
|
|
}
|
|
return this;
|
|
}
|
|
supertypeNames.forEach(function (baseName) {
|
|
// This indexOf lookup may be O(n), but the typical number of base
|
|
// names is very small, and indexOf is a native Array method.
|
|
if (bases.indexOf(baseName) < 0) {
|
|
bases.push(baseName);
|
|
}
|
|
});
|
|
return this; // For chaining.
|
|
};
|
|
return Def;
|
|
}());
|
|
exports.Def = Def;
|
|
var Field = /** @class */ (function () {
|
|
function Field(name, type, defaultFn, hidden) {
|
|
this.name = name;
|
|
this.type = type;
|
|
this.defaultFn = defaultFn;
|
|
this.hidden = !!hidden;
|
|
}
|
|
Field.prototype.toString = function () {
|
|
return JSON.stringify(this.name) + ": " + this.type;
|
|
};
|
|
Field.prototype.getValue = function (obj) {
|
|
var value = obj[this.name];
|
|
if (typeof value !== "undefined") {
|
|
return value;
|
|
}
|
|
if (typeof this.defaultFn === "function") {
|
|
value = this.defaultFn.call(obj);
|
|
}
|
|
return value;
|
|
};
|
|
return Field;
|
|
}());
|
|
function shallowStringify(value) {
|
|
if (Array.isArray(value)) {
|
|
return "[" + value.map(shallowStringify).join(", ") + "]";
|
|
}
|
|
if (value && typeof value === "object") {
|
|
return "{ " + Object.keys(value).map(function (key) {
|
|
return key + ": " + value[key];
|
|
}).join(", ") + " }";
|
|
}
|
|
return JSON.stringify(value);
|
|
}
|
|
function typesPlugin(_fork) {
|
|
var Type = {
|
|
or: function () {
|
|
var types = [];
|
|
for (var _i = 0; _i < arguments.length; _i++) {
|
|
types[_i] = arguments[_i];
|
|
}
|
|
return new OrType(types.map(function (type) { return Type.from(type); }));
|
|
},
|
|
from: function (value, name) {
|
|
if (value instanceof ArrayType ||
|
|
value instanceof IdentityType ||
|
|
value instanceof ObjectType ||
|
|
value instanceof OrType ||
|
|
value instanceof PredicateType) {
|
|
return value;
|
|
}
|
|
// The Def type is used as a helper for constructing compound
|
|
// interface types for AST nodes.
|
|
if (value instanceof Def) {
|
|
return value.type;
|
|
}
|
|
// Support [ElemType] syntax.
|
|
if (isArray.check(value)) {
|
|
if (value.length !== 1) {
|
|
throw new Error("only one element type is permitted for typed arrays");
|
|
}
|
|
return new ArrayType(Type.from(value[0]));
|
|
}
|
|
// Support { someField: FieldType, ... } syntax.
|
|
if (isObject.check(value)) {
|
|
return new ObjectType(Object.keys(value).map(function (name) {
|
|
return new Field(name, Type.from(value[name], name));
|
|
}));
|
|
}
|
|
if (typeof value === "function") {
|
|
var bicfIndex = builtInCtorFns.indexOf(value);
|
|
if (bicfIndex >= 0) {
|
|
return builtInCtorTypes[bicfIndex];
|
|
}
|
|
if (typeof name !== "string") {
|
|
throw new Error("missing name");
|
|
}
|
|
return new PredicateType(name, value);
|
|
}
|
|
// As a last resort, toType returns a type that matches any value that
|
|
// is === from. This is primarily useful for literal values like
|
|
// toType(null), but it has the additional advantage of allowing
|
|
// toType to be a total function.
|
|
return new IdentityType(value);
|
|
},
|
|
// Define a type whose name is registered in a namespace (the defCache) so
|
|
// that future definitions will return the same type given the same name.
|
|
// In particular, this system allows for circular and forward definitions.
|
|
// The Def object d returned from Type.def may be used to configure the
|
|
// type d.type by calling methods such as d.bases, d.build, and d.field.
|
|
def: function (typeName) {
|
|
return hasOwn.call(defCache, typeName)
|
|
? defCache[typeName]
|
|
: defCache[typeName] = new DefImpl(typeName);
|
|
},
|
|
hasDef: function (typeName) {
|
|
return hasOwn.call(defCache, typeName);
|
|
}
|
|
};
|
|
var builtInCtorFns = [];
|
|
var builtInCtorTypes = [];
|
|
function defBuiltInType(name, example) {
|
|
var objStr = objToStr.call(example);
|
|
var type = new PredicateType(name, function (value) { return objToStr.call(value) === objStr; });
|
|
if (example && typeof example.constructor === "function") {
|
|
builtInCtorFns.push(example.constructor);
|
|
builtInCtorTypes.push(type);
|
|
}
|
|
return type;
|
|
}
|
|
// These types check the underlying [[Class]] attribute of the given
|
|
// value, rather than using the problematic typeof operator. Note however
|
|
// that no subtyping is considered; so, for instance, isObject.check
|
|
// returns false for [], /./, new Date, and null.
|
|
var isString = defBuiltInType("string", "truthy");
|
|
var isFunction = defBuiltInType("function", function () { });
|
|
var isArray = defBuiltInType("array", []);
|
|
var isObject = defBuiltInType("object", {});
|
|
var isRegExp = defBuiltInType("RegExp", /./);
|
|
var isDate = defBuiltInType("Date", new Date());
|
|
var isNumber = defBuiltInType("number", 3);
|
|
var isBoolean = defBuiltInType("boolean", true);
|
|
var isNull = defBuiltInType("null", null);
|
|
var isUndefined = defBuiltInType("undefined", undefined);
|
|
var builtInTypes = {
|
|
string: isString,
|
|
function: isFunction,
|
|
array: isArray,
|
|
object: isObject,
|
|
RegExp: isRegExp,
|
|
Date: isDate,
|
|
number: isNumber,
|
|
boolean: isBoolean,
|
|
null: isNull,
|
|
undefined: isUndefined,
|
|
};
|
|
// In order to return the same Def instance every time Type.def is called
|
|
// with a particular name, those instances need to be stored in a cache.
|
|
var defCache = Object.create(null);
|
|
function defFromValue(value) {
|
|
if (value && typeof value === "object") {
|
|
var type = value.type;
|
|
if (typeof type === "string" &&
|
|
hasOwn.call(defCache, type)) {
|
|
var d = defCache[type];
|
|
if (d.finalized) {
|
|
return d;
|
|
}
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
var DefImpl = /** @class */ (function (_super) {
|
|
tslib_1.__extends(DefImpl, _super);
|
|
function DefImpl(typeName) {
|
|
var _this = _super.call(this, new PredicateType(typeName, function (value, deep) { return _this.check(value, deep); }), typeName) || this;
|
|
return _this;
|
|
}
|
|
DefImpl.prototype.check = function (value, deep) {
|
|
if (this.finalized !== true) {
|
|
throw new Error("prematurely checking unfinalized type " + this.typeName);
|
|
}
|
|
// A Def type can only match an object value.
|
|
if (value === null || typeof value !== "object") {
|
|
return false;
|
|
}
|
|
var vDef = defFromValue(value);
|
|
if (!vDef) {
|
|
// If we couldn't infer the Def associated with the given value,
|
|
// and we expected it to be a SourceLocation or a Position, it was
|
|
// probably just missing a "type" field (because Esprima does not
|
|
// assign a type property to such nodes). Be optimistic and let
|
|
// this.checkAllFields make the final decision.
|
|
if (this.typeName === "SourceLocation" ||
|
|
this.typeName === "Position") {
|
|
return this.checkAllFields(value, deep);
|
|
}
|
|
// Calling this.checkAllFields for any other type of node is both
|
|
// bad for performance and way too forgiving.
|
|
return false;
|
|
}
|
|
// If checking deeply and vDef === this, then we only need to call
|
|
// checkAllFields once. Calling checkAllFields is too strict when deep
|
|
// is false, because then we only care about this.isSupertypeOf(vDef).
|
|
if (deep && vDef === this) {
|
|
return this.checkAllFields(value, deep);
|
|
}
|
|
// In most cases we rely exclusively on isSupertypeOf to make O(1)
|
|
// subtyping determinations. This suffices in most situations outside
|
|
// of unit tests, since interface conformance is checked whenever new
|
|
// instances are created using builder functions.
|
|
if (!this.isSupertypeOf(vDef)) {
|
|
return false;
|
|
}
|
|
// The exception is when deep is true; then, we recursively check all
|
|
// fields.
|
|
if (!deep) {
|
|
return true;
|
|
}
|
|
// Use the more specific Def (vDef) to perform the deep check, but
|
|
// shallow-check fields defined by the less specific Def (this).
|
|
return vDef.checkAllFields(value, deep)
|
|
&& this.checkAllFields(value, false);
|
|
};
|
|
DefImpl.prototype.build = function () {
|
|
var _this = this;
|
|
var buildParams = [];
|
|
for (var _i = 0; _i < arguments.length; _i++) {
|
|
buildParams[_i] = arguments[_i];
|
|
}
|
|
// Calling Def.prototype.build multiple times has the effect of merely
|
|
// redefining this property.
|
|
this.buildParams = buildParams;
|
|
if (this.buildable) {
|
|
// If this Def is already buildable, update self.buildParams and
|
|
// continue using the old builder function.
|
|
return this;
|
|
}
|
|
// Every buildable type will have its "type" field filled in
|
|
// automatically. This includes types that are not subtypes of Node,
|
|
// like SourceLocation, but that seems harmless (TODO?).
|
|
this.field("type", String, function () { return _this.typeName; });
|
|
// Override Dp.buildable for this Def instance.
|
|
this.buildable = true;
|
|
var addParam = function (built, param, arg, isArgAvailable) {
|
|
if (hasOwn.call(built, param))
|
|
return;
|
|
var all = _this.allFields;
|
|
if (!hasOwn.call(all, param)) {
|
|
throw new Error("" + param);
|
|
}
|
|
var field = all[param];
|
|
var type = field.type;
|
|
var value;
|
|
if (isArgAvailable) {
|
|
value = arg;
|
|
}
|
|
else if (field.defaultFn) {
|
|
// Expose the partially-built object to the default
|
|
// function as its `this` object.
|
|
value = field.defaultFn.call(built);
|
|
}
|
|
else {
|
|
var message = "no value or default function given for field " +
|
|
JSON.stringify(param) + " of " + _this.typeName + "(" +
|
|
_this.buildParams.map(function (name) {
|
|
return all[name];
|
|
}).join(", ") + ")";
|
|
throw new Error(message);
|
|
}
|
|
if (!type.check(value)) {
|
|
throw new Error(shallowStringify(value) +
|
|
" does not match field " + field +
|
|
" of type " + _this.typeName);
|
|
}
|
|
built[param] = value;
|
|
};
|
|
// Calling the builder function will construct an instance of the Def,
|
|
// with positional arguments mapped to the fields original passed to .build.
|
|
// If not enough arguments are provided, the default value for the remaining fields
|
|
// will be used.
|
|
var builder = function () {
|
|
var args = [];
|
|
for (var _i = 0; _i < arguments.length; _i++) {
|
|
args[_i] = arguments[_i];
|
|
}
|
|
var argc = args.length;
|
|
if (!_this.finalized) {
|
|
throw new Error("attempting to instantiate unfinalized type " +
|
|
_this.typeName);
|
|
}
|
|
var built = Object.create(nodePrototype);
|
|
_this.buildParams.forEach(function (param, i) {
|
|
if (i < argc) {
|
|
addParam(built, param, args[i], true);
|
|
}
|
|
else {
|
|
addParam(built, param, null, false);
|
|
}
|
|
});
|
|
Object.keys(_this.allFields).forEach(function (param) {
|
|
// Use the default value.
|
|
addParam(built, param, null, false);
|
|
});
|
|
// Make sure that the "type" field was filled automatically.
|
|
if (built.type !== _this.typeName) {
|
|
throw new Error("");
|
|
}
|
|
return built;
|
|
};
|
|
// Calling .from on the builder function will construct an instance of the Def,
|
|
// using field values from the passed object. For fields missing from the passed object,
|
|
// their default value will be used.
|
|
builder.from = function (obj) {
|
|
if (!_this.finalized) {
|
|
throw new Error("attempting to instantiate unfinalized type " +
|
|
_this.typeName);
|
|
}
|
|
var built = Object.create(nodePrototype);
|
|
Object.keys(_this.allFields).forEach(function (param) {
|
|
if (hasOwn.call(obj, param)) {
|
|
addParam(built, param, obj[param], true);
|
|
}
|
|
else {
|
|
addParam(built, param, null, false);
|
|
}
|
|
});
|
|
// Make sure that the "type" field was filled automatically.
|
|
if (built.type !== _this.typeName) {
|
|
throw new Error("");
|
|
}
|
|
return built;
|
|
};
|
|
Object.defineProperty(builders, getBuilderName(this.typeName), {
|
|
enumerable: true,
|
|
value: builder
|
|
});
|
|
return this;
|
|
};
|
|
// The reason fields are specified using .field(...) instead of an object
|
|
// literal syntax is somewhat subtle: the object literal syntax would
|
|
// support only one key and one value, but with .field(...) we can pass
|
|
// any number of arguments to specify the field.
|
|
DefImpl.prototype.field = function (name, type, defaultFn, hidden) {
|
|
if (this.finalized) {
|
|
console.error("Ignoring attempt to redefine field " +
|
|
JSON.stringify(name) + " of finalized type " +
|
|
JSON.stringify(this.typeName));
|
|
return this;
|
|
}
|
|
this.ownFields[name] = new Field(name, Type.from(type), defaultFn, hidden);
|
|
return this; // For chaining.
|
|
};
|
|
DefImpl.prototype.finalize = function () {
|
|
var _this = this;
|
|
// It's not an error to finalize a type more than once, but only the
|
|
// first call to .finalize does anything.
|
|
if (!this.finalized) {
|
|
var allFields = this.allFields;
|
|
var allSupertypes = this.allSupertypes;
|
|
this.baseNames.forEach(function (name) {
|
|
var def = defCache[name];
|
|
if (def instanceof Def) {
|
|
def.finalize();
|
|
extend(allFields, def.allFields);
|
|
extend(allSupertypes, def.allSupertypes);
|
|
}
|
|
else {
|
|
var message = "unknown supertype name " +
|
|
JSON.stringify(name) +
|
|
" for subtype " +
|
|
JSON.stringify(_this.typeName);
|
|
throw new Error(message);
|
|
}
|
|
});
|
|
// TODO Warn if fields are overridden with incompatible types.
|
|
extend(allFields, this.ownFields);
|
|
allSupertypes[this.typeName] = this;
|
|
this.fieldNames.length = 0;
|
|
for (var fieldName in allFields) {
|
|
if (hasOwn.call(allFields, fieldName) &&
|
|
!allFields[fieldName].hidden) {
|
|
this.fieldNames.push(fieldName);
|
|
}
|
|
}
|
|
// Types are exported only once they have been finalized.
|
|
Object.defineProperty(namedTypes, this.typeName, {
|
|
enumerable: true,
|
|
value: this.type
|
|
});
|
|
this.finalized = true;
|
|
// A linearization of the inheritance hierarchy.
|
|
populateSupertypeList(this.typeName, this.supertypeList);
|
|
if (this.buildable &&
|
|
this.supertypeList.lastIndexOf("Expression") >= 0) {
|
|
wrapExpressionBuilderWithStatement(this.typeName);
|
|
}
|
|
}
|
|
};
|
|
return DefImpl;
|
|
}(Def));
|
|
// Note that the list returned by this function is a copy of the internal
|
|
// supertypeList, *without* the typeName itself as the first element.
|
|
function getSupertypeNames(typeName) {
|
|
if (!hasOwn.call(defCache, typeName)) {
|
|
throw new Error("");
|
|
}
|
|
var d = defCache[typeName];
|
|
if (d.finalized !== true) {
|
|
throw new Error("");
|
|
}
|
|
return d.supertypeList.slice(1);
|
|
}
|
|
// Returns an object mapping from every known type in the defCache to the
|
|
// most specific supertype whose name is an own property of the candidates
|
|
// object.
|
|
function computeSupertypeLookupTable(candidates) {
|
|
var table = {};
|
|
var typeNames = Object.keys(defCache);
|
|
var typeNameCount = typeNames.length;
|
|
for (var i = 0; i < typeNameCount; ++i) {
|
|
var typeName = typeNames[i];
|
|
var d = defCache[typeName];
|
|
if (d.finalized !== true) {
|
|
throw new Error("" + typeName);
|
|
}
|
|
for (var j = 0; j < d.supertypeList.length; ++j) {
|
|
var superTypeName = d.supertypeList[j];
|
|
if (hasOwn.call(candidates, superTypeName)) {
|
|
table[typeName] = superTypeName;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return table;
|
|
}
|
|
var builders = Object.create(null);
|
|
// This object is used as prototype for any node created by a builder.
|
|
var nodePrototype = {};
|
|
// Call this function to define a new method to be shared by all AST
|
|
// nodes. The replaced method (if any) is returned for easy wrapping.
|
|
function defineMethod(name, func) {
|
|
var old = nodePrototype[name];
|
|
// Pass undefined as func to delete nodePrototype[name].
|
|
if (isUndefined.check(func)) {
|
|
delete nodePrototype[name];
|
|
}
|
|
else {
|
|
isFunction.assert(func);
|
|
Object.defineProperty(nodePrototype, name, {
|
|
enumerable: true,
|
|
configurable: true,
|
|
value: func
|
|
});
|
|
}
|
|
return old;
|
|
}
|
|
function getBuilderName(typeName) {
|
|
return typeName.replace(/^[A-Z]+/, function (upperCasePrefix) {
|
|
var len = upperCasePrefix.length;
|
|
switch (len) {
|
|
case 0: return "";
|
|
// If there's only one initial capital letter, just lower-case it.
|
|
case 1: return upperCasePrefix.toLowerCase();
|
|
default:
|
|
// If there's more than one initial capital letter, lower-case
|
|
// all but the last one, so that XMLDefaultDeclaration (for
|
|
// example) becomes xmlDefaultDeclaration.
|
|
return upperCasePrefix.slice(0, len - 1).toLowerCase() +
|
|
upperCasePrefix.charAt(len - 1);
|
|
}
|
|
});
|
|
}
|
|
function getStatementBuilderName(typeName) {
|
|
typeName = getBuilderName(typeName);
|
|
return typeName.replace(/(Expression)?$/, "Statement");
|
|
}
|
|
var namedTypes = {};
|
|
// Like Object.keys, but aware of what fields each AST type should have.
|
|
function getFieldNames(object) {
|
|
var d = defFromValue(object);
|
|
if (d) {
|
|
return d.fieldNames.slice(0);
|
|
}
|
|
if ("type" in object) {
|
|
throw new Error("did not recognize object of type " +
|
|
JSON.stringify(object.type));
|
|
}
|
|
return Object.keys(object);
|
|
}
|
|
// Get the value of an object property, taking object.type and default
|
|
// functions into account.
|
|
function getFieldValue(object, fieldName) {
|
|
var d = defFromValue(object);
|
|
if (d) {
|
|
var field = d.allFields[fieldName];
|
|
if (field) {
|
|
return field.getValue(object);
|
|
}
|
|
}
|
|
return object && object[fieldName];
|
|
}
|
|
// Iterate over all defined fields of an object, including those missing
|
|
// or undefined, passing each field name and effective value (as returned
|
|
// by getFieldValue) to the callback. If the object has no corresponding
|
|
// Def, the callback will never be called.
|
|
function eachField(object, callback, context) {
|
|
getFieldNames(object).forEach(function (name) {
|
|
callback.call(this, name, getFieldValue(object, name));
|
|
}, context);
|
|
}
|
|
// Similar to eachField, except that iteration stops as soon as the
|
|
// callback returns a truthy value. Like Array.prototype.some, the final
|
|
// result is either true or false to indicates whether the callback
|
|
// returned true for any element or not.
|
|
function someField(object, callback, context) {
|
|
return getFieldNames(object).some(function (name) {
|
|
return callback.call(this, name, getFieldValue(object, name));
|
|
}, context);
|
|
}
|
|
// Adds an additional builder for Expression subtypes
|
|
// that wraps the built Expression in an ExpressionStatements.
|
|
function wrapExpressionBuilderWithStatement(typeName) {
|
|
var wrapperName = getStatementBuilderName(typeName);
|
|
// skip if the builder already exists
|
|
if (builders[wrapperName])
|
|
return;
|
|
// the builder function to wrap with builders.ExpressionStatement
|
|
var wrapped = builders[getBuilderName(typeName)];
|
|
// skip if there is nothing to wrap
|
|
if (!wrapped)
|
|
return;
|
|
var builder = function () {
|
|
var args = [];
|
|
for (var _i = 0; _i < arguments.length; _i++) {
|
|
args[_i] = arguments[_i];
|
|
}
|
|
return builders.expressionStatement(wrapped.apply(builders, args));
|
|
};
|
|
builder.from = function () {
|
|
var args = [];
|
|
for (var _i = 0; _i < arguments.length; _i++) {
|
|
args[_i] = arguments[_i];
|
|
}
|
|
return builders.expressionStatement(wrapped.from.apply(builders, args));
|
|
};
|
|
builders[wrapperName] = builder;
|
|
}
|
|
function populateSupertypeList(typeName, list) {
|
|
list.length = 0;
|
|
list.push(typeName);
|
|
var lastSeen = Object.create(null);
|
|
for (var pos = 0; pos < list.length; ++pos) {
|
|
typeName = list[pos];
|
|
var d = defCache[typeName];
|
|
if (d.finalized !== true) {
|
|
throw new Error("");
|
|
}
|
|
// If we saw typeName earlier in the breadth-first traversal,
|
|
// delete the last-seen occurrence.
|
|
if (hasOwn.call(lastSeen, typeName)) {
|
|
delete list[lastSeen[typeName]];
|
|
}
|
|
// Record the new index of the last-seen occurrence of typeName.
|
|
lastSeen[typeName] = pos;
|
|
// Enqueue the base names of this type.
|
|
list.push.apply(list, d.baseNames);
|
|
}
|
|
// Compaction loop to remove array holes.
|
|
for (var to = 0, from = to, len = list.length; from < len; ++from) {
|
|
if (hasOwn.call(list, from)) {
|
|
list[to++] = list[from];
|
|
}
|
|
}
|
|
list.length = to;
|
|
}
|
|
function extend(into, from) {
|
|
Object.keys(from).forEach(function (name) {
|
|
into[name] = from[name];
|
|
});
|
|
return into;
|
|
}
|
|
function finalize() {
|
|
Object.keys(defCache).forEach(function (name) {
|
|
defCache[name].finalize();
|
|
});
|
|
}
|
|
return {
|
|
Type: Type,
|
|
builtInTypes: builtInTypes,
|
|
getSupertypeNames: getSupertypeNames,
|
|
computeSupertypeLookupTable: computeSupertypeLookupTable,
|
|
builders: builders,
|
|
defineMethod: defineMethod,
|
|
getBuilderName: getBuilderName,
|
|
getStatementBuilderName: getStatementBuilderName,
|
|
namedTypes: namedTypes,
|
|
getFieldNames: getFieldNames,
|
|
getFieldValue: getFieldValue,
|
|
eachField: eachField,
|
|
someField: someField,
|
|
finalize: finalize,
|
|
};
|
|
}
|
|
exports.default = typesPlugin;
|
|
;
|