Browse Source

Fix for State and Transaction.

Serpent update.
cl-refactor
Gav Wood 10 years ago
parent
commit
ca2f4ed274
  1. 9
      libethereum/Executive.cpp
  2. 32
      libethereum/State.cpp
  3. 2
      libethereum/Transaction.cpp
  4. 4
      libethereum/Transaction.h
  5. 14
      libserpent/bignum.cpp
  6. 12
      libserpent/bignum.h
  7. 212
      libserpent/compiler.cpp
  8. 224
      libserpent/opcodes.h
  9. 53
      libserpent/parser.cpp
  10. 870
      libserpent/rewriter.cpp
  11. 2
      libserpent/tokenize.cpp
  12. 22
      libserpent/util.cpp
  13. 6
      libserpent/util.h

9
libethereum/Executive.cpp

@ -184,15 +184,10 @@ bool Executive::go(OnOpFunc const& _onOp)
{
return false;
}
catch (OutOfGas const& /*_e*/)
{
clog(StateChat) << "Out of Gas! Reverting.";
revert = true;
}
catch (VMException const& _e)
{
clog(StateChat) << "VM Exception: " << diagnostic_information(_e);
m_endGas = m_vm->gas();
clog(StateChat) << "Safe VM Exception: " << diagnostic_information(_e);
m_endGas = 0;//m_vm->gas();
revert = true;
}
catch (Exception const& _e)

32
libethereum/State.cpp

@ -1213,32 +1213,29 @@ bool State::call(Address _receiveAddress, Address _codeAddress, Address _senderA
*o_sub += evm.sub;
if (o_ms)
o_ms->output = out.toBytes();
}
catch (OutOfGas const& /*_e*/)
{
clog(StateChat) << "Out of Gas! Reverting.";
revert = true;
*_gas = vm.gas();
}
catch (VMException const& _e)
{
clog(StateChat) << "VM Exception: " << diagnostic_information(_e);
clog(StateChat) << "Safe VM Exception: " << diagnostic_information(_e);
revert = true;
*_gas = 0;
}
catch (Exception const& _e)
{
clog(StateChat) << "Exception in VM: " << diagnostic_information(_e);
cwarn << "Unexpected exception in VM: " << diagnostic_information(_e) << ". This is exceptionally bad.";
// TODO: use fallback known-safe VM.
}
catch (std::exception const& _e)
{
clog(StateChat) << "std::exception in VM: " << _e.what();
cwarn << "Unexpected exception in VM: " << _e.what() << ". This is exceptionally bad.";
// TODO: use fallback known-safe VM.
}
// Write state out only in the case of a non-excepted transaction.
if (revert)
evm.revert();
*_gas = vm.gas();
return !revert;
}
else
@ -1281,16 +1278,13 @@ h160 State::create(Address _sender, u256 _endowment, u256 _gasPrice, u256* _gas,
o_ms->output = out.toBytes();
if (o_sub)
*o_sub += evm.sub;
}
catch (OutOfGas const& /*_e*/)
{
clog(StateChat) << "Out of Gas! Reverting.";
revert = true;
*_gas = vm.gas();
}
catch (VMException const& _e)
{
clog(StateChat) << "VM Exception: " << diagnostic_information(_e);
clog(StateChat) << "Safe VM Exception: " << diagnostic_information(_e);
revert = true;
*_gas = 0;
}
catch (Exception const& _e)
{
@ -1317,8 +1311,6 @@ h160 State::create(Address _sender, u256 _endowment, u256 _gasPrice, u256* _gas,
if (addressInUse(newAddress))
m_cache[newAddress].setCode(out);
*_gas = vm.gas();
return newAddress;
}
@ -1380,7 +1372,7 @@ std::ostream& dev::eth::operator<<(std::ostream& _out, State const& _s)
stringstream contout;
if ((cache && cache->codeBearing()) || (!cache && r && !r[3].isEmpty()))
if ((cache && cache->codeBearing()) || (!cache && r && (h256)r[3] != EmptySHA3))
{
std::map<u256, u256> mem;
std::set<u256> back;
@ -1409,7 +1401,7 @@ std::ostream& dev::eth::operator<<(std::ostream& _out, State const& _s)
else
contout << r[2].toHash<h256>();
if (cache && cache->isFreshCode())
contout << " $" << cache->code();
contout << " $" << toHex(cache->code());
else
contout << " $" << (cache ? cache->codeHash() : r[3].toHash<h256>());

2
libethereum/Transaction.cpp

@ -47,7 +47,7 @@ Transaction::Transaction(bytesConstRef _rlpData, bool _checkSender)
if (_checkSender)
m_sender = sender();
}
catch (Exception & _e)
catch (Exception& _e)
{
_e << errinfo_name("invalid transaction format") << BadFieldError(field,toHex(rlp[field].data().toBytes()));
throw;

4
libethereum/Transaction.h

@ -57,10 +57,10 @@ public:
Transaction(u256 _value, u256 _gasPrice, u256 _gas, bytes const& _data): m_type(ContractCreation), m_value(_value), m_gasPrice(_gasPrice), m_gas(_gas), m_data(_data) {}
/// Constructs a transaction from the given RLP.
Transaction(bytesConstRef _rlp, bool _checkSender = false);
explicit Transaction(bytesConstRef _rlp, bool _checkSender = false);
/// Constructs a transaction from the given RLP.
Transaction(bytes const& _rlp, bool _checkSender = false): Transaction(&_rlp, _checkSender) {}
explicit Transaction(bytes const& _rlp, bool _checkSender = false): Transaction(&_rlp, _checkSender) {}
/// Checks equality of transactions.

14
libserpent/bignum.cpp

@ -48,6 +48,20 @@ std::string decimalMul(std::string a, std::string b) {
return o;
}
//Modexp
std::string decimalModExp(std::string b, std::string e, std::string m) {
if (e == "0") return "1";
else if (e == "1") return b;
else if (decimalMod(e, "2") == "0") {
std::string o = decimalModExp(b, decimalDiv(e, "2"), m);
return decimalMod(decimalMul(o, o), m);
}
else {
std::string o = decimalModExp(b, decimalDiv(e, "2"), m);
return decimalMod(decimalMul(decimalMul(o, o), b), m);
}
}
//Is a greater than b? Flag allows equality
bool decimalGt(std::string a, std::string b, bool eqAllowed) {
if (a == b) return eqAllowed;

12
libserpent/bignum.h

@ -7,10 +7,16 @@ const std::string tt256 =
"115792089237316195423570985008687907853269984665640564039457584007913129639936"
;
const std::string tt255 =
"57896044618658097711785492504343953926634992332820282019728792003956564819968"
const std::string tt256m1 =
"115792089237316195423570985008687907853269984665640564039457584007913129639935"
;
const std::string tt255 =
"57896044618658097711785492504343953926634992332820282019728792003956564819968";
const std::string tt176 =
"95780971304118053647396689196894323976171195136475136";
std::string unsignedToDecimal(unsigned branch);
std::string decimalAdd(std::string a, std::string b);
@ -23,6 +29,8 @@ std::string decimalDiv(std::string a, std::string b);
std::string decimalMod(std::string a, std::string b);
std::string decimalModExp(std::string b, std::string e, std::string m);
bool decimalGt(std::string a, std::string b, bool eqAllowed=false);
unsigned decimalToUnsigned(std::string a);

212
libserpent/compiler.cpp

@ -8,10 +8,18 @@
struct programAux {
std::map<std::string, std::string> vars;
int nextVarMem;
bool allocUsed;
bool calldataUsed;
int step;
int labelLength;
int functionCount;
};
struct programVerticalAux {
int height;
std::map<std::string, int> dupvars;
std::map<std::string, int> funvars;
};
struct programData {
@ -25,6 +33,16 @@ programAux Aux() {
o.allocUsed = false;
o.calldataUsed = false;
o.step = 0;
o.nextVarMem = 32;
o.functionCount = 0;
return o;
}
programVerticalAux verticalAux() {
programVerticalAux o;
o.height = 0;
o.dupvars = std::map<std::string, int>();
o.funvars = std::map<std::string, int>();
return o;
}
@ -57,28 +75,28 @@ Node popwrap(Node node) {
// Turns LLL tree into tree of code fragments
programData opcodeify(Node node,
programAux aux=Aux(),
int height=0,
std::map<std::string, int> dupvars=
std::map<std::string, int>()) {
programVerticalAux vaux=verticalAux()) {
std::string symb = "_"+mkUniqueToken();
Metadata m = node.metadata;
// Numbers
if (node.type == TOKEN) {
return pd(aux, nodeToNumeric(node), 1);
}
else if (node.val == "ref" || node.val == "get" || node.val == "set") {
else if (node.val == "ref" || node.val == "get" ||
node.val == "set" || node.val == "declare") {
std::string varname = node.args[0].val;
if (!aux.vars.count(varname)) {
aux.vars[varname] = unsignedToDecimal(aux.vars.size() * 32);
aux.vars[varname] = unsignedToDecimal(aux.nextVarMem);
aux.nextVarMem += 32;
}
if (varname == "'msg.data") aux.calldataUsed = true;
// Set variable
if (node.val == "set") {
programData sub = opcodeify(node.args[1], aux, height, dupvars);
programData sub = opcodeify(node.args[1], aux, vaux);
if (!sub.outs)
err("Value to set variable must have nonzero arity!", m);
if (dupvars.count(node.args[0].val)) {
int h = height - dupvars[node.args[0].val];
if (vaux.dupvars.count(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);
Node nodelist[] = {
sub.code,
@ -96,8 +114,8 @@ programData opcodeify(Node node,
}
// Get variable
else if (node.val == "get") {
if (dupvars.count(node.args[0].val)) {
int h = height - dupvars[node.args[0].val];
if (vaux.dupvars.count(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);
return pd(aux, token("DUP"+unsignedToDecimal(h)), 1);
}
@ -106,36 +124,157 @@ programData opcodeify(Node node,
return pd(aux, multiToken(nodelist, 2, m), 1);
}
// Refer variable
else {
if (dupvars.count(node.args[0].val))
else if (node.val == "ref") {
if (vaux.dupvars.count(node.args[0].val))
err("Cannot ref stack variable!", m);
return pd(aux, token(aux.vars[varname], m), 1);
}
// Declare variable
else {
Node nodelist[] = { };
return pd(aux, multiToken(nodelist, 0, m), 0);
}
}
// Define functions (TODO: eventually move to rewriter.cpp, keep
// compiler pure LLL)
if (node.val == "def") {
std::vector<std::string> varNames;
std::vector<int> varSizes;
bool useLt32 = false;
int totalSz = 0;
if (node.args.size() != 2)
err("Malformed def!", m);
// Collect the list of variable names and variable byte counts
for (unsigned i = 0; i < node.args[0].args.size(); i++) {
if (node.args[0].args[i].val == "kv") {
if (node.args[0].args[i].args.size() != 2)
err("Malformed def!", m);
varNames.push_back(node.args[0].args[i].args[0].val);
varSizes.push_back(
decimalToUnsigned(node.args[0].args[i].args[1].val));
if (varSizes.back() > 32)
err("Max argument width: 32 bytes", m);
useLt32 = true;
}
else {
varNames.push_back(node.args[0].args[i].val);
varSizes.push_back(32);
}
aux.vars[varNames.back()] = unsignedToDecimal(aux.nextVarMem + 32 * i);
totalSz += varSizes.back();
}
int functionCount = aux.functionCount;
int nextVarMem = aux.nextVarMem;
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
if (node.val == "lll" && node.args.size() == 2) {
if (node.args[1].val != "0") aux.allocUsed = true;
std::vector<Node> o;
o.push_back(finalize(opcodeify(node.args[0])));
programData sub = opcodeify(node.args[1], aux, height, dupvars);
programData sub = opcodeify(node.args[1], aux, vaux);
Node code = astnode("____CODE", o, m);
Node nodelist[] = {
token("$begincode"+symb+".endcode"+symb, m), token("DUP1", m),
token("$begincode"+symb, m), sub.code, token("CODECOPY", m),
token("$endcode"+symb, m), token("JUMP", m),
token("~begincode"+symb, m), code, token("~endcode"+symb, m),
token("JUMPDEST", m)
token("~begincode"+symb, m), code,
token("~endcode"+symb, m), token("JUMPDEST", m)
};
return pd(sub.aux, multiToken(nodelist, 11, m), 1);
}
// Stack variables
if (node.val == "with") {
std::map<std::string, int> dupvars2 = dupvars;
dupvars2[node.args[0].val] = height;
programData initial = opcodeify(node.args[1], aux, height, dupvars);
programData initial = opcodeify(node.args[1], aux, vaux);
programVerticalAux vaux2 = vaux;
vaux2.dupvars[node.args[0].val] = vaux.height;
vaux2.height += 1;
if (!initial.outs)
err("Initial variable value must have nonzero arity!", m);
programData sub = opcodeify(node.args[2], initial.aux, height + 1, dupvars2);
programData sub = opcodeify(node.args[2], initial.aux, vaux2);
Node nodelist[] = {
initial.code,
sub.code
@ -151,7 +290,7 @@ programData opcodeify(Node node,
std::vector<Node> children;
int lastOut = 0;
for (unsigned i = 0; i < node.args.size(); i++) {
programData sub = opcodeify(node.args[i], aux, height, dupvars);
programData sub = opcodeify(node.args[i], aux, vaux);
aux = sub.aux;
if (sub.outs == 1) {
if (i < node.args.size() - 1) sub.code = popwrap(sub.code);
@ -163,8 +302,8 @@ programData opcodeify(Node node,
}
// 2-part conditional (if gets rewritten to unless in rewrites)
else if (node.val == "unless" && node.args.size() == 2) {
programData cond = opcodeify(node.args[0], aux, height, dupvars);
programData action = opcodeify(node.args[1], cond.aux, height, dupvars);
programData cond = opcodeify(node.args[0], aux, vaux);
programData action = opcodeify(node.args[1], cond.aux, vaux);
aux = action.aux;
if (!cond.outs) err("Condition of if/unless statement has arity 0", m);
if (action.outs) action.code = popwrap(action.code);
@ -178,9 +317,9 @@ programData opcodeify(Node node,
}
// 3-part conditional
else if (node.val == "if" && node.args.size() == 3) {
programData ifd = opcodeify(node.args[0], aux, height, dupvars);
programData thend = opcodeify(node.args[1], ifd.aux, height, dupvars);
programData elsed = opcodeify(node.args[2], thend.aux, height, dupvars);
programData ifd = opcodeify(node.args[0], aux, vaux);
programData thend = opcodeify(node.args[1], ifd.aux, vaux);
programData elsed = opcodeify(node.args[2], thend.aux, vaux);
aux = elsed.aux;
if (!ifd.outs)
err("Condition of if/unless statement has arity 0", m);
@ -191,7 +330,7 @@ programData opcodeify(Node node,
if (elsed.outs > outs) elsed.code = popwrap(elsed.code);
Node nodelist[] = {
ifd.code,
token("NOT", m),
token("ISZERO", m),
token("$else"+symb, m), token("JUMPI", m),
thend.code,
token("$endif"+symb, m), token("JUMP", m),
@ -203,8 +342,8 @@ programData opcodeify(Node node,
}
// While (rewritten to this in rewrites)
else if (node.val == "until") {
programData cond = opcodeify(node.args[0], aux, height, dupvars);
programData action = opcodeify(node.args[1], cond.aux, height, dupvars);
programData cond = opcodeify(node.args[0], aux, vaux);
programData action = opcodeify(node.args[1], cond.aux, vaux);
aux = action.aux;
if (!cond.outs)
err("Condition of while/until loop has arity 0", m);
@ -215,13 +354,13 @@ programData opcodeify(Node node,
token("$end"+symb, m), token("JUMPI", m),
action.code,
token("$beg"+symb, m), token("JUMP", m),
token("~end"+symb, m), token("JUMPDEST", m)
token("~end"+symb, m), token("JUMPDEST", m),
};
return pd(aux, multiToken(nodelist, 10, m));
}
// Memory allocations
else if (node.val == "alloc") {
programData bytez = opcodeify(node.args[0], aux, height, dupvars);
programData bytez = opcodeify(node.args[0], aux, vaux);
aux = bytez.aux;
if (!bytez.outs)
err("Alloc input has arity 0", m);
@ -251,7 +390,9 @@ programData opcodeify(Node node,
for (unsigned i = 0; i < node.args.size(); i++) {
Metadata m2 = node.args[i].metadata;
nodes.push_back(token("DUP1", m2));
programData sub = opcodeify(node.args[i], aux, height + 2, dupvars);
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;
@ -276,10 +417,9 @@ programData opcodeify(Node node,
err("Invalid arity for "+node.val, m);
}
for (int i = node.args.size() - 1; i >= 0; i--) {
programData sub = opcodeify(node.args[i],
aux,
height - i - 1 + node.args.size(),
dupvars);
programVerticalAux vaux2 = vaux;
vaux2.height = vaux.height - i - 1 + node.args.size();
programData sub = opcodeify(node.args[i], aux, vaux2);
aux = sub.aux;
if (!sub.outs)
err("Input "+unsignedToDecimal(i)+" has arity 0", sub.code.metadata);
@ -305,7 +445,7 @@ Node finalize(programData c) {
if ((c.aux.allocUsed || c.aux.calldataUsed) && c.aux.vars.size() > 0) {
Node nodelist[] = {
token("0", m),
token(unsignedToDecimal(c.aux.vars.size() * 32 - 1)),
token(unsignedToDecimal(c.aux.nextVarMem - 1)),
token("MSTORE8", m)
};
bottom.push_back(multiToken(nodelist, 3, m));

224
libserpent/opcodes.h

@ -1,20 +1,3 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ETHSERP_OPCODES
#define ETHSERP_OPCODES
@ -24,128 +7,131 @@
#include <map>
class Mapping {
public:
Mapping(std::string Op, int Opcode, int In, int Out) {
op = Op;
opcode = Opcode;
in = In;
out = Out;
}
std::string op;
int opcode;
int in;
int out;
public:
Mapping(std::string Op, int Opcode, int In, int Out) {
op = Op;
opcode = Opcode;
in = In;
out = Out;
}
std::string op;
int opcode;
int in;
int out;
};
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("EXP", 0x08, 2, 1),
Mapping("NEG", 0x09, 1, 1),
Mapping("LT", 0x0a, 2, 1),
Mapping("GT", 0x0b, 2, 1),
Mapping("SLT", 0x0c, 2, 1),
Mapping("SGT", 0x0d, 2, 1),
Mapping("EQ", 0x0e, 2, 1),
Mapping("NOT", 0x0f, 1, 1),
Mapping("AND", 0x10, 2, 1),
Mapping("OR", 0x11, 2, 1),
Mapping("XOR", 0x12, 2, 1),
Mapping("BYTE", 0x13, 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", 0x53, 1, 1),
Mapping("MSTORE", 0x54, 2, 0),
Mapping("MSTORE8", 0x55, 2, 0),
Mapping("SLOAD", 0x56, 1, 1),
Mapping("SSTORE", 0x57, 2, 0),
Mapping("JUMP", 0x58, 1, 0),
Mapping("JUMPI", 0x59, 2, 0),
Mapping("PC", 0x5a, 0, 1),
Mapping("MSIZE", 0x5b, 0, 1),
Mapping("GAS", 0x5c, 0, 1),
Mapping("JUMPDEST", 0x5d, 0, 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),
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;
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;
}
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);
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;
}
}
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)
{
int opcode(std::string op) {
return _opdata(op, -1).second[0];
}
int opinputs(std::string op)
{
int opinputs(std::string op) {
return _opdata(op, -1).second[1];
}
int opoutputs(std::string op)
{
int opoutputs(std::string op) {
return _opdata(op, -1).second[2];
}
std::string op(int opcode)
{
std::string op(int opcode) {
return _opdata("", opcode).first;
}

53
libserpent/parser.cpp

@ -9,20 +9,21 @@
// Extended BEDMAS precedence order
int precedence(Node tok) {
std::string v = tok.val;
if (v == "!" || v == "not") return 0;
else if (v=="^" || v == "**") return 1;
else if (v=="*" || v=="/" || v=="@/" || v=="%" || v=="@%") return 2;
else if (v=="+" || v=="-") return 3;
else if (v=="<" || v==">" || v=="<=" || v==">=") return 4;
else if (v=="@<" || v=="@>" || v=="@<=" || v=="@>=") return 4;
else if (v=="&" || v=="|" || v=="xor" || v=="==" || v == "!=") return 5;
else if (v=="&&" || v=="and") return 6;
else if (v=="||" || v=="or") return 7;
else if (v==":") return 8;
if (v == ".") return -1;
else if (v == "!" || v == "not") return 1;
else if (v=="^" || v == "**") return 2;
else if (v=="*" || v=="/" || v=="@/" || v=="%" || v=="@%") return 3;
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=="xor" || v=="==" || v == "!=") return 6;
else if (v=="&&" || v=="and") return 7;
else if (v=="||" || v=="or") return 8;
else if (v==":") return 9;
else if (v=="=") return 10;
else if (v=="+=" || v=="-=" || v=="*=" || v=="/=" || v=="%=") return 10;
else if (v=="@/=" || v=="@%=") return 10;
else return -1;
else return 0;
}
// Token classification for shunting-yard purposes
@ -32,8 +33,9 @@ int toktype(Node tok) {
if (v == "(" || v == "[" || v == "{") return LPAREN;
else if (v == ")" || v == "]" || v == "}") return RPAREN;
else if (v == ",") return COMMA;
else if (v == "!" || v == "not" || v == "neg") return UNARY_OP;
else if (precedence(tok) >= 0) return BINARY_OP;
else if (v == "!" || v == "~" || v == "not") return UNARY_OP;
else if (precedence(tok) > 0) return BINARY_OP;
else if (precedence(tok) < 0) return TOKEN_SPLITTER;
if (tok.val[0] != '"' && tok.val[0] != '\'') {
for (unsigned i = 0; i < tok.val.length(); i++) {
if (chartype(tok.val[i]) == SYMB) {
@ -68,6 +70,10 @@ std::vector<Node> shuntingYard(std::vector<Node> tokens) {
}
// Left parens go on stack and output queue
else if (toktyp == LPAREN) {
while (stack.size() && toktype(stack.back()) == TOKEN_SPLITTER) {
oq.push_back(stack.back());
stack.pop_back();
}
if (prevtyp != ALPHANUM && prevtyp != RPAREN) {
oq.push_back(token("id", tok.metadata));
}
@ -88,16 +94,26 @@ std::vector<Node> shuntingYard(std::vector<Node> tokens) {
else if (toktyp == UNARY_OP) {
stack.push_back(tok);
}
// If token splitter, just push it to the stack
else if (toktyp == TOKEN_SPLITTER) {
while (stack.size() && toktype(stack.back()) == TOKEN_SPLITTER) {
oq.push_back(stack.back());
stack.pop_back();
}
stack.push_back(tok);
}
// If binary op, keep popping from stack while higher bedmas precedence
else if (toktyp == BINARY_OP) {
if (tok.val == "-" && prevtyp != ALPHANUM && prevtyp != RPAREN) {
stack.push_back(token("neg", tok.metadata));
stack.push_back(tok);
oq.push_back(token("0", tok.metadata));
}
else {
int prec = precedence(tok);
while (stack.size()
&& (toktype(stack.back()) == BINARY_OP
|| toktype(stack.back()) == UNARY_OP)
|| toktype(stack.back()) == UNARY_OP
|| toktype(stack.back()) == TOKEN_SPLITTER)
&& precedence(stack.back()) <= prec) {
oq.push_back(stack.back());
stack.pop_back();
@ -133,9 +149,9 @@ Node treefy(std::vector<Node> stream) {
int typ = toktype(tok);
// If unary, take node off end of oq and wrap it with the operator
// If binary, do the same with two nodes
if (typ == UNARY_OP || typ == BINARY_OP) {
if (typ == UNARY_OP || typ == BINARY_OP || typ == TOKEN_SPLITTER) {
std::vector<Node> args;
int rounds = (typ == BINARY_OP) ? 2 : 1;
int rounds = (typ == UNARY_OP) ? 1 : 2;
for (int i = 0; i < rounds; i++) {
if (oq.size() == 0) {
err("Line malformed, not enough args for "+tok.val,
@ -245,7 +261,8 @@ int spaceCount(std::string s) {
// Is this a command that takes an argument on the same line?
bool bodied(std::string tok) {
return tok == "if" || tok == "elif" || tok == "while"
|| tok == "with" || tok == "def";
|| tok == "with" || tok == "def" || tok == "extern"
|| tok == "data";
}
// Is this a command that takes an argument as a child block?

870
libserpent/rewriter.cpp

File diff suppressed because it is too large

2
libserpent/tokenize.cpp

@ -13,7 +13,7 @@ int chartype(char c) {
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 (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 (std::string("()[]{}").find(c) != std::string::npos) return BRACK;
else if (c == '"') return DQUOTE;

22
libserpent/util.cpp

@ -26,6 +26,28 @@ Node astnode(std::string val, std::vector<Node> args, Metadata met) {
return o;
}
//AST node constructors for a specific number of children
Node astnode(std::string val, Node a, Metadata met) {
std::vector<Node> args;
args.push_back(a);
return astnode(val, args, met);
}
Node astnode(std::string val, Node a, Node b, Metadata met) {
std::vector<Node> args;
args.push_back(a);
args.push_back(b);
return astnode(val, args, met);
}
Node astnode(std::string val, Node a, Node b, Node c, Metadata met) {
std::vector<Node> args;
args.push_back(a);
args.push_back(b);
args.push_back(c);
return astnode(val, args, met);
}
// Print token list
std::string printTokens(std::vector<Node> tokens) {
std::string s = "";

6
libserpent/util.h

@ -22,7 +22,8 @@ const int TOKEN = 0,
COLON = 11,
UNARY_OP = 12,
BINARY_OP = 13,
COMPOUND = 14;
COMPOUND = 14,
TOKEN_SPLITTER = 15;
// Stores metadata about each token
class Metadata {
@ -48,6 +49,9 @@ struct Node {
};
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, 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, Node c, Metadata met=Metadata());
// Number of tokens in a tree
int treeSize(Node prog);

Loading…
Cancel
Save