initial draft of return, break, and show

This commit is contained in:
Michal Moskal 2015-05-20 09:13:12 -07:00
Родитель e0a1be0007
Коммит 6d1b18393d
12 изменённых файлов: 333 добавлений и 13 удалений

Просмотреть файл

@ -131,6 +131,7 @@ module TDev.AST {
private stableVersions : string[];
public tutorialWarning: string;
public _hint:string;
public _compilerBreakLabel:any;
constructor() {
super()
@ -678,6 +679,97 @@ module TDev.AST {
public forSearch() { return "for do " + this.upperBound.forSearch(); }
}
export class Return
extends Stmt
{
public expr:ExprHolder;
public retLocal:LocalDef;
public action:Stmt;
constructor() {
super()
}
public nodeType() { return "return"; }
public calcNode() { return this.expr; }
public accept(v:NodeVisitor) { return v.visitReturn(this); }
public isExecutableStmt() { return true; }
public allowSimplify() { return true }
public writeTo(tw:TokenWriter)
{
this.writeIdOpt(tw);
tw.keyword("return").node(this.expr);
tw.op0(";").nl();
}
private parseFrom(p:Parser)
{
this.setStableName(p.consumeLabel());
this.expr = p.parseExpr();
p.skipOp(";")
}
public forSearch() { return "return " + this.expr.forSearch(); }
}
export class Show
extends Stmt
{
public expr:ExprHolder;
public postCall:Expr;
constructor() {
super()
}
public nodeType() { return "show"; }
public calcNode() { return this.expr; }
public accept(v:NodeVisitor) { return v.visitShow(this); }
public isExecutableStmt() { return true; }
public allowSimplify() { return true }
public writeTo(tw:TokenWriter)
{
this.writeIdOpt(tw);
tw.keyword("do").id("show").node(this.expr);
tw.op0(";").nl();
}
private parseFrom(p:Parser)
{
this.setStableName(p.consumeLabel());
this.expr = p.parseExpr();
p.skipOp(";")
}
public forSearch() { return "show " + this.expr.forSearch(); }
}
export class Break
extends Stmt
{
constructor() {
super()
}
public loop:Stmt;
public nodeType() { return "break"; }
public accept(v:NodeVisitor) { return v.visitBreak(this); }
public isExecutableStmt() { return true; }
public writeTo(tw:TokenWriter)
{
this.writeIdOpt(tw);
tw.keyword("break").op0(";").nl();
}
private parseFrom(p:Parser)
{
this.setStableName(p.consumeLabel());
p.skipOp(";")
}
public forSearch() { return "break"; }
}
export class Foreach
extends Stmt
{
@ -3725,6 +3817,9 @@ module TDev.AST {
public visitForeach(n:Foreach) { return this.visitStmt(n); }
public visitWhile(n:While) { return this.visitStmt(n); }
public visitBox(n:Box) { return this.visitStmt(n); }
public visitShow(n:Show) { return this.visitStmt(n); }
public visitBreak(n:Break) { return this.visitStmt(n); }
public visitReturn(n:Return) { return this.visitStmt(n); }
public visitAnyIf(n:If) { return this.visitStmt(n); }
public visitIf(n:If) { return this.visitAnyIf(n); }
public visitElseIf(n:If) { return this.visitAnyIf(n); }
@ -5138,6 +5233,9 @@ module TDev.AST {
public visitInlineActions(n:InlineActions) { this.visitChStmt(n); }
public visitActionHeader(n:ActionHeader) { this.visitChStmt(n); }
public visitExprStmt(n:ExprStmt) { this.visitChStmt(n); }
public visitBreak(n:Break) { this.visitChStmt(n); }
public visitReturn(n:Return) { this.visitChStmt(n); }
public visitShow(n:Show) { this.visitChStmt(n); }
public visitActionParameter(n:ActionParameter) { this.withBoundLocal(n, n.local) }

Просмотреть файл

@ -885,6 +885,7 @@ module TDev.AST
this.updateAnalysisInfo(w.calcNode());
this.markLocation(w);
var begLabel = this.allocateLabel();
w._compilerBreakLabel = this.allocateLabel()
this.aux.push(begLabel);
var cond = this.topExpr(w.condition, w);
this.resetAnalysisInfo();
@ -895,9 +896,47 @@ module TDev.AST
ifNode.thenBody.push(JsGoto.simple(begLabel));
ifNode.elseBody = [];
res.push(ifNode);
res.push(w._compilerBreakLabel)
return res;
}
public visitBreak(b:Break)
{
this.markLocation(b);
var res = this.flushAux();
if (b.loop)
res.push(JsGoto.simple(b.loop._compilerBreakLabel))
this.resetAnalysisInfo();
return res
}
public visitReturn(r:Return)
{
this.markLocation(r);
if (r.retLocal) {
this.updateAnalysisInfo(r.expr);
var val = this.topExpr(r.expr, r);
this.resetAnalysisInfo();
}
var res = this.flushAux();
if (r.retLocal)
res.push(this.localVarRef(r.retLocal).gets(val))
res.push(JsGoto.simple(r.action._compilerBreakLabel))
return res
}
public visitShow(s:Show)
{
this.markLocation(s);
this.updateAnalysisInfo(s.expr);
var ex = this.doExpr(s.postCall)
var r = this.flushAux();
if (ex != this.unit)
r.push(new JsExprStmt(ex));
this.resetAnalysisInfo();
return s
}
private compileInlineAction(inl:InlineAction)
{
var a = new Action();
@ -982,6 +1021,7 @@ module TDev.AST
collTmp = this.newTmpVar("coll", this.topExpr(f.collection, f));
}
this.resetAnalysisInfo();
f._compilerBreakLabel = this.allocateLabel()
var idxTmp = this.newTmpVarOK("idx", this.term("0"));
var begLabel = this.allocateLabel();
@ -1028,6 +1068,7 @@ module TDev.AST
ifNode.thenBody.push(JsGoto.simple(begLabel));
ifNode.elseBody = [];
res.push(ifNode);
res.push(f._compilerBreakLabel);
return res;
}
@ -1040,6 +1081,7 @@ module TDev.AST
var idx = this.localVarRef(f.boundLocal);
this.aux.push(idx.gets(this.term("0")));
var begLabel = this.allocateLabel();
f._compilerBreakLabel = this.allocateLabel()
var res = this.flushAux();
res.push(begLabel);
var ifNode = this.allocateIf();
@ -1049,6 +1091,7 @@ module TDev.AST
ifNode.thenBody.push(JsGoto.simple(begLabel));
ifNode.elseBody = [];
res.push(ifNode);
res.push(f._compilerBreakLabel);
return res;
}
@ -2005,6 +2048,7 @@ module TDev.AST
this.wr("function " + this.actionName + "(s ");
a.getInParameters().forEach((l) => { this.wr(", " + this.localVarName(l.local)); });
this.wr(") {\n");
a._compilerBreakLabel = this.allocateLabel();
if (this.throwSyntaxError(a)) {
this.wr("\n }\n");
@ -2025,6 +2069,7 @@ module TDev.AST
});
var jsNodes: JsStmt[] = this.dispatch(a.body);
jsNodes.push(a._compilerBreakLabel)
var prevMax = this.maxArgsToCheck + 1;
this.insertFinalLabels(jsNodes);
jsNodes.forEach((n) => n.forEachExpr((e:JsExpr) => {
@ -2086,6 +2131,7 @@ module TDev.AST
a.getInParameters().forEach((l) => { this.wr(", " + this.localVarName(l.local)); });
var lab0 = this.allocateLabel();
this.wr(") {\n")
a._compilerBreakLabel = this.allocateLabel();
if (!this.throwSyntaxError(a)) {
this.wr(
@ -2144,6 +2190,7 @@ module TDev.AST
jsNodes.unshift(lab0);
lab0.refs.push(lab0);
jsNodes.push(a._compilerBreakLabel);
var prevMax = this.maxArgsToCheck + 1;
this.insertFinalLabels(jsNodes);
jsNodes.forEach((n) => n.forEachExpr((e:JsExpr) => {

Просмотреть файл

@ -73,6 +73,18 @@ module TDev.AST {
public visitForeach(stmt: Foreach) { return this.visitLoop(stmt); }
public visitWhile(stmt: While) { return this.visitLoop(stmt); }
public visitBreak(stmt: Break) {
return this.nextInBlock(stmt); // TODO wrong
}
public visitReturn(stmt: Return) {
return this.nextInBlock(stmt); // TODO wrong
}
public visitShow(stmt: Show) {
return this.nextInBlock(stmt);
}
public visitExprStmt(stmt: ExprStmt) {
return this.nextInBlock(stmt);
}
@ -987,6 +999,15 @@ module TDev.AST {
visitExprStmt(n: ExprStmt) {
this.visitExprHolderHolder(n);
}
visitBreak(n: Break) {
this.visitExprHolderHolder(n);
}
visitReturn(n: Return) {
this.visitExprHolderHolder(n);
}
visitShow(n: Show) {
this.visitExprHolderHolder(n);
}
// Non-recursive version of a topological sort to order our first
// visit to the Action's nodes. Recursive versions are simpler
@ -2253,6 +2274,14 @@ module TDev.AST {
this.nowVisiting.canInline = false;
this.visitChildren(n);
}
visitBreak(n: Break) {
this.nowVisiting.canInline = false;
this.visitChildren(n);
}
visitReturn(n: Return) {
this.nowVisiting.canInline = false;
this.visitChildren(n);
}
// Build a new edge of the callgraph if calling another action
visitCall(n: Call) {

Просмотреть файл

@ -613,6 +613,22 @@ module TDev.AST.Json
}
}
public visitShow(n:Show) {
return {
expr: n.expr,
}
}
public visitReturn(n:Return) {
return {
expr: n.expr,
}
}
public visitBreak(n:Break) {
return { }
}
public visitBox(n:Box) {
return { body: n.body }
}
@ -1431,6 +1447,25 @@ module TDev.AST.Json
block(n.body);
},
"break": (n:JBreak) => {
stmt(n)
tw.keyword("break").op0(";").nl();
},
"return": (n:JReturn) => {
stmt(n)
tw.keyword("return");
selfEh(n, n.expr);
tw.op0(";").nl();
},
"show": (n:JShow) => {
stmt(n)
tw.keyword("do").id("show");
selfEh(n, n.expr);
tw.op0(";").nl();
},
"if": (n:JIf) => {
stmt(n)
tw.keyword("if");

Просмотреть файл

@ -191,6 +191,10 @@ module TDev.AST.Json
body:JStmt[];
}
export interface JBreak extends JStmt {}
export interface JReturn extends JStmt { expr: JExprHolder; }
export interface JShow extends JStmt { expr: JExprHolder; }
// Sequences of if / else if / else statements are not represented the usual
// way. That is, instead of having a structured AST:
//

Просмотреть файл

@ -645,6 +645,14 @@ module TDev.AST {
result = new While();
(<While>result).body = <CodeBlock>getNewChild(0); // TODO - check cast?
(<While>result).condition = mergeExprHolder(x => (<While>x).condition);
} else if(test(Return)) {
result = new Return();
(<Return>result).expr = mergeExprHolder(x => (<Return>x).expr);
} else if(test(Show)) {
result = new Show();
(<Show>result).expr = mergeExprHolder(x => (<Show>x).expr);
} else if(test(Break)) {
result = new Break();
} else if(test(OptionalParameter)) {
result = new OptionalParameter();
(<OptionalParameter>result)._opt_name = getChange(x => (<OptionalParameter>x).getName());

Просмотреть файл

@ -978,8 +978,10 @@ module TDev { export module AST {
var node;
if (id == "box")
node = Box;
else if (id == "show")
node = Show;
else {
this.error("expecting 'box' here");
this.error("expecting 'box' or 'show' here");
return;
}

Просмотреть файл

@ -612,6 +612,23 @@ module TDev
this.endKeyword("while"));
}
public visitBreak(n:AST.Break)
{
return this.stmt(n, this.tline(this.kw("break")) + this.possibleError(n))
}
public visitReturn(n:AST.Return)
{
return this.stmt(n, this.tline(this.kw("return") + this.expr(n.expr))
+ this.possibleError(n))
}
public visitShow(n:AST.Show)
{
return this.stmt(n, this.tline(this.kw("show") + this.expr(n.expr))
+ this.possibleError(n))
}
public visitBox(n:AST.Box)
{
return this.stmt(n,

Просмотреть файл

@ -2,7 +2,7 @@
// TODO events and async
// Next available error: TD200:
// Next available error: TD205:
module TDev.AST
{
@ -200,18 +200,21 @@ module TDev.AST
public topApp:App;
private currentAction:Action;
private currentAnyAction:Stmt;
private seenAwait = false;
private numFixes = 0;
private nothingLocals:LocalDef[] = [];
private localScopes: LocalDef[][] = [[]];
private readOnlyLocals:LocalDef[] = [];
private writtenLocals:LocalDef[] = [];
private currLoop:Stmt;
private inAtomic = false;
private actionSection = ActionSection.Normal;
private pageInit:Block;
private pageDisplay:Block;
private saveFixes = 0;
private outLocals:LocalDef[] = [];
private allLocals :LocalDef[] = [];
private recentErrors: string[] = [];
@ -295,6 +298,7 @@ module TDev.AST
case "while": return lf("TD129: 'while' condition wants {1:a}", whoExpects, tp);
case "for": return lf("TD130: bound of 'for' wants {1:a}", whoExpects, tp);
case "optional": return lf("TD186: this optional parameter wants {1:a}", whoExpects, tp);
case "return": return lf("TD204: 'return' value wants {1:a}", whoExpects, tp);
default: Util.die()
}
}
@ -562,10 +566,12 @@ module TDev.AST
private conditionalScope(f: () => any)
{
var prevWritten = this.writtenLocals.slice(0);
var prevLoop = this.currLoop
try {
return this.scope(f);
} finally {
this.writtenLocals = prevWritten;
this.currLoop = prevLoop
}
}
@ -642,10 +648,52 @@ module TDev.AST
this.updateStmtUsage(node);
this.expect(node.condition, this.core.Boolean, 'while');
this.conditionalScope(() => {
this.currLoop = node;
this.typeCheck(node.body);
});
}
public visitBreak(node:Break)
{
this.updateStmtUsage(node);
node.loop = this.currLoop
if (!node.loop)
this.setNodeError(node, lf("TD200: 'break' can be only used inside a loop"))
}
public visitShow(node:Show)
{
this.updateStmtUsage(node);
this.expect(node.expr, null, "show")
var tp = node.expr.getKind()
if (tp == api.core.Unknown) return
var show = tp.getProperty("post to wall")
node.postCall = null;
if (!show)
this.setNodeError(node, lf("TD201: we don't know how to display {0}", tp.toString()))
else
node.postCall = mkFakeCall(PropertyRef.mkProp(show), [node.expr.parsed])
}
public visitReturn(node:Return)
{
this.updateStmtUsage(node);
node.retLocal = null;
if (!node.expr.isPlaceholder()) {
if (this.outLocals.length == 0)
this.setNodeError(node, lf("TD202: the function doesn't have output parameters; return with value is not allowed"))
else if (this.outLocals.length > 1)
this.setNodeError(node, lf("TD203: the function has more than one output parameter; return with value is not allowed"))
else {
node.retLocal = this.outLocals[0]
this.expect(node.expr, node.retLocal.getKind(), "return")
this.recordLocalWrite(node.retLocal)
}
}
node.action = this.currentAnyAction;
this.checkAssignment(node)
}
public visitActionParameter(node:ActionParameter)
{
}
@ -711,6 +759,7 @@ module TDev.AST
node.boundLocal._kind = this.core.Number;
this.readOnlyLocals.push(node.boundLocal);
this.conditionalScope(() => {
this.currLoop = node;
this.declareLocal(node.boundLocal);
this.typeCheck(node.body);
});
@ -738,6 +787,7 @@ module TDev.AST
node.boundLocal._kind = ek;
this.readOnlyLocals.push(node.boundLocal);
this.conditionalScope(() => {
this.currLoop = node;
this.declareLocal(node.boundLocal);
this.typeCheck(node.conditions);
this.typeCheck(node.body);
@ -761,6 +811,7 @@ module TDev.AST
this.readOnlyLocals = [];
this.allLocals = [];
this.currentAction = node;
this.currentAnyAction = node;
node.clearError();
this.actionSection = ActionSection.Normal;
this.inAtomic = node.isAtomic;
@ -768,6 +819,7 @@ module TDev.AST
this.scope(() => {
// TODO in - read-only?
var prevErr = this.errorCount;
this.outLocals = node.getOutParameters().map((p) => p.local)
this.typeResolver.visitAction(node);
@ -791,7 +843,7 @@ module TDev.AST
this.setNodeError(node, lf("TD171: currently action types support at most one output parameter; sorry"))
}
} else
this.checkAssignment(node, node.getOutParameters().map((p) => p.local));
this.checkAssignment(node);
node._hasErrors = this.errorCount > prevErr;
node.allLocals = this.allLocals;
});
@ -1049,9 +1101,9 @@ module TDev.AST
this.updateStmtUsage(expr, "var");
}
private checkAssignment(node:Stmt, vars:LocalDef[])
private checkAssignment(node:Stmt)
{
var unassigned = vars.filter((v) => this.writtenLocals.indexOf(v) < 0);
var unassigned = this.outLocals.filter((v) => this.writtenLocals.indexOf(v) < 0);
if (unassigned.length > 0) {
node.addHint(
lf("parameter{0:s} {1} may be unassigned before the action finishes",
@ -1067,6 +1119,8 @@ module TDev.AST
var prevWritten = this.writtenLocals;
var prevSect = this.actionSection;
var prevAtomic = this.inAtomic;
var prevOut = this.outLocals;
var prevAct = this.currentAnyAction;
this.writtenLocals = [];
@ -1081,6 +1135,8 @@ module TDev.AST
this.readOnlyLocals = prevReadOnly;
this.inAtomic = prevAtomic;
this.actionSection = prevSect;
this.outLocals = prevOut;
this.currentAnyAction = prevAct;
}
})
}
@ -1089,10 +1145,12 @@ module TDev.AST
private typeCheckInlineAction(inl:InlineAction)
{
this.actionScope(inl.name.getKind(), () => {
this.currentAnyAction = inl;
inl.inParameters.forEach((d) => this.declareLocal(d));
inl.outParameters.forEach((d) => this.declareLocal(d));
this.outLocals = inl.outParameters.slice(0);
this.typeCheck(inl.body);
this.checkAssignment(inl, inl.outParameters);
this.checkAssignment(inl);
})
}
@ -1399,6 +1457,12 @@ module TDev.AST
t._kind = ak || this.core.Unknown
}
private recordLocalWrite(loc:LocalDef)
{
if (this.writtenLocals.indexOf(loc) < 0)
this.writtenLocals.push(loc);
}
private handleAssignment(t:Call, args:Expr[])
{
t._kind = this.core.Nothing;
@ -1466,8 +1530,7 @@ module TDev.AST
else
this.markError(trg, lf("TD108: you cannot assign to the local variable '{0}'", name));
} else {
if (this.writtenLocals.indexOf(loc) < 0)
this.writtenLocals.push(loc);
this.recordLocalWrite(loc)
}
}
this.typeCheckExpr(trg);
@ -1586,11 +1649,7 @@ module TDev.AST
case "javascript":
case "javascript async":
if (!checkArgumentCount(3)) return;
this.currentAction.getOutParameters().forEach(p => {
var loc = p.local
if (this.writtenLocals.indexOf(loc) < 0)
this.writtenLocals.push(loc);
})
this.currentAction.getOutParameters().forEach(p => this.recordLocalWrite(p.local))
this.lintJavaScript(t.args[2].getStringLiteral(), /async/.test(t.prop().getName()))
break;
case "import":

Просмотреть файл

@ -134,6 +134,9 @@ module TDev.Browser {
comment: true,
foreach: true,
boxed: true,
show: true,
"return": true,
"break": true,
stringConcatProperty: true,
// hub
scriptAddToChannel: true,

Просмотреть файл

@ -74,6 +74,21 @@ module TDev
node: "do box { }",
widget: "boxed"
},
{
name: "show", desc: lf("show value on the screen"), tick: Ticks.codeShow,
node: "do show \\u0001need_String\\u003Awhat_to_show ;",
widget: "show"
},
{
name: "break", desc: lf("stop a loop"), tick: Ticks.codeBreak,
node: "break;",
widget: "break"
},
{
name: "return", desc: lf("stop a function"), tick: Ticks.codeReturn,
node: "return ... ;",
widget: "return"
},
].filter(btn => !btn.widget || TheEditor.widgetEnabled(btn.widget));
}

Просмотреть файл

@ -135,6 +135,9 @@ module TDev {
codeAddAbove,
codeAddBelow,
codeShow,
codeReturn,
codeBreak,
codeBoxed,
codeCopy,
codeCopySelection,