Gav Wood
10 years ago
9 changed files with 1177 additions and 0 deletions
@ -0,0 +1,203 @@ |
|||
#include <stdio.h> |
|||
#include <iostream> |
|||
#include <vector> |
|||
#include <map> |
|||
#include "util.h" |
|||
#include "lllparser.h" |
|||
#include "bignum.h" |
|||
#include "optimize.h" |
|||
#include "rewriteutils.h" |
|||
#include "preprocess.h" |
|||
#include "functions.h" |
|||
|
|||
std::string getSignature(std::vector<Node> args) { |
|||
std::string o; |
|||
for (unsigned i = 0; i < args.size(); i++) { |
|||
if (args[i].val == ":" && args[i].args[1].val == "s") |
|||
o += "s"; |
|||
else if (args[i].val == ":" && args[i].args[1].val == "a") |
|||
o += "a"; |
|||
else |
|||
o += "i"; |
|||
} |
|||
return o; |
|||
} |
|||
|
|||
// Convert a list of arguments into a node containing a
|
|||
// < datastart, datasz > pair
|
|||
|
|||
Node packArguments(std::vector<Node> args, std::string sig, |
|||
int funId, Metadata m) { |
|||
// Plain old 32 byte arguments
|
|||
std::vector<Node> nargs; |
|||
// Variable-sized arguments
|
|||
std::vector<Node> vargs; |
|||
// Variable sizes
|
|||
std::vector<Node> sizes; |
|||
// Is a variable an array?
|
|||
std::vector<bool> isArray; |
|||
// Fill up above three argument lists
|
|||
int argCount = 0; |
|||
for (unsigned i = 0; i < args.size(); i++) { |
|||
Metadata m = args[i].metadata; |
|||
if (args[i].val == "=") { |
|||
// do nothing
|
|||
} |
|||
else { |
|||
// Determine the correct argument type
|
|||
char argType; |
|||
if (sig.size() > 0) { |
|||
if (argCount >= (signed)sig.size()) |
|||
err("Too many args", m); |
|||
argType = sig[argCount]; |
|||
} |
|||
else argType = 'i'; |
|||
// Integer (also usable for short strings)
|
|||
if (argType == 'i') { |
|||
if (args[i].val == ":") |
|||
err("Function asks for int, provided string or array", m); |
|||
nargs.push_back(args[i]); |
|||
} |
|||
// Long string
|
|||
else if (argType == 's') { |
|||
if (args[i].val != ":") |
|||
err("Must specify string length", m); |
|||
vargs.push_back(args[i].args[0]); |
|||
sizes.push_back(args[i].args[1]); |
|||
isArray.push_back(false); |
|||
} |
|||
// Array
|
|||
else if (argType == 'a') { |
|||
if (args[i].val != ":") |
|||
err("Must specify array length", m); |
|||
vargs.push_back(args[i].args[0]); |
|||
sizes.push_back(args[i].args[1]); |
|||
isArray.push_back(true); |
|||
} |
|||
else err("Invalid arg type in signature", m); |
|||
argCount++; |
|||
} |
|||
} |
|||
int static_arg_size = 1 + (vargs.size() + nargs.size()) * 32; |
|||
// Start off by saving the size variables and calculating the total
|
|||
msn kwargs; |
|||
kwargs["funid"] = tkn(utd(funId), m); |
|||
std::string pattern = |
|||
"(with _sztot "+utd(static_arg_size)+" " |
|||
" (with _sizes (alloc "+utd(sizes.size() * 32)+") " |
|||
" (seq "; |
|||
for (unsigned i = 0; i < sizes.size(); i++) { |
|||
std::string sizeIncrement = |
|||
isArray[i] ? "(mul 32 _x)" : "_x"; |
|||
pattern += |
|||
"(with _x $sz"+utd(i)+"(seq " |
|||
" (mstore (add _sizes "+utd(i * 32)+") _x) " |
|||
" (set _sztot (add _sztot "+sizeIncrement+" )))) "; |
|||
kwargs["sz"+utd(i)] = sizes[i]; |
|||
} |
|||
// Allocate memory, and set first data byte
|
|||
pattern += |
|||
"(with _datastart (alloc (add _sztot 32)) (seq " |
|||
" (mstore8 _datastart $funid) "; |
|||
// Copy over size variables
|
|||
for (unsigned i = 0; i < sizes.size(); i++) { |
|||
int v = 1 + i * 32; |
|||
pattern += |
|||
" (mstore " |
|||
" (add _datastart "+utd(v)+") " |
|||
" (mload (add _sizes "+utd(v-1)+"))) "; |
|||
} |
|||
// Store normal arguments
|
|||
for (unsigned i = 0; i < nargs.size(); i++) { |
|||
int v = 1 + (i + sizes.size()) * 32; |
|||
pattern += |
|||
" (mstore (add _datastart "+utd(v)+") $"+utd(i)+") "; |
|||
kwargs[utd(i)] = nargs[i]; |
|||
} |
|||
// Loop through variable-sized arguments, store them
|
|||
pattern += |
|||
" (with _pos (add _datastart "+utd(static_arg_size)+") (seq"; |
|||
for (unsigned i = 0; i < vargs.size(); i++) { |
|||
std::string copySize = |
|||
isArray[i] ? "(mul 32 (mload (add _sizes "+utd(i * 32)+")))" |
|||
: "(mload (add _sizes "+utd(i * 32)+"))"; |
|||
pattern += |
|||
" (unsafe_mcopy _pos $vl"+utd(i)+" "+copySize+") " |
|||
" (set _pos (add _pos "+copySize+")) "; |
|||
kwargs["vl"+utd(i)] = vargs[i]; |
|||
} |
|||
// Return a 2-item array containing the start and size
|
|||
pattern += " (array_lit _datastart _sztot))))))))"; |
|||
std::string prefix = "_temp_"+mkUniqueToken(); |
|||
// Fill in pattern, return triple
|
|||
return subst(parseLLL(pattern), kwargs, prefix, m); |
|||
} |
|||
|
|||
// Create a node for argument unpacking
|
|||
Node unpackArguments(std::vector<Node> vars, Metadata m) { |
|||
std::vector<std::string> varNames; |
|||
std::vector<std::string> longVarNames; |
|||
std::vector<bool> longVarIsArray; |
|||
// Fill in variable and long variable names, as well as which
|
|||
// long variables are arrays and which are strings
|
|||
for (unsigned i = 0; i < vars.size(); i++) { |
|||
if (vars[i].val == ":") { |
|||
if (vars[i].args.size() != 2) |
|||
err("Malformed def!", m); |
|||
longVarNames.push_back(vars[i].args[0].val); |
|||
std::string tag = vars[i].args[1].val; |
|||
if (tag == "s") |
|||
longVarIsArray.push_back(false); |
|||
else if (tag == "a") |
|||
longVarIsArray.push_back(true); |
|||
else |
|||
err("Function value can only be string or array", m); |
|||
} |
|||
else { |
|||
varNames.push_back(vars[i].val); |
|||
} |
|||
} |
|||
std::vector<Node> sub; |
|||
if (!varNames.size() && !longVarNames.size()) { |
|||
// do nothing if we have no arguments
|
|||
} |
|||
else { |
|||
std::vector<Node> varNodes; |
|||
for (unsigned i = 0; i < longVarNames.size(); i++) |
|||
varNodes.push_back(token(longVarNames[i], m)); |
|||
for (unsigned i = 0; i < varNames.size(); i++) |
|||
varNodes.push_back(token(varNames[i], m)); |
|||
// Copy over variable lengths and short variables
|
|||
for (unsigned i = 0; i < varNodes.size(); i++) { |
|||
int pos = 1 + i * 32; |
|||
std::string prefix = (i < longVarNames.size()) ? "_len_" : ""; |
|||
sub.push_back(asn("untyped", asn("set", |
|||
token(prefix+varNodes[i].val, m), |
|||
asn("calldataload", tkn(utd(pos), m), m), |
|||
m))); |
|||
} |
|||
// Copy over long variables
|
|||
if (longVarNames.size() > 0) { |
|||
std::vector<Node> sub2; |
|||
int pos = varNodes.size() * 32 + 1; |
|||
Node tot = tkn("_tot", m); |
|||
for (unsigned i = 0; i < longVarNames.size(); i++) { |
|||
Node var = tkn(longVarNames[i], m); |
|||
Node varlen = longVarIsArray[i] |
|||
? asn("mul", tkn("32", m), tkn("_len_"+longVarNames[i], m)) |
|||
: tkn("_len_"+longVarNames[i], m); |
|||
sub2.push_back(asn("untyped", |
|||
asn("set", var, asn("alloc", varlen)))); |
|||
sub2.push_back(asn("calldatacopy", var, tot, varlen)); |
|||
sub2.push_back(asn("set", tot, asn("add", tot, varlen))); |
|||
} |
|||
std::string prefix = "_temp_"+mkUniqueToken(); |
|||
sub.push_back(subst( |
|||
astnode("with", tot, tkn(utd(pos), m), asn("seq", sub2)), |
|||
msn(), |
|||
prefix, |
|||
m)); |
|||
} |
|||
} |
|||
return asn("seq", sub, m); |
|||
} |
@ -0,0 +1,39 @@ |
|||
#ifndef ETHSERP_FUNCTIONS |
|||
#define ETHSERP_FUNCTIONS |
|||
|
|||
#include <stdio.h> |
|||
#include <iostream> |
|||
#include <vector> |
|||
#include <map> |
|||
#include "util.h" |
|||
#include "lllparser.h" |
|||
#include "bignum.h" |
|||
#include "optimize.h" |
|||
#include "rewriteutils.h" |
|||
#include "preprocess.h" |
|||
|
|||
|
|||
class argPack { |
|||
public: |
|||
argPack(Node a, Node b, Node c) { |
|||
pre = a; |
|||
datastart = b; |
|||
datasz = c; |
|||
} |
|||
Node pre; |
|||
Node datastart; |
|||
Node datasz; |
|||
}; |
|||
|
|||
// Get a signature from a function
|
|||
std::string getSignature(std::vector<Node> args); |
|||
|
|||
// Convert a list of arguments into a <pre, mstart, msize> node
|
|||
// triple, given the signature of a function
|
|||
Node packArguments(std::vector<Node> args, std::string sig, |
|||
int funId, Metadata m); |
|||
|
|||
// Create a node for argument unpacking
|
|||
Node unpackArguments(std::vector<Node> vars, Metadata m); |
|||
|
|||
#endif |
@ -0,0 +1,154 @@ |
|||
#include <stdio.h> |
|||
#include <iostream> |
|||
#include <vector> |
|||
#include <map> |
|||
#include "opcodes.h" |
|||
#include "util.h" |
|||
#include "bignum.h" |
|||
|
|||
Mapping mapping[] = { |
|||
Mapping("STOP", 0x00, 0, 0), |
|||
Mapping("ADD", 0x01, 2, 1), |
|||
Mapping("MUL", 0x02, 2, 1), |
|||
Mapping("SUB", 0x03, 2, 1), |
|||
Mapping("DIV", 0x04, 2, 1), |
|||
Mapping("SDIV", 0x05, 2, 1), |
|||
Mapping("MOD", 0x06, 2, 1), |
|||
Mapping("SMOD", 0x07, 2, 1), |
|||
Mapping("ADDMOD", 0x08, 3, 1), |
|||
Mapping("MULMOD", 0x09, 3, 1), |
|||
Mapping("EXP", 0x0a, 2, 1), |
|||
Mapping("SIGNEXTEND", 0x0b, 2, 1), |
|||
Mapping("LT", 0x10, 2, 1), |
|||
Mapping("GT", 0x11, 2, 1), |
|||
Mapping("SLT", 0x12, 2, 1), |
|||
Mapping("SGT", 0x13, 2, 1), |
|||
Mapping("EQ", 0x14, 2, 1), |
|||
Mapping("ISZERO", 0x15, 1, 1), |
|||
Mapping("AND", 0x16, 2, 1), |
|||
Mapping("OR", 0x17, 2, 1), |
|||
Mapping("XOR", 0x18, 2, 1), |
|||
Mapping("NOT", 0x19, 1, 1), |
|||
Mapping("BYTE", 0x1a, 2, 1), |
|||
Mapping("SHA3", 0x20, 2, 1), |
|||
Mapping("ADDRESS", 0x30, 0, 1), |
|||
Mapping("BALANCE", 0x31, 1, 1), |
|||
Mapping("ORIGIN", 0x32, 0, 1), |
|||
Mapping("CALLER", 0x33, 0, 1), |
|||
Mapping("CALLVALUE", 0x34, 0, 1), |
|||
Mapping("CALLDATALOAD", 0x35, 1, 1), |
|||
Mapping("CALLDATASIZE", 0x36, 0, 1), |
|||
Mapping("CALLDATACOPY", 0x37, 3, 0), |
|||
Mapping("CODESIZE", 0x38, 0, 1), |
|||
Mapping("CODECOPY", 0x39, 3, 0), |
|||
Mapping("GASPRICE", 0x3a, 0, 1), |
|||
Mapping("EXTCODESIZE", 0x3b, 1, 1), |
|||
Mapping("EXTCODECOPY", 0x3c, 4, 0), |
|||
Mapping("PREVHASH", 0x40, 0, 1), |
|||
Mapping("COINBASE", 0x41, 0, 1), |
|||
Mapping("TIMESTAMP", 0x42, 0, 1), |
|||
Mapping("NUMBER", 0x43, 0, 1), |
|||
Mapping("DIFFICULTY", 0x44, 0, 1), |
|||
Mapping("GASLIMIT", 0x45, 0, 1), |
|||
Mapping("POP", 0x50, 1, 0), |
|||
Mapping("MLOAD", 0x51, 1, 1), |
|||
Mapping("MSTORE", 0x52, 2, 0), |
|||
Mapping("MSTORE8", 0x53, 2, 0), |
|||
Mapping("SLOAD", 0x54, 1, 1), |
|||
Mapping("SSTORE", 0x55, 2, 0), |
|||
Mapping("JUMP", 0x56, 1, 0), |
|||
Mapping("JUMPI", 0x57, 2, 0), |
|||
Mapping("PC", 0x58, 0, 1), |
|||
Mapping("MSIZE", 0x59, 0, 1), |
|||
Mapping("GAS", 0x5a, 0, 1), |
|||
Mapping("JUMPDEST", 0x5b, 0, 0), |
|||
Mapping("LOG0", 0xa0, 2, 0), |
|||
Mapping("LOG1", 0xa1, 3, 0), |
|||
Mapping("LOG2", 0xa2, 4, 0), |
|||
Mapping("LOG3", 0xa3, 5, 0), |
|||
Mapping("LOG4", 0xa4, 6, 0), |
|||
Mapping("CREATE", 0xf0, 3, 1), |
|||
Mapping("CALL", 0xf1, 7, 1), |
|||
Mapping("CALLCODE", 0xf2, 7, 1), |
|||
Mapping("RETURN", 0xf3, 2, 0), |
|||
Mapping("SUICIDE", 0xff, 1, 0), |
|||
Mapping("---END---", 0x00, 0, 0), |
|||
}; |
|||
|
|||
std::map<std::string, std::vector<int> > opcodes; |
|||
std::map<int, std::string> reverseOpcodes; |
|||
|
|||
// Fetches everything EXCEPT PUSH1..32
|
|||
std::pair<std::string, std::vector<int> > _opdata(std::string ops, int opi) { |
|||
if (!opcodes.size()) { |
|||
int i = 0; |
|||
while (mapping[i].op != "---END---") { |
|||
Mapping mi = mapping[i]; |
|||
opcodes[mi.op] = triple(mi.opcode, mi.in, mi.out); |
|||
i++; |
|||
} |
|||
for (i = 1; i <= 16; i++) { |
|||
opcodes["DUP"+unsignedToDecimal(i)] = triple(0x7f + i, i, i+1); |
|||
opcodes["SWAP"+unsignedToDecimal(i)] = triple(0x8f + i, i+1, i+1); |
|||
} |
|||
for (std::map<std::string, std::vector<int> >::iterator it=opcodes.begin(); |
|||
it != opcodes.end(); |
|||
it++) { |
|||
reverseOpcodes[(*it).second[0]] = (*it).first; |
|||
} |
|||
} |
|||
ops = upperCase(ops); |
|||
std::string op; |
|||
std::vector<int> opdata; |
|||
op = reverseOpcodes.count(opi) ? reverseOpcodes[opi] : ""; |
|||
opdata = opcodes.count(ops) ? opcodes[ops] : triple(-1, -1, -1); |
|||
return std::pair<std::string, std::vector<int> >(op, opdata); |
|||
} |
|||
|
|||
int opcode(std::string op) { |
|||
return _opdata(op, -1).second[0]; |
|||
} |
|||
|
|||
int opinputs(std::string op) { |
|||
return _opdata(op, -1).second[1]; |
|||
} |
|||
|
|||
int opoutputs(std::string op) { |
|||
return _opdata(op, -1).second[2]; |
|||
} |
|||
|
|||
std::string op(int opcode) { |
|||
return _opdata("", opcode).first; |
|||
} |
|||
|
|||
std::string lllSpecials[][3] = { |
|||
{ "ref", "1", "1" }, |
|||
{ "get", "1", "1" }, |
|||
{ "set", "2", "2" }, |
|||
{ "with", "3", "3" }, |
|||
{ "comment", "0", "2147483647" }, |
|||
{ "ops", "0", "2147483647" }, |
|||
{ "lll", "2", "2" }, |
|||
{ "seq", "0", "2147483647" }, |
|||
{ "if", "3", "3" }, |
|||
{ "unless", "2", "2" }, |
|||
{ "until", "2", "2" }, |
|||
{ "alloc", "1", "1" }, |
|||
{ "---END---", "0", "0" }, |
|||
}; |
|||
|
|||
std::map<std::string, std::pair<int, int> > lllMap; |
|||
|
|||
// Is a function name one of the valid functions above?
|
|||
bool isValidLLLFunc(std::string f, int argc) { |
|||
if (lllMap.size() == 0) { |
|||
for (int i = 0; ; i++) { |
|||
if (lllSpecials[i][0] == "---END---") break; |
|||
lllMap[lllSpecials[i][0]] = std::pair<int, int>( |
|||
dtu(lllSpecials[i][1]), dtu(lllSpecials[i][2])); |
|||
} |
|||
} |
|||
return lllMap.count(f) |
|||
&& argc >= lllMap[f].first |
|||
&& argc <= lllMap[f].second; |
|||
} |
@ -0,0 +1,98 @@ |
|||
#include <stdio.h> |
|||
#include <iostream> |
|||
#include <vector> |
|||
#include <map> |
|||
#include "util.h" |
|||
#include "lllparser.h" |
|||
#include "bignum.h" |
|||
|
|||
// Compile-time arithmetic calculations
|
|||
Node optimize(Node inp) { |
|||
if (inp.type == TOKEN) { |
|||
Node o = tryNumberize(inp); |
|||
if (decimalGt(o.val, tt256, true)) |
|||
err("Value too large (exceeds 32 bytes or 2^256)", inp.metadata); |
|||
return o; |
|||
} |
|||
for (unsigned i = 0; i < inp.args.size(); i++) { |
|||
inp.args[i] = optimize(inp.args[i]); |
|||
} |
|||
// Arithmetic-specific transform
|
|||
if (inp.val == "+") inp.val = "add"; |
|||
if (inp.val == "*") inp.val = "mul"; |
|||
if (inp.val == "-") inp.val = "sub"; |
|||
if (inp.val == "/") inp.val = "sdiv"; |
|||
if (inp.val == "^") inp.val = "exp"; |
|||
if (inp.val == "**") inp.val = "exp"; |
|||
if (inp.val == "%") inp.val = "smod"; |
|||
// Degenerate cases for add and mul
|
|||
if (inp.args.size() == 2) { |
|||
if (inp.val == "add" && inp.args[0].type == TOKEN && |
|||
inp.args[0].val == "0") { |
|||
Node x = inp.args[1]; |
|||
inp = x; |
|||
} |
|||
if (inp.val == "add" && inp.args[1].type == TOKEN && |
|||
inp.args[1].val == "0") { |
|||
Node x = inp.args[0]; |
|||
inp = x; |
|||
} |
|||
if (inp.val == "mul" && inp.args[0].type == TOKEN && |
|||
inp.args[0].val == "1") { |
|||
Node x = inp.args[1]; |
|||
inp = x; |
|||
} |
|||
if (inp.val == "mul" && inp.args[1].type == TOKEN && |
|||
inp.args[1].val == "1") { |
|||
Node x = inp.args[0]; |
|||
inp = x; |
|||
} |
|||
} |
|||
// Arithmetic computation
|
|||
if (inp.args.size() == 2 |
|||
&& inp.args[0].type == TOKEN |
|||
&& inp.args[1].type == TOKEN) { |
|||
std::string o; |
|||
if (inp.val == "add") { |
|||
o = decimalMod(decimalAdd(inp.args[0].val, inp.args[1].val), tt256); |
|||
} |
|||
else if (inp.val == "sub") { |
|||
if (decimalGt(inp.args[0].val, inp.args[1].val, true)) |
|||
o = decimalSub(inp.args[0].val, inp.args[1].val); |
|||
} |
|||
else if (inp.val == "mul") { |
|||
o = decimalMod(decimalMul(inp.args[0].val, inp.args[1].val), tt256); |
|||
} |
|||
else if (inp.val == "div" && inp.args[1].val != "0") { |
|||
o = decimalDiv(inp.args[0].val, inp.args[1].val); |
|||
} |
|||
else if (inp.val == "sdiv" && inp.args[1].val != "0" |
|||
&& decimalGt(tt255, inp.args[0].val) |
|||
&& decimalGt(tt255, inp.args[1].val)) { |
|||
o = decimalDiv(inp.args[0].val, inp.args[1].val); |
|||
} |
|||
else if (inp.val == "mod" && inp.args[1].val != "0") { |
|||
o = decimalMod(inp.args[0].val, inp.args[1].val); |
|||
} |
|||
else if (inp.val == "smod" && inp.args[1].val != "0" |
|||
&& decimalGt(tt255, inp.args[0].val) |
|||
&& decimalGt(tt255, inp.args[1].val)) { |
|||
o = decimalMod(inp.args[0].val, inp.args[1].val); |
|||
} |
|||
else if (inp.val == "exp") { |
|||
o = decimalModExp(inp.args[0].val, inp.args[1].val, tt256); |
|||
} |
|||
if (o.length()) return token(o, inp.metadata); |
|||
} |
|||
return inp; |
|||
} |
|||
|
|||
// Is a node degenerate (ie. trivial to calculate) ?
|
|||
bool isDegenerate(Node n) { |
|||
return optimize(n).type == TOKEN; |
|||
} |
|||
|
|||
// Is a node purely arithmetic?
|
|||
bool isPureArithmetic(Node n) { |
|||
return isNumberLike(optimize(n)); |
|||
} |
@ -0,0 +1,19 @@ |
|||
#ifndef ETHSERP_OPTIMIZER |
|||
#define ETHSERP_OPTIMIZER |
|||
|
|||
#include <stdio.h> |
|||
#include <iostream> |
|||
#include <vector> |
|||
#include <map> |
|||
#include "util.h" |
|||
|
|||
// Compile-time arithmetic calculations
|
|||
Node optimize(Node inp); |
|||
|
|||
// Is a node degenerate (ie. trivial to calculate) ?
|
|||
bool isDegenerate(Node n); |
|||
|
|||
// Is a node purely arithmetic?
|
|||
bool isPureArithmetic(Node n); |
|||
|
|||
#endif |
@ -0,0 +1,327 @@ |
|||
#include <stdio.h> |
|||
#include <iostream> |
|||
#include <vector> |
|||
#include <map> |
|||
#include "util.h" |
|||
#include "lllparser.h" |
|||
#include "bignum.h" |
|||
#include "rewriteutils.h" |
|||
#include "optimize.h" |
|||
#include "preprocess.h" |
|||
#include "functions.h" |
|||
#include "opcodes.h" |
|||
|
|||
// Convert a function of the form (def (f x y z) (do stuff)) into
|
|||
// (if (first byte of ABI is correct) (seq (setup x y z) (do stuff)))
|
|||
Node convFunction(Node node, int functionCount) { |
|||
std::string prefix = "_temp"+mkUniqueToken()+"_"; |
|||
Metadata m = node.metadata; |
|||
|
|||
if (node.args.size() != 2) |
|||
err("Malformed def!", m); |
|||
// Collect the list of variable names and variable byte counts
|
|||
Node unpack = unpackArguments(node.args[0].args, m); |
|||
// And the actual code
|
|||
Node body = node.args[1]; |
|||
// Main LLL-based function body
|
|||
return astnode("if", |
|||
astnode("eq", |
|||
astnode("get", token("__funid", m), m), |
|||
token(unsignedToDecimal(functionCount), m), |
|||
m), |
|||
astnode("seq", unpack, body, m)); |
|||
} |
|||
|
|||
// Populate an svObj with the arguments needed to determine
|
|||
// the storage position of a node
|
|||
svObj getStorageVars(svObj pre, Node node, std::string prefix, |
|||
int index) { |
|||
Metadata m = node.metadata; |
|||
if (!pre.globalOffset.size()) pre.globalOffset = "0"; |
|||
std::vector<Node> h; |
|||
std::vector<std::string> coefficients; |
|||
// Array accesses or atoms
|
|||
if (node.val == "access" || node.type == TOKEN) { |
|||
std::string tot = "1"; |
|||
h = listfyStorageAccess(node); |
|||
coefficients.push_back("1"); |
|||
for (unsigned i = h.size() - 1; i >= 1; i--) { |
|||
// Array sizes must be constant or at least arithmetically
|
|||
// evaluable at compile time
|
|||
if (!isPureArithmetic(h[i])) |
|||
err("Array size must be fixed value", m); |
|||
// Create a list of the coefficient associated with each
|
|||
// array index
|
|||
coefficients.push_back(decimalMul(coefficients.back(), h[i].val)); |
|||
} |
|||
} |
|||
// Tuples
|
|||
else { |
|||
int startc; |
|||
// Handle the (fun <fun_astnode> args...) case
|
|||
if (node.val == "fun") { |
|||
startc = 1; |
|||
h = listfyStorageAccess(node.args[0]); |
|||
} |
|||
// Handle the (<fun_name> args...) case, which
|
|||
// the serpent parser produces when the function
|
|||
// is a simple name and not a complex astnode
|
|||
else { |
|||
startc = 0; |
|||
h = listfyStorageAccess(token(node.val, m)); |
|||
} |
|||
svObj sub = pre; |
|||
sub.globalOffset = "0"; |
|||
// Evaluate tuple elements recursively
|
|||
for (unsigned i = startc; i < node.args.size(); i++) { |
|||
sub = getStorageVars(sub, |
|||
node.args[i], |
|||
prefix+h[0].val.substr(2)+".", |
|||
i-startc); |
|||
} |
|||
coefficients.push_back(sub.globalOffset); |
|||
for (unsigned i = h.size() - 1; i >= 1; i--) { |
|||
// Array sizes must be constant or at least arithmetically
|
|||
// evaluable at compile time
|
|||
if (!isPureArithmetic(h[i])) |
|||
err("Array size must be fixed value", m); |
|||
// Create a list of the coefficient associated with each
|
|||
// array index
|
|||
coefficients.push_back(decimalMul(coefficients.back(), h[i].val)); |
|||
} |
|||
pre.offsets = sub.offsets; |
|||
pre.coefficients = sub.coefficients; |
|||
pre.nonfinal = sub.nonfinal; |
|||
pre.nonfinal[prefix+h[0].val.substr(2)] = true; |
|||
} |
|||
pre.coefficients[prefix+h[0].val.substr(2)] = coefficients; |
|||
pre.offsets[prefix+h[0].val.substr(2)] = pre.globalOffset; |
|||
pre.indices[prefix+h[0].val.substr(2)] = index; |
|||
if (decimalGt(tt176, coefficients.back())) |
|||
pre.globalOffset = decimalAdd(pre.globalOffset, coefficients.back()); |
|||
return pre; |
|||
} |
|||
|
|||
// 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); |
|||
} |
|||
|
|||
preprocessResult processTypes (preprocessResult pr) { |
|||
preprocessAux aux = pr.second; |
|||
Node node = pr.first; |
|||
if (node.type == TOKEN && aux.types.count(node.val)) |
|||
node = asn(aux.types[node.val], node, node.metadata); |
|||
else if (node.val == "untyped") |
|||
return preprocessResult(node.args[0], aux); |
|||
else if (node.val == "outer") |
|||
return preprocessResult(node, aux); |
|||
else { |
|||
for (unsigned i = 0; i < node.args.size(); i++) { |
|||
node.args[i] = |
|||
processTypes(preprocessResult(node.args[i], aux)).first; |
|||
} |
|||
} |
|||
return preprocessResult(node, aux); |
|||
} |
|||
|
|||
preprocessResult preprocess(Node n) { |
|||
return processTypes(preprocessInit(n)); |
|||
} |
@ -0,0 +1,50 @@ |
|||
#ifndef ETHSERP_PREPROCESSOR |
|||
#define ETHSERP_PREPROCESSOR |
|||
|
|||
#include <stdio.h> |
|||
#include <iostream> |
|||
#include <vector> |
|||
#include <map> |
|||
#include "util.h" |
|||
#include "rewriteutils.h" |
|||
|
|||
// Storage variable index storing object
|
|||
struct svObj { |
|||
std::map<std::string, std::string> offsets; |
|||
std::map<std::string, int> indices; |
|||
std::map<std::string, std::vector<std::string> > coefficients; |
|||
std::map<std::string, bool> nonfinal; |
|||
std::string globalOffset; |
|||
}; |
|||
|
|||
|
|||
|
|||
// Preprocessing result storing object
|
|||
class preprocessAux { |
|||
public: |
|||
preprocessAux() { |
|||
globalExterns = std::map<std::string, int>(); |
|||
localExterns = std::map<std::string, std::map<std::string, int> >(); |
|||
localExterns["self"] = std::map<std::string, int>(); |
|||
} |
|||
std::map<std::string, int> globalExterns; |
|||
std::map<std::string, std::string> globalExternSigs; |
|||
std::map<std::string, std::map<std::string, int> > localExterns; |
|||
std::map<std::string, std::map<std::string, std::string> > localExternSigs; |
|||
std::map<int, rewriteRuleSet > customMacros; |
|||
std::map<std::string, std::string> types; |
|||
svObj storageVars; |
|||
}; |
|||
|
|||
#define preprocessResult std::pair<Node, preprocessAux> |
|||
|
|||
// Populate an svObj with the arguments needed to determine
|
|||
// the storage position of a node
|
|||
svObj getStorageVars(svObj pre, Node node, std::string prefix="", |
|||
int index=0); |
|||
|
|||
// Preprocess a function (see cpp for details)
|
|||
preprocessResult preprocess(Node inp); |
|||
|
|||
|
|||
#endif |
@ -0,0 +1,211 @@ |
|||
#include <stdio.h> |
|||
#include <iostream> |
|||
#include <vector> |
|||
#include <map> |
|||
#include "util.h" |
|||
#include "lllparser.h" |
|||
#include "bignum.h" |
|||
#include "rewriteutils.h" |
|||
#include "optimize.h" |
|||
|
|||
// Valid functions and their min and max argument counts
|
|||
std::string validFunctions[][3] = { |
|||
{ "if", "2", "3" }, |
|||
{ "unless", "2", "2" }, |
|||
{ "while", "2", "2" }, |
|||
{ "until", "2", "2" }, |
|||
{ "alloc", "1", "1" }, |
|||
{ "array", "1", "1" }, |
|||
{ "call", "2", tt256 }, |
|||
{ "callcode", "2", tt256 }, |
|||
{ "create", "1", "4" }, |
|||
{ "getch", "2", "2" }, |
|||
{ "setch", "3", "3" }, |
|||
{ "sha3", "1", "2" }, |
|||
{ "return", "1", "2" }, |
|||
{ "inset", "1", "1" }, |
|||
{ "min", "2", "2" }, |
|||
{ "max", "2", "2" }, |
|||
{ "array_lit", "0", tt256 }, |
|||
{ "seq", "0", tt256 }, |
|||
{ "log", "1", "6" }, |
|||
{ "outer", "1", "1" }, |
|||
{ "set", "2", "2" }, |
|||
{ "get", "1", "1" }, |
|||
{ "ref", "1", "1" }, |
|||
{ "declare", "1", tt256 }, |
|||
{ "with", "3", "3" }, |
|||
{ "outer", "1", "1" }, |
|||
{ "mcopy", "3", "3" }, |
|||
{ "unsafe_mcopy", "3", "3" }, |
|||
{ "save", "3", "3" }, |
|||
{ "load", "2", "2" }, |
|||
{ "---END---", "", "" } //Keep this line at the end of the list
|
|||
}; |
|||
|
|||
std::map<std::string, bool> vfMap; |
|||
|
|||
// Is a function name one of the valid functions above?
|
|||
bool isValidFunctionName(std::string f) { |
|||
if (vfMap.size() == 0) { |
|||
for (int i = 0; ; i++) { |
|||
if (validFunctions[i][0] == "---END---") break; |
|||
vfMap[validFunctions[i][0]] = true; |
|||
} |
|||
} |
|||
return vfMap.count(f); |
|||
} |
|||
|
|||
// Cool function for debug purposes (named cerrStringList to make
|
|||
// all prints searchable via 'cerr')
|
|||
void cerrStringList(std::vector<std::string> s, std::string suffix) { |
|||
for (unsigned i = 0; i < s.size(); i++) std::cerr << s[i] << " "; |
|||
std::cerr << suffix << "\n"; |
|||
} |
|||
|
|||
// Convert:
|
|||
// self.cow -> ["cow"]
|
|||
// self.horse[0] -> ["horse", "0"]
|
|||
// self.a[6][7][self.storage[3]].chicken[9] ->
|
|||
// ["6", "7", (sload 3), "chicken", "9"]
|
|||
std::vector<Node> listfyStorageAccess(Node node) { |
|||
std::vector<Node> out; |
|||
std::vector<Node> nodez; |
|||
nodez.push_back(node); |
|||
while (1) { |
|||
if (nodez.back().type == TOKEN) { |
|||
out.push_back(token("--" + nodez.back().val, node.metadata)); |
|||
std::vector<Node> outrev; |
|||
for (int i = (signed)out.size() - 1; i >= 0; i--) { |
|||
outrev.push_back(out[i]); |
|||
} |
|||
return outrev; |
|||
} |
|||
if (nodez.back().val == ".") |
|||
nodez.back().args[1].val = "--" + nodez.back().args[1].val; |
|||
if (nodez.back().args.size() == 0) |
|||
err("Error parsing storage variable statement", node.metadata); |
|||
if (nodez.back().args.size() == 1) |
|||
out.push_back(token(tt256m1, node.metadata)); |
|||
else |
|||
out.push_back(nodez.back().args[1]); |
|||
nodez.push_back(nodez.back().args[0]); |
|||
} |
|||
} |
|||
|
|||
// Is the given node something of the form
|
|||
// self.cow
|
|||
// self.horse[0]
|
|||
// self.a[6][7][self.storage[3]].chicken[9]
|
|||
bool isNodeStorageVariable(Node node) { |
|||
std::vector<Node> nodez; |
|||
nodez.push_back(node); |
|||
while (1) { |
|||
if (nodez.back().type == TOKEN) return false; |
|||
if (nodez.back().args.size() == 0) return false; |
|||
if (nodez.back().val != "." && nodez.back().val != "access") |
|||
return false; |
|||
if (nodez.back().args[0].val == "self") return true; |
|||
nodez.push_back(nodez.back().args[0]); |
|||
} |
|||
} |
|||
|
|||
// Main pattern matching routine, for those patterns that can be expressed
|
|||
// using our standard mini-language above
|
|||
//
|
|||
// Returns two values. First, a boolean to determine whether the node matches
|
|||
// the pattern, second, if the node does match then a map mapping variables
|
|||
// in the pattern to nodes
|
|||
matchResult match(Node p, Node n) { |
|||
matchResult o; |
|||
o.success = false; |
|||
if (p.type == TOKEN) { |
|||
if (p.val == n.val && n.type == TOKEN) o.success = true; |
|||
else if (p.val[0] == '$' || p.val[0] == '@') { |
|||
o.success = true; |
|||
o.map[p.val.substr(1)] = n; |
|||
} |
|||
} |
|||
else if (n.type==TOKEN || p.val!=n.val || p.args.size()!=n.args.size()) { |
|||
// do nothing
|
|||
} |
|||
else { |
|||
for (unsigned i = 0; i < p.args.size(); i++) { |
|||
matchResult oPrime = match(p.args[i], n.args[i]); |
|||
if (!oPrime.success) { |
|||
o.success = false; |
|||
return o; |
|||
} |
|||
for (std::map<std::string, Node>::iterator it = oPrime.map.begin(); |
|||
it != oPrime.map.end(); |
|||
it++) { |
|||
o.map[(*it).first] = (*it).second; |
|||
} |
|||
} |
|||
o.success = true; |
|||
} |
|||
return o; |
|||
} |
|||
|
|||
|
|||
// Fills in the pattern with a dictionary mapping variable names to
|
|||
// nodes (these dicts are generated by match). Match and subst together
|
|||
// create a full pattern-matching engine.
|
|||
Node subst(Node pattern, |
|||
std::map<std::string, Node> dict, |
|||
std::string varflag, |
|||
Metadata m) { |
|||
// Swap out patterns at the token level
|
|||
if (pattern.metadata.ln == -1) |
|||
pattern.metadata = m; |
|||
if (pattern.type == TOKEN && |
|||
pattern.val[0] == '$') { |
|||
if (dict.count(pattern.val.substr(1))) { |
|||
return dict[pattern.val.substr(1)]; |
|||
} |
|||
else { |
|||
return token(varflag + pattern.val.substr(1), m); |
|||
} |
|||
} |
|||
// Other tokens are untouched
|
|||
else if (pattern.type == TOKEN) { |
|||
return pattern; |
|||
} |
|||
// Substitute recursively for ASTs
|
|||
else { |
|||
std::vector<Node> args; |
|||
for (unsigned i = 0; i < pattern.args.size(); i++) { |
|||
args.push_back(subst(pattern.args[i], dict, varflag, m)); |
|||
} |
|||
return asn(pattern.val, args, m); |
|||
} |
|||
} |
|||
|
|||
// Transforms a sequence containing two-argument with statements
|
|||
// into a statement containing those statements in nested form
|
|||
Node withTransform (Node source) { |
|||
Node o = token("--"); |
|||
Metadata m = source.metadata; |
|||
std::vector<Node> args; |
|||
for (int i = source.args.size() - 1; i >= 0; i--) { |
|||
Node a = source.args[i]; |
|||
if (a.val == "with" && a.args.size() == 2) { |
|||
std::vector<Node> flipargs; |
|||
for (int j = args.size() - 1; j >= 0; j--) |
|||
flipargs.push_back(args[i]); |
|||
if (o.val != "--") |
|||
flipargs.push_back(o); |
|||
o = asn("with", a.args[0], a.args[1], asn("seq", flipargs, m), m); |
|||
args = std::vector<Node>(); |
|||
} |
|||
else { |
|||
args.push_back(a); |
|||
} |
|||
} |
|||
std::vector<Node> flipargs; |
|||
for (int j = args.size() - 1; j >= 0; j--) |
|||
flipargs.push_back(args[j]); |
|||
if (o.val != "--") |
|||
flipargs.push_back(o); |
|||
return asn("seq", flipargs, m); |
|||
} |
@ -0,0 +1,76 @@ |
|||
#ifndef ETHSERP_REWRITEUTILS |
|||
#define ETHSERP_REWRITEUTILS |
|||
|
|||
#include <stdio.h> |
|||
#include <iostream> |
|||
#include <vector> |
|||
#include <map> |
|||
#include "util.h" |
|||
|
|||
// Valid functions and their min and max argument counts
|
|||
extern std::string validFunctions[][3]; |
|||
|
|||
extern std::map<std::string, bool> vfMap; |
|||
|
|||
bool isValidFunctionName(std::string f); |
|||
|
|||
// Converts deep array access into ordered list of the arguments
|
|||
// along the descent
|
|||
std::vector<Node> listfyStorageAccess(Node node); |
|||
|
|||
// Cool function for debug purposes (named cerrStringList to make
|
|||
// all prints searchable via 'cerr')
|
|||
void cerrStringList(std::vector<std::string> s, std::string suffix=""); |
|||
|
|||
// Is the given node something of the form
|
|||
// self.cow
|
|||
// self.horse[0]
|
|||
// self.a[6][7][self.storage[3]].chicken[9]
|
|||
bool isNodeStorageVariable(Node node); |
|||
|
|||
// Applies rewrite rules adding without wrapper
|
|||
Node rewriteChunk(Node inp); |
|||
|
|||
// Match result storing object
|
|||
struct matchResult { |
|||
bool success; |
|||
std::map<std::string, Node> map; |
|||
}; |
|||
|
|||
// Match node to pattern
|
|||
matchResult match(Node p, Node n); |
|||
|
|||
// Substitute node using pattern
|
|||
Node subst(Node pattern, |
|||
std::map<std::string, Node> dict, |
|||
std::string varflag, |
|||
Metadata m); |
|||
|
|||
Node withTransform(Node source); |
|||
|
|||
class rewriteRule { |
|||
public: |
|||
rewriteRule(Node p, Node s) { |
|||
pattern = p; |
|||
substitution = s; |
|||
} |
|||
Node pattern; |
|||
Node substitution; |
|||
}; |
|||
|
|||
class rewriteRuleSet { |
|||
public: |
|||
rewriteRuleSet() { |
|||
ruleLists = std::map<std::string, std::vector<rewriteRule> >(); |
|||
} |
|||
void addRule(rewriteRule r) { |
|||
if (!ruleLists.count(r.pattern.val)) |
|||
ruleLists[r.pattern.val] = std::vector<rewriteRule>(); |
|||
ruleLists[r.pattern.val].push_back(r); |
|||
} |
|||
std::map<std::string, std::vector<rewriteRule> > ruleLists; |
|||
}; |
|||
|
|||
|
|||
|
|||
#endif |
Loading…
Reference in new issue