// Prints a lisp AST on one line std::string printSimple(Node ast) { if (ast.type == TOKEN) return ast.val; std::string o = "(" + ast.val; std::vector<std::string> subs; for (unsigned i = 0; i < ast.args.size(); i++) { o += " " + printSimple(ast.args[i]); } return o + ")"; }
// Preprocess input containing functions // // localExterns is a map of the form, eg, // // { x: { foo: 0, bar: 1, baz: 2 }, y: { qux: 0, foo: 1 } ... } // // localExternSigs is a map of the form, eg, // // { x : { foo: iii, bar: iis, baz: ia }, y: { qux: i, foo: as } ... } // // Signifying that x.foo = 0, x.baz = 2, y.foo = 1, etc // and that x.foo has three integers as arguments, x.bar has two // integers and a variable-length string, and baz has an integer // and an array // // globalExterns is a one-level map, eg from above // // { foo: 1, bar: 1, baz: 2, qux: 0 } // // globalExternSigs is a one-level map, eg from above // // { foo: as, bar: iis, baz: ia, qux: i} // // Note that globalExterns and globalExternSigs may be ambiguous // Also, a null signature implies an infinite tail of integers preprocessResult preprocessInit(Node inp) { Metadata m = inp.metadata; if (inp.val != "seq") inp = astnode("seq", inp, m); std::vector<Node> empty = std::vector<Node>(); Node init = astnode("seq", empty, m); Node shared = astnode("seq", empty, m); std::vector<Node> any; std::vector<Node> functions; preprocessAux out = preprocessAux(); out.localExterns["self"] = std::map<std::string, int>(); int functionCount = 0; int storageDataCount = 0; for (unsigned i = 0; i < inp.args.size(); i++) { Node obj = inp.args[i]; // Functions if (obj.val == "def") { if (obj.args.size() == 0) err("Empty def", m); std::string funName = obj.args[0].val; // Init, shared and any are special functions if (funName == "init" || funName == "shared" || funName == "any") { if (obj.args[0].args.size()) err(funName+" cannot have arguments", m); } if (funName == "init") init = obj.args[1]; else if (funName == "shared") shared = obj.args[1]; else if (funName == "any") any.push_back(obj.args[1]); else { // Other functions functions.push_back(convFunction(obj, functionCount)); out.localExterns["self"][obj.args[0].val] = functionCount; out.localExternSigs["self"][obj.args[0].val] = getSignature(obj.args[0].args); functionCount++; } } // Extern declarations else if (obj.val == "extern") { std::string externName = obj.args[0].val; Node al = obj.args[1]; if (!out.localExterns.count(externName)) out.localExterns[externName] = std::map<std::string, int>(); for (unsigned i = 0; i < al.args.size(); i++) { if (al.args[i].val == ":") { std::string v = al.args[i].args[0].val; std::string sig = al.args[i].args[1].val; out.globalExterns[v] = i; out.globalExternSigs[v] = sig; out.localExterns[externName][v] = i; out.localExternSigs[externName][v] = sig; } else { std::string v = al.args[i].val; out.globalExterns[v] = i; out.globalExternSigs[v] = ""; out.localExterns[externName][v] = i; out.localExternSigs[externName][v] = ""; } } } // Custom macros else if (obj.val == "macro" || (obj.val == "fun" && obj.args[0].val == "macro")) { // Rules for valid macros: // // There are only four categories of valid macros: // // 1. a macro where the outer function is something // which is NOT an existing valid function/extern/datum // 2. a macro of the form set(c(x), d) where c must NOT // be an existing valid function/extern/datum // 3. something of the form access(c(x)), where c must NOT // be an existing valid function/extern/datum // 4. something of the form set(access(c(x)), d) where c must // NOT be an existing valid function/extern/datum // 5. something of the form with(c(x), d, e) where c must // NOT be an existing valid function/extern/datum bool valid = false; Node pattern; Node substitution; int priority; // Priority not set: default zero if (obj.val == "macro") { pattern = obj.args[0]; substitution = obj.args[1]; priority = 0; } // Specified priority else { pattern = obj.args[1]; substitution = obj.args[2]; if (obj.args[0].args.size()) priority = dtu(obj.args[0].args[0].val); else priority = 0; } if (opcode(pattern.val) < 0 && !isValidFunctionName(pattern.val)) valid = true; if (pattern.val == "set" && opcode(pattern.args[0].val) < 0 && !isValidFunctionName(pattern.args[0].val)) valid = true; if (pattern.val == "access" && opcode(pattern.args[0].val) < 0 && !isValidFunctionName(pattern.args[0].val)) if (pattern.val == "set" && pattern.args[0].val == "access" && opcode(pattern.args[0].args[0].val) < 0 && !isValidFunctionName(pattern.args[0].args[0].val)) valid = true; if (pattern.val == "with" && opcode(pattern.args[0].val) < 0 && !isValidFunctionName(pattern.args[0].val)) valid = true; if (valid) { if (!out.customMacros.count(priority)) out.customMacros[priority] = rewriteRuleSet(); out.customMacros[priority].addRule (rewriteRule(pattern, substitution)); } else warn("Macro does not fit valid template: "+printSimple(pattern), m); } // Variable types else if (obj.val == "type") { std::string typeName = obj.args[0].val; std::vector<Node> vars = obj.args[1].args; for (unsigned i = 0; i < vars.size(); i++) out.types[vars[i].val] = typeName; } // Storage variables/structures else if (obj.val == "data") { out.storageVars = getStorageVars(out.storageVars, obj.args[0], "", storageDataCount); storageDataCount += 1; } else any.push_back(obj); } // Set up top-level AST structure std::vector<Node> main; if (shared.args.size()) main.push_back(shared); if (init.args.size()) main.push_back(init); std::vector<Node> code; if (shared.args.size()) code.push_back(shared); for (unsigned i = 0; i < any.size(); i++) code.push_back(any[i]); for (unsigned i = 0; i < functions.size(); i++) code.push_back(functions[i]); Node codeNode; if (functions.size() > 0) { codeNode = astnode("with", token("__funid", m), astnode("byte", token("0", m), astnode("calldataload", token("0", m), m), m), astnode("seq", code, m), m); } else codeNode = astnode("seq", code, m); main.push_back(astnode("~return", token("0", m), astnode("lll", codeNode, token("0", m), m), m)); Node result; if (main.size() == 1) result = main[0]; else result = astnode("seq", main, inp.metadata); return preprocessResult(result, out); }