2015-04-26 18:10:04 +03:00
|
|
|
///<reference path='refs.ts'/>
|
2015-04-13 23:24:20 +03:00
|
|
|
|
|
|
|
module TDev {
|
2015-04-14 02:59:52 +03:00
|
|
|
|
2015-04-26 18:10:04 +03:00
|
|
|
export module Embedded {
|
2015-04-14 02:59:52 +03:00
|
|
|
|
2015-04-13 23:24:20 +03:00
|
|
|
import J = AST.Json
|
2015-04-15 01:26:39 +03:00
|
|
|
import H = Helpers
|
2015-04-13 23:24:20 +03:00
|
|
|
|
2015-04-18 03:01:20 +03:00
|
|
|
// --- Environments
|
|
|
|
|
2015-05-28 02:09:39 +03:00
|
|
|
export interface EmitterEnv extends H.Env {
|
2015-04-14 02:59:52 +03:00
|
|
|
indent: string;
|
|
|
|
}
|
|
|
|
|
2015-06-03 00:20:12 +03:00
|
|
|
export function emptyEnv(): EmitterEnv {
|
|
|
|
return {
|
|
|
|
indent: "",
|
|
|
|
ident_of_id: {},
|
|
|
|
id_of_ident: {},
|
|
|
|
};
|
|
|
|
}
|
2015-04-14 02:59:52 +03:00
|
|
|
|
2015-05-26 21:04:39 +03:00
|
|
|
export function indent(e: EmitterEnv) {
|
2015-04-15 01:26:39 +03:00
|
|
|
return {
|
|
|
|
indent: e.indent + " ",
|
2015-05-28 02:09:39 +03:00
|
|
|
ident_of_id: e.ident_of_id,
|
|
|
|
id_of_ident: e.id_of_ident,
|
2015-04-15 01:26:39 +03:00
|
|
|
};
|
2015-04-14 02:59:52 +03:00
|
|
|
}
|
|
|
|
|
2015-04-18 03:01:20 +03:00
|
|
|
|
|
|
|
// --- The code emitter.
|
|
|
|
|
2015-04-15 01:26:39 +03:00
|
|
|
export class Emitter extends JsonAstVisitor<EmitterEnv, string> {
|
2015-04-14 02:59:52 +03:00
|
|
|
|
2015-04-17 03:45:19 +03:00
|
|
|
// Output "parameters", written to at the end.
|
2015-04-17 03:15:34 +03:00
|
|
|
public prototypes = "";
|
|
|
|
public code = "";
|
2015-04-28 02:25:15 +03:00
|
|
|
public prelude = "";
|
2015-04-17 03:15:34 +03:00
|
|
|
|
2015-05-26 21:04:39 +03:00
|
|
|
private libraryMap: H.LibMap = {};
|
2015-05-22 01:32:12 +03:00
|
|
|
|
2015-05-28 20:45:40 +03:00
|
|
|
private imageLiterals = [];
|
|
|
|
|
2015-04-17 03:45:19 +03:00
|
|
|
// All the libraries needed to compile this [JApp].
|
|
|
|
constructor(
|
2015-04-18 03:01:20 +03:00
|
|
|
private libRef: J.JCall,
|
2015-05-26 21:04:39 +03:00
|
|
|
public libName: string,
|
2015-04-17 03:45:19 +03:00
|
|
|
private libs: J.JApp[]
|
|
|
|
) {
|
|
|
|
super();
|
|
|
|
}
|
|
|
|
|
2015-04-15 01:26:39 +03:00
|
|
|
public visitMany(e: EmitterEnv, ss: J.JNode[]) {
|
2015-04-14 02:59:52 +03:00
|
|
|
var code = [];
|
2015-04-15 01:26:39 +03:00
|
|
|
ss.forEach((s: J.JNode) => { code.push(this.visit(e, s)) });
|
|
|
|
return code.join("\n");
|
2015-04-14 02:59:52 +03:00
|
|
|
}
|
|
|
|
|
2015-04-17 03:15:34 +03:00
|
|
|
public visitComment(env: EmitterEnv, c: string) {
|
2015-05-26 21:04:39 +03:00
|
|
|
return env.indent+"// "+c.replace("\n", "\n"+env.indent+"// ");
|
2015-04-17 03:15:34 +03:00
|
|
|
}
|
|
|
|
|
2015-05-20 23:03:32 +03:00
|
|
|
public visitBreak(env: EmitterEnv) {
|
|
|
|
return env.indent + "break;";
|
|
|
|
}
|
|
|
|
|
2015-05-20 23:54:40 +03:00
|
|
|
public visitContinue(env: EmitterEnv) {
|
|
|
|
return env.indent + "continue;";
|
|
|
|
}
|
|
|
|
|
2015-05-20 23:03:32 +03:00
|
|
|
public visitShow(env: EmitterEnv, expr: J.JExpr) {
|
|
|
|
// TODO hook this up to "post to wall" handling if any
|
|
|
|
return env.indent + "serial.print(" + this.visit(env, expr) + ");";
|
|
|
|
}
|
|
|
|
|
|
|
|
public visitReturn(env: EmitterEnv, expr: J.JExpr) {
|
|
|
|
return env.indent + H.mkReturn(this.visit(env, expr));
|
|
|
|
}
|
|
|
|
|
2015-04-15 02:48:24 +03:00
|
|
|
public visitExprStmt(env: EmitterEnv, expr: J.JExpr) {
|
|
|
|
return env.indent + this.visit(env, expr)+";";
|
|
|
|
}
|
|
|
|
|
2015-05-26 21:58:02 +03:00
|
|
|
public visitExprHolder(env: EmitterEnv, locals: J.JLocalDef[], expr: J.JExprHolder) {
|
2015-05-30 01:58:29 +03:00
|
|
|
var decls = locals.map(d => {
|
2015-06-05 00:10:39 +03:00
|
|
|
var x = H.defaultValueForType(this.libraryMap, d.type);
|
2015-05-30 01:58:29 +03:00
|
|
|
return this.visit(env, d) + (x ? " = " + x : "") + ";";
|
|
|
|
});
|
2015-05-26 21:58:02 +03:00
|
|
|
return decls.join("\n"+env.indent) +
|
|
|
|
(decls.length ? "\n" + env.indent : "") +
|
|
|
|
this.visit(env, expr);
|
2015-04-15 02:48:24 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
public visitLocalRef(env: EmitterEnv, name: string, id: string) {
|
2015-04-15 20:29:05 +03:00
|
|
|
// In our translation, referring to a TouchDevelop identifier never
|
|
|
|
// requires adding a reference operator (&). Things passed by reference
|
|
|
|
// are either:
|
|
|
|
// - function pointers (a.k.a. "handlers" in TouchDevelop lingo), for
|
|
|
|
// which C and C++ accept both "f" and "&f" (we hence use the former)
|
|
|
|
// - arrays, strings, user-defined objects, which are in fact of type
|
|
|
|
// "shared_ptr<T>", no "&" operator here.
|
2015-06-05 01:04:09 +03:00
|
|
|
return H.mangleUnique(env, name, id);
|
2015-04-15 02:48:24 +03:00
|
|
|
}
|
|
|
|
|
2015-04-15 03:38:18 +03:00
|
|
|
public visitLocalDef(env: EmitterEnv, name: string, id: string, type: J.JTypeRef) {
|
2015-06-05 01:04:09 +03:00
|
|
|
return H.mkType(env, this.libraryMap, type)+" "+H.mangleUnique(env, name, id);
|
2015-04-15 03:38:18 +03:00
|
|
|
}
|
|
|
|
|
2015-05-28 00:10:25 +03:00
|
|
|
// Allows the target to redefine their own string type.
|
2015-04-15 02:48:24 +03:00
|
|
|
public visitStringLiteral(env: EmitterEnv, s: string) {
|
2015-05-28 00:10:25 +03:00
|
|
|
return 'touch_develop::mk_string("'+s.replace(/["\\\n]/g, c => {
|
2015-04-24 15:21:28 +03:00
|
|
|
if (c == '"') return '\\"';
|
2015-04-15 02:48:24 +03:00
|
|
|
if (c == '\\') return '\\\\';
|
2015-05-20 00:22:29 +03:00
|
|
|
if (c == "\n") return '\\n';
|
2015-05-28 00:10:25 +03:00
|
|
|
}) + '")';
|
2015-04-15 02:48:24 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
public visitNumberLiteral(env: EmitterEnv, n: number) {
|
|
|
|
return n+"";
|
|
|
|
}
|
|
|
|
|
|
|
|
public visitBooleanLiteral(env: EmitterEnv, b: boolean) {
|
|
|
|
return b+"";
|
|
|
|
}
|
|
|
|
|
|
|
|
public visitWhile(env: EmitterEnv, cond: J.JExprHolder, body: J.JStmt[]) {
|
|
|
|
var condCode = this.visit(env, cond);
|
|
|
|
var bodyCode = this.visitMany(indent(env), body);
|
|
|
|
return env.indent + "while ("+condCode+") {\n" + bodyCode + "\n" + env.indent + "}";
|
|
|
|
}
|
|
|
|
|
2015-04-16 00:01:02 +03:00
|
|
|
public visitFor(env: EmitterEnv, index: J.JLocalDef, bound: J.JExprHolder, body: J.JStmt[]) {
|
2015-06-03 00:20:12 +03:00
|
|
|
var indexCode = this.visit(env, index) + " = 0";
|
|
|
|
var testCode = H.mangleDef(env, index) + " < " + this.visit(env, bound);
|
2015-05-28 02:09:39 +03:00
|
|
|
var incrCode = "++"+H.mangleDef(env, index);
|
2015-04-16 00:01:02 +03:00
|
|
|
var bodyCode = this.visitMany(indent(env), body);
|
|
|
|
return (
|
|
|
|
env.indent + "for ("+indexCode+"; "+testCode+"; "+incrCode+") {\n" +
|
|
|
|
bodyCode + "\n" +
|
|
|
|
env.indent + "}"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
public visitIf(
|
|
|
|
env: EmitterEnv,
|
|
|
|
cond: J.JExprHolder,
|
|
|
|
thenBranch: J.JStmt[],
|
|
|
|
elseBranch: J.JStmt[],
|
|
|
|
isElseIf: boolean)
|
|
|
|
{
|
2015-06-26 19:16:16 +03:00
|
|
|
var isIfFalse = cond.tree.nodeType == "booleanLiteral" && (<J.JBooleanLiteral> cond.tree).value === false;
|
2015-06-24 03:07:52 +03:00
|
|
|
// TouchDevelop abuses "if false" to comment out code. Commented out
|
2015-06-26 19:16:16 +03:00
|
|
|
// code is not type-checked, so don't try to compile it. However, an
|
|
|
|
// "if false" followed by an "else" is *not* understood to be a comment.
|
|
|
|
return [
|
|
|
|
env.indent, isElseIf ? "else " : "", "if (" + this.visit(env, cond) + "){\n",
|
|
|
|
isIfFalse ? "" : this.visitMany(indent(env), thenBranch) + "\n",
|
|
|
|
env.indent, "}",
|
|
|
|
elseBranch ? " else {\n" : "",
|
|
|
|
elseBranch ? this.visitMany(indent(env), elseBranch) + "\n" : "",
|
|
|
|
elseBranch ? env.indent + "}" : ""
|
|
|
|
].join("");
|
2015-04-16 00:01:02 +03:00
|
|
|
}
|
|
|
|
|
2015-06-05 01:04:09 +03:00
|
|
|
private resolveCall(env: EmitterEnv, receiver: J.JExpr, name: string) {
|
2015-05-21 23:57:40 +03:00
|
|
|
if (!receiver)
|
|
|
|
return null;
|
|
|
|
|
2015-04-18 03:01:20 +03:00
|
|
|
// Is this a call in the current scope?
|
2015-05-19 03:26:24 +03:00
|
|
|
var scoped = H.isScopedCall(receiver);
|
2015-04-18 03:01:20 +03:00
|
|
|
if (scoped)
|
2015-05-28 00:10:25 +03:00
|
|
|
if (this.libRef)
|
|
|
|
// If compiling a library, no scope actually means the library's
|
|
|
|
// scope. This step is required to possibly find a shim. This means
|
|
|
|
// that we may generate "lib::f()" where we could've just written
|
|
|
|
// "f()", but since the prototypes have been written out already,
|
|
|
|
// that's fine.
|
2015-06-05 01:04:09 +03:00
|
|
|
return this.resolveCall(env, this.libRef, name);
|
2015-05-28 00:10:25 +03:00
|
|
|
else
|
|
|
|
// Call to a function from the current script.
|
|
|
|
return H.mangleName(name);
|
|
|
|
|
2015-04-18 03:01:20 +03:00
|
|
|
|
|
|
|
// Is this a call to a library?
|
2015-05-19 03:26:24 +03:00
|
|
|
var n = H.isLibrary(receiver);
|
2015-04-18 03:01:20 +03:00
|
|
|
if (n) {
|
|
|
|
// I expect all libraries and all library calls to be properly resolved.
|
|
|
|
var lib = this.libs.filter(l => l.name == n)[0];
|
|
|
|
var action = lib.decls.filter((d: J.JDecl) => d.name == name)[0];
|
2015-05-19 03:26:24 +03:00
|
|
|
var s = H.isShimBody((<J.JAction> action).body);
|
2015-06-09 01:28:42 +03:00
|
|
|
if (s != null) {
|
2015-04-18 03:01:20 +03:00
|
|
|
// Call to a built-in C++ function
|
2015-06-09 01:28:42 +03:00
|
|
|
if (!s.length)
|
|
|
|
throw new Error("Library author: some (compiled) function is trying to call "+name+" "+
|
|
|
|
"which is marked as {shim:}, i.e. for simulator purposes only.\n\n"+
|
|
|
|
"Hint: break on exceptions in the debugger and walk up the call stack to "+
|
|
|
|
"figure out which action it is.");
|
2015-04-18 03:01:20 +03:00
|
|
|
return s;
|
2015-06-09 01:28:42 +03:00
|
|
|
} else {
|
2015-04-24 15:21:28 +03:00
|
|
|
// Actual call to a library function
|
2015-06-05 01:04:09 +03:00
|
|
|
return H.manglePrefixedName(env, n, name);
|
2015-06-09 01:28:42 +03:00
|
|
|
}
|
2015-04-17 03:45:19 +03:00
|
|
|
}
|
2015-04-18 03:01:20 +03:00
|
|
|
|
|
|
|
return null;
|
2015-04-17 03:45:19 +03:00
|
|
|
}
|
|
|
|
|
2015-05-28 20:45:40 +03:00
|
|
|
// Some conversions cannot be expressed using the simple "enums" feature
|
|
|
|
// (which maps a string literal to a constant). This function transforms
|
|
|
|
// the arguments for some known specific C++ functions.
|
2015-06-10 19:25:40 +03:00
|
|
|
private specialTreatment(e: EmitterEnv, f: string, actualArgs: J.JExpr[]) {
|
|
|
|
if (f == "micro_bit::createImage" || f == "micro_bit::showAnimation") {
|
2015-05-28 20:45:40 +03:00
|
|
|
var x = H.isStringLiteral(actualArgs[0]);
|
2015-06-16 01:47:41 +03:00
|
|
|
if (x === "")
|
|
|
|
x = "0 0 0 0 0\n0 0 0 0 0\n0 0 0 0 0\n0 0 0 0 0\n0 0 0 0 0\n";
|
|
|
|
if (!x)
|
2015-06-12 20:30:54 +03:00
|
|
|
throw new Error("create image / show animation takes a string literal only");
|
2015-05-28 20:45:40 +03:00
|
|
|
var r = "literals::bitmap"+this.imageLiterals.length;
|
2015-06-10 19:25:40 +03:00
|
|
|
var otherArgs = actualArgs.splice(1).map((x: J.JExpr) => this.visit(e, x));
|
|
|
|
var code = f+"("+r+"_w, "+r+"_h, "+r+
|
|
|
|
(otherArgs.length ? ", "+otherArgs : "")+
|
|
|
|
")";
|
2015-05-28 20:45:40 +03:00
|
|
|
this.imageLiterals.push(x);
|
|
|
|
return code;
|
|
|
|
} else {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-21 23:57:40 +03:00
|
|
|
public visitCall(env: EmitterEnv,
|
|
|
|
name: string,
|
|
|
|
args: J.JExpr[],
|
|
|
|
parent: J.JTypeRef,
|
2015-05-22 00:59:49 +03:00
|
|
|
callType: string)
|
2015-05-21 23:57:40 +03:00
|
|
|
{
|
|
|
|
var mkCall = (f: string, skipReceiver: boolean) => {
|
|
|
|
var actualArgs = skipReceiver ? args.slice(1) : args;
|
2015-06-10 19:25:40 +03:00
|
|
|
var s = this.specialTreatment(env, f, actualArgs);
|
2015-05-28 20:45:40 +03:00
|
|
|
if (s)
|
|
|
|
return s;
|
|
|
|
else {
|
|
|
|
var argsCode =
|
|
|
|
actualArgs.map(a => {
|
|
|
|
var k = H.isEnumLiteral(a);
|
|
|
|
if (k)
|
|
|
|
return k+"";
|
|
|
|
else
|
|
|
|
return this.visit(env, a)
|
|
|
|
});
|
|
|
|
return f + "(" + argsCode.join(", ") + ")";
|
|
|
|
}
|
2015-04-15 03:38:18 +03:00
|
|
|
};
|
|
|
|
|
2015-05-21 23:57:40 +03:00
|
|
|
// The [JCall] node has several, different, often unrelated purposes.
|
|
|
|
// This function identifies (tentatively) the different cases and
|
|
|
|
// compiles each one of them into something that makes sense.
|
|
|
|
|
|
|
|
// 1) A call to a function, either in the current scope, or belonging to
|
|
|
|
// a TouchDevelop library. Resolves to a C++ function call.
|
2015-06-05 01:04:09 +03:00
|
|
|
var resolvedName = this.resolveCall(env, args[0], name);
|
2015-04-17 03:45:19 +03:00
|
|
|
if (resolvedName)
|
2015-05-21 23:57:40 +03:00
|
|
|
return mkCall(resolvedName, true);
|
|
|
|
|
|
|
|
// 2) A call to the assignment operator on the receiver. C++ assignment.
|
2015-04-17 03:45:19 +03:00
|
|
|
else if (name == ":=")
|
2015-05-21 23:57:40 +03:00
|
|
|
return this.visit(env, args[0]) + " = " + this.visit(env, args[1]);
|
|
|
|
|
2015-05-28 02:09:39 +03:00
|
|
|
// 3) Reference to a variable in the global scope.
|
2015-05-21 23:57:40 +03:00
|
|
|
else if (args.length && H.isSingletonRef(args[0]) == "data")
|
2015-06-05 01:04:09 +03:00
|
|
|
return H.manglePrefixedName(env, "globals", name);
|
2015-05-21 23:57:40 +03:00
|
|
|
|
2015-05-22 01:32:12 +03:00
|
|
|
// 4) Extension method, where p(x) is represented as x→ p. In case we're
|
|
|
|
// actually referencing a function from a library, go through
|
|
|
|
// [resolveCall] again, so that we find the shim if any.
|
|
|
|
else if (callType == "extension") {
|
2015-05-26 21:04:39 +03:00
|
|
|
var t = H.resolveTypeRef(this.libraryMap, parent);
|
|
|
|
var prefixedName = t.lib
|
2015-06-05 01:04:09 +03:00
|
|
|
? this.resolveCall(env, H.mkLibraryRef(t.lib), name)
|
2015-06-11 04:51:43 +03:00
|
|
|
: this.resolveCall(env, H.mkCodeRef(), name);
|
2015-05-22 01:32:12 +03:00
|
|
|
return mkCall(prefixedName, false);
|
|
|
|
}
|
2015-05-21 23:57:40 +03:00
|
|
|
|
2015-05-22 00:59:49 +03:00
|
|
|
// 5) Field access for an object.
|
|
|
|
else if (callType == "field")
|
2015-05-21 23:57:40 +03:00
|
|
|
return this.visit(env, args[0]) + "->" + H.mangleName(name);
|
|
|
|
|
2015-06-24 03:30:44 +03:00
|
|
|
// 6a) Lone reference to a library (e.g. ♻ micro:bit just by itself).
|
|
|
|
else if (args.length && H.isSingletonRef(args[0]) == "♻")
|
|
|
|
return "";
|
|
|
|
|
|
|
|
// 6b) Reference to a built-in library method, e.g. Math→ max
|
2015-05-22 00:59:49 +03:00
|
|
|
else if (args.length && H.isSingletonRef(args[0]))
|
2015-05-27 03:25:18 +03:00
|
|
|
return H.isSingletonRef(args[0]).toLowerCase() + "::" + mkCall(H.mangleName(name), true);
|
2015-05-22 00:59:49 +03:00
|
|
|
|
2015-05-21 23:57:40 +03:00
|
|
|
// 7) Instance method (e.g. Number's > operator, for which the receiver
|
2015-05-27 03:25:18 +03:00
|
|
|
// is the number itself). Lowercase so that "number" is the namespace
|
|
|
|
// that contains the functions that operate on typedef "Number".
|
2015-06-04 20:24:45 +03:00
|
|
|
else {
|
|
|
|
var t = H.resolveTypeRef(this.libraryMap, parent);
|
|
|
|
return t.type.toLowerCase()+"::"+mkCall(H.mangleName(name), false);
|
|
|
|
}
|
2015-05-19 20:25:53 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
public visitSingletonRef(e, n: string) {
|
2015-05-27 19:36:25 +03:00
|
|
|
if (n == "$skip")
|
|
|
|
return "";
|
|
|
|
else
|
2015-06-24 03:37:20 +03:00
|
|
|
// Reference to "data", "Math" (or other namespaces), that makes no
|
|
|
|
// sense.
|
|
|
|
return "";
|
2015-04-15 02:48:24 +03:00
|
|
|
}
|
|
|
|
|
2015-07-08 19:12:05 +03:00
|
|
|
public visitGlobalDef(e: EmitterEnv, name: string, t: J.JTypeRef, comment: string) {
|
2015-06-09 20:09:32 +03:00
|
|
|
H.reserveName(e, name);
|
|
|
|
|
2015-07-08 19:12:05 +03:00
|
|
|
// TODO: we skip definitions marked as shims, but we do not do anything
|
|
|
|
// meaningful when we *refer* to them.
|
|
|
|
var s = H.isShim(comment);
|
|
|
|
if (s !== null)
|
|
|
|
return null;
|
|
|
|
|
2015-06-05 00:10:39 +03:00
|
|
|
var x = H.defaultValueForType(this.libraryMap, t);
|
2015-06-09 20:09:32 +03:00
|
|
|
// A reference to a global is already unique (i.e. un-ambiguous).
|
|
|
|
// [mkType] calls [mangleName] (NOT [mangleUnique], and so should we).
|
|
|
|
return e.indent + H.mkType(e, this.libraryMap, t) + " " + H.mangleName(name) +
|
2015-05-30 01:58:29 +03:00
|
|
|
(x ? " = " + x : "") + ";"
|
2015-05-22 00:59:49 +03:00
|
|
|
}
|
|
|
|
|
2015-04-14 02:59:52 +03:00
|
|
|
public visitAction(
|
|
|
|
env: EmitterEnv,
|
|
|
|
name: string,
|
2015-05-28 03:39:56 +03:00
|
|
|
id: string,
|
2015-04-14 02:59:52 +03:00
|
|
|
inParams: J.JLocalDef[],
|
|
|
|
outParams: J.JLocalDef[],
|
|
|
|
body: J.JStmt[])
|
|
|
|
{
|
2015-06-02 19:46:02 +03:00
|
|
|
// This function is always called with H.willCompile == true, meaning
|
|
|
|
// it's not a shim.
|
2015-04-14 19:40:30 +03:00
|
|
|
if (outParams.length > 1)
|
2015-04-17 03:15:34 +03:00
|
|
|
throw new Error("Not supported (multiple return parameters)");
|
2015-04-14 19:40:30 +03:00
|
|
|
|
2015-04-15 03:38:18 +03:00
|
|
|
var env2 = indent(env);
|
2015-04-14 19:40:30 +03:00
|
|
|
var bodyText = [
|
2015-04-15 03:38:18 +03:00
|
|
|
outParams.length ? env2.indent + this.visit(env2, outParams[0]) + ";" : "",
|
|
|
|
this.visitMany(env2, body),
|
2015-05-28 02:09:39 +03:00
|
|
|
outParams.length ? env2.indent + H.mkReturn(H.mangleDef(env, outParams[0])) : "",
|
2015-04-15 02:48:24 +03:00
|
|
|
].filter(x => x != "").join("\n");
|
2015-06-09 20:09:32 +03:00
|
|
|
// The name of a function is unique per library, so don't go through
|
|
|
|
// [mangleUnique].
|
|
|
|
var head = H.mkSignature(env, this.libraryMap, H.mangleName(name), inParams, outParams);
|
2015-05-26 21:04:39 +03:00
|
|
|
return env.indent + head + " {\n" + bodyText + "\n"+env.indent+"}";
|
2015-04-14 02:59:52 +03:00
|
|
|
}
|
|
|
|
|
2015-06-13 01:29:03 +03:00
|
|
|
private compileImageLiterals(e: EmitterEnv) {
|
2015-05-28 20:45:40 +03:00
|
|
|
if (!this.imageLiterals.length)
|
|
|
|
return "";
|
|
|
|
|
2015-06-13 01:29:03 +03:00
|
|
|
return e.indent + "namespace literals {\n" +
|
2015-05-28 20:45:40 +03:00
|
|
|
this.imageLiterals.map((s: string, n: number) => {
|
|
|
|
var x = 0;
|
|
|
|
var w = 0;
|
|
|
|
var h = 0;
|
|
|
|
var lit = "{ ";
|
|
|
|
for (var i = 0; i < s.length; ++i) {
|
|
|
|
switch (s[i]) {
|
|
|
|
case "0":
|
|
|
|
case "1":
|
|
|
|
lit += s[i]+", ";
|
|
|
|
x++;
|
|
|
|
break;
|
|
|
|
case " ":
|
|
|
|
break;
|
|
|
|
case "\n":
|
|
|
|
if (w == 0)
|
|
|
|
w = x;
|
|
|
|
else if (x != w)
|
|
|
|
// Sanity check
|
|
|
|
throw new Error("Malformed string literal");
|
|
|
|
x = 0;
|
|
|
|
h++;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
throw new Error("Malformed string literal");
|
|
|
|
}
|
|
|
|
}
|
2015-05-28 20:47:22 +03:00
|
|
|
h++;
|
2015-05-28 20:45:40 +03:00
|
|
|
lit += "}";
|
|
|
|
var r = "bitmap"+n;
|
2015-06-13 01:29:03 +03:00
|
|
|
return e.indent + " const int "+r+"_w = "+w+";\n" +
|
|
|
|
e.indent + " const int "+r+"_h = "+h+";\n"+
|
|
|
|
e.indent + " const uint8_t "+r+"[] = "+lit+";\n";
|
2015-06-10 01:35:31 +03:00
|
|
|
}).join("\n") +
|
2015-06-13 01:29:03 +03:00
|
|
|
e.indent + "}\n\n";
|
2015-05-28 20:45:40 +03:00
|
|
|
}
|
|
|
|
|
2015-06-16 01:26:59 +03:00
|
|
|
private wrapNamespaceIf (s: string) {
|
|
|
|
if (this.libName != null)
|
2015-07-08 19:12:05 +03:00
|
|
|
return (s.length
|
|
|
|
? "namespace "+this.libName+" {\n"+
|
|
|
|
s +
|
|
|
|
"\n}"
|
|
|
|
: "");
|
2015-06-16 01:26:59 +03:00
|
|
|
else
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
2015-05-19 03:26:24 +03:00
|
|
|
// This function runs over all declarations. After execution, the three
|
|
|
|
// member fields [prelude], [prototypes] and [code] are filled accordingly.
|
2015-04-14 02:59:52 +03:00
|
|
|
public visitApp(e: EmitterEnv, decls: J.JDecl[]) {
|
2015-06-16 01:26:59 +03:00
|
|
|
if (this.libName)
|
|
|
|
e = indent(e);
|
|
|
|
|
2015-05-22 01:32:12 +03:00
|
|
|
// Some parts of the emitter need to lookup library names by their id
|
|
|
|
decls.forEach((x: J.JDecl) => {
|
|
|
|
if (x.nodeType == "library") {
|
|
|
|
var l: J.JLibrary = <J.JLibrary> x;
|
|
|
|
this.libraryMap[l.id] = l.name;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2015-06-05 01:04:09 +03:00
|
|
|
// Globals are in their own namespace (otherwise they would collide with
|
|
|
|
// "math", "number", etc.).
|
|
|
|
var globals = decls.map((f: J.JDecl) => {
|
|
|
|
var e1 = indent(e)
|
|
|
|
if (f.nodeType == "data")
|
|
|
|
return this.visit(e1, f);
|
|
|
|
else
|
|
|
|
return null;
|
|
|
|
}).filter(x => x != null);
|
|
|
|
var globalsCode = globals.length
|
|
|
|
? e.indent + "namespace globals {\n" +
|
|
|
|
globals.join("\n") + "\n" +
|
|
|
|
e.indent + "}\n"
|
|
|
|
: "";
|
|
|
|
|
2015-04-15 02:48:24 +03:00
|
|
|
// We need forward declarations for all functions (they're,
|
2015-04-14 02:59:52 +03:00
|
|
|
// by default, mutually recursive in TouchDevelop).
|
2015-04-15 01:26:39 +03:00
|
|
|
var forwardDeclarations = decls.map((f: J.JDecl) => {
|
2015-06-09 20:09:32 +03:00
|
|
|
if (f.nodeType == "action" && H.willCompile(<J.JAction> f)) {
|
|
|
|
H.reserveName(e, f.name, f.id);
|
|
|
|
return e.indent + H.mkSignature(e, this.libraryMap, H.mangleName(f.name), (<J.JAction> f).inParameters, (<J.JAction> f).outParameters)+";";
|
|
|
|
} else {
|
2015-04-14 02:59:52 +03:00
|
|
|
return null;
|
2015-06-09 20:09:32 +03:00
|
|
|
}
|
2015-04-14 02:59:52 +03:00
|
|
|
}).filter(x => x != null);
|
|
|
|
|
2015-04-15 02:48:24 +03:00
|
|
|
// Compile all the top-level functions.
|
2015-04-14 02:59:52 +03:00
|
|
|
var userFunctions = decls.map((d: J.JDecl) => {
|
2015-05-19 03:26:24 +03:00
|
|
|
if (d.nodeType == "action" && H.willCompile(<J.JAction> d)) {
|
2015-04-15 01:26:39 +03:00
|
|
|
return this.visit(e, d);
|
2015-04-28 02:29:19 +03:00
|
|
|
} else if (d.nodeType == "art" && d.name == "prelude.cpp") {
|
2015-04-28 02:25:15 +03:00
|
|
|
this.prelude += (<J.JArt> d).value;
|
2015-05-19 03:26:24 +03:00
|
|
|
} else {
|
|
|
|
// The typical library has other stuff mixed in (pictures, other
|
|
|
|
// resources) that are used, say, when running the simulator. Just
|
|
|
|
// silently ignore these.
|
|
|
|
return null;
|
2015-04-14 02:59:52 +03:00
|
|
|
}
|
|
|
|
return null;
|
2015-04-15 02:48:24 +03:00
|
|
|
}).filter(x => x != null);
|
2015-04-14 02:59:52 +03:00
|
|
|
|
2015-04-17 03:15:34 +03:00
|
|
|
// By convention, because we're forced to return a string, write the
|
2015-05-28 20:45:40 +03:00
|
|
|
// output parameters in the member variables. Image literals are scoped
|
|
|
|
// within our namespace.
|
2015-06-16 01:26:59 +03:00
|
|
|
this.prototypes = this.wrapNamespaceIf(globalsCode + forwardDeclarations.join("\n"));
|
|
|
|
this.code = this.wrapNamespaceIf(this.compileImageLiterals(e) + userFunctions.join("\n"));
|
2015-04-17 03:15:34 +03:00
|
|
|
|
2015-05-19 03:26:24 +03:00
|
|
|
// [embedded.ts] now reads the three member fields separately and
|
|
|
|
// ignores this return value.
|
2015-05-26 21:04:39 +03:00
|
|
|
return null;
|
2015-04-14 02:59:52 +03:00
|
|
|
}
|
2015-04-13 23:24:20 +03:00
|
|
|
}
|
2015-04-14 02:59:52 +03:00
|
|
|
}
|
2015-04-13 23:24:20 +03:00
|
|
|
}
|
2015-04-15 01:50:13 +03:00
|
|
|
|
|
|
|
// vim: set ts=2 sw=2 sts=2:
|