1028 строки
36 KiB
TypeScript
1028 строки
36 KiB
TypeScript
///<reference path='refs.ts'/>
|
|
|
|
module TDev.AST {
|
|
export var libSymbol = "\u267B";
|
|
|
|
export class LibraryRefAction
|
|
extends Action
|
|
{
|
|
private _description:string;
|
|
private _flags:PropertyFlags;
|
|
public template: Action;
|
|
public wasUsed: boolean;
|
|
public _extensionAction:LibExtensionAction;
|
|
|
|
constructor(public _lib:LibraryRef) {
|
|
super()
|
|
this.parentKind = _lib.getKind();
|
|
}
|
|
|
|
public isInLibrary() { return true }
|
|
public getNamespace() { return libSymbol + this.parentLibrary().getName() + this.getArrow(); }
|
|
public parentLibrary() { return this._lib; }
|
|
public getFlags() { return this._flags }
|
|
public getDescription() {
|
|
if (this._description) return this._description;
|
|
return super.getDescription();
|
|
}
|
|
public getArrow() { return "\u200A\u2192\u00A0" }
|
|
|
|
public markUsed() { this.wasUsed = true }
|
|
public canRename() { return false; }
|
|
|
|
public usageKey()
|
|
{
|
|
return Util.tagify(this._lib.getName() + " " + this.getName())
|
|
}
|
|
|
|
private setAllLocals()
|
|
{
|
|
this.allLocals = this.getAllParameters().map((ap) => ap.local)
|
|
this.body = Parser.emptyBlock();
|
|
}
|
|
|
|
public updateSubstitution()
|
|
{
|
|
if (!this.template) return;
|
|
var copyParam = (ap:ActionParameter) => new ActionParameter(mkLocal(ap.getName(), this.parentLibrary().substitute(ap.getKind())));
|
|
this.header.inParameters.setChildren(this.template.getInParameters().map(copyParam));
|
|
this.header.outParameters.setChildren(this.template.getOutParameters().map(copyParam));
|
|
this.setAllLocals()
|
|
}
|
|
|
|
public fromTemplateCore(template:Action)
|
|
{
|
|
this.template = template;
|
|
this.updateSubstitution();
|
|
this._description = template.getInlineHelp();
|
|
this._flags = template.getFlags()
|
|
this.setName(template.getName());
|
|
this._isPage = template.isPage();
|
|
this.isAtomic = template.isAtomic;
|
|
this._isActionTypeDef = template.isActionTypeDef();
|
|
this.setStableName(template.getStableName());
|
|
this.setAllLocals();
|
|
}
|
|
|
|
private addExtensionAction()
|
|
{
|
|
if (this.isActionTypeDef())
|
|
return
|
|
|
|
var k = <ExtensionEnabledKind>this.getExtensionKind()
|
|
if (k)
|
|
k.addExtensionAction(this._extensionAction = new LibExtensionAction(this));
|
|
}
|
|
|
|
public fromTemplate(template:Action)
|
|
{
|
|
this.fromTemplateCore(template)
|
|
if (!template.isPage())
|
|
this.addExtensionAction();
|
|
return this;
|
|
}
|
|
|
|
public parse(p:Parser)
|
|
{
|
|
this.setStableName(p.consumeLabel())
|
|
|
|
while (true) {
|
|
if (p.gotOp("sync")) {
|
|
p.shift();
|
|
this.isAtomic = true;
|
|
continue;
|
|
}
|
|
if (p.gotOp("async")) {
|
|
p.shift();
|
|
this.isAtomic = false;
|
|
continue;
|
|
}
|
|
if (p.gotOp("page")) {
|
|
p.shift();
|
|
this._isPage = true;
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
|
|
var hd = p.parseActionHeader();
|
|
this.setName(hd.name);
|
|
this.header.inParameters.setChildren(hd.inParameters);
|
|
this.header.outParameters.setChildren(hd.outParameters);
|
|
this._isActionTypeDef = hd.isType
|
|
this.setAllLocals();
|
|
this.addExtensionAction();
|
|
this._flags = super.getFlags()
|
|
}
|
|
|
|
public toString()
|
|
{
|
|
return this.getNamespace() + this.getName();
|
|
}
|
|
|
|
public showIntelliButton()
|
|
{
|
|
if (this.isExtensionAction()) return true;
|
|
return this.getExtensionKind() == null;
|
|
}
|
|
}
|
|
|
|
export class LibExtensionAction
|
|
extends LibraryRefAction
|
|
{
|
|
constructor(public shortcutTo:LibraryRefAction)
|
|
{
|
|
super(shortcutTo._lib)
|
|
this.fromTemplateCore(shortcutTo)
|
|
}
|
|
|
|
public updateSubstitution()
|
|
{
|
|
super.updateSubstitution()
|
|
this.parentKind = this.getInParameters()[0].getKind();
|
|
this.header.inParameters.stmts.shift()
|
|
}
|
|
|
|
public isExtensionAction() { return true }
|
|
public extensionForward():Action { return this.shortcutTo }
|
|
|
|
public getDescription()
|
|
{
|
|
return this.shortcutTo.getDescription()
|
|
}
|
|
|
|
public getFlags()
|
|
{
|
|
return this.shortcutTo.getFlags()
|
|
}
|
|
|
|
public markUsed() {
|
|
super.markUsed()
|
|
this.shortcutTo.markUsed();
|
|
}
|
|
}
|
|
|
|
export class LibraryRefAbstractKind
|
|
extends ExtensionEnabledKind
|
|
{
|
|
constructor(public _lib:LibraryRef, n:string) {
|
|
super(n, "a kind from library");
|
|
this._stemName = n;
|
|
this._contexts = KindContext.General | KindContext.GcKey;
|
|
|
|
var invl = Property.md_make(0, this, "is invalid", "Returns true if the current instance is useless", [], api.core.Boolean)
|
|
invl.md_runOnInvalid();
|
|
this.addExtensionAction(invl)
|
|
}
|
|
|
|
private deleted = false;
|
|
public kill()
|
|
{
|
|
this.deleted = true;
|
|
}
|
|
|
|
public isError() { return this.deleted || this._lib.deleted; }
|
|
public isUserDefined() { return true; }
|
|
|
|
public parentLibrary() { return this._lib; }
|
|
public getNamespace() { return LibraryRef.libNamespace(this.parentLibrary().getName()) }
|
|
public toString()
|
|
{
|
|
return this.getNamespace() + this.getName();
|
|
}
|
|
|
|
public listPriority() { return 3; }
|
|
|
|
public listProperties() { return this.listExtensions() }
|
|
public getProperty(name:string) { return this.getExtension(name) }
|
|
}
|
|
|
|
export class LibraryRefKind
|
|
extends Kind
|
|
{
|
|
constructor(public lib:LibraryRef) {
|
|
super("no name yet", "a library reference")
|
|
}
|
|
public listProperties() : IProperty[] { return this.lib.getPublicActions().map(Property.withParent(this)); }
|
|
|
|
public getProperty(name:string) : IProperty
|
|
{
|
|
var a = this.lib.getPublicActions().filter((a:IProperty) => a.getName() == name);
|
|
if (!a[0])
|
|
return undefined;
|
|
return Property.withParent(this)(a[0]);
|
|
}
|
|
|
|
public getName() { return this.lib.getName() }
|
|
public toString() { return this.lib.toString() }
|
|
|
|
public getHelp() { return this.lib.getDescription() }
|
|
}
|
|
|
|
export class LibraryRecordDef
|
|
extends RecordDef
|
|
{
|
|
public template:RecordDef;
|
|
|
|
constructor(public _lib:LibraryRef) {
|
|
super()
|
|
}
|
|
|
|
public updateSubstitution()
|
|
{
|
|
if (!this.template) return;
|
|
var copyField = (fld:RecordField) => {
|
|
var k = this.parentLibrary().substitute(fld.dataKind)
|
|
var rf = new RecordField(fld.getName(), k, fld.isKey, fld.getDescription())
|
|
rf.setStableName(fld.getStableName())
|
|
return rf
|
|
}
|
|
this.keys.setChildren(this.template.keys.map(copyField))
|
|
this.values.setChildren(this.template.values.map(copyField))
|
|
}
|
|
|
|
public fromTemplate(template:RecordDef)
|
|
{
|
|
this.template = template;
|
|
this.recordType = template.recordType;
|
|
this.cloudEnabled = template.cloudEnabled;
|
|
this.cloudPartiallyEnabled = template.cloudPartiallyEnabled;
|
|
this._isExported = template._isExported;
|
|
this.persistent = template.persistent;
|
|
this.description = template.description;
|
|
this.setName(template.getCoreName());
|
|
this.setStableName(template.getStableName());
|
|
this.updateSubstitution();
|
|
}
|
|
|
|
public parentLibrary()
|
|
{
|
|
return this._lib;
|
|
}
|
|
|
|
public getNamespace() { return LibraryRef.libNamespace(this.parentLibrary().getName()) }
|
|
}
|
|
|
|
interface Assumption {
|
|
formal: Kind;
|
|
actual: Kind;
|
|
}
|
|
|
|
export class UnificationCtx
|
|
{
|
|
public mapping:StringMap<Kind> = {};
|
|
public clause:ResolveClause = null;
|
|
|
|
constructor(public lib:LibraryRef)
|
|
{
|
|
}
|
|
|
|
public resolve(form:Kind)
|
|
{
|
|
if (form instanceof LibraryRefAbstractKind) {
|
|
var fk = <LibraryRefAbstractKind>form
|
|
if (fk.parentLibrary().parent == this.lib.resolved) {
|
|
var n = fk.toString()
|
|
if (this.mapping.hasOwnProperty(n)) {
|
|
return this.mapping[n]
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
return form;
|
|
}
|
|
|
|
private unifyActionKinds(form:ActionKind, act:ActionKind)
|
|
{
|
|
var cmpLists = (a:PropertyParameter[], b:PropertyParameter[]) => {
|
|
if (a.length != b.length) return false
|
|
return a.every((aa, i) => this.unify(aa.getKind(), b[i].getKind()))
|
|
}
|
|
|
|
return cmpLists(form.getInParameters(), act.getInParameters()) &&
|
|
cmpLists(form.getOutParameters(), act.getOutParameters());
|
|
}
|
|
|
|
private unifyParametricKinds(form:ParametricKind, act:ParametricKind)
|
|
{
|
|
return form.getRoot().equals(act.getRoot()) &&
|
|
form.parameters.length == act.parameters.length &&
|
|
form.parameters.every((f, i) => this.unify(f, act.parameters[i]))
|
|
}
|
|
|
|
private unifyRecords(form:RecordDef, act:RecordDef)
|
|
{
|
|
if (form.getName() != act.getName() ||
|
|
form.recordType != act.recordType ||
|
|
form.getRecordPersistence() != act.getRecordPersistence())
|
|
return false;
|
|
|
|
var actFields = Util.toDictionary(act.getFields(), f => f.getName())
|
|
var allOK = true
|
|
|
|
form.getFields().forEach(f => {
|
|
if (actFields.hasOwnProperty(f.getName())) {
|
|
var a = actFields[f.getName()]
|
|
if (f.isKey != a.isKey ||
|
|
!this.unify(f.dataKind, a.dataKind))
|
|
allOK = false
|
|
} else {
|
|
allOK = false
|
|
}
|
|
})
|
|
|
|
return allOK
|
|
}
|
|
|
|
private assumptions:Assumption[] = [];
|
|
|
|
public unify(form:Kind, act:Kind)
|
|
{
|
|
if (form.equals(act)) return true;
|
|
|
|
if (form instanceof ActionKind && act instanceof ActionKind)
|
|
return this.unifyActionKinds(<ActionKind>form, <ActionKind>act)
|
|
|
|
if (form instanceof ParametricKind && act instanceof ParametricKind)
|
|
return this.unifyParametricKinds(<ParametricKind>form, <ParametricKind>act)
|
|
|
|
if (form.getRecord() && act.getRecord()) {
|
|
if (this.assumptions.some(a => a.formal == form && a.actual == act))
|
|
return true
|
|
|
|
this.assumptions.push({ formal: form, actual: act })
|
|
try {
|
|
return this.unifyRecords(form.getRecord(), act.getRecord())
|
|
} finally {
|
|
this.assumptions.pop()
|
|
}
|
|
}
|
|
|
|
var resolved = this.resolve(form);
|
|
if (!resolved) {
|
|
/*
|
|
// note that this would actually possibly add it to the wrong resolve clause
|
|
if (this.clause) {
|
|
var k = new KindBinding(form.getName())
|
|
// k.isExplicit = true;
|
|
k.actual = act;
|
|
this.clause.kindBindings.push(k)
|
|
}
|
|
*/
|
|
this.mapping[form.toString()] = act;
|
|
return true;
|
|
} else return resolved.equals(act);
|
|
}
|
|
}
|
|
|
|
export class ResolveClause
|
|
extends Stmt
|
|
{
|
|
public defaultLib:LibraryRef;
|
|
public formalLib:LibraryRef; // from resolved version of the library
|
|
public kindBindings = new BindingBlock();
|
|
public actionBindings = new BindingBlock();
|
|
public primaryBody():Block { return this.actionBindings; }
|
|
public children() { return <Stmt[]>[this.kindBindings, this.actionBindings]; }
|
|
public accept(v:NodeVisitor) { return v.visitResolveClause(this); }
|
|
public nodeType() { return "resolveClause"; }
|
|
|
|
constructor(public name:string) {
|
|
super()
|
|
this.kindBindings.parent = this;
|
|
this.actionBindings.parent = this;
|
|
}
|
|
public getName() { return this.name; }
|
|
|
|
public resolveExplicit(par:LibraryRef)
|
|
{
|
|
this.clearError();
|
|
if (!this.defaultLib) {
|
|
par._hasErrors = true;
|
|
this.setError("TD135: not bound to any library; tap to bind");
|
|
return;
|
|
}
|
|
this.actionBindings.stmts.forEach((binding:ActionBinding) => {
|
|
var al = binding.actualLib;
|
|
var act = al.getPublicActions().filter((a) => a.getName() == binding.actualName)[0];
|
|
binding.actual = act;
|
|
if (!act)
|
|
binding.setError(Util.fmt("TD138: cannot find action '{1}' in {0}", al, binding.actualName));
|
|
})
|
|
}
|
|
|
|
public typeCheck(ctx:UnificationCtx)
|
|
{
|
|
this.clearError();
|
|
if (!this.defaultLib) {
|
|
this.setError("TD135: not bound to any library; tap to bind");
|
|
return;
|
|
}
|
|
|
|
this.defaultLib.typeCheck();
|
|
|
|
ctx.clause = this;
|
|
|
|
this.kindBindings.setChildren([])
|
|
var newBindings = this.formalLib.getPublicActions().filter((a:LibraryRefAction) => a.wasUsed).map((form:Action,ix:number) => {
|
|
Util.assert(form instanceof LibraryRefAction);
|
|
var binding = <ActionBinding>(this.actionBindings.stmts.filter((a:ActionBinding) => a.formalName == form.getName())[0]);
|
|
if (!binding)
|
|
binding = new ActionBinding(form.getName());
|
|
|
|
if (!binding.isExplicit) {
|
|
binding.actualLib = this.defaultLib;
|
|
binding.actualName = form.getName();
|
|
}
|
|
var al = binding.actualLib;
|
|
al.typeCheck();
|
|
var act = al.getPublicActions().filter((a) => a.getName() == binding.actualName)[0];
|
|
binding.formal = <LibraryRefAction>form;
|
|
binding.actual = act;
|
|
if (act) {
|
|
var err = binding.getSignatureError(ctx, act);
|
|
if (err) {
|
|
binding.setError(err);
|
|
} else {
|
|
binding.clearError();
|
|
}
|
|
if (!al.isTypechecked)
|
|
binding.setError("TD174: cause of circular library reference")
|
|
} else if (al.isReal()) {
|
|
binding.setError(Util.fmt("TD138: cannot find action '{1}' in {0}", al, binding.actualName));
|
|
} else {
|
|
this.setError(Util.fmt("TD139: no library reference named {0}", al));
|
|
}
|
|
return binding;
|
|
});
|
|
|
|
var cmpBindings = (a:AST.Binding, b:AST.Binding) => {
|
|
if (a.isExplicit == b.isExplicit)
|
|
return a.formalName.localeCompare(b.formalName);
|
|
else if (a.isExplicit) return -1;
|
|
else return 1;
|
|
}
|
|
newBindings.sort(cmpBindings);
|
|
|
|
this.actionBindings.setChildren(newBindings);
|
|
|
|
if (!this.getError()) {
|
|
var err = newBindings.filter(b => !!b.getError())[0]
|
|
if (err) this.setError("TD173: some binding(s) have errors")
|
|
}
|
|
}
|
|
}
|
|
|
|
export class Binding
|
|
extends Stmt
|
|
{
|
|
public isExplicit = false;
|
|
constructor(public formalName:string) {
|
|
super()
|
|
}
|
|
}
|
|
|
|
export class KindBinding
|
|
extends Binding
|
|
{
|
|
public actual:Kind;
|
|
constructor(f:string) {
|
|
super(f)
|
|
}
|
|
public accept(v:NodeVisitor) { return v.visitKindBinding(this); }
|
|
public nodeType() { return "kindBinding"; }
|
|
}
|
|
|
|
export class ActionBinding
|
|
extends Binding
|
|
{
|
|
public formal:LibraryRefAction;
|
|
public actualLib:LibraryRef;
|
|
public actualName:string;
|
|
public actual:Action;
|
|
constructor(f:string) {
|
|
super(f)
|
|
}
|
|
public accept(v:NodeVisitor) { return v.visitActionBinding(this); }
|
|
public nodeType() { return "actionBinding"; }
|
|
public getActualName() { return this.actual ? this.actual.getName() : this.actualName || "?"; }
|
|
|
|
public getSignatureError(ctx:UnificationCtx, act:Action)
|
|
{
|
|
var form = this.formal;
|
|
var err = null;
|
|
// var al = binding.actualLib;
|
|
var compareParameters = (formal:ActionParameter[], actual:ActionParameter[], tp:string) =>
|
|
{
|
|
if (formal.length != actual.length)
|
|
err = Util.fmt("TD136: '{0}' has {1} {2}-paramters, but '{3}' requires {4}", act, actual.length, tp, form.getName(), formal.length);
|
|
else
|
|
for (var i = 0; i < formal.length; ++i) {
|
|
var fk = formal[i].getKind();
|
|
var ak = actual[i].getKind();
|
|
if (!ctx.unify(fk, ak))
|
|
err = Util.fmt("TD137: '{0}' has {1} as {3}-paramter #{2}, but '{4}' requires {5}",
|
|
act, ak, i, tp, form.getName(), ctx.resolve(fk));
|
|
}
|
|
}
|
|
|
|
if (form.isAtomic && !act.isAtomic)
|
|
return Util.fmt("TD162: an atomic action is required for '{0}'", form.getName());
|
|
|
|
compareParameters(form.getInParameters(), act.getInParameters(), "in");
|
|
compareParameters(form.getOutParameters(), act.getOutParameters(), "out");
|
|
|
|
return err;
|
|
}
|
|
}
|
|
|
|
export class LibraryRef
|
|
extends PropertyDecl
|
|
implements IProperty
|
|
{
|
|
public guid:string = "";
|
|
public pubid:string = "";
|
|
public resolved:App;
|
|
public _publicActions:LibraryRefAction[] = [];
|
|
public _publicKinds:Kind[] = [];
|
|
private kindMapping:any = {};
|
|
private externalKindMapping:StringMap<Kind> = {};
|
|
|
|
public isTypechecked = false;
|
|
private isTypechecking = false;
|
|
|
|
public updateId:string;
|
|
public needsUpdate:boolean;
|
|
public isDeclared:boolean;
|
|
public isCloud() { return this.resolved && this.resolved.isCloud }
|
|
|
|
private publicActionsCopiedFromResolved = false;
|
|
public resolveClauses = new ResolveBlock();
|
|
public isTopLevel() { return this.parent.isTopLevel; }
|
|
public isReal() { return !!this.getId(); }
|
|
public children() { return <AstNode[]>[this.resolveClauses]; }
|
|
public isThis() { return false; }
|
|
public isPublished() { return !this.guid }
|
|
public thingSetKindName() { return libSymbol; }
|
|
|
|
public _hasErrors:boolean;
|
|
public getPublicActions():Action[] { return this._publicActions; }
|
|
public getPublicKinds() { return this._publicKinds; }
|
|
|
|
public getIconArtId() : string { return this.resolved ? this.resolved.iconArtId : ""; }
|
|
|
|
public getPublicActionsAndActionTypes():Action[]
|
|
{
|
|
var acts:Action[] =
|
|
this.getPublicKinds()
|
|
.map(k => k instanceof UserActionKind ? (<UserActionKind>k).userAction : null)
|
|
.filter(a => a != null)
|
|
if (acts.length == 0) return this.getPublicActions()
|
|
else return acts.concat(this.getPublicActions())
|
|
}
|
|
|
|
public getId() { return this.guid ? this.guid : this.pubid; }
|
|
public hasErrors() { return this._hasErrors; }
|
|
|
|
public getCategory() { return PropertyCategory.Library; }
|
|
public helpTopic() { return "libraries"; }
|
|
|
|
public listedKinds:string[] = [];
|
|
|
|
public nodeType() { return "libraryRef"; }
|
|
public accept(v:NodeVisitor) { return v.visitLibraryRef(this); }
|
|
public getDescription() { return ": " + (this.resolved ? this.resolved.getName() : "?") + (this.pubid ? "" : " (local)"); }
|
|
|
|
public getNamespace() { return libSymbol + "\u200A"; }
|
|
public toString() { return this.getNamespace() + this.getName(); }
|
|
|
|
public hasAbstractKind(n:string)
|
|
{
|
|
return !!this.kindMapping[n];
|
|
}
|
|
|
|
public isTutorial() { return this.getStableName() == "tutorialLib" }
|
|
|
|
public isBrowsable()
|
|
{
|
|
if (followingTutorial && /^_/.test(this.getName())) return false
|
|
return true
|
|
}
|
|
|
|
private updateSubstitutions()
|
|
{
|
|
this._publicActions.forEach(a => {
|
|
a.updateSubstitution()
|
|
if (a._extensionAction)
|
|
a._extensionAction.updateSubstitution()
|
|
})
|
|
}
|
|
|
|
public importBindings()
|
|
{
|
|
this.externalKindMapping = {}
|
|
/*
|
|
this.resolveClauses.forEach((c:ResolveClause) => {
|
|
c.kindBindings.forEach((k:KindBinding) => {
|
|
//Util.log("repl: " + k.formalName + " to " + k.actual.toString())
|
|
this.externalKindMapping[LibraryRef.libNamespace(c.name) + k.formalName] = k.actual;
|
|
});
|
|
})
|
|
*/
|
|
this.updateSubstitutions();
|
|
}
|
|
|
|
public getAbstractKind(n:string):Kind
|
|
{
|
|
var k:Kind = this.kindMapping[n];
|
|
if (k) return k;
|
|
k = new LibraryRefAbstractKind(this, n);
|
|
this.defKind(k)
|
|
return k;
|
|
}
|
|
|
|
constructor() {
|
|
super()
|
|
this._kind = new LibraryRefKind(this);
|
|
}
|
|
|
|
public resolve()
|
|
{
|
|
this._hasErrors = false;
|
|
this.isTypechecked = false;
|
|
|
|
if (!this.isTopLevel()) return;
|
|
|
|
if (!this.resolved) {
|
|
this._hasErrors = true;
|
|
return;
|
|
}
|
|
|
|
if (!this.publicActionsCopiedFromResolved) {
|
|
this.resolved.setStableNames();
|
|
this.publicActionsCopiedFromResolved = true;
|
|
this._publicActions.forEach((a:Action) => {
|
|
a.deleted = true;
|
|
});
|
|
this._publicKinds.forEach((k) => { k.kill(); });
|
|
this.kindMapping = {};
|
|
this._publicKinds = [];
|
|
// first collect all the action types
|
|
var tasks = this.resolved.actionTypeDefs()
|
|
.filter((a) => !a.isPrivate)
|
|
.map(a => {
|
|
var la = new LibraryRefAction(this)
|
|
la._isActionTypeDef = true
|
|
la.setName(a.getName())
|
|
this.defKind(la.getDefinedKind())
|
|
return () => { la.fromTemplate(a) }
|
|
})
|
|
|
|
tasks.pushRange(this.resolved.records()
|
|
.filter(r => r.isExported())
|
|
.map(r => {
|
|
var rec = new LibraryRecordDef(this)
|
|
rec.setName(r.getCoreName())
|
|
this.defKind(rec.entryKind)
|
|
return () => { rec.fromTemplate(r) }
|
|
}))
|
|
|
|
// populate the parameters/fields
|
|
tasks.forEach(f => f())
|
|
// and proceed to normal actions which will produce any remaining kinds as abstract
|
|
this._publicActions = this.resolved.actions()
|
|
.filter((a) => !a.isPrivate)
|
|
.map((a) => new LibraryRefAction(this).fromTemplate(a));
|
|
}
|
|
}
|
|
|
|
public substitute(k:Kind) : Kind
|
|
{
|
|
if (k instanceof LibraryRefAbstractKind) {
|
|
var r:Kind = this.externalKindMapping[k.toString()];
|
|
//Util.log("subst: in " + this.getName() + " type " + k.toString() + " to " + r)
|
|
if (r) {
|
|
if ((<any>r).deleted)
|
|
r = r.parentLibrary().getAbstractKind(r.getName())
|
|
return r;
|
|
}
|
|
else return k;
|
|
} else if (k.isUserDefined()) {
|
|
return this.getAbstractKind(k.getName());
|
|
} else if (k instanceof ParametricKind) {
|
|
var pk = <ParametricKind>k;
|
|
return pk.createInstance(pk.parameters.map(kk => this.substitute(kk)));
|
|
} else {
|
|
return k;
|
|
}
|
|
}
|
|
|
|
public rebind()
|
|
{
|
|
this.publicActionsCopiedFromResolved = false;
|
|
}
|
|
|
|
public typeCheck()
|
|
{
|
|
if (this.isTypechecked || this.deleted) return;
|
|
|
|
if (this.isTypechecking) {
|
|
this.setError("TD174: cicular library reference")
|
|
return;
|
|
}
|
|
|
|
if (!this.isTopLevel()) return;
|
|
|
|
if (!this.resolved) {
|
|
this.resolveClauses.forEach((r:ResolveClause) => r.resolveExplicit(this));
|
|
return;
|
|
}
|
|
|
|
this.isTypechecking = true;
|
|
this.clearError();
|
|
|
|
var existing:any = {};
|
|
this.resolveClauses.forEach((r:ResolveClause) => {
|
|
existing[r.name] = r
|
|
});
|
|
var newResolves = this.resolved.libraries().map((l:LibraryRef,ix:number) => {
|
|
var clause:ResolveClause = existing[l.getName()];
|
|
if (!(clause instanceof ResolveClause))
|
|
clause = new ResolveClause(l.getName());
|
|
clause.formalLib = l;
|
|
return clause;
|
|
});
|
|
this.resolveClauses.setChildren(newResolves);
|
|
|
|
var ctx = new UnificationCtx(this);
|
|
this.resolveClauses.forEach((r:ResolveClause) => {
|
|
r.typeCheck(ctx)
|
|
if (r.getError())
|
|
this._hasErrors = true;
|
|
});
|
|
|
|
this.updateExternalKindMapping(ctx);
|
|
|
|
this.isTypechecking = false;
|
|
this.isTypechecked = true;
|
|
}
|
|
|
|
private updateExternalKindMapping(ctx:UnificationCtx)
|
|
{
|
|
var k0 = Object.keys(ctx.mapping)
|
|
var changed = k0.length != Object.keys(this.externalKindMapping).length;
|
|
if (!changed)
|
|
k0.forEach(k => {
|
|
if (ctx.mapping[k] != this.externalKindMapping[k]) changed = true;
|
|
})
|
|
if (changed) {
|
|
this.externalKindMapping = ctx.mapping;
|
|
this.updateSubstitutions();
|
|
}
|
|
}
|
|
|
|
public writeTo(tw:TokenWriter)
|
|
{
|
|
this.writeId(tw)
|
|
tw.keyword("meta").id("import").id(this.getName());
|
|
tw.beginBlock();
|
|
if (this.pubid)
|
|
tw.id("pub").string(this.pubid).nl();
|
|
else
|
|
tw.id("guid").string(this.guid).nl();
|
|
tw.id("usage");
|
|
tw.beginBlock();
|
|
this.getPublicKinds().forEach((k) => {
|
|
if (k instanceof LibraryRefAbstractKind)
|
|
tw.keyword("type").id(k.getName()).nl();
|
|
else if (k instanceof UserActionKind) {
|
|
(<UserActionKind>k).userAction.writeHeader(tw, true)
|
|
tw.nl()
|
|
} else if (k instanceof RecordEntryKind) {
|
|
k.getRecord().writeTo(tw)
|
|
}
|
|
});
|
|
// TODO only dump the used ones
|
|
this.getPublicActions().forEach((a) => {
|
|
a.writeHeader(tw, true);
|
|
});
|
|
tw.nl();
|
|
tw.endBlock();
|
|
this.resolveClauses.forEach((r:ResolveClause) => {
|
|
r.writeId(tw)
|
|
if (!r.defaultLib) return;
|
|
tw.id("resolve").id(r.name).op("=");
|
|
r.defaultLib.writeRef(tw);
|
|
tw.id("with");
|
|
tw.beginBlock();
|
|
r.kindBindings.forEach((k:KindBinding) => {
|
|
if (k.isExplicit) {
|
|
k.writeId(tw)
|
|
tw.keyword("type").id(k.formalName).op("=").kind(this.parent, k.actual).nl();
|
|
}
|
|
});
|
|
r.actionBindings.forEach((a:ActionBinding) => {
|
|
if (a.isExplicit) {
|
|
a.writeId(tw)
|
|
tw.keyword("action").id(a.formalName).op("=");
|
|
a.actualLib.writeRef(tw);
|
|
tw.op("\u2192").id(a.getActualName()).nl();
|
|
}
|
|
});
|
|
tw.endBlock();
|
|
});
|
|
tw.endBlock();
|
|
}
|
|
|
|
public writeRef(tw:TokenWriter) { return tw.op(libSymbol).id(this.getName()); }
|
|
|
|
private defKind(k:Kind)
|
|
{
|
|
this._publicKinds.push(k)
|
|
this.kindMapping[k.getName()] = k
|
|
}
|
|
|
|
static parse(p:Parser)
|
|
{
|
|
var l = p.getLibraryRef(p.parseId());
|
|
l.setStableName(p.consumeLabel())
|
|
|
|
if (l.isDeclared) {
|
|
p.parseBraced(() => { p.shift() })
|
|
return;
|
|
}
|
|
l.isDeclared = true;
|
|
|
|
p.declareLibrary(l);
|
|
p.parseBraced(() => {
|
|
if (p.gotKey("pub")) l.pubid = p.parseString();
|
|
if (p.gotKey("guid")) l.guid = p.parseString();
|
|
if (p.gotKey("usage")) {
|
|
var seenNames:any = {}
|
|
p.parseBraced(() => {
|
|
if (p.gotKw("action")) {
|
|
p.shift();
|
|
var a = new LibraryRefAction(l);
|
|
a.parse(p);
|
|
if (!seenNames.hasOwnProperty(a.getName())) {
|
|
seenNames[a.getName()] = true;
|
|
if (a.isActionTypeDef()) {
|
|
l.defKind(a.getDefinedKind())
|
|
} else {
|
|
l._publicActions.push(a);
|
|
}
|
|
}
|
|
} else if (p.gotId("type")) {
|
|
p.shift();
|
|
//TODO do something with these
|
|
l.listedKinds.push(p.parseId());
|
|
} else if (p.gotKw("table")) {
|
|
p.shift()
|
|
var rec = new LibraryRecordDef(l)
|
|
rec._initalizeFromParser(p)
|
|
l.defKind(rec.entryKind)
|
|
}
|
|
});
|
|
}
|
|
if (p.gotKey("resolve")) {
|
|
var r = new ResolveClause(p.parseId());
|
|
r.setStableName(p.consumeLabel());
|
|
l.resolveClauses.push(r);
|
|
p.skipOp("=");
|
|
r.defaultLib = p.parseLibRef();
|
|
if (p.gotId("with")) p.shift();
|
|
p.parseBraced(() => {
|
|
if (p.gotKw("action")) {
|
|
p.shift();
|
|
var a = new ActionBinding(p.parseId());
|
|
a.setStableName(p.consumeLabel());
|
|
a.isExplicit = true;
|
|
p.skipOp("=");
|
|
a.actualLib = p.parseLibRef();
|
|
p.skipOp("\u2192"); // ->
|
|
a.actualName = p.parseId();
|
|
r.actionBindings.push(a);
|
|
} else if (p.gotId("type")) {
|
|
p.shift();
|
|
var k = new KindBinding(p.parseId());
|
|
k.setStableName(p.consumeLabel());
|
|
k.isExplicit = true;
|
|
p.skipOp("=");
|
|
k.actual = p.parseType();
|
|
r.kindBindings.push(k);
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
// default to an empty published library
|
|
if (!l.getId()) l.pubid = "sixvorgj";
|
|
l.importBindings();
|
|
}
|
|
|
|
// TSBUG remove return type annoation - error given in wrong place
|
|
static topScriptLibrary(par:App):LibraryRef { return new ThisLibraryRef(par); }
|
|
|
|
static fresh()
|
|
{
|
|
var decl = <LibraryRef>AST.Parser.parseDecl("meta import __touch_develop__library__ { }");
|
|
decl.setName(Script.freshName("lib"));
|
|
decl.wasAutoNamed = true;
|
|
decl.pubid = "";
|
|
return <LibraryRef>decl;
|
|
}
|
|
|
|
public initializeResolves()
|
|
{
|
|
var findMatching = (l:LibraryRef) =>
|
|
Script.libraries().filter((q:LibraryRef) => l.getId() == q.getId() ||
|
|
(q.resolved && l.resolved && q.resolved.getName() == l.resolved.getName()))[0];
|
|
|
|
var findMapped = (l:LibraryRef) =>
|
|
l == this.resolved.thisLibRef ? this :
|
|
this.resolveClauses.map((r:ResolveClause) =>
|
|
r.formalLib == l ? r.defaultLib : null).filter((x) => !!x)[0];
|
|
|
|
if (!this.resolved) return [];
|
|
|
|
this.isTypechecked = false;
|
|
this.typeCheck(); // force creation of resolve clauses
|
|
var newLibs = [];
|
|
|
|
this.resolveClauses.forEach((r:ResolveClause) => {
|
|
var lib = findMatching(r.formalLib);
|
|
if (lib) {
|
|
r.defaultLib = lib;
|
|
return;
|
|
}
|
|
|
|
lib = LibraryRef.fresh();
|
|
r.defaultLib = lib;
|
|
newLibs.push(r);
|
|
});
|
|
|
|
newLibs.forEach((r:ResolveClause) => {
|
|
var lib = r.defaultLib;
|
|
Script.addDecl(lib);
|
|
lib.setName(Script.freshName(r.formalLib.getName()));
|
|
lib.pubid = r.formalLib.pubid;
|
|
lib.guid = r.formalLib.guid;
|
|
lib.typeCheck();
|
|
});
|
|
|
|
newLibs.forEach((r:ResolveClause) => {
|
|
var lib = r.defaultLib;
|
|
var copyResolve = (c:ResolveClause,ix:number) =>
|
|
{
|
|
var o = new ResolveClause(c.name);
|
|
o.defaultLib = findMapped(c.defaultLib);
|
|
o.actionBindings.setChildren(c.actionBindings.stmts.map((a:ActionBinding,ix:number) => {
|
|
var ao = new ActionBinding(a.formalName);
|
|
ao.isExplicit = a.isExplicit;
|
|
ao.actualLib = findMapped(a.actualLib);
|
|
ao.actualName = a.actualName;
|
|
return ao;
|
|
}));
|
|
return o;
|
|
}
|
|
lib.resolveClauses.setChildren(r.formalLib.resolveClauses.stmts.map(copyResolve));
|
|
});
|
|
|
|
return newLibs.map((r:ResolveClause) => r.defaultLib);
|
|
}
|
|
|
|
static libNamespace(name:string)
|
|
{
|
|
if (!name) return ""
|
|
return libSymbol + name + "\u200A\u2192\u00A0";
|
|
}
|
|
}
|
|
|
|
|
|
class ThisLibraryRef
|
|
extends LibraryRef
|
|
{
|
|
public isReal() { return true; }
|
|
public getPublicActions()
|
|
{
|
|
return <Action[]>this.resolved.things.filter((a:Decl) => a instanceof Action && !(<Action>a).isPrivate);
|
|
}
|
|
public isThis() { return true; }
|
|
|
|
constructor(p:App) {
|
|
super()
|
|
this.resolved = p;
|
|
this.parent = p;
|
|
this.setName("this");
|
|
this.setStableName("this");
|
|
}
|
|
|
|
public typeCheck()
|
|
{
|
|
this.isTypechecked = true;
|
|
}
|
|
}
|
|
|
|
}
|