Browse Source

Merge branch 'develop' into poc-7+

cl-refactor
Gav Wood 10 years ago
parent
commit
a04e7c5098
  1. 3
      libserpent/bignum.h
  2. 279
      libserpent/compiler.cpp
  3. 123
      libserpent/opcodes.h
  4. 86
      libserpent/parser.cpp
  5. 1206
      libserpent/rewriter.cpp
  6. 4
      libserpent/tokenize.cpp
  7. 57
      libserpent/util.cpp
  8. 36
      libserpent/util.h
  9. 12
      libsolidity/CompilerStack.cpp
  10. 9
      libsolidity/CompilerStack.h
  11. 13
      sc/cmdline.cpp

3
libserpent/bignum.h

@ -35,4 +35,7 @@ bool decimalGt(std::string a, std::string b, bool eqAllowed=false);
unsigned decimalToUnsigned(std::string a); unsigned decimalToUnsigned(std::string a);
#define utd unsignedToDecimal
#define dtu decimalToUnsigned
#endif #endif

279
libserpent/compiler.cpp

@ -6,6 +6,7 @@
#include "bignum.h" #include "bignum.h"
#include "opcodes.h" #include "opcodes.h"
// Auxiliary data that is gathered while compiling
struct programAux { struct programAux {
std::map<std::string, std::string> vars; std::map<std::string, std::string> vars;
int nextVarMem; int nextVarMem;
@ -13,15 +14,19 @@ struct programAux {
bool calldataUsed; bool calldataUsed;
int step; int step;
int labelLength; int labelLength;
int functionCount;
}; };
// Auxiliary data that gets passed down vertically
// but not back up
struct programVerticalAux { struct programVerticalAux {
int height; int height;
std::string innerScopeName;
std::map<std::string, int> dupvars; std::map<std::string, int> dupvars;
std::map<std::string, int> funvars; std::map<std::string, int> funvars;
std::vector<mss> scopes;
}; };
// Compilation result
struct programData { struct programData {
programAux aux; programAux aux;
Node code; Node code;
@ -34,7 +39,6 @@ programAux Aux() {
o.calldataUsed = false; o.calldataUsed = false;
o.step = 0; o.step = 0;
o.nextVarMem = 32; o.nextVarMem = 32;
o.functionCount = 0;
return o; return o;
} }
@ -43,6 +47,7 @@ programVerticalAux verticalAux() {
o.height = 0; o.height = 0;
o.dupvars = std::map<std::string, int>(); o.dupvars = std::map<std::string, int>();
o.funvars = std::map<std::string, int>(); o.funvars = std::map<std::string, int>();
o.scopes = std::vector<mss>();
return o; return o;
} }
@ -72,29 +77,58 @@ Node popwrap(Node node) {
return multiToken(nodelist, 2, node.metadata); return multiToken(nodelist, 2, node.metadata);
} }
// Grabs variables
mss getVariables(Node node, mss cur=mss()) {
Metadata m = node.metadata;
// Tokens don't contain any variables
if (node.type == TOKEN)
return cur;
// Don't descend into call fragments
else if (node.val == "lll")
return getVariables(node.args[1], cur);
// At global scope get/set/ref also declare
else if (node.val == "get" || node.val == "set" || node.val == "ref") {
if (node.args[0].type != TOKEN)
err("Variable name must be simple token,"
" not complex expression! " + printSimple(node.args[0]), m);
if (!cur.count(node.args[0].val)) {
cur[node.args[0].val] = utd(cur.size() * 32 + 32);
//std::cerr << node.args[0].val << " " << cur[node.args[0].val] << "\n";
}
}
// Recursively process children
for (unsigned i = 0; i < node.args.size(); i++) {
cur = getVariables(node.args[i], cur);
}
return cur;
}
// Turns LLL tree into tree of code fragments // Turns LLL tree into tree of code fragments
programData opcodeify(Node node, programData opcodeify(Node node,
programAux aux=Aux(), programAux aux=Aux(),
programVerticalAux vaux=verticalAux()) { programVerticalAux vaux=verticalAux()) {
std::string symb = "_"+mkUniqueToken(); std::string symb = "_"+mkUniqueToken();
Metadata m = node.metadata; Metadata m = node.metadata;
// Get variables
if (!aux.vars.size()) {
aux.vars = getVariables(node);
aux.nextVarMem = aux.vars.size() * 32 + 32;
}
// Numbers // Numbers
if (node.type == TOKEN) { if (node.type == TOKEN) {
return pd(aux, nodeToNumeric(node), 1); return pd(aux, nodeToNumeric(node), 1);
} }
else if (node.val == "ref" || node.val == "get" || else if (node.val == "ref" || node.val == "get" || node.val == "set") {
node.val == "set" || node.val == "declare") {
std::string varname = node.args[0].val; std::string varname = node.args[0].val;
if (!aux.vars.count(varname)) { // Determine reference to variable
aux.vars[varname] = unsignedToDecimal(aux.nextVarMem); Node varNode = tkn(aux.vars[varname], m);
aux.nextVarMem += 32; //std::cerr << varname << " " << printSimple(varNode) << "\n";
}
if (varname == "'msg.data") aux.calldataUsed = true;
// Set variable // Set variable
if (node.val == "set") { if (node.val == "set") {
programData sub = opcodeify(node.args[1], aux, vaux); programData sub = opcodeify(node.args[1], aux, vaux);
if (!sub.outs) if (!sub.outs)
err("Value to set variable must have nonzero arity!", m); err("Value to set variable must have nonzero arity!", m);
// What if we are setting a stack variable?
if (vaux.dupvars.count(node.args[0].val)) { if (vaux.dupvars.count(node.args[0].val)) {
int h = vaux.height - vaux.dupvars[node.args[0].val]; int h = vaux.height - vaux.dupvars[node.args[0].val];
if (h > 16) err("Too deep for stack variable (max 16)", m); if (h > 16) err("Too deep for stack variable (max 16)", m);
@ -105,149 +139,65 @@ programData opcodeify(Node node,
}; };
return pd(sub.aux, multiToken(nodelist, 3, m), 0); return pd(sub.aux, multiToken(nodelist, 3, m), 0);
} }
Node nodelist[] = { // Setting a memory variable
sub.code, else {
token(sub.aux.vars[varname], m), Node nodelist[] = {
token("MSTORE", m), sub.code,
}; varNode,
return pd(sub.aux, multiToken(nodelist, 3, m), 0); token("MSTORE", m),
};
return pd(sub.aux, multiToken(nodelist, 3, m), 0);
}
} }
// Get variable // Get variable
else if (node.val == "get") { else if (node.val == "get") {
if (vaux.dupvars.count(node.args[0].val)) { // Getting a stack variable
if (vaux.dupvars.count(node.args[0].val)) {
int h = vaux.height - vaux.dupvars[node.args[0].val]; int h = vaux.height - vaux.dupvars[node.args[0].val];
if (h > 16) err("Too deep for stack variable (max 16)", m); if (h > 16) err("Too deep for stack variable (max 16)", m);
return pd(aux, token("DUP"+unsignedToDecimal(h)), 1); return pd(aux, token("DUP"+unsignedToDecimal(h)), 1);
} }
Node nodelist[] = // Getting a memory variable
{ token(aux.vars[varname], m), token("MLOAD", m) }; else {
return pd(aux, multiToken(nodelist, 2, m), 1); Node nodelist[] =
{ varNode, token("MLOAD", m) };
return pd(aux, multiToken(nodelist, 2, m), 1);
}
} }
// Refer variable // Refer variable
else if (node.val == "ref") { else if (node.val == "ref") {
if (vaux.dupvars.count(node.args[0].val)) if (vaux.dupvars.count(node.args[0].val))
err("Cannot ref stack variable!", m); err("Cannot ref stack variable!", m);
return pd(aux, token(aux.vars[varname], m), 1); return pd(aux, varNode, 1);
}
// Declare variable
else {
return pd(aux, multiToken(nullptr, 0, m), 0);
} }
} }
// Define functions (TODO: eventually move to rewriter.cpp, keep // Comments do nothing
// compiler pure LLL) else if (node.val == "comment") {
if (node.val == "def") { Node nodelist[] = { };
std::vector<std::string> varNames; return pd(aux, multiToken(nodelist, 0, m), 0);
std::vector<int> varSizes; }
bool useLt32 = false; // Custom operation sequence
int totalSz = 0; // eg. (ops bytez id msize swap1 msize add 0 swap1 mstore) == alloc
if (node.args.size() != 2) if (node.val == "ops") {
err("Malformed def!", m); std::vector<Node> subs2;
// Collect the list of variable names and variable byte counts int depth = 0;
for (unsigned i = 0; i < node.args[0].args.size(); i++) { for (unsigned i = 0; i < node.args.size(); i++) {
if (node.args[0].args[i].val == "kv") { std::string op = upperCase(node.args[i].val);
if (node.args[0].args[i].args.size() != 2) if (node.args[i].type == ASTNODE || opinputs(op) == -1) {
err("Malformed def!", m); programVerticalAux vaux2 = vaux;
varNames.push_back(node.args[0].args[i].args[0].val); vaux2.height = vaux.height - i - 1 + node.args.size();
varSizes.push_back( programData sub = opcodeify(node.args[i], aux, vaux2);
decimalToUnsigned(node.args[0].args[i].args[1].val)); aux = sub.aux;
if (varSizes.back() > 32) depth += sub.outs;
err("Max argument width: 32 bytes", m); subs2.push_back(sub.code);
useLt32 = true;
} }
else { else {
varNames.push_back(node.args[0].args[i].val); subs2.push_back(token(op, m));
varSizes.push_back(32); depth += opoutputs(op) - opinputs(op);
} }
aux.vars[varNames.back()] = unsignedToDecimal(aux.nextVarMem + 32 * i);
totalSz += varSizes.back();
} }
int functionCount = aux.functionCount; if (depth < 0 || depth > 1) err("Stack depth mismatch", m);
int nextVarMem = aux.nextVarMem; return pd(aux, astnode("_", subs2, m), 0);
aux.nextVarMem += 32 * varNames.size();
aux.functionCount += 1;
programData inner;
// If we're only using 32-byte variables, then great, just copy
// over the calldata!
if (!useLt32) {
programData sub = opcodeify(node.args[1], aux, vaux);
Node nodelist[] = {
token(unsignedToDecimal(totalSz), m),
token("1", m),
token(unsignedToDecimal(nextVarMem), m),
token("CALLDATACOPY", m),
sub.code
};
inner = pd(sub.aux, multiToken(nodelist, 5, m), 0);
}
else {
std::vector<Node> innerList;
int cum = 1;
for (unsigned i = 0; i < varNames.size();) {
// If we get a series of 32-byte values, we calldatacopy them
if (varSizes[i] == 32) {
unsigned until = i+1;
while (until < varNames.size() && varSizes[until] == 32)
until += 1;
innerList.push_back(token(unsignedToDecimal((until - i) * 32), m));
innerList.push_back(token(unsignedToDecimal(cum), m));
innerList.push_back(token(unsignedToDecimal(nextVarMem + i * 32), m));
innerList.push_back(token("CALLDATACOPY", m));
cum += (until - i) * 32;
i = until;
}
// Otherwise, we do a clever trick to extract the value
else {
innerList.push_back(token(unsignedToDecimal(32 - varSizes[i]), m));
innerList.push_back(token("256", m));
innerList.push_back(token("EXP", m));
innerList.push_back(token(unsignedToDecimal(cum), m));
innerList.push_back(token("CALLDATALOAD", m));
innerList.push_back(token("DIV", m));
innerList.push_back(token(unsignedToDecimal(nextVarMem + i * 32), m));
innerList.push_back(token("MSTORE", m));
cum += varSizes[i];
i += 1;
}
}
// If caller == origin, then it's from a tx, so unpack, otherwise
// plain copy
programData sub = opcodeify(node.args[1], aux, vaux);
Node ilnode = astnode("", innerList, m);
Node nodelist[] = {
token(unsignedToDecimal(32 * varNames.size()), m),
token("1", m),
token(unsignedToDecimal(nextVarMem), m),
token("CALLDATACOPY", m),
token("CALLER", m),
token("ORIGIN", m),
token("EQ", m),
token("ISZERO", m),
token("$maincode"+symb, m),
token("JUMPI", m),
ilnode,
token("~maincode"+symb, m),
token("JUMPDEST", m),
sub.code
};
inner = pd(sub.aux, multiToken(nodelist, 14, m), 0);
}
// Check if the function call byte is the same
Node nodelist2[] = {
token("0", m),
token("CALLDATALOAD", m),
token("0", m),
token("BYTE", m),
token(unsignedToDecimal(functionCount), m),
token("EQ", m),
token("ISZERO", m),
token("$endcode"+symb, m),
token("JUMPI", m),
inner.code,
token("~endcode"+symb, m),
token("JUMPDEST", m),
};
return pd(inner.aux, multiToken(nodelist2, 12, m), 0);
} }
// Code blocks // Code blocks
if (node.val == "lll" && node.args.size() == 2) { if (node.val == "lll" && node.args.size() == 2) {
@ -372,49 +322,14 @@ programData opcodeify(Node node,
}; };
return pd(aux, multiToken(nodelist, 8, m), 1); return pd(aux, multiToken(nodelist, 8, m), 1);
} }
// Array literals
else if (node.val == "array_lit") {
aux.allocUsed = true;
std::vector<Node> nodes;
if (!node.args.size()) {
nodes.push_back(token("MSIZE", m));
return pd(aux, astnode("_", nodes, m));
}
nodes.push_back(token("MSIZE", m));
nodes.push_back(token("0", m));
nodes.push_back(token("MSIZE", m));
nodes.push_back(token(unsignedToDecimal(node.args.size() * 32 - 1), m));
nodes.push_back(token("ADD", m));
nodes.push_back(token("MSTORE8", m));
for (unsigned i = 0; i < node.args.size(); i++) {
Metadata m2 = node.args[i].metadata;
nodes.push_back(token("DUP1", m2));
programVerticalAux vaux2 = vaux;
vaux2.height += 2;
programData sub = opcodeify(node.args[i], aux, vaux2);
if (!sub.outs)
err("Array_lit item " + unsignedToDecimal(i) + " has zero arity", m2);
aux = sub.aux;
nodes.push_back(sub.code);
nodes.push_back(token("SWAP1", m2));
if (i > 0) {
nodes.push_back(token(unsignedToDecimal(i * 32), m2));
nodes.push_back(token("ADD", m2));
}
nodes.push_back(token("MSTORE", m2));
}
return pd(aux, astnode("_", nodes, m), 1);
}
// All other functions/operators // All other functions/operators
else { else {
std::vector<Node> subs2; std::vector<Node> subs2;
int depth = opinputs(upperCase(node.val)); int depth = opinputs(upperCase(node.val));
if (node.val != "debug") { if (depth == -1)
if (depth == -1) err("Not a function or opcode: "+node.val, m);
err("Not a function or opcode: "+node.val, m); if ((int)node.args.size() != depth)
if ((int)node.args.size() != depth) err("Invalid arity for "+node.val, m);
err("Invalid arity for "+node.val, m);
}
for (int i = node.args.size() - 1; i >= 0; i--) { for (int i = node.args.size() - 1; i >= 0; i--) {
programVerticalAux vaux2 = vaux; programVerticalAux vaux2 = vaux;
vaux2.height = vaux.height - i - 1 + node.args.size(); vaux2.height = vaux.height - i - 1 + node.args.size();
@ -424,13 +339,8 @@ programData opcodeify(Node node,
err("Input "+unsignedToDecimal(i)+" has arity 0", sub.code.metadata); err("Input "+unsignedToDecimal(i)+" has arity 0", sub.code.metadata);
subs2.push_back(sub.code); subs2.push_back(sub.code);
} }
if (node.val == "debug") { subs2.push_back(token(upperCase(node.val), m));
subs2.push_back(token("DUP"+unsignedToDecimal(node.args.size()), m)); int outdepth = opoutputs(upperCase(node.val));
for (int i = 0; i <= (int)node.args.size(); i++)
subs2.push_back(token("POP", m));
}
else subs2.push_back(token(upperCase(node.val), m));
int outdepth = node.val == "debug" ? 0 : opoutputs(upperCase(node.val));
return pd(aux, astnode("_", subs2, m), outdepth); return pd(aux, astnode("_", subs2, m), outdepth);
} }
} }
@ -449,15 +359,6 @@ Node finalize(programData c) {
}; };
bottom.push_back(multiToken(nodelist, 3, m)); bottom.push_back(multiToken(nodelist, 3, m));
} }
// If msg.data is being used as an array, then we need to copy it
if (c.aux.calldataUsed) {
Node nodelist[] = {
token("MSIZE", m), token("CALLDATASIZE", m), token("0", m),
token("MSIZE", m), token("CALLDATACOPY", m),
token(c.aux.vars["'msg.data"], m), token("MSTORE", m)
};
bottom.push_back(multiToken(nodelist, 7, m));
}
// The actual code // The actual code
bottom.push_back(c.code); bottom.push_back(c.code);
return astnode("_", bottom, m); return astnode("_", bottom, m);

123
libserpent/opcodes.h

@ -5,6 +5,7 @@
#include <iostream> #include <iostream>
#include <vector> #include <vector>
#include <map> #include <map>
#include "util.h"
class Mapping { class Mapping {
public: public:
@ -20,119 +21,25 @@ class Mapping {
int out; int out;
}; };
Mapping mapping[] = { extern 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("ADDMOD", 0x14, 3, 1),
Mapping("MULMOD", 0x15, 3, 1),
Mapping("SIGNEXTEND", 0x16, 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, 1),
Mapping("CODESIZE", 0x38, 0, 1),
Mapping("CODECOPY", 0x39, 3, 1),
Mapping("GASPRICE", 0x3a, 0, 1),
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("RETURN", 0xf2, 2, 0),
Mapping("CALL_CODE", 0xf3, 7, 1),
Mapping("SUICIDE", 0xff, 1, 0),
Mapping("---END---", 0x00, 0, 0),
};
std::map<std::string, std::vector<int> > opcodes; extern std::map<std::string, std::vector<int> > opcodes;
std::map<int, std::string> reverseOpcodes; extern std::map<int, std::string> reverseOpcodes;
// Fetches everything EXCEPT PUSH1..32 std::pair<std::string, std::vector<int> > _opdata(std::string ops, int opi);
std::pair<std::string, std::vector<int> > _opdata(std::string ops, int opi) {
if (!opcodes.size()) { int opcode(std::string op);
int i = 0;
while (mapping[i].op != "---END---") { int opinputs(std::string op);
Mapping mi = mapping[i];
opcodes[mi.op] = triple(mi.opcode, mi.in, mi.out); int opoutputs(std::string op);
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;
}
}
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) { std::string op(int opcode);
return _opdata(op, -1).second[0];
}
int opinputs(std::string op) { extern std::string lllSpecials[][3];
return _opdata(op, -1).second[1];
}
int opoutputs(std::string op) { extern std::map<std::string, std::pair<int, int> > lllMap;
return _opdata(op, -1).second[2];
}
std::string op(int opcode) { bool isValidLLLFunc(std::string f, int argc);
return _opdata("", opcode).first;
}
#endif #endif

86
libserpent/parser.cpp

@ -12,17 +12,15 @@ int precedence(Node tok) {
if (v == ".") return -1; if (v == ".") return -1;
else if (v == "!" || v == "not") return 1; else if (v == "!" || v == "not") return 1;
else if (v=="^" || v == "**") return 2; else if (v=="^" || v == "**") return 2;
else if (v=="*" || v=="/" || v=="@/" || v=="%" || v=="@%") return 3; else if (v=="*" || v=="/" || v=="%") return 3;
else if (v=="+" || v=="-") return 4; else if (v=="+" || v=="-") return 4;
else if (v=="<" || v==">" || v=="<=" || v==">=") return 5; else if (v=="<" || v==">" || v=="<=" || v==">=") return 5;
else if (v=="@<" || v=="@>" || v=="@<=" || v=="@>=") return 5;
else if (v=="&" || v=="|" || v=="xor" || v=="==" || v == "!=") return 6; else if (v=="&" || v=="|" || v=="xor" || v=="==" || v == "!=") return 6;
else if (v=="&&" || v=="and") return 7; else if (v=="&&" || v=="and") return 7;
else if (v=="||" || v=="or") return 8; else if (v=="||" || v=="or") return 8;
else if (v==":") return 9;
else if (v=="=") return 10; else if (v=="=") return 10;
else if (v=="+=" || v=="-=" || v=="*=" || v=="/=" || v=="%=") return 10; else if (v=="+=" || v=="-=" || v=="*=" || v=="/=" || v=="%=") return 10;
else if (v=="@/=" || v=="@%=") return 10; else if (v==":" || v == "::") return 11;
else return 0; else return 0;
} }
@ -223,8 +221,15 @@ Node treefy(std::vector<Node> stream) {
filename = filename.substr(1, filename.length() - 2); filename = filename.substr(1, filename.length() - 2);
if (!exists(root + filename)) if (!exists(root + filename))
err("File does not exist: "+root + filename, tok.metadata); err("File does not exist: "+root + filename, tok.metadata);
oq.back().args.pop_back(); if (v == "inset") {
oq.back().args.push_back(parseSerpent(root + filename)); oq.pop_back();
oq.push_back(parseSerpent(root + filename));
}
else {
oq.back().args.pop_back();
oq.back().args.push_back(
asn("outer", parseSerpent(root + filename), tok.metadata));
}
} }
//Useful for debugging //Useful for debugging
//for (int i = 0; i < oq.size(); i++) { //for (int i = 0; i < oq.size(); i++) {
@ -237,7 +242,7 @@ Node treefy(std::vector<Node> stream) {
err("Output blank", Metadata()); err("Output blank", Metadata());
} }
else if (oq.size() > 1) { else if (oq.size() > 1) {
err("Multiple expressions or unclosed bracket", oq[1].metadata); return asn("multi", oq, oq[0].metadata);
} }
return oq[0]; return oq[0];
@ -262,15 +267,9 @@ int spaceCount(std::string s) {
bool bodied(std::string tok) { bool bodied(std::string tok) {
return tok == "if" || tok == "elif" || tok == "while" return tok == "if" || tok == "elif" || tok == "while"
|| tok == "with" || tok == "def" || tok == "extern" || tok == "with" || tok == "def" || tok == "extern"
|| tok == "data"; || tok == "data" || tok == "assert" || tok == "return"
} || tok == "fun" || tok == "scope" || tok == "macro"
|| tok == "type";
// Is this a command that takes an argument as a child block?
bool childBlocked(std::string tok) {
return tok == "if" || tok == "elif" || tok == "else"
|| tok == "code" || tok == "shared" || tok == "init"
|| tok == "while" || tok == "repeat" || tok == "for"
|| tok == "with" || tok == "def";
} }
// Are the two commands meant to continue each other? // Are the two commands meant to continue each other?
@ -278,10 +277,7 @@ bool bodiedContinued(std::string prev, std::string tok) {
return (prev == "if" && tok == "elif") return (prev == "if" && tok == "elif")
|| (prev == "elif" && tok == "else") || (prev == "elif" && tok == "else")
|| (prev == "elif" && tok == "elif") || (prev == "elif" && tok == "elif")
|| (prev == "if" && tok == "else") || (prev == "if" && tok == "else");
|| (prev == "init" && tok == "code")
|| (prev == "shared" && tok == "code")
|| (prev == "shared" && tok == "init");
} }
// Is a line of code empty? // Is a line of code empty?
@ -310,16 +306,17 @@ Node parseLines(std::vector<std::string> lines, Metadata metadata, int sp) {
} }
// Tokenize current line // Tokenize current line
std::vector<Node> tokens = tokenize(main.substr(sp), metadata); std::vector<Node> tokens = tokenize(main.substr(sp), metadata);
// Remove extraneous tokens, including if / elif // Remove comments
std::vector<Node> tokens2; std::vector<Node> tokens2;
for (unsigned j = 0; j < tokens.size(); j++) { for (unsigned j = 0; j < tokens.size(); j++) {
if (tokens[j].val == "#" || tokens[j].val == "//") break; if (tokens[j].val == "#" || tokens[j].val == "//") break;
if (j >= 1 || !bodied(tokens[j].val)) { tokens2.push_back(tokens[j]);
tokens2.push_back(tokens[j]);
}
} }
if (tokens2.size() > 0 && tokens2.back().val == ":") bool expectingChildBlock = false;
if (tokens2.size() > 0 && tokens2.back().val == ":") {
tokens2.pop_back(); tokens2.pop_back();
expectingChildBlock = true;
}
// Parse current line // Parse current line
Node out = parseSerpentTokenStream(tokens2); Node out = parseSerpentTokenStream(tokens2);
// Parse child block // Parse child block
@ -343,14 +340,8 @@ Node parseLines(std::vector<std::string> lines, Metadata metadata, int sp) {
for (unsigned i = 0; i < childBlock.size(); i++) { for (unsigned i = 0; i < childBlock.size(); i++) {
if (childBlock[i].length() > 0) { cbe = false; break; } if (childBlock[i].length() > 0) { cbe = false; break; }
} }
// Bring back if / elif into AST
if (bodied(tokens[0].val)) {
std::vector<Node> args;
args.push_back(out);
out = astnode(tokens[0].val, args, out.metadata);
}
// Add child block to AST // Add child block to AST
if (childBlocked(tokens[0].val)) { if (expectingChildBlock) {
if (cbe) if (cbe)
err("Expected indented child block!", out.metadata); err("Expected indented child block!", out.metadata);
out.type = ASTNODE; out.type = ASTNODE;
@ -360,6 +351,37 @@ Node parseLines(std::vector<std::string> lines, Metadata metadata, int sp) {
} }
else if (!cbe) else if (!cbe)
err("Did not expect indented child block!", out.metadata); err("Did not expect indented child block!", out.metadata);
else if (out.args.size() && out.args[out.args.size() - 1].val == ":") {
Node n = out.args[out.args.size() - 1];
out.args.pop_back();
out.args.push_back(n.args[0]);
out.args.push_back(n.args[1]);
}
// Bring back if / elif into AST
if (bodied(tokens[0].val)) {
if (out.val != "multi") {
// token not being used in bodied form
}
else if (out.args[0].val == "id")
out = astnode(tokens[0].val, out.args[1].args, out.metadata);
else if (out.args[0].type == TOKEN) {
std::vector<Node> out2;
for (unsigned i = 1; i < out.args.size(); i++)
out2.push_back(out.args[i]);
out = astnode(tokens[0].val, out2, out.metadata);
}
else
out = astnode("fun", out.args, out.metadata);
}
// Multi not supported
if (out.val == "multi")
err("Multiple expressions or unclosed bracket", out.metadata);
// Convert top-level colon expressions into non-colon expressions;
// makes if statements and the like equivalent indented or not
//if (out.val == ":" && out.args[0].type == TOKEN)
// out = asn(out.args[0].val, out.args[1], out.metadata);
//if (bodied(tokens[0].val) && out.args[0].val == ":")
// out = asn(tokens[0].val, out.args[0].args);
if (o.size() == 0 || o.back().type == TOKEN) { if (o.size() == 0 || o.back().type == TOKEN) {
o.push_back(out); o.push_back(out);
continue; continue;

1206
libserpent/rewriter.cpp

File diff suppressed because it is too large

4
libserpent/tokenize.cpp

@ -13,8 +13,8 @@ int chartype(char c) {
if (c >= '0' && c <= '9') return ALPHANUM; if (c >= '0' && c <= '9') return ALPHANUM;
else if (c >= 'a' && c <= 'z') return ALPHANUM; else if (c >= 'a' && c <= 'z') return ALPHANUM;
else if (c >= 'A' && c <= 'Z') return ALPHANUM; else if (c >= 'A' && c <= 'Z') return ALPHANUM;
else if (std::string("~_$").find(c) != std::string::npos) return ALPHANUM; else if (std::string("~_$@").find(c) != std::string::npos) return ALPHANUM;
else if (c == '\t' || c == ' ' || c == '\n') return SPACE; else if (c == '\t' || c == ' ' || c == '\n' || c == '\r') return SPACE;
else if (std::string("()[]{}").find(c) != std::string::npos) return BRACK; else if (std::string("()[]{}").find(c) != std::string::npos) return BRACK;
else if (c == '"') return DQUOTE; else if (c == '"') return DQUOTE;
else if (c == '\'') return SQUOTE; else if (c == '\'') return SQUOTE;

57
libserpent/util.cpp

@ -2,7 +2,6 @@
#include <iostream> #include <iostream>
#include <vector> #include <vector>
#include <map> #include <map>
#include <string>
#include "util.h" #include "util.h"
#include "bignum.h" #include "bignum.h"
#include <fstream> #include <fstream>
@ -28,6 +27,11 @@ Node astnode(std::string val, std::vector<Node> args, Metadata met) {
} }
//AST node constructors for a specific number of children //AST node constructors for a specific number of children
Node astnode(std::string val, Metadata met) {
std::vector<Node> args;
return astnode(val, args, met);
}
Node astnode(std::string val, Node a, Metadata met) { Node astnode(std::string val, Node a, Metadata met) {
std::vector<Node> args; std::vector<Node> args;
args.push_back(a); args.push_back(a);
@ -49,6 +53,16 @@ Node astnode(std::string val, Node a, Node b, Node c, Metadata met) {
return astnode(val, args, met); return astnode(val, args, met);
} }
Node astnode(std::string val, Node a, Node b, Node c, Node d, Metadata met) {
std::vector<Node> args;
args.push_back(a);
args.push_back(b);
args.push_back(c);
args.push_back(d);
return astnode(val, args, met);
}
// Print token list // Print token list
std::string printTokens(std::vector<Node> tokens) { std::string printTokens(std::vector<Node> tokens) {
std::string s = ""; std::string s = "";
@ -146,6 +160,15 @@ std::string indentLines(std::string inp) {
return joinLines(lines); return joinLines(lines);
} }
// Binary to hexadecimal
std::string binToNumeric(std::string inp) {
std::string o = "0";
for (unsigned i = 0; i < inp.length(); i++) {
o = decimalAdd(decimalMul(o,"256"), unsignedToDecimal((unsigned char)inp[i]));
}
return o;
}
// Converts string to simple numeric format // Converts string to simple numeric format
std::string strToNumeric(std::string inp) { std::string strToNumeric(std::string inp) {
std::string o = "0"; std::string o = "0";
@ -154,7 +177,7 @@ std::string strToNumeric(std::string inp) {
} }
else if ((inp[0] == '"' && inp[inp.length()-1] == '"') else if ((inp[0] == '"' && inp[inp.length()-1] == '"')
|| (inp[0] == '\'' && inp[inp.length()-1] == '\'')) { || (inp[0] == '\'' && inp[inp.length()-1] == '\'')) {
for (unsigned i = 1; i < inp.length() - 1; i++) { for (unsigned i = 1; i < inp.length() - 1; i++) {
o = decimalAdd(decimalMul(o,"256"), unsignedToDecimal((unsigned char)inp[i])); o = decimalAdd(decimalMul(o,"256"), unsignedToDecimal((unsigned char)inp[i]));
} }
} }
@ -181,6 +204,14 @@ bool isNumberLike(Node node) {
return strToNumeric(node.val) != ""; return strToNumeric(node.val) != "";
} }
// Is the number decimal?
bool isDecimal(std::string inp) {
for (unsigned i = 0; i < inp.length(); i++) {
if (inp[i] < '0' || inp[i] > '9') return false;
}
return true;
}
//Normalizes number representations //Normalizes number representations
Node nodeToNumeric(Node node) { Node nodeToNumeric(Node node) {
std::string o = strToNumeric(node.val); std::string o = strToNumeric(node.val);
@ -246,6 +277,14 @@ void err(std::string errtext, Metadata met) {
throw(err); throw(err);
} }
//Report warning
void warn(std::string errtext, Metadata met) {
std::string err = "Warning (file \"" + met.file + "\", line " +
unsignedToDecimal(met.ln + 1) + ", char " + unsignedToDecimal(met.ch) +
"): " + errtext;
std::cerr << err << "\n";
}
//Bin to hex //Bin to hex
std::string binToHex(std::string inp) { std::string binToHex(std::string inp) {
std::string o = ""; std::string o = "";
@ -280,7 +319,15 @@ std::string upperCase(std::string inp) {
//Three-int vector //Three-int vector
std::vector<int> triple(int a, int b, int c) { std::vector<int> triple(int a, int b, int c) {
std::vector<int> o; std::vector<int> v;
o.push_back(a); o.push_back(b); o.push_back(c); v.push_back(a);
return o; v.push_back(b);
v.push_back(c);
return v;
}
//Extend node vector
std::vector<Node> extend(std::vector<Node> a, std::vector<Node> b) {
for (unsigned i = 0; i < b.size(); i++) a.push_back(b[i]);
return a;
} }

36
libserpent/util.h

@ -28,30 +28,36 @@ const int TOKEN = 0,
// Stores metadata about each token // Stores metadata about each token
class Metadata { class Metadata {
public: public:
Metadata(std::string File="main", int Ln=0, int Ch=0) { Metadata(std::string File="main", int Ln=-1, int Ch=-1) {
file = File; file = File;
ln = Ln; ln = Ln;
ch = Ch; ch = Ch;
fixed = false;
} }
std::string file; std::string file;
int ln; int ln;
int ch; int ch;
bool fixed;
}; };
std::string mkUniqueToken(); std::string mkUniqueToken();
// type can be TOKEN or ASTNODE // type can be TOKEN or ASTNODE
struct Node { class Node {
int type; public:
std::string val; int type;
std::vector<Node> args; std::string val;
Metadata metadata; std::vector<Node> args;
Metadata metadata;
}; };
Node token(std::string val, Metadata met=Metadata()); Node token(std::string val, Metadata met=Metadata());
Node astnode(std::string val, std::vector<Node> args, Metadata met=Metadata()); Node astnode(std::string val, std::vector<Node> args, Metadata met=Metadata());
Node astnode(std::string val, Metadata met=Metadata());
Node astnode(std::string val, Node a, Metadata met=Metadata()); Node astnode(std::string val, Node a, Metadata met=Metadata());
Node astnode(std::string val, Node a, Node b, Metadata met=Metadata()); Node astnode(std::string val, Node a, Node b, Metadata met=Metadata());
Node astnode(std::string val, Node a, Node b, Node c, Metadata met=Metadata()); Node astnode(std::string val, Node a, Node b, Node c, Metadata met=Metadata());
Node astnode(std::string val, Node a, Node b,
Node c, Node d, Metadata met=Metadata());
// Number of tokens in a tree // Number of tokens in a tree
int treeSize(Node prog); int treeSize(Node prog);
@ -74,6 +80,9 @@ std::string joinLines(std::vector<std::string> lines);
// Indent all lines by 4 spaces // Indent all lines by 4 spaces
std::string indentLines(std::string inp); std::string indentLines(std::string inp);
// Converts binary to simple numeric format
std::string binToNumeric(std::string inp);
// Converts string to simple numeric format // Converts string to simple numeric format
std::string strToNumeric(std::string inp); std::string strToNumeric(std::string inp);
@ -98,6 +107,9 @@ bool exists(std::string fileName);
//Report error //Report error
void err(std::string errtext, Metadata met); void err(std::string errtext, Metadata met);
//Report warning
void warn(std::string errtext, Metadata met);
//Bin to hex //Bin to hex
std::string binToHex(std::string inp); std::string binToHex(std::string inp);
@ -110,4 +122,16 @@ std::string upperCase(std::string inp);
//Three-int vector //Three-int vector
std::vector<int> triple(int a, int b, int c); std::vector<int> triple(int a, int b, int c);
//Extend node vector
std::vector<Node> extend(std::vector<Node> a, std::vector<Node> b);
// Is the number decimal?
bool isDecimal(std::string inp);
#define asn astnode
#define tkn token
#define msi std::map<std::string, int>
#define msn std::map<std::string, Node>
#define mss std::map<std::string, std::string>
#endif #endif

12
libsolidity/CompilerStack.cpp

@ -36,13 +36,12 @@ namespace dev
namespace solidity namespace solidity
{ {
void CompilerStack::addSource(string const& _name, string const& _content) bool CompilerStack::addSource(string const& _name, string const& _content)
{ {
if (m_sources.count(_name)) bool existed = m_sources.count(_name);
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Source by given name already exists."));
reset(true); reset(true);
m_sources[_name].scanner = make_shared<Scanner>(CharStream(_content), _name); m_sources[_name].scanner = make_shared<Scanner>(CharStream(_content), _name);
return existed;
} }
void CompilerStack::setSource(string const& _sourceCode) void CompilerStack::setSource(string const& _sourceCode)
@ -181,6 +180,11 @@ SourceUnit const& CompilerStack::getAST(string const& _sourceName) const
return *getSource(_sourceName).ast; return *getSource(_sourceName).ast;
} }
ContractDefinition const& CompilerStack::getContractDefinition(string const& _contractName) const
{
return *getContract(_contractName).contract;
}
bytes CompilerStack::staticCompile(std::string const& _sourceCode, bool _optimize) bytes CompilerStack::staticCompile(std::string const& _sourceCode, bool _optimize)
{ {
CompilerStack stack; CompilerStack stack;

9
libsolidity/CompilerStack.h

@ -57,7 +57,8 @@ public:
CompilerStack(): m_parseSuccessful(false) {} CompilerStack(): m_parseSuccessful(false) {}
/// Adds a source object (e.g. file) to the parser. After this, parse has to be called again. /// Adds a source object (e.g. file) to the parser. After this, parse has to be called again.
void addSource(std::string const& _name, std::string const& _content); /// @returns true if a source object by the name already existed and was replaced.
bool addSource(std::string const& _name, std::string const& _content);
void setSource(std::string const& _sourceCode); void setSource(std::string const& _sourceCode);
/// Parses all source units that were added /// Parses all source units that were added
void parse(); void parse();
@ -86,9 +87,13 @@ public:
/// Can be one of 3 types defined at @c DocumentationType /// Can be one of 3 types defined at @c DocumentationType
std::string const& getJsonDocumentation(std::string const& _contractName, DocumentationType _type) const; std::string const& getJsonDocumentation(std::string const& _contractName, DocumentationType _type) const;
/// Returns the previously used scanner, useful for counting lines during error reporting. /// @returns the previously used scanner, useful for counting lines during error reporting.
Scanner const& getScanner(std::string const& _sourceName = "") const; Scanner const& getScanner(std::string const& _sourceName = "") const;
/// @returns the parsed source unit with the supplied name.
SourceUnit const& getAST(std::string const& _sourceName = "") const; SourceUnit const& getAST(std::string const& _sourceName = "") const;
/// @returns the parsed contract with the supplied name. Throws an exception if the contract
/// does not exist.
ContractDefinition const& getContractDefinition(std::string const& _contractName) const;
/// Compile the given @a _sourceCode to bytecode. If a scanner is provided, it is used for /// Compile the given @a _sourceCode to bytecode. If a scanner is provided, it is used for
/// scanning the source code - this is useful for printing exception information. /// scanning the source code - this is useful for printing exception information.

13
sc/cmdline.cpp

@ -10,6 +10,19 @@ int main(int argv, char** argc) {
std::cerr << "Must provide a command and arguments! Try parse, rewrite, compile, assemble\n"; std::cerr << "Must provide a command and arguments! Try parse, rewrite, compile, assemble\n";
return 0; return 0;
} }
if (argv == 2 && (std::string(argc[1]) == "--help" || std::string(argc[1]) == "-h" )) {
std::cout << argc[1] << "\n";
std::cout << "serpent command input\n";
std::cout << "where input -s for from stdin, a file, or interpreted as serpent code if does not exist as file.";
std::cout << "where command: \n";
std::cout << " parse: Just parses and returns s-expression code.\n";
std::cout << " rewrite: Parse, use rewrite rules print s-expressions of result.\n";
std::cout << " compile: Return resulting compiled EVM code in hex.\n";
std::cout << " assemble: Return result from step before compilation.\n";
return 0;
}
std::string flag = ""; std::string flag = "";
std::string command = argc[1]; std::string command = argc[1];
std::string input; std::string input;

Loading…
Cancel
Save