TouchDevelop/ast/features.ts

137 строки
4.1 KiB
TypeScript

///<reference path='refs.ts'/>
module TDev.AST {
export class FeatureDetector
extends NodeVisitor
{
public features:StringMap<number> = { script: 1 }
public stmtCount = 0;
public libroots:(v:string)=>string;
public includeCaps = false;
public anonBrowser = true;
use(feature:string)
{
if (!feature) return;
var n = this.features[feature] || 0;
this.features[feature] = n + 1
}
visitAstNode(n:AstNode)
{
super.visitChildren(n);
}
visitExprHolder(eh:ExprHolder)
{
super.visitExprHolder(eh);
if (eh.parsed) this.dispatch(eh.parsed)
}
visitCall(c:Call)
{
super.visitCall(c);
var p = c.prop()
if (this.libroots) {
var act = c.calledExtensionAction()
if (!act) act = c.calledAction()
if (act instanceof LibraryRefAction) {
var lib = this.libroots(act.parentLibrary().getId())
if (lib) {
this.use("l:" + lib + ":" + act.getName())
}
}
}
if (c.referencedRecordField() || c.referencedData() || c.calledExtensionAction()) {
} else if (p.forwardsTo() || p.forwardsToStmt()) {
} else if (p == api.core.AssignmentProp) {
this.use("assignment");
} else if (p == api.core.TupleProp) {
//TODO need help
//this.use("actionsWithManyReturnValues");
} else if (p.parentKind instanceof RecordEntryKind || p.parentKind instanceof RecordDefKind) {
} else if (p) {
this.use(p.helpTopic())
if (p.parentKind)
this.use(p.parentKind.helpTopic())
var cap = p.getCapability()
if (cap & ~(PlatformCapability.Accelerometer|PlatformCapability.MusicAndSounds))
this.anonBrowser = false
if (this.includeCaps) {
if (cap)
App.capabilityString(p.getCapability()).split(/,/).forEach(k => {
this.use(Util.tagify("cap " + k))
})
}
}
}
visitWhere(w:Where)
{
super.visitStmt(w);
if (w.condition.tokens.length > 1)
this.use("where")
}
visitStmt(s:Stmt)
{
super.visitStmt(s);
if (s instanceof Block) return;
if (s instanceof ActionHeader) return;
if (!s.isPlaceholder())
this.stmtCount++;
this.use(s.helpTopic())
}
visitDecl(d:Decl)
{
super.visitChildren(d);
this.use(d.helpTopic())
}
visitApp(a:App)
{
super.visitApp(a);
if (a.isLibrary) this.use("library");
if (a.isCloud) this.use("cloud");
if (this.includeCaps && this.anonBrowser)
this.use("anonBrowser")
}
static bucketId(features:StringMap<number>)
{
var f = Util.clone(features)
delete f['assignment']
delete f['commands']
delete f['var']
var kk = Object.keys(f)
kk.sort(Util.stringCompare)
return kk.map(k => k + ":" + f[k]).join(",")
}
static astInfo(a:App, libroots:(v:string)=>string = null, flags:StringMap<string> = null)
{
var fd = new FeatureDetector()
fd.libroots = libroots
if (!flags) flags = {}
if (Runtime.stringToBoolean(flags["caps"]))
fd.includeCaps = true
fd.dispatch(a)
var b = FeatureDetector.bucketId(fd.features)
fd.features['anystmt'] = fd.stmtCount
fd.features['anycode'] = a.allActions().length
return {
features: fd.features,
bucketId: b,
}
}
}
}