37 changed files with 2 additions and 4423 deletions
@ -1,9 +0,0 @@ |
|||
#pragma once |
|||
|
|||
#include "compiler.h" |
|||
#include "funcs.h" |
|||
#include "lllparser.h" |
|||
#include "parser.h" |
|||
#include "rewriter.h" |
|||
#include "tokenize.h" |
|||
#include "util.h" |
@ -1,22 +0,0 @@ |
|||
cmake_policy(SET CMP0015 NEW) |
|||
set(CMAKE_AUTOMOC OFF) |
|||
|
|||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DSTATICLIB") |
|||
|
|||
aux_source_directory(. SRC_LIST) |
|||
|
|||
include_directories(BEFORE ..) |
|||
|
|||
set(EXECUTABLE serpent) |
|||
|
|||
file(GLOB HEADERS "*.h") |
|||
|
|||
add_library(${EXECUTABLE} ${SRC_LIST} ${HEADERS}) |
|||
|
|||
target_link_libraries(${EXECUTABLE} lll) |
|||
target_link_libraries(${EXECUTABLE} evmcore) |
|||
target_link_libraries(${EXECUTABLE} devcore) |
|||
|
|||
install( TARGETS ${EXECUTABLE} RUNTIME DESTINATION bin ARCHIVE DESTINATION lib LIBRARY DESTINATION lib ) |
|||
install( FILES ${HEADERS} DESTINATION include/${EXECUTABLE} ) |
|||
|
@ -1,112 +0,0 @@ |
|||
#include <stdio.h> |
|||
#include <iostream> |
|||
#include <vector> |
|||
#include <map> |
|||
#include "bignum.h" |
|||
|
|||
//Integer to string conversion
|
|||
std::string unsignedToDecimal(unsigned branch) { |
|||
if (branch < 10) return nums.substr(branch, 1); |
|||
else return unsignedToDecimal(branch / 10) + nums.substr(branch % 10,1); |
|||
} |
|||
|
|||
//Add two strings representing decimal values
|
|||
std::string decimalAdd(std::string a, std::string b) { |
|||
std::string o = a; |
|||
while (b.length() < a.length()) b = "0" + b; |
|||
while (o.length() < b.length()) o = "0" + o; |
|||
bool carry = false; |
|||
for (int i = o.length() - 1; i >= 0; i--) { |
|||
o[i] = o[i] + b[i] - '0'; |
|||
if (carry) o[i]++; |
|||
if (o[i] > '9') { |
|||
o[i] -= 10; |
|||
carry = true; |
|||
} |
|||
else carry = false; |
|||
} |
|||
if (carry) o = "1" + o; |
|||
return o; |
|||
} |
|||
|
|||
//Helper function for decimalMul
|
|||
std::string decimalDigitMul(std::string a, int dig) { |
|||
if (dig == 0) return "0"; |
|||
else return decimalAdd(a, decimalDigitMul(a, dig - 1)); |
|||
} |
|||
|
|||
//Multiply two strings representing decimal values
|
|||
std::string decimalMul(std::string a, std::string b) { |
|||
std::string o = "0"; |
|||
for (unsigned i = 0; i < b.length(); i++) { |
|||
std::string n = decimalDigitMul(a, b[i] - '0'); |
|||
if (n != "0") { |
|||
for (unsigned j = i + 1; j < b.length(); j++) n += "0"; |
|||
} |
|||
o = decimalAdd(o, n); |
|||
} |
|||
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; |
|||
return (a.length() > b.length()) || (a.length() >= b.length() && a > b); |
|||
} |
|||
|
|||
//Subtract the two strings representing decimal values
|
|||
std::string decimalSub(std::string a, std::string b) { |
|||
if (b == "0") return a; |
|||
if (b == a) return "0"; |
|||
while (b.length() < a.length()) b = "0" + b; |
|||
std::string c = b; |
|||
for (unsigned i = 0; i < c.length(); i++) c[i] = '0' + ('9' - c[i]); |
|||
std::string o = decimalAdd(decimalAdd(a, c).substr(1), "1"); |
|||
while (o.size() > 1 && o[0] == '0') o = o.substr(1); |
|||
return o; |
|||
} |
|||
|
|||
//Divide the two strings representing decimal values
|
|||
std::string decimalDiv(std::string a, std::string b) { |
|||
std::string c = b; |
|||
if (decimalGt(c, a)) return "0"; |
|||
int zeroes = -1; |
|||
while (decimalGt(a, c, true)) { |
|||
zeroes += 1; |
|||
c = c + "0"; |
|||
} |
|||
c = c.substr(0, c.size() - 1); |
|||
std::string quot = "0"; |
|||
while (decimalGt(a, c, true)) { |
|||
a = decimalSub(a, c); |
|||
quot = decimalAdd(quot, "1"); |
|||
} |
|||
for (int i = 0; i < zeroes; i++) quot += "0"; |
|||
return decimalAdd(quot, decimalDiv(a, b)); |
|||
} |
|||
|
|||
//Modulo the two strings representing decimal values
|
|||
std::string decimalMod(std::string a, std::string b) { |
|||
return decimalSub(a, decimalMul(decimalDiv(a, b), b)); |
|||
} |
|||
|
|||
//String to int conversion
|
|||
unsigned decimalToUnsigned(std::string a) { |
|||
if (a.size() == 0) return 0; |
|||
else return (a[a.size() - 1] - '0') |
|||
+ decimalToUnsigned(a.substr(0,a.size()-1)) * 10; |
|||
} |
@ -1,41 +0,0 @@ |
|||
#ifndef ETHSERP_BIGNUM |
|||
#define ETHSERP_BIGNUM |
|||
|
|||
const std::string nums = "0123456789"; |
|||
|
|||
const std::string tt256 = |
|||
"115792089237316195423570985008687907853269984665640564039457584007913129639936" |
|||
; |
|||
|
|||
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); |
|||
|
|||
std::string decimalMul(std::string a, std::string b); |
|||
|
|||
std::string decimalSub(std::string a, std::string b); |
|||
|
|||
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); |
|||
|
|||
#define utd unsignedToDecimal |
|||
#define dtu decimalToUnsigned |
|||
|
|||
#endif |
@ -1,510 +0,0 @@ |
|||
#include <stdio.h> |
|||
#include <iostream> |
|||
#include <vector> |
|||
#include <map> |
|||
#include "util.h" |
|||
#include "bignum.h" |
|||
#include "opcodes.h" |
|||
|
|||
// Auxiliary data that is gathered while compiling
|
|||
struct programAux { |
|||
std::map<std::string, std::string> vars; |
|||
int nextVarMem; |
|||
bool allocUsed; |
|||
bool calldataUsed; |
|||
int step; |
|||
int labelLength; |
|||
}; |
|||
|
|||
// Auxiliary data that gets passed down vertically
|
|||
// but not back up
|
|||
struct programVerticalAux { |
|||
int height; |
|||
std::string innerScopeName; |
|||
std::map<std::string, int> dupvars; |
|||
std::map<std::string, int> funvars; |
|||
std::vector<mss> scopes; |
|||
}; |
|||
|
|||
// Compilation result
|
|||
struct programData { |
|||
programAux aux; |
|||
Node code; |
|||
int outs; |
|||
}; |
|||
|
|||
programAux Aux() { |
|||
programAux o; |
|||
o.allocUsed = false; |
|||
o.calldataUsed = false; |
|||
o.step = 0; |
|||
o.nextVarMem = 32; |
|||
return o; |
|||
} |
|||
|
|||
programVerticalAux verticalAux() { |
|||
programVerticalAux o; |
|||
o.height = 0; |
|||
o.dupvars = std::map<std::string, int>(); |
|||
o.funvars = std::map<std::string, int>(); |
|||
o.scopes = std::vector<mss>(); |
|||
return o; |
|||
} |
|||
|
|||
programData pd(programAux aux = Aux(), Node code=token("_"), int outs=0) { |
|||
programData o; |
|||
o.aux = aux; |
|||
o.code = code; |
|||
o.outs = outs; |
|||
return o; |
|||
} |
|||
|
|||
Node multiToken(Node nodes[], int len, Metadata met) { |
|||
std::vector<Node> out; |
|||
for (int i = 0; i < len; i++) { |
|||
out.push_back(nodes[i]); |
|||
} |
|||
return astnode("_", out, met); |
|||
} |
|||
|
|||
Node finalize(programData c); |
|||
|
|||
Node popwrap(Node node) { |
|||
Node nodelist[] = { |
|||
node, |
|||
token("POP", node.metadata) |
|||
}; |
|||
return multiToken(nodelist, 2, node.metadata); |
|||
} |
|||
|
|||
// Turns LLL tree into tree of code fragments
|
|||
programData opcodeify(Node node, |
|||
programAux aux=Aux(), |
|||
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") { |
|||
std::string varname = node.args[0].val; |
|||
// Determine reference to variable
|
|||
if (!aux.vars.count(node.args[0].val)) { |
|||
aux.vars[node.args[0].val] = utd(aux.nextVarMem); |
|||
aux.nextVarMem += 32; |
|||
} |
|||
Node varNode = tkn(aux.vars[varname], m); |
|||
//std::cerr << varname << " " << printSimple(varNode) << "\n";
|
|||
// Set variable
|
|||
if (node.val == "set") { |
|||
programData sub = opcodeify(node.args[1], aux, vaux); |
|||
if (!sub.outs) |
|||
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)) { |
|||
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, |
|||
token("SWAP"+unsignedToDecimal(h), m), |
|||
token("POP", m) |
|||
}; |
|||
return pd(sub.aux, multiToken(nodelist, 3, m), 0); |
|||
} |
|||
// Setting a memory variable
|
|||
else { |
|||
Node nodelist[] = { |
|||
sub.code, |
|||
varNode, |
|||
token("MSTORE", m), |
|||
}; |
|||
return pd(sub.aux, multiToken(nodelist, 3, m), 0); |
|||
} |
|||
} |
|||
// Get variable
|
|||
else if (node.val == "get") { |
|||
// Getting a stack variable
|
|||
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); |
|||
} |
|||
// Getting a memory variable
|
|||
else { |
|||
Node nodelist[] = |
|||
{ varNode, token("MLOAD", m) }; |
|||
return pd(aux, multiToken(nodelist, 2, m), 1); |
|||
} |
|||
} |
|||
// Refer variable
|
|||
else if (node.val == "ref") { |
|||
if (vaux.dupvars.count(node.args[0].val)) |
|||
err("Cannot ref stack variable!", m); |
|||
return pd(aux, varNode, 1); |
|||
} |
|||
} |
|||
// Comments do nothing
|
|||
else if (node.val == "comment") { |
|||
return pd(aux, astnode("_", m), 0); |
|||
} |
|||
// Custom operation sequence
|
|||
// eg. (ops bytez id msize swap1 msize add 0 swap1 mstore) == alloc
|
|||
if (node.val == "ops") { |
|||
std::vector<Node> subs2; |
|||
int depth = 0; |
|||
for (unsigned i = 0; i < node.args.size(); i++) { |
|||
std::string op = upperCase(node.args[i].val); |
|||
if (node.args[i].type == ASTNODE || opinputs(op) == -1) { |
|||
programVerticalAux vaux2 = vaux; |
|||
vaux2.height = vaux.height - i - 1 + node.args.size(); |
|||
programData sub = opcodeify(node.args[i], aux, vaux2); |
|||
aux = sub.aux; |
|||
depth += sub.outs; |
|||
subs2.push_back(sub.code); |
|||
} |
|||
else { |
|||
subs2.push_back(token(op, m)); |
|||
depth += opoutputs(op) - opinputs(op); |
|||
} |
|||
} |
|||
if (depth < 0 || depth > 1) err("Stack depth mismatch", m); |
|||
return pd(aux, astnode("_", subs2, 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, 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) |
|||
}; |
|||
return pd(sub.aux, multiToken(nodelist, 11, m), 1); |
|||
} |
|||
// Stack variables
|
|||
if (node.val == "with") { |
|||
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, vaux2); |
|||
Node nodelist[] = { |
|||
initial.code, |
|||
sub.code |
|||
}; |
|||
programData o = pd(sub.aux, multiToken(nodelist, 2, m), sub.outs); |
|||
if (sub.outs) |
|||
o.code.args.push_back(token("SWAP1", m)); |
|||
o.code.args.push_back(token("POP", m)); |
|||
return o; |
|||
} |
|||
// Seq of multiple statements
|
|||
if (node.val == "seq") { |
|||
std::vector<Node> children; |
|||
int lastOut = 0; |
|||
for (unsigned i = 0; i < node.args.size(); i++) { |
|||
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); |
|||
else lastOut = 1; |
|||
} |
|||
children.push_back(sub.code); |
|||
} |
|||
return pd(aux, astnode("_", children, m), lastOut); |
|||
} |
|||
// 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, 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); |
|||
Node nodelist[] = { |
|||
cond.code, |
|||
token("$endif"+symb, m), token("JUMPI", m), |
|||
action.code, |
|||
token("~endif"+symb, m), token("JUMPDEST", m) |
|||
}; |
|||
return pd(aux, multiToken(nodelist, 6, m), 0); |
|||
} |
|||
// 3-part conditional
|
|||
else if (node.val == "if" && node.args.size() == 3) { |
|||
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); |
|||
// Handle cases where one conditional outputs something
|
|||
// and the other does not
|
|||
int outs = (thend.outs && elsed.outs) ? 1 : 0; |
|||
if (thend.outs > outs) thend.code = popwrap(thend.code); |
|||
if (elsed.outs > outs) elsed.code = popwrap(elsed.code); |
|||
Node nodelist[] = { |
|||
ifd.code, |
|||
token("ISZERO", m), |
|||
token("$else"+symb, m), token("JUMPI", m), |
|||
thend.code, |
|||
token("$endif"+symb, m), token("JUMP", m), |
|||
token("~else"+symb, m), token("JUMPDEST", m), |
|||
elsed.code, |
|||
token("~endif"+symb, m), token("JUMPDEST", m) |
|||
}; |
|||
return pd(aux, multiToken(nodelist, 12, m), outs); |
|||
} |
|||
// While (rewritten to this in rewrites)
|
|||
else if (node.val == "until") { |
|||
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); |
|||
if (action.outs) action.code = popwrap(action.code); |
|||
Node nodelist[] = { |
|||
token("~beg"+symb, m), token("JUMPDEST", m), |
|||
cond.code, |
|||
token("$end"+symb, m), token("JUMPI", m), |
|||
action.code, |
|||
token("$beg"+symb, m), token("JUMP", 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, vaux); |
|||
aux = bytez.aux; |
|||
if (!bytez.outs) |
|||
err("Alloc input has arity 0", m); |
|||
aux.allocUsed = true; |
|||
Node nodelist[] = { |
|||
bytez.code, |
|||
token("MSIZE", m), token("SWAP1", m), token("MSIZE", m), |
|||
token("ADD", m), |
|||
token("0", m), token("SWAP1", m), token("MSTORE", m) |
|||
}; |
|||
return pd(aux, multiToken(nodelist, 8, m), 1); |
|||
} |
|||
// All other functions/operators
|
|||
else { |
|||
std::vector<Node> subs2; |
|||
int depth = opinputs(upperCase(node.val)); |
|||
if (depth == -1) |
|||
err("Not a function or opcode: "+node.val, m); |
|||
if ((int)node.args.size() != depth) |
|||
err("Invalid arity for "+node.val, m); |
|||
for (int i = node.args.size() - 1; i >= 0; i--) { |
|||
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); |
|||
subs2.push_back(sub.code); |
|||
} |
|||
subs2.push_back(token(upperCase(node.val), m)); |
|||
int outdepth = opoutputs(upperCase(node.val)); |
|||
return pd(aux, astnode("_", subs2, m), outdepth); |
|||
} |
|||
} |
|||
|
|||
// Adds necessary wrappers to a program
|
|||
Node finalize(programData c) { |
|||
std::vector<Node> bottom; |
|||
Metadata m = c.code.metadata; |
|||
// If we are using both alloc and variables, we need to pre-zfill
|
|||
// some memory
|
|||
if ((c.aux.allocUsed || c.aux.calldataUsed) && c.aux.vars.size() > 0) { |
|||
Node nodelist[] = { |
|||
token("0", m), |
|||
token(unsignedToDecimal(c.aux.nextVarMem - 1)), |
|||
token("MSTORE8", m) |
|||
}; |
|||
bottom.push_back(multiToken(nodelist, 3, m)); |
|||
} |
|||
// The actual code
|
|||
bottom.push_back(c.code); |
|||
return astnode("_", bottom, m); |
|||
} |
|||
|
|||
//LLL -> code fragment tree
|
|||
Node buildFragmentTree(Node node) { |
|||
return finalize(opcodeify(node)); |
|||
} |
|||
|
|||
|
|||
// Builds a dictionary mapping labels to variable names
|
|||
void buildDict(Node program, programAux &aux, int labelLength) { |
|||
Metadata m = program.metadata; |
|||
// Token
|
|||
if (program.type == TOKEN) { |
|||
if (isNumberLike(program)) { |
|||
aux.step += 1 + toByteArr(program.val, m).size(); |
|||
} |
|||
else if (program.val[0] == '~') { |
|||
aux.vars[program.val.substr(1)] = unsignedToDecimal(aux.step); |
|||
} |
|||
else if (program.val[0] == '$') { |
|||
aux.step += labelLength + 1; |
|||
} |
|||
else aux.step += 1; |
|||
} |
|||
// A sub-program (ie. LLL)
|
|||
else if (program.val == "____CODE") { |
|||
int step = aux.step; |
|||
aux.step = 0; |
|||
for (unsigned i = 0; i < program.args.size(); i++) { |
|||
buildDict(program.args[i], aux, labelLength); |
|||
} |
|||
aux.step += step; |
|||
} |
|||
// Normal sub-block
|
|||
else { |
|||
for (unsigned i = 0; i < program.args.size(); i++) { |
|||
buildDict(program.args[i], aux, labelLength); |
|||
} |
|||
} |
|||
} |
|||
|
|||
// Applies that dictionary
|
|||
void substDict(Node program, programAux aux, int labelLength, std::vector<Node> &out) { |
|||
Metadata m = program.metadata; |
|||
std::vector<Node> inner; |
|||
if (program.type == TOKEN) { |
|||
if (program.val[0] == '$') { |
|||
std::string tokStr = "PUSH"+unsignedToDecimal(labelLength); |
|||
out.push_back(token(tokStr, m)); |
|||
int dotLoc = program.val.find('.'); |
|||
if (dotLoc == -1) { |
|||
std::string val = aux.vars[program.val.substr(1)]; |
|||
inner = toByteArr(val, m, labelLength); |
|||
} |
|||
else { |
|||
std::string start = aux.vars[program.val.substr(1, dotLoc-1)], |
|||
end = aux.vars[program.val.substr(dotLoc + 1)], |
|||
dist = decimalSub(end, start); |
|||
inner = toByteArr(dist, m, labelLength); |
|||
} |
|||
for (unsigned i = 0; i < inner.size(); i++) out.push_back(inner[i]); |
|||
} |
|||
else if (program.val[0] == '~') { } |
|||
else if (isNumberLike(program)) { |
|||
inner = toByteArr(program.val, m); |
|||
out.push_back(token("PUSH"+unsignedToDecimal(inner.size()))); |
|||
for (unsigned i = 0; i < inner.size(); i++) out.push_back(inner[i]); |
|||
} |
|||
else out.push_back(program); |
|||
} |
|||
else { |
|||
for (unsigned i = 0; i < program.args.size(); i++) { |
|||
substDict(program.args[i], aux, labelLength, out); |
|||
} |
|||
} |
|||
} |
|||
|
|||
// Compiled fragtree -> compiled fragtree without labels
|
|||
std::vector<Node> dereference(Node program) { |
|||
int sz = treeSize(program) * 4; |
|||
int labelLength = 1; |
|||
while (sz >= 256) { labelLength += 1; sz /= 256; } |
|||
programAux aux = Aux(); |
|||
buildDict(program, aux, labelLength); |
|||
std::vector<Node> o; |
|||
substDict(program, aux, labelLength, o); |
|||
return o; |
|||
} |
|||
|
|||
// Opcodes -> bin
|
|||
std::string serialize(std::vector<Node> codons) { |
|||
std::string o; |
|||
for (unsigned i = 0; i < codons.size(); i++) { |
|||
int v; |
|||
if (isNumberLike(codons[i])) { |
|||
v = decimalToUnsigned(codons[i].val); |
|||
} |
|||
else if (codons[i].val.substr(0,4) == "PUSH") { |
|||
v = 95 + decimalToUnsigned(codons[i].val.substr(4)); |
|||
} |
|||
else { |
|||
v = opcode(codons[i].val); |
|||
} |
|||
o += (char)v; |
|||
} |
|||
return o; |
|||
} |
|||
|
|||
// Bin -> opcodes
|
|||
std::vector<Node> deserialize(std::string ser) { |
|||
std::vector<Node> o; |
|||
int backCount = 0; |
|||
for (unsigned i = 0; i < ser.length(); i++) { |
|||
unsigned char v = (unsigned char)ser[i]; |
|||
std::string oper = op((int)v); |
|||
if (oper != "" && backCount <= 0) o.push_back(token(oper)); |
|||
else if (v >= 96 && v < 128 && backCount <= 0) { |
|||
o.push_back(token("PUSH"+unsignedToDecimal(v - 95))); |
|||
} |
|||
else o.push_back(token(unsignedToDecimal(v))); |
|||
if (v >= 96 && v < 128 && backCount <= 0) { |
|||
backCount = v - 95; |
|||
} |
|||
else backCount--; |
|||
} |
|||
return o; |
|||
} |
|||
|
|||
// Fragtree -> bin
|
|||
std::string assemble(Node fragTree) { |
|||
return serialize(dereference(fragTree)); |
|||
} |
|||
|
|||
// Fragtree -> tokens
|
|||
std::vector<Node> prettyAssemble(Node fragTree) { |
|||
return dereference(fragTree); |
|||
} |
|||
|
|||
// LLL -> bin
|
|||
std::string compileLLL(Node program) { |
|||
return assemble(buildFragmentTree(program)); |
|||
} |
|||
|
|||
// LLL -> tokens
|
|||
std::vector<Node> prettyCompileLLL(Node program) { |
|||
return prettyAssemble(buildFragmentTree(program)); |
|||
} |
|||
|
|||
// Converts a list of integer values to binary transaction data
|
|||
std::string encodeDatalist(std::vector<std::string> vals) { |
|||
std::string o; |
|||
for (unsigned i = 0; i < vals.size(); i++) { |
|||
std::vector<Node> n = toByteArr(strToNumeric(vals[i]), Metadata(), 32); |
|||
for (unsigned j = 0; j < n.size(); j++) { |
|||
int v = decimalToUnsigned(n[j].val); |
|||
o += (char)v; |
|||
} |
|||
} |
|||
return o; |
|||
} |
|||
|
|||
// Converts binary transaction data into a list of integer values
|
|||
std::vector<std::string> decodeDatalist(std::string ser) { |
|||
std::vector<std::string> out; |
|||
for (unsigned i = 0; i < ser.length(); i+= 32) { |
|||
std::string o = "0"; |
|||
for (unsigned j = i; j < i + 32; j++) { |
|||
int vj = (int)(unsigned char)ser[j]; |
|||
o = decimalAdd(decimalMul(o, "256"), unsignedToDecimal(vj)); |
|||
} |
|||
out.push_back(o); |
|||
} |
|||
return out; |
|||
} |
@ -1,40 +0,0 @@ |
|||
#ifndef ETHSERP_COMPILER |
|||
#define ETHSERP_COMPILER |
|||
|
|||
#include <stdio.h> |
|||
#include <iostream> |
|||
#include <vector> |
|||
#include <map> |
|||
#include "util.h" |
|||
|
|||
// Compiled fragtree -> compiled fragtree without labels
|
|||
std::vector<Node> dereference(Node program); |
|||
|
|||
// LLL -> fragtree
|
|||
Node buildFragmentTree(Node program); |
|||
|
|||
// opcodes -> bin
|
|||
std::string serialize(std::vector<Node> codons); |
|||
|
|||
// Fragtree -> bin
|
|||
std::string assemble(Node fragTree); |
|||
|
|||
// Fragtree -> opcodes
|
|||
std::vector<Node> prettyAssemble(Node fragTree); |
|||
|
|||
// LLL -> bin
|
|||
std::string compileLLL(Node program); |
|||
|
|||
// LLL -> opcodes
|
|||
std::vector<Node> prettyCompileLLL(Node program); |
|||
|
|||
// bin -> opcodes
|
|||
std::vector<Node> deserialize(std::string ser); |
|||
|
|||
// Converts a list of integer values to binary transaction data
|
|||
std::string encodeDatalist(std::vector<std::string> vals); |
|||
|
|||
// Converts binary transaction data into a list of integer values
|
|||
std::vector<std::string> decodeDatalist(std::string ser); |
|||
|
|||
#endif |
@ -1,35 +0,0 @@ |
|||
#include <stdio.h> |
|||
#include <iostream> |
|||
#include <vector> |
|||
#include "funcs.h" |
|||
#include "bignum.h" |
|||
#include "util.h" |
|||
#include "parser.h" |
|||
#include "lllparser.h" |
|||
#include "compiler.h" |
|||
#include "rewriter.h" |
|||
#include "tokenize.h" |
|||
|
|||
Node compileToLLL(std::string input) { |
|||
return rewrite(parseSerpent(input)); |
|||
} |
|||
|
|||
Node compileChunkToLLL(std::string input) { |
|||
return rewriteChunk(parseSerpent(input)); |
|||
} |
|||
|
|||
std::string compile(std::string input) { |
|||
return compileLLL(compileToLLL(input)); |
|||
} |
|||
|
|||
std::vector<Node> prettyCompile(std::string input) { |
|||
return prettyCompileLLL(compileToLLL(input)); |
|||
} |
|||
|
|||
std::string compileChunk(std::string input) { |
|||
return compileLLL(compileChunkToLLL(input)); |
|||
} |
|||
|
|||
std::vector<Node> prettyCompileChunk(std::string input) { |
|||
return prettyCompileLLL(compileChunkToLLL(input)); |
|||
} |
@ -1,35 +0,0 @@ |
|||
#include <stdio.h> |
|||
#include <iostream> |
|||
#include <vector> |
|||
#include "bignum.h" |
|||
#include "util.h" |
|||
#include "parser.h" |
|||
#include "lllparser.h" |
|||
#include "compiler.h" |
|||
#include "rewriter.h" |
|||
#include "tokenize.h" |
|||
|
|||
// Function listing:
|
|||
//
|
|||
// parseSerpent (serpent -> AST) std::string -> Node
|
|||
// parseLLL (LLL -> AST) std::string -> Node
|
|||
// rewrite (apply rewrite rules) Node -> Node
|
|||
// compileToLLL (serpent -> LLL) std::string -> Node
|
|||
// compileLLL (LLL -> EVMhex) Node -> std::string
|
|||
// prettyCompileLLL (LLL -> EVMasm) Node -> std::vector<Node>
|
|||
// prettyCompile (serpent -> EVMasm) std::string -> std::vector>Node>
|
|||
// compile (serpent -> EVMhex) std::string -> std::string
|
|||
// get_file_contents (filename -> file) std::string -> std::string
|
|||
// exists (does file exist?) std::string -> bool
|
|||
|
|||
Node compileToLLL(std::string input); |
|||
|
|||
Node compileChunkToLLL(std::string input); |
|||
|
|||
std::string compile(std::string input); |
|||
|
|||
std::vector<Node> prettyCompile(std::string input); |
|||
|
|||
std::string compileChunk(std::string input); |
|||
|
|||
std::vector<Node> prettyCompileChunk(std::string input); |
@ -1,203 +0,0 @@ |
|||
#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); |
|||
} |
@ -1,39 +0,0 @@ |
|||
#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 |
@ -1,70 +0,0 @@ |
|||
#include <stdio.h> |
|||
#include <iostream> |
|||
#include <vector> |
|||
#include <map> |
|||
#include "util.h" |
|||
#include "lllparser.h" |
|||
#include "tokenize.h" |
|||
|
|||
struct _parseOutput { |
|||
Node node; |
|||
int newpos; |
|||
}; |
|||
|
|||
// Helper, returns subtree and position of start of next node
|
|||
_parseOutput _parse(std::vector<Node> inp, int pos) { |
|||
Metadata met = inp[pos].metadata; |
|||
_parseOutput o; |
|||
// Bracket: keep grabbing tokens until we get to the
|
|||
// corresponding closing bracket
|
|||
if (inp[pos].val == "(" || inp[pos].val == "[") { |
|||
std::string fun, rbrack; |
|||
std::vector<Node> args; |
|||
pos += 1; |
|||
if (inp[pos].val == "[") { |
|||
fun = "access"; |
|||
rbrack = "]"; |
|||
} |
|||
else rbrack = ")"; |
|||
// First argument is the function
|
|||
while (inp[pos].val != ")") { |
|||
_parseOutput po = _parse(inp, pos); |
|||
if (fun.length() == 0 && po.node.type == 1) { |
|||
std::cerr << "Error: first arg must be function\n"; |
|||
fun = po.node.val; |
|||
} |
|||
else if (fun.length() == 0) { |
|||
fun = po.node.val; |
|||
} |
|||
else { |
|||
args.push_back(po.node); |
|||
} |
|||
pos = po.newpos; |
|||
} |
|||
o.newpos = pos + 1; |
|||
o.node = astnode(fun, args, met); |
|||
} |
|||
// Normal token, return it and advance to next token
|
|||
else { |
|||
o.newpos = pos + 1; |
|||
o.node = token(inp[pos].val, met); |
|||
} |
|||
return o; |
|||
} |
|||
|
|||
// stream of tokens -> lisp parse tree
|
|||
Node parseLLLTokenStream(std::vector<Node> inp) { |
|||
_parseOutput o = _parse(inp, 0); |
|||
return o.node; |
|||
} |
|||
|
|||
// Parses LLL
|
|||
Node parseLLL(std::string s, bool allowFileRead) { |
|||
std::string input = s; |
|||
std::string file = "main"; |
|||
if (exists(s) && allowFileRead) { |
|||
file = s; |
|||
input = get_file_contents(s); |
|||
} |
|||
return parseLLLTokenStream(tokenize(s, Metadata(file, 0, 0), true)); |
|||
} |
@ -1,13 +0,0 @@ |
|||
#ifndef ETHSERP_LLLPARSER |
|||
#define ETHSERP_LLLPARSER |
|||
|
|||
#include <stdio.h> |
|||
#include <iostream> |
|||
#include <vector> |
|||
#include <map> |
|||
#include "util.h" |
|||
|
|||
// LLL text -> parse tree
|
|||
Node parseLLL(std::string s, bool allowFileRead=false); |
|||
|
|||
#endif |
@ -1,154 +0,0 @@ |
|||
#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("BLOCKHASH", 0x40, 1, 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; |
|||
} |
@ -1,45 +0,0 @@ |
|||
#ifndef ETHSERP_OPCODES |
|||
#define ETHSERP_OPCODES |
|||
|
|||
#include <stdio.h> |
|||
#include <iostream> |
|||
#include <vector> |
|||
#include <map> |
|||
#include "util.h" |
|||
|
|||
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; |
|||
}; |
|||
|
|||
extern Mapping mapping[]; |
|||
|
|||
extern std::map<std::string, std::vector<int> > opcodes; |
|||
extern std::map<int, std::string> reverseOpcodes; |
|||
|
|||
std::pair<std::string, std::vector<int> > _opdata(std::string ops, int opi); |
|||
|
|||
int opcode(std::string op); |
|||
|
|||
int opinputs(std::string op); |
|||
|
|||
int opoutputs(std::string op); |
|||
|
|||
std::string op(int opcode); |
|||
|
|||
extern std::string lllSpecials[][3]; |
|||
|
|||
extern std::map<std::string, std::pair<int, int> > lllMap; |
|||
|
|||
bool isValidLLLFunc(std::string f, int argc); |
|||
|
|||
#endif |
@ -1,98 +0,0 @@ |
|||
#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)); |
|||
} |
@ -1,19 +0,0 @@ |
|||
#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 |
@ -1,437 +0,0 @@ |
|||
#include <stdio.h> |
|||
#include <iostream> |
|||
#include <vector> |
|||
#include <map> |
|||
#include "util.h" |
|||
#include "parser.h" |
|||
#include "tokenize.h" |
|||
|
|||
// Extended BEDMAS precedence order
|
|||
int precedence(Node tok) { |
|||
std::string v = tok.val; |
|||
if (v == ".") return -1; |
|||
else if (v == "!" || v == "not") return 1; |
|||
else if (v=="^" || v == "**") return 2; |
|||
else if (v=="*" || v=="/" || v=="%") return 3; |
|||
else if (v=="+" || v=="-") return 4; |
|||
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 10; |
|||
else if (v=="+=" || v=="-=" || v=="*=" || v=="/=" || v=="%=") return 10; |
|||
else if (v==":" || v == "::") return 11; |
|||
else return 0; |
|||
} |
|||
|
|||
// Token classification for shunting-yard purposes
|
|||
int toktype(Node tok) { |
|||
if (tok.type == ASTNODE) return COMPOUND; |
|||
std::string v = tok.val; |
|||
if (v == "(" || v == "[" || v == "{") return LPAREN; |
|||
else if (v == ")" || v == "]" || v == "}") return RPAREN; |
|||
else if (v == ",") return COMMA; |
|||
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) { |
|||
err("Invalid symbol: "+tok.val, tok.metadata); |
|||
} |
|||
} |
|||
} |
|||
return ALPHANUM; |
|||
} |
|||
|
|||
|
|||
// Converts to reverse polish notation
|
|||
std::vector<Node> shuntingYard(std::vector<Node> tokens) { |
|||
std::vector<Node> iq; |
|||
for (int i = tokens.size() - 1; i >= 0; i--) { |
|||
iq.push_back(tokens[i]); |
|||
} |
|||
std::vector<Node> oq; |
|||
std::vector<Node> stack; |
|||
Node prev, tok; |
|||
int prevtyp = 0, toktyp = 0; |
|||
|
|||
while (iq.size()) { |
|||
prev = tok; |
|||
prevtyp = toktyp; |
|||
tok = iq.back(); |
|||
toktyp = toktype(tok); |
|||
iq.pop_back(); |
|||
// Alphanumerics go straight to output queue
|
|||
if (toktyp == ALPHANUM) { |
|||
oq.push_back(tok); |
|||
} |
|||
// 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)); |
|||
} |
|||
stack.push_back(tok); |
|||
oq.push_back(tok); |
|||
} |
|||
// If rparen, keep moving from stack to output queue until lparen
|
|||
else if (toktyp == RPAREN) { |
|||
while (stack.size() && toktype(stack.back()) != LPAREN) { |
|||
oq.push_back(stack.back()); |
|||
stack.pop_back(); |
|||
} |
|||
if (stack.size()) { |
|||
stack.pop_back(); |
|||
} |
|||
oq.push_back(tok); |
|||
} |
|||
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(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()) == TOKEN_SPLITTER) |
|||
&& precedence(stack.back()) <= prec) { |
|||
oq.push_back(stack.back()); |
|||
stack.pop_back(); |
|||
} |
|||
stack.push_back(tok); |
|||
} |
|||
} |
|||
// Comma means finish evaluating the argument
|
|||
else if (toktyp == COMMA) { |
|||
while (stack.size() && toktype(stack.back()) != LPAREN) { |
|||
oq.push_back(stack.back()); |
|||
stack.pop_back(); |
|||
} |
|||
} |
|||
} |
|||
while (stack.size()) { |
|||
oq.push_back(stack.back()); |
|||
stack.pop_back(); |
|||
} |
|||
return oq; |
|||
} |
|||
|
|||
// Converts reverse polish notation into tree
|
|||
Node treefy(std::vector<Node> stream) { |
|||
std::vector<Node> iq; |
|||
for (int i = stream.size() -1; i >= 0; i--) { |
|||
iq.push_back(stream[i]); |
|||
} |
|||
std::vector<Node> oq; |
|||
while (iq.size()) { |
|||
Node tok = iq.back(); |
|||
iq.pop_back(); |
|||
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 || typ == TOKEN_SPLITTER) { |
|||
std::vector<Node> args; |
|||
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, |
|||
tok.metadata); |
|||
} |
|||
args.push_back(oq.back()); |
|||
oq.pop_back(); |
|||
} |
|||
std::vector<Node> args2; |
|||
while (args.size()) { |
|||
args2.push_back(args.back()); |
|||
args.pop_back(); |
|||
} |
|||
oq.push_back(astnode(tok.val, args2, tok.metadata)); |
|||
} |
|||
// If rparen, keep grabbing until we get to an lparen
|
|||
else if (typ == RPAREN) { |
|||
std::vector<Node> args; |
|||
while (1) { |
|||
if (toktype(oq.back()) == LPAREN) break; |
|||
args.push_back(oq.back()); |
|||
oq.pop_back(); |
|||
if (!oq.size()) err("Bracket without matching", tok.metadata); |
|||
} |
|||
oq.pop_back(); |
|||
args.push_back(oq.back()); |
|||
oq.pop_back(); |
|||
// We represent a[b] as (access a b)
|
|||
if (tok.val == "]") |
|||
args.push_back(token("access", tok.metadata)); |
|||
if (args.back().type == ASTNODE) |
|||
args.push_back(token("fun", tok.metadata)); |
|||
std::string fun = args.back().val; |
|||
args.pop_back(); |
|||
// We represent [1,2,3] as (array_lit 1 2 3)
|
|||
if (fun == "access" && args.size() && args.back().val == "id") { |
|||
fun = "array_lit"; |
|||
args.pop_back(); |
|||
} |
|||
std::vector<Node> args2; |
|||
while (args.size()) { |
|||
args2.push_back(args.back()); |
|||
args.pop_back(); |
|||
} |
|||
// When evaluating 2 + (3 * 5), the shunting yard algo turns that
|
|||
// into 2 ( id 3 5 * ) +, effectively putting "id" as a dummy
|
|||
// function where the algo was expecting a function to call the
|
|||
// thing inside the brackets. This reverses that step
|
|||
if (fun == "id" && args2.size() == 1) { |
|||
oq.push_back(args2[0]); |
|||
} |
|||
else { |
|||
oq.push_back(astnode(fun, args2, tok.metadata)); |
|||
} |
|||
} |
|||
else oq.push_back(tok); |
|||
// This is messy, but has to be done. Import/inset other files here
|
|||
std::string v = oq.back().val; |
|||
if ((v == "inset" || v == "import" || v == "create") |
|||
&& oq.back().args.size() == 1 |
|||
&& oq.back().args[0].type == TOKEN) { |
|||
int lastSlashPos = tok.metadata.file.rfind("/"); |
|||
std::string root; |
|||
if (lastSlashPos >= 0) |
|||
root = tok.metadata.file.substr(0, lastSlashPos) + "/"; |
|||
else |
|||
root = ""; |
|||
std::string filename = oq.back().args[0].val; |
|||
filename = filename.substr(1, filename.length() - 2); |
|||
if (!exists(root + filename)) |
|||
err("File does not exist: "+root + filename, tok.metadata); |
|||
if (v == "inset") { |
|||
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
|
|||
//for (int i = 0; i < oq.size(); i++) {
|
|||
// std::cerr << printSimple(oq[i]) << " ";
|
|||
//}
|
|||
//std::cerr << " <-\n";
|
|||
} |
|||
// Output must have one argument
|
|||
if (oq.size() == 0) { |
|||
err("Output blank", Metadata()); |
|||
} |
|||
else if (oq.size() > 1) { |
|||
return asn("multi", oq, oq[0].metadata); |
|||
} |
|||
|
|||
return oq[0]; |
|||
} |
|||
|
|||
|
|||
// Parses one line of serpent
|
|||
Node parseSerpentTokenStream(std::vector<Node> s) { |
|||
return treefy(shuntingYard(s)); |
|||
} |
|||
|
|||
|
|||
// Count spaces at beginning of line
|
|||
int spaceCount(std::string s) { |
|||
unsigned pos = 0; |
|||
while (pos < s.length() && (s[pos] == ' ' || s[pos] == '\t')) |
|||
pos++; |
|||
return pos; |
|||
} |
|||
|
|||
// 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 == "extern" |
|||
|| tok == "data" || tok == "assert" || tok == "return" |
|||
|| tok == "fun" || tok == "scope" || tok == "macro" |
|||
|| tok == "type"; |
|||
} |
|||
|
|||
// Are the two commands meant to continue each other?
|
|||
bool bodiedContinued(std::string prev, std::string tok) { |
|||
return (prev == "if" && tok == "elif") |
|||
|| (prev == "elif" && tok == "else") |
|||
|| (prev == "elif" && tok == "elif") |
|||
|| (prev == "if" && tok == "else"); |
|||
} |
|||
|
|||
// Is a line of code empty?
|
|||
bool isLineEmpty(std::string line) { |
|||
std::vector<Node> tokens = tokenize(line); |
|||
if (!tokens.size() || tokens[0].val == "#" || tokens[0].val == "//") |
|||
return true; |
|||
return false; |
|||
} |
|||
|
|||
// Parse lines of serpent (helper function)
|
|||
Node parseLines(std::vector<std::string> lines, Metadata metadata, int sp) { |
|||
std::vector<Node> o; |
|||
int origLine = metadata.ln; |
|||
unsigned i = 0; |
|||
while (i < lines.size()) { |
|||
metadata.ln = origLine + i; |
|||
std::string main = lines[i]; |
|||
if (isLineEmpty(main)) { |
|||
i += 1; |
|||
continue; |
|||
} |
|||
int spaces = spaceCount(main); |
|||
if (spaces != sp) { |
|||
err("Indent mismatch", metadata); |
|||
} |
|||
// Tokenize current line
|
|||
std::vector<Node> tokens = tokenize(main.substr(sp), metadata); |
|||
// Remove comments
|
|||
std::vector<Node> tokens2; |
|||
for (unsigned j = 0; j < tokens.size(); j++) { |
|||
if (tokens[j].val == "#" || tokens[j].val == "//") break; |
|||
tokens2.push_back(tokens[j]); |
|||
} |
|||
bool expectingChildBlock = false; |
|||
if (tokens2.size() > 0 && tokens2.back().val == ":") { |
|||
tokens2.pop_back(); |
|||
expectingChildBlock = true; |
|||
} |
|||
// Parse current line
|
|||
Node out = parseSerpentTokenStream(tokens2); |
|||
// Parse child block
|
|||
int childIndent = 999999; |
|||
std::vector<std::string> childBlock; |
|||
while (1) { |
|||
i++; |
|||
if (i >= lines.size()) |
|||
break; |
|||
bool ile = isLineEmpty(lines[i]); |
|||
if (!ile) { |
|||
int spaces = spaceCount(lines[i]); |
|||
if (spaces <= sp) break; |
|||
childBlock.push_back(lines[i]); |
|||
if (spaces < childIndent) childIndent = spaces; |
|||
} |
|||
else childBlock.push_back(""); |
|||
} |
|||
// Child block empty?
|
|||
bool cbe = true; |
|||
for (unsigned i = 0; i < childBlock.size(); i++) { |
|||
if (childBlock[i].length() > 0) { cbe = false; break; } |
|||
} |
|||
// Add child block to AST
|
|||
if (expectingChildBlock) { |
|||
if (cbe) |
|||
err("Expected indented child block!", out.metadata); |
|||
out.type = ASTNODE; |
|||
metadata.ln += 1; |
|||
out.args.push_back(parseLines(childBlock, metadata, childIndent)); |
|||
metadata.ln -= 1; |
|||
} |
|||
else if (!cbe) |
|||
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) { |
|||
o.push_back(out); |
|||
continue; |
|||
} |
|||
// This is a little complicated. Basically, the idea here is to build
|
|||
// constructions like [if [< x 5] [a] [elif [< x 10] [b] [else [c]]]]
|
|||
std::vector<Node> u; |
|||
u.push_back(o.back()); |
|||
if (bodiedContinued(o.back().val, out.val)) { |
|||
while (1) { |
|||
if (!bodiedContinued(u.back().val, out.val)) { |
|||
u.pop_back(); |
|||
break; |
|||
} |
|||
if (!u.back().args.size() |
|||
|| !bodiedContinued(u.back().val, u.back().args.back().val)) { |
|||
break; |
|||
} |
|||
u.push_back(u.back().args.back()); |
|||
} |
|||
u.back().args.push_back(out); |
|||
while (u.size() > 1) { |
|||
Node v = u.back(); |
|||
u.pop_back(); |
|||
u.back().args.pop_back(); |
|||
u.back().args.push_back(v); |
|||
} |
|||
o.pop_back(); |
|||
o.push_back(u[0]); |
|||
} |
|||
else o.push_back(out); |
|||
} |
|||
if (o.size() == 1) |
|||
return o[0]; |
|||
else if (o.size()) |
|||
return astnode("seq", o, o[0].metadata); |
|||
else |
|||
return astnode("seq", o, Metadata()); |
|||
} |
|||
|
|||
// Parses serpent code
|
|||
Node parseSerpent(std::string s) { |
|||
std::string input = s; |
|||
std::string file = "main"; |
|||
if (exists(s)) { |
|||
file = s; |
|||
input = get_file_contents(s); |
|||
} |
|||
return parseLines(splitLines(input), Metadata(file, 0, 0), 0); |
|||
} |
|||
|
|||
|
|||
using namespace std; |
@ -1,13 +0,0 @@ |
|||
#ifndef ETHSERP_PARSER |
|||
#define ETHSERP_PARSER |
|||
|
|||
#include <stdio.h> |
|||
#include <iostream> |
|||
#include <vector> |
|||
#include <map> |
|||
#include "util.h" |
|||
|
|||
// Serpent text -> parse tree
|
|||
Node parseSerpent(std::string s); |
|||
|
|||
#endif |
@ -1,327 +0,0 @@ |
|||
#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)); |
|||
} |
@ -1,50 +0,0 @@ |
|||
#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 |
@ -1,905 +0,0 @@ |
|||
#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" |
|||
#include "opcodes.h" |
|||
|
|||
// Rewrite rules
|
|||
std::string macros[][2] = { |
|||
{ |
|||
"(seq $x)", |
|||
"$x" |
|||
}, |
|||
{ |
|||
"(seq (seq) $x)", |
|||
"$x" |
|||
}, |
|||
{ |
|||
"(+= $a $b)", |
|||
"(set $a (+ $a $b))" |
|||
}, |
|||
{ |
|||
"(*= $a $b)", |
|||
"(set $a (* $a $b))" |
|||
}, |
|||
{ |
|||
"(-= $a $b)", |
|||
"(set $a (- $a $b))" |
|||
}, |
|||
{ |
|||
"(/= $a $b)", |
|||
"(set $a (/ $a $b))" |
|||
}, |
|||
{ |
|||
"(%= $a $b)", |
|||
"(set $a (% $a $b))" |
|||
}, |
|||
{ |
|||
"(^= $a $b)", |
|||
"(set $a (^ $a $b))" |
|||
}, |
|||
{ |
|||
"(!= $a $b)", |
|||
"(iszero (eq $a $b))" |
|||
}, |
|||
{ |
|||
"(assert $x)", |
|||
"(unless $x (stop))" |
|||
}, |
|||
{ |
|||
"(min $a $b)", |
|||
"(with $1 $a (with $2 $b (if (lt $1 $2) $1 $2)))" |
|||
}, |
|||
{ |
|||
"(max $a $b)", |
|||
"(with $1 $a (with $2 $b (if (lt $1 $2) $2 $1)))" |
|||
}, |
|||
{ |
|||
"(smin $a $b)", |
|||
"(with $1 $a (with $2 $b (if (slt $1 $2) $1 $2)))" |
|||
}, |
|||
{ |
|||
"(smax $a $b)", |
|||
"(with $1 $a (with $2 $b (if (slt $1 $2) $2 $1)))" |
|||
}, |
|||
{ |
|||
"(if $cond $do (else $else))", |
|||
"(if $cond $do $else)" |
|||
}, |
|||
{ |
|||
"(code $code)", |
|||
"$code" |
|||
}, |
|||
{ |
|||
"(slice $arr $pos)", |
|||
"(add $arr (mul 32 $pos))", |
|||
}, |
|||
{ |
|||
"(array $len)", |
|||
"(alloc (mul 32 $len))" |
|||
}, |
|||
{ |
|||
"(while $cond $do)", |
|||
"(until (iszero $cond) $do)", |
|||
}, |
|||
{ |
|||
"(while (iszero $cond) $do)", |
|||
"(until $cond $do)", |
|||
}, |
|||
{ |
|||
"(if $cond $do)", |
|||
"(unless (iszero $cond) $do)", |
|||
}, |
|||
{ |
|||
"(if (iszero $cond) $do)", |
|||
"(unless $cond $do)", |
|||
}, |
|||
{ |
|||
"(access (. self storage) $ind)", |
|||
"(sload $ind)" |
|||
}, |
|||
{ |
|||
"(access $var $ind)", |
|||
"(mload (add $var (mul 32 $ind)))" |
|||
}, |
|||
{ |
|||
"(set (access (. self storage) $ind) $val)", |
|||
"(sstore $ind $val)" |
|||
}, |
|||
{ |
|||
"(set (sload $ind) $val)", |
|||
"(sstore $ind $val)" |
|||
}, |
|||
{ |
|||
"(set (access $var $ind) $val)", |
|||
"(mstore (add $var (mul 32 $ind)) $val)" |
|||
}, |
|||
{ |
|||
"(getch $var $ind)", |
|||
"(mod (mload (sub (add $var $ind) 31)) 256)" |
|||
}, |
|||
{ |
|||
"(setch $var $ind $val)", |
|||
"(mstore8 (add $var $ind) $val)", |
|||
}, |
|||
{ |
|||
"(send $to $value)", |
|||
"(~call (sub (gas) 25) $to $value 0 0 0 0)" |
|||
}, |
|||
{ |
|||
"(send $gas $to $value)", |
|||
"(~call $gas $to $value 0 0 0 0)" |
|||
}, |
|||
{ |
|||
"(sha3 $x)", |
|||
"(seq (set $1 $x) (~sha3 (ref $1) 32))" |
|||
}, |
|||
{ |
|||
"(sha3 $mstart (= chars $msize))", |
|||
"(~sha3 $mstart $msize)" |
|||
}, |
|||
{ |
|||
"(sha3 $mstart $msize)", |
|||
"(~sha3 $mstart (mul 32 $msize))" |
|||
}, |
|||
{ |
|||
"(id $0)", |
|||
"$0" |
|||
}, |
|||
{ |
|||
"(return $x)", |
|||
"(seq (set $1 $x) (~return (ref $1) 32))" |
|||
}, |
|||
{ |
|||
"(return $mstart (= chars $msize))", |
|||
"(~return $mstart $msize)" |
|||
}, |
|||
{ |
|||
"(return $start $len)", |
|||
"(~return $start (mul 32 $len))" |
|||
}, |
|||
{ |
|||
"(&& $x $y)", |
|||
"(if $x $y 0)" |
|||
}, |
|||
{ |
|||
"(|| $x $y)", |
|||
"(with $1 $x (if $1 $1 $y))" |
|||
}, |
|||
{ |
|||
"(>= $x $y)", |
|||
"(iszero (slt $x $y))" |
|||
}, |
|||
{ |
|||
"(<= $x $y)", |
|||
"(iszero (sgt $x $y))" |
|||
}, |
|||
{ |
|||
"(create $code)", |
|||
"(create 0 $code)" |
|||
}, |
|||
{ |
|||
"(create $endowment $code)", |
|||
"(with $1 (msize) (create $endowment (get $1) (lll $code (msize))))" |
|||
}, |
|||
{ |
|||
"(sha256 $x)", |
|||
"(with $1 (alloc 64) (seq (mstore (add (get $1) 32) $x) (pop (~call 101 2 0 (add (get $1) 32) 32 (get $1) 32)) (mload (get $1))))" |
|||
}, |
|||
{ |
|||
"(sha256 $arr (= chars $sz))", |
|||
"(with $1 (alloc 32) (seq (pop (~call 101 2 0 $arr $sz (get $1) 32)) (mload (get $1))))" |
|||
}, |
|||
{ |
|||
"(sha256 $arr $sz)", |
|||
"(with $1 (alloc 32) (seq (pop (~call 101 2 0 $arr (mul 32 $sz) (get $1) 32)) (mload (get $1))))" |
|||
}, |
|||
{ |
|||
"(ripemd160 $x)", |
|||
"(with $1 (alloc 64) (seq (mstore (add (get $1) 32) $x) (pop (~call 101 3 0 (add (get $1) 32) 32 (get $1) 32)) (mload (get $1))))" |
|||
}, |
|||
{ |
|||
"(ripemd160 $arr (= chars $sz))", |
|||
"(with $1 (alloc 32) (seq (pop (~call 101 3 0 $arr $sz (mload $1) 32)) (mload (get $1))))" |
|||
}, |
|||
{ |
|||
"(ripemd160 $arr $sz)", |
|||
"(with $1 (alloc 32) (seq (pop (~call 101 3 0 $arr (mul 32 $sz) (get $1) 32)) (mload (get $1))))" |
|||
}, |
|||
{ |
|||
"(ecrecover $h $v $r $s)", |
|||
"(with $1 (alloc 160) (seq (mstore (get $1) $h) (mstore (add (get $1) 32) $v) (mstore (add (get $1) 64) $r) (mstore (add (get $1) 96) $s) (pop (~call 101 1 0 (get $1) 128 (add (get $1 128)) 32)) (mload (add (get $1) 128))))" |
|||
}, |
|||
{ |
|||
"(inset $x)", |
|||
"$x" |
|||
}, |
|||
{ |
|||
"(create $x)", |
|||
"(with $1 (msize) (create $val (get $1) (lll $code (get $1))))" |
|||
}, |
|||
{ |
|||
"(with (= $var $val) $cond)", |
|||
"(with $var $val $cond)" |
|||
}, |
|||
{ |
|||
"(log $t1)", |
|||
"(~log1 0 0 $t1)" |
|||
}, |
|||
{ |
|||
"(log $t1 $t2)", |
|||
"(~log2 0 0 $t1 $t2)" |
|||
}, |
|||
{ |
|||
"(log $t1 $t2 $t3)", |
|||
"(~log3 0 0 $t1 $t2 $t3)" |
|||
}, |
|||
{ |
|||
"(log $t1 $t2 $t3 $t4)", |
|||
"(~log4 0 0 $t1 $t2 $t3 $t4)" |
|||
}, |
|||
{ |
|||
"(logarr $a $sz)", |
|||
"(~log0 $a (mul 32 $sz))" |
|||
}, |
|||
{ |
|||
"(logarr $a $sz $t1)", |
|||
"(~log1 $a (mul 32 $sz) $t1)" |
|||
}, |
|||
{ |
|||
"(logarr $a $sz $t1 $t2)", |
|||
"(~log2 $a (mul 32 $sz) $t1 $t2)" |
|||
}, |
|||
{ |
|||
"(logarr $a $sz $t1 $t2 $t3)", |
|||
"(~log3 $a (mul 32 $sz) $t1 $t2 $t3)" |
|||
}, |
|||
{ |
|||
"(logarr $a $sz $t1 $t2 $t3 $t4)", |
|||
"(~log4 $a (mul 32 $sz) $t1 $t2 $t3 $t4)" |
|||
}, |
|||
{ |
|||
"(save $loc $array (= chars $count))", |
|||
"(with $location (ref $loc) (with $c $count (with $end (div $c 32) (with $i 0 (seq (while (slt $i $end) (seq (sstore (add $i $location) (access $array $i)) (set $i (add $i 1)))) (sstore (add $i $location) (~and (access $array $i) (sub 0 (exp 256 (sub 32 (mod $c 32)))))))))))" |
|||
}, |
|||
{ |
|||
"(save $loc $array $count)", |
|||
"(with $location (ref $loc) (with $end $count (with $i 0 (while (slt $i $end) (seq (sstore (add $i $location) (access $array $i)) (set $i (add $i 1)))))))" |
|||
}, |
|||
{ |
|||
"(load $loc (= chars $count))", |
|||
"(with $location (ref $loc) (with $c $count (with $a (alloc $c) (with $i 0 (seq (while (slt $i (div $c 32)) (seq (set (access $a $i) (sload (add $location $i))) (set $i (add $i 1)))) (set (access $a $i) (~and (sload (add $location $i)) (sub 0 (exp 256 (sub 32 (mod $c 32)))))) $a)))))" |
|||
}, |
|||
{ |
|||
"(load $loc $count)", |
|||
"(with $location (ref $loc) (with $c $count (with $a (alloc $c) (with $i 0 (seq (while (slt $i $c) (seq (set (access $a $i) (sload (add $location $i))) (set $i (add $i 1)))) $a)))))" |
|||
}, |
|||
{ |
|||
"(unsafe_mcopy $to $from $sz)", |
|||
"(with _sz $sz (with _from $from (with _to $to (seq (comment STARTING UNSAFE MCOPY) (with _i 0 (while (lt _i _sz) (seq (mstore (add $to _i) (mload (add _from _i))) (set _i (add _i 32)))))))))" |
|||
}, |
|||
{ |
|||
"(mcopy $to $from $_sz)", |
|||
"(with _to $to (with _from $from (with _sz $sz (seq (comment STARTING MCOPY (with _i 0 (seq (while (lt (add _i 31) _sz) (seq (mstore (add _to _i) (mload (add _from _i))) (set _i (add _i 32)))) (with _mask (exp 256 (sub 32 (mod _sz 32))) (mstore (add $to _i) (add (mod (mload (add $to _i)) _mask) (and (mload (add $from _i)) (sub 0 _mask))))))))))))" |
|||
}, |
|||
{ "(. msg sender)", "(caller)" }, |
|||
{ "(. msg value)", "(callvalue)" }, |
|||
{ "(. tx gasprice)", "(gasprice)" }, |
|||
{ "(. tx origin)", "(origin)" }, |
|||
{ "(. tx gas)", "(gas)" }, |
|||
{ "(. $x balance)", "(balance $x)" }, |
|||
{ "self", "(address)" }, |
|||
{ "(. block prevhash)", "(prevhash)" }, |
|||
{ "(. block coinbase)", "(coinbase)" }, |
|||
{ "(. block timestamp)", "(timestamp)" }, |
|||
{ "(. block number)", "(number)" }, |
|||
{ "(. block difficulty)", "(difficulty)" }, |
|||
{ "(. block gaslimit)", "(gaslimit)" }, |
|||
{ "stop", "(stop)" }, |
|||
{ "---END---", "" } //Keep this line at the end of the list
|
|||
}; |
|||
|
|||
// Token synonyms
|
|||
std::string synonyms[][2] = { |
|||
{ "or", "||" }, |
|||
{ "and", "&&" }, |
|||
{ "|", "~or" }, |
|||
{ "&", "~and" }, |
|||
{ "elif", "if" }, |
|||
{ "!", "iszero" }, |
|||
{ "~", "~not" }, |
|||
{ "not", "iszero" }, |
|||
{ "string", "alloc" }, |
|||
{ "+", "add" }, |
|||
{ "-", "sub" }, |
|||
{ "*", "mul" }, |
|||
{ "/", "sdiv" }, |
|||
{ "^", "exp" }, |
|||
{ "**", "exp" }, |
|||
{ "%", "smod" }, |
|||
{ "<", "slt" }, |
|||
{ ">", "sgt" }, |
|||
{ "=", "set" }, |
|||
{ "==", "eq" }, |
|||
{ ":", "kv" }, |
|||
{ "---END---", "" } //Keep this line at the end of the list
|
|||
}; |
|||
|
|||
std::map<std::string, std::string> synonymMap; |
|||
|
|||
// Custom setters (need to be registered separately
|
|||
// for use with managed storage)
|
|||
std::string setters[][2] = { |
|||
{ "+=", "+" }, |
|||
{ "-=", "-" }, |
|||
{ "*=", "*" }, |
|||
{ "/=", "/" }, |
|||
{ "%=", "%" }, |
|||
{ "^=", "^" }, |
|||
{ "---END---", "" } //Keep this line at the end of the list
|
|||
}; |
|||
|
|||
std::map<std::string, std::string> setterMap; |
|||
|
|||
// Processes mutable array literals
|
|||
Node array_lit_transform(Node node) { |
|||
std::string prefix = "_temp"+mkUniqueToken() + "_"; |
|||
Metadata m = node.metadata; |
|||
std::map<std::string, Node> d; |
|||
std::string o = "(seq (set $arr (alloc "+utd(node.args.size()*32)+"))"; |
|||
for (unsigned i = 0; i < node.args.size(); i++) { |
|||
o += " (mstore (add (get $arr) "+utd(i * 32)+") $"+utd(i)+")"; |
|||
d[utd(i)] = node.args[i]; |
|||
} |
|||
o += " (get $arr))"; |
|||
return subst(parseLLL(o), d, prefix, m); |
|||
} |
|||
|
|||
// Processes long text literals
|
|||
Node string_transform(Node node) { |
|||
Metadata m = node.metadata; |
|||
if (!node.args.size()) |
|||
err("Empty text!", m); |
|||
if (node.args[0].val.size() < 2 |
|||
|| node.args[0].val[0] != '"' |
|||
|| node.args[0].val[node.args[0].val.size() - 1] != '"') |
|||
err("Text contents don't look like a string!", m); |
|||
std::string bin = node.args[0].val.substr(1, node.args[0].val.size() - 2); |
|||
unsigned sz = bin.size(); |
|||
std::vector<Node> o; |
|||
for (unsigned i = 0; i < sz; i += 32) { |
|||
std::string t = binToNumeric(bin.substr(i, 32)); |
|||
if ((sz - i) < 32 && (sz - i) > 0) { |
|||
while ((sz - i) < 32) { |
|||
t = decimalMul(t, "256"); |
|||
i--; |
|||
} |
|||
i = sz; |
|||
} |
|||
o.push_back(token(t, node.metadata)); |
|||
} |
|||
node = astnode("array_lit", o, node.metadata); |
|||
return array_lit_transform(node); |
|||
} |
|||
|
|||
|
|||
Node apply_rules(preprocessResult pr); |
|||
|
|||
// Transform "<variable>.<fun>(args...)" into
|
|||
// a call
|
|||
Node dotTransform(Node node, preprocessAux aux) { |
|||
Metadata m = node.metadata; |
|||
// We're gonna make lots of temporary variables,
|
|||
// so set up a unique flag for them
|
|||
std::string prefix = "_temp"+mkUniqueToken()+"_"; |
|||
// Check that the function name is a token
|
|||
if (node.args[0].args[1].type == ASTNODE) |
|||
err("Function name must be static", m); |
|||
|
|||
Node dotOwner = node.args[0].args[0]; |
|||
std::string dotMember = node.args[0].args[1].val; |
|||
// kwargs = map of special arguments
|
|||
std::map<std::string, Node> kwargs; |
|||
kwargs["value"] = token("0", m); |
|||
kwargs["gas"] = subst(parseLLL("(- (gas) 25)"), msn(), prefix, m); |
|||
// Search for as=? and call=code keywords, and isolate the actual
|
|||
// function arguments
|
|||
std::vector<Node> fnargs; |
|||
std::string as = ""; |
|||
std::string op = "call"; |
|||
for (unsigned i = 1; i < node.args.size(); i++) { |
|||
fnargs.push_back(node.args[i]); |
|||
Node arg = fnargs.back(); |
|||
if (arg.val == "=" || arg.val == "set") { |
|||
if (arg.args[0].val == "as") |
|||
as = arg.args[1].val; |
|||
if (arg.args[0].val == "call" && arg.args[1].val == "code") |
|||
op = "callcode"; |
|||
if (arg.args[0].val == "gas") |
|||
kwargs["gas"] = arg.args[1]; |
|||
if (arg.args[0].val == "value") |
|||
kwargs["value"] = arg.args[1]; |
|||
if (arg.args[0].val == "outsz") |
|||
kwargs["outsz"] = arg.args[1]; |
|||
} |
|||
} |
|||
if (dotOwner.val == "self") { |
|||
if (as.size()) err("Cannot use \"as\" when calling self!", m); |
|||
as = dotOwner.val; |
|||
} |
|||
// Determine the funId and sig assuming the "as" keyword was used
|
|||
int funId = 0; |
|||
std::string sig; |
|||
if (as.size() > 0 && aux.localExterns.count(as)) { |
|||
if (!aux.localExterns[as].count(dotMember)) |
|||
err("Invalid call: "+printSimple(dotOwner)+"."+dotMember, m); |
|||
funId = aux.localExterns[as][dotMember]; |
|||
sig = aux.localExternSigs[as][dotMember]; |
|||
} |
|||
// Determine the funId and sig otherwise
|
|||
else if (!as.size()) { |
|||
if (!aux.globalExterns.count(dotMember)) |
|||
err("Invalid call: "+printSimple(dotOwner)+"."+dotMember, m); |
|||
std::string key = unsignedToDecimal(aux.globalExterns[dotMember]); |
|||
funId = aux.globalExterns[dotMember]; |
|||
sig = aux.globalExternSigs[dotMember]; |
|||
} |
|||
else err("Invalid call: "+printSimple(dotOwner)+"."+dotMember, m); |
|||
// Pack arguments
|
|||
kwargs["data"] = packArguments(fnargs, sig, funId, m); |
|||
kwargs["to"] = dotOwner; |
|||
Node main; |
|||
// Pack output
|
|||
if (!kwargs.count("outsz")) { |
|||
main = parseLLL( |
|||
"(with _data $data (seq " |
|||
"(pop (~"+op+" $gas $to $value (access _data 0) (access _data 1) (ref $dataout) 32))" |
|||
"(get $dataout)))"); |
|||
} |
|||
else { |
|||
main = parseLLL( |
|||
"(with _data $data (with _outsz (mul 32 $outsz) (with _out (alloc _outsz) (seq " |
|||
"(pop (~"+op+" $gas $to $value (access _data 0) (access _data 1) _out _outsz))" |
|||
"(get _out)))))"); |
|||
} |
|||
// Set up main call
|
|||
|
|||
Node o = subst(main, kwargs, prefix, m); |
|||
return o; |
|||
} |
|||
|
|||
// Transform an access of the form self.bob, self.users[5], etc into
|
|||
// a storage access
|
|||
//
|
|||
// There exist two types of objects: finite objects, and infinite
|
|||
// objects. Finite objects are packed optimally tightly into storage
|
|||
// accesses; for example:
|
|||
//
|
|||
// data obj[100](a, b[2][4], c)
|
|||
//
|
|||
// obj[0].a -> 0
|
|||
// obj[0].b[0][0] -> 1
|
|||
// obj[0].b[1][3] -> 8
|
|||
// obj[45].c -> 459
|
|||
//
|
|||
// Infinite objects are accessed by sha3([v1, v2, v3 ... ]), where
|
|||
// the values are a list of array indices and keyword indices, for
|
|||
// example:
|
|||
// data obj[](a, b[2][4], c)
|
|||
// data obj2[](a, b[][], c)
|
|||
//
|
|||
// obj[0].a -> sha3([0, 0, 0])
|
|||
// obj[5].b[1][3] -> sha3([0, 5, 1, 1, 3])
|
|||
// obj[45].c -> sha3([0, 45, 2])
|
|||
// obj2[0].a -> sha3([1, 0, 0])
|
|||
// obj2[5].b[1][3] -> sha3([1, 5, 1, 1, 3])
|
|||
// obj2[45].c -> sha3([1, 45, 2])
|
|||
Node storageTransform(Node node, preprocessAux aux, |
|||
bool mapstyle=false, bool ref=false) { |
|||
Metadata m = node.metadata; |
|||
// Get a list of all of the "access parameters" used in order
|
|||
// eg. self.users[5].cow[4][m[2]][woof] ->
|
|||
// [--self, --users, 5, --cow, 4, m[2], woof]
|
|||
std::vector<Node> hlist = listfyStorageAccess(node); |
|||
// For infinite arrays, the terms array will just provide a list
|
|||
// of indices. For finite arrays, it's a list of index*coefficient
|
|||
std::vector<Node> terms; |
|||
std::string offset = "0"; |
|||
std::string prefix = ""; |
|||
std::string varPrefix = "_temp"+mkUniqueToken()+"_"; |
|||
int c = 0; |
|||
std::vector<std::string> coefficients; |
|||
coefficients.push_back(""); |
|||
for (unsigned i = 1; i < hlist.size(); i++) { |
|||
// We pre-add the -- flag to parameter-like terms. For example,
|
|||
// self.users[m] -> [--self, --users, m]
|
|||
// self.users.m -> [--self, --users, --m]
|
|||
if (hlist[i].val.substr(0, 2) == "--") { |
|||
prefix += hlist[i].val.substr(2) + "."; |
|||
std::string tempPrefix = prefix.substr(0, prefix.size()-1); |
|||
if (!aux.storageVars.offsets.count(tempPrefix)) |
|||
return node; |
|||
if (c < (signed)coefficients.size() - 1) |
|||
err("Too few array index lookups", m); |
|||
if (c > (signed)coefficients.size() - 1) |
|||
err("Too many array index lookups", m); |
|||
coefficients = aux.storageVars.coefficients[tempPrefix]; |
|||
// If the size of an object exceeds 2^176, we make it an infinite
|
|||
// array
|
|||
if (decimalGt(coefficients.back(), tt176) && !mapstyle) |
|||
return storageTransform(node, aux, true, ref); |
|||
offset = decimalAdd(offset, aux.storageVars.offsets[tempPrefix]); |
|||
c = 0; |
|||
if (mapstyle) |
|||
terms.push_back(token(unsignedToDecimal( |
|||
aux.storageVars.indices[tempPrefix]))); |
|||
} |
|||
else if (mapstyle) { |
|||
terms.push_back(hlist[i]); |
|||
c += 1; |
|||
} |
|||
else { |
|||
if (c > (signed)coefficients.size() - 2) |
|||
err("Too many array index lookups", m); |
|||
terms.push_back( |
|||
astnode("mul", |
|||
hlist[i], |
|||
token(coefficients[coefficients.size() - 2 - c], m), |
|||
m)); |
|||
|
|||
c += 1; |
|||
} |
|||
} |
|||
if (aux.storageVars.nonfinal.count(prefix.substr(0, prefix.size()-1))) |
|||
err("Storage variable access not deep enough", m); |
|||
if (c < (signed)coefficients.size() - 1) { |
|||
err("Too few array index lookups", m); |
|||
} |
|||
if (c > (signed)coefficients.size() - 1) { |
|||
err("Too many array index lookups", m); |
|||
} |
|||
Node o; |
|||
if (mapstyle) { |
|||
std::string t = "_temp_"+mkUniqueToken(); |
|||
std::vector<Node> sub; |
|||
for (unsigned i = 0; i < terms.size(); i++) |
|||
sub.push_back(asn("mstore", |
|||
asn("add", |
|||
tkn(utd(i * 32), m), |
|||
asn("get", tkn(t+"pos", m), m), |
|||
m), |
|||
terms[i], |
|||
m)); |
|||
sub.push_back(tkn(t+"pos", m)); |
|||
Node main = asn("with", |
|||
tkn(t+"pos", m), |
|||
asn("alloc", tkn(utd(terms.size() * 32), m), m), |
|||
asn("seq", sub, m), |
|||
m); |
|||
Node sz = token(utd(terms.size() * 32), m); |
|||
o = astnode("~sha3", |
|||
main, |
|||
sz, |
|||
m); |
|||
} |
|||
else { |
|||
// We add up all the index*coefficients
|
|||
Node out = token(offset, node.metadata); |
|||
for (unsigned i = 0; i < terms.size(); i++) { |
|||
std::vector<Node> temp; |
|||
temp.push_back(out); |
|||
temp.push_back(terms[i]); |
|||
out = astnode("add", temp, node.metadata); |
|||
} |
|||
o = out; |
|||
} |
|||
if (ref) return o; |
|||
else return astnode("sload", o, node.metadata); |
|||
} |
|||
|
|||
// Basic rewrite rule execution
|
|||
std::pair<Node, bool> rulesTransform(Node node, rewriteRuleSet macros) { |
|||
std::string prefix = "_temp_"+mkUniqueToken(); |
|||
bool changed = false; |
|||
if (!macros.ruleLists.count(node.val)) |
|||
return std::pair<Node, bool>(node, false); |
|||
std::vector<rewriteRule> rules = macros.ruleLists[node.val]; |
|||
for (unsigned pos = 0; pos < rules.size(); pos++) { |
|||
rewriteRule macro = rules[pos]; |
|||
matchResult mr = match(macro.pattern, node); |
|||
if (mr.success) { |
|||
node = subst(macro.substitution, mr.map, prefix, node.metadata); |
|||
std::pair<Node, bool> o = rulesTransform(node, macros); |
|||
o.second = true; |
|||
return o; |
|||
} |
|||
} |
|||
return std::pair<Node, bool>(node, changed); |
|||
} |
|||
|
|||
std::pair<Node, bool> synonymTransform(Node node) { |
|||
bool changed = false; |
|||
if (node.type == ASTNODE && synonymMap.count(node.val)) { |
|||
node.val = synonymMap[node.val]; |
|||
changed = true; |
|||
} |
|||
return std::pair<Node, bool>(node, changed); |
|||
} |
|||
|
|||
rewriteRuleSet nodeMacros; |
|||
rewriteRuleSet setterMacros; |
|||
|
|||
bool dontDescend(std::string s) { |
|||
return s == "macro" || s == "comment" || s == "outer"; |
|||
} |
|||
|
|||
// Recursively applies any set of rewrite rules
|
|||
std::pair<Node, bool> apply_rules_iter(preprocessResult pr, rewriteRuleSet rules) { |
|||
bool changed = false; |
|||
Node node = pr.first; |
|||
if (dontDescend(node.val)) |
|||
return std::pair<Node, bool>(node, false); |
|||
std::pair<Node, bool> o = rulesTransform(node, rules); |
|||
node = o.first; |
|||
changed = changed || o.second; |
|||
if (node.type == ASTNODE) { |
|||
for (unsigned i = 0; i < node.args.size(); i++) { |
|||
std::pair<Node, bool> r = |
|||
apply_rules_iter(preprocessResult(node.args[i], pr.second), rules); |
|||
node.args[i] = r.first; |
|||
changed = changed || r.second; |
|||
} |
|||
} |
|||
return std::pair<Node, bool>(node, changed); |
|||
} |
|||
|
|||
// Recursively applies rewrite rules and other primary transformations
|
|||
std::pair<Node, bool> mainTransform(preprocessResult pr) { |
|||
bool changed = false; |
|||
Node node = pr.first; |
|||
|
|||
// Anything inside "outer" should be treated as a separate program
|
|||
// and thus recursively compiled in its entirety
|
|||
if (node.val == "outer") { |
|||
node = apply_rules(preprocess(node.args[0])); |
|||
changed = true; |
|||
} |
|||
|
|||
// Don't descend into comments, macros and inner scopes
|
|||
if (dontDescend(node.val)) |
|||
return std::pair<Node, bool>(node, changed); |
|||
|
|||
// Special storage transformation
|
|||
if (isNodeStorageVariable(node)) { |
|||
node = storageTransform(node, pr.second); |
|||
changed = true; |
|||
} |
|||
if (node.val == "ref" && isNodeStorageVariable(node.args[0])) { |
|||
node = storageTransform(node.args[0], pr.second, false, true); |
|||
changed = true; |
|||
} |
|||
if (node.val == "=" && isNodeStorageVariable(node.args[0])) { |
|||
Node t = storageTransform(node.args[0], pr.second); |
|||
if (t.val == "sload") { |
|||
std::vector<Node> o; |
|||
o.push_back(t.args[0]); |
|||
o.push_back(node.args[1]); |
|||
node = astnode("sstore", o, node.metadata); |
|||
} |
|||
changed = true; |
|||
} |
|||
// Main code
|
|||
std::pair<Node, bool> pnb = synonymTransform(node); |
|||
node = pnb.first; |
|||
changed = changed || pnb.second; |
|||
// std::cerr << priority << " " << macros.size() << "\n";
|
|||
std::pair<Node, bool> pnc = rulesTransform(node, nodeMacros); |
|||
node = pnc.first; |
|||
changed = changed || pnc.second; |
|||
|
|||
|
|||
// Special transformations
|
|||
if (node.val == "array_lit") { |
|||
node = array_lit_transform(node); |
|||
changed = true; |
|||
} |
|||
if (node.val == "fun" && node.args[0].val == ".") { |
|||
node = dotTransform(node, pr.second); |
|||
changed = true; |
|||
} |
|||
if (node.val == "text") { |
|||
node = string_transform(node); |
|||
changed = true; |
|||
} |
|||
if (node.type == ASTNODE) { |
|||
unsigned i = 0; |
|||
// Arg 0 of all of these is a variable, so should not be changed
|
|||
if (node.val == "set" || node.val == "ref" |
|||
|| node.val == "get" || node.val == "with") { |
|||
if (node.args[0].type == TOKEN && |
|||
node.args[0].val.size() > 0 && node.args[0].val[0] != '\'') { |
|||
node.args[0].val = "'" + node.args[0].val; |
|||
changed = true; |
|||
} |
|||
i = 1; |
|||
} |
|||
// Convert arglen(x) to '_len_x
|
|||
else if (node.val == "arglen") { |
|||
node.val = "get"; |
|||
node.args[0].val = "'_len_" + node.args[0].val; |
|||
i = 1; |
|||
changed = true; |
|||
} |
|||
// Recursively process children
|
|||
for (; i < node.args.size(); i++) { |
|||
std::pair<Node, bool> r = |
|||
mainTransform(preprocessResult(node.args[i], pr.second)); |
|||
node.args[i] = r.first; |
|||
changed = changed || r.second; |
|||
} |
|||
} |
|||
// Add leading ' to variable names, and wrap them inside get
|
|||
else if (node.type == TOKEN && !isNumberLike(node)) { |
|||
if (node.val.size() && node.val[0] != '\'' && node.val[0] != '$') { |
|||
Node n = astnode("get", tkn("'"+node.val), node.metadata); |
|||
node = n; |
|||
changed = true; |
|||
} |
|||
} |
|||
// Convert all numbers to normalized form
|
|||
else if (node.type == TOKEN && isNumberLike(node) && !isDecimal(node.val)) { |
|||
node.val = strToNumeric(node.val); |
|||
changed = true; |
|||
} |
|||
return std::pair<Node, bool>(node, changed); |
|||
} |
|||
|
|||
// Do some preprocessing to convert all of our macro lists into compiled
|
|||
// forms that can then be reused
|
|||
void parseMacros() { |
|||
for (int i = 0; i < 9999; i++) { |
|||
std::vector<Node> o; |
|||
if (macros[i][0] == "---END---") break; |
|||
nodeMacros.addRule(rewriteRule( |
|||
parseLLL(macros[i][0]), |
|||
parseLLL(macros[i][1]) |
|||
)); |
|||
} |
|||
for (int i = 0; i < 9999; i++) { |
|||
std::vector<Node> o; |
|||
if (setters[i][0] == "---END---") break; |
|||
setterMacros.addRule(rewriteRule( |
|||
asn(setters[i][0], tkn("$x"), tkn("$y")), |
|||
asn("=", tkn("$x"), asn(setters[i][1], tkn("$x"), tkn("$y"))) |
|||
)); |
|||
} |
|||
for (int i = 0; i < 9999; i++) { |
|||
if (synonyms[i][0] == "---END---") break; |
|||
synonymMap[synonyms[i][0]] = synonyms[i][1]; |
|||
} |
|||
} |
|||
|
|||
Node apply_rules(preprocessResult pr) { |
|||
// If the rewrite rules have not yet been parsed, parse them
|
|||
if (!nodeMacros.ruleLists.size()) parseMacros(); |
|||
// Iterate over macros by priority list
|
|||
std::map<int, rewriteRuleSet >::iterator it; |
|||
std::pair<Node, bool> r; |
|||
for(it=pr.second.customMacros.begin(); |
|||
it != pr.second.customMacros.end(); it++) { |
|||
while (1) { |
|||
// std::cerr << "STARTING ARI CYCLE: " << (*it).first <<"\n";
|
|||
// std::cerr << printAST(pr.first) << "\n";
|
|||
r = apply_rules_iter(pr, (*it).second); |
|||
pr.first = r.first; |
|||
if (!r.second) break; |
|||
} |
|||
} |
|||
// Apply setter macros
|
|||
while (1) { |
|||
r = apply_rules_iter(pr, setterMacros); |
|||
pr.first = r.first; |
|||
if (!r.second) break; |
|||
} |
|||
// Apply all other mactos
|
|||
while (1) { |
|||
r = mainTransform(pr); |
|||
pr.first = r.first; |
|||
if (!r.second) break; |
|||
} |
|||
return r.first; |
|||
} |
|||
|
|||
// Pre-validation
|
|||
Node validate(Node inp) { |
|||
Metadata m = inp.metadata; |
|||
if (inp.type == ASTNODE) { |
|||
int i = 0; |
|||
while(validFunctions[i][0] != "---END---") { |
|||
if (inp.val == validFunctions[i][0]) { |
|||
std::string sz = unsignedToDecimal(inp.args.size()); |
|||
if (decimalGt(validFunctions[i][1], sz)) { |
|||
err("Too few arguments for "+inp.val, inp.metadata); |
|||
} |
|||
if (decimalGt(sz, validFunctions[i][2])) { |
|||
err("Too many arguments for "+inp.val, inp.metadata); |
|||
} |
|||
} |
|||
i++; |
|||
} |
|||
} |
|||
else if (inp.type == TOKEN) { |
|||
if (!inp.val.size()) err("??? empty token", m); |
|||
if (inp.val[0] == '_') err("Variables cannot start with _", m); |
|||
} |
|||
for (unsigned i = 0; i < inp.args.size(); i++) validate(inp.args[i]); |
|||
return inp; |
|||
} |
|||
|
|||
Node postValidate(Node inp) { |
|||
// This allows people to use ~x as a way of having functions with the same
|
|||
// name and arity as macros; the idea is that ~x is a "final" form, and
|
|||
// should not be remacroed, but it is converted back at the end
|
|||
if (inp.val.size() > 0 && inp.val[0] == '~') { |
|||
inp.val = inp.val.substr(1); |
|||
} |
|||
if (inp.type == ASTNODE) { |
|||
if (inp.val == ".") |
|||
err("Invalid object member (ie. a foo.bar not mapped to anything)", |
|||
inp.metadata); |
|||
else if (opcode(inp.val) >= 0) { |
|||
if ((signed)inp.args.size() < opinputs(inp.val)) |
|||
err("Too few arguments for "+inp.val, inp.metadata); |
|||
if ((signed)inp.args.size() > opinputs(inp.val)) |
|||
err("Too many arguments for "+inp.val, inp.metadata); |
|||
} |
|||
else if (isValidLLLFunc(inp.val, inp.args.size())) { |
|||
// do nothing
|
|||
} |
|||
else err ("Invalid argument count or LLL function: "+printSimple(inp), inp.metadata); |
|||
for (unsigned i = 0; i < inp.args.size(); i++) { |
|||
inp.args[i] = postValidate(inp.args[i]); |
|||
} |
|||
} |
|||
return inp; |
|||
} |
|||
|
|||
|
|||
Node rewriteChunk(Node inp) { |
|||
return postValidate(optimize(apply_rules( |
|||
preprocessResult( |
|||
validate(inp), preprocessAux())))); |
|||
} |
|||
|
|||
// Flatten nested sequence into flat sequence
|
|||
Node flattenSeq(Node inp) { |
|||
std::vector<Node> o; |
|||
if (inp.val == "seq" && inp.type == ASTNODE) { |
|||
for (unsigned i = 0; i < inp.args.size(); i++) { |
|||
if (inp.args[i].val == "seq" && inp.args[i].type == ASTNODE) |
|||
o = extend(o, flattenSeq(inp.args[i]).args); |
|||
else |
|||
o.push_back(flattenSeq(inp.args[i])); |
|||
} |
|||
} |
|||
else if (inp.type == ASTNODE) { |
|||
for (unsigned i = 0; i < inp.args.size(); i++) { |
|||
o.push_back(flattenSeq(inp.args[i])); |
|||
} |
|||
} |
|||
else return inp; |
|||
return asn(inp.val, o, inp.metadata); |
|||
} |
|||
|
|||
Node rewrite(Node inp) { |
|||
return postValidate(optimize(apply_rules(preprocess(flattenSeq(inp))))); |
|||
} |
|||
|
|||
using namespace std; |
@ -1,16 +0,0 @@ |
|||
#ifndef ETHSERP_REWRITER |
|||
#define ETHSERP_REWRITER |
|||
|
|||
#include <stdio.h> |
|||
#include <iostream> |
|||
#include <vector> |
|||
#include <map> |
|||
#include "util.h" |
|||
|
|||
// Applies rewrite rules
|
|||
Node rewrite(Node inp); |
|||
|
|||
// Applies rewrite rules adding without wrapper
|
|||
Node rewriteChunk(Node inp); |
|||
|
|||
#endif |
@ -1,212 +0,0 @@ |
|||
#include <stdio.h> |
|||
#include <iostream> |
|||
#include <vector> |
|||
#include <map> |
|||
#include <string> |
|||
#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) != 0; |
|||
} |
|||
|
|||
// 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); |
|||
} |
@ -1,76 +0,0 @@ |
|||
#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 |
@ -1,115 +0,0 @@ |
|||
#include <stdio.h> |
|||
#include <iostream> |
|||
#include <vector> |
|||
#include <map> |
|||
#include "util.h" |
|||
|
|||
// These appear as independent tokens even if inside a stream of symbols
|
|||
const std::string atoms[] = { "#", "//", "(", ")", "[", "]", "{", "}" }; |
|||
const int numAtoms = 8; |
|||
|
|||
// Is the char alphanumeric, a space, a bracket, a quote, a symbol?
|
|||
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 (c == '\t' || c == ' ' || c == '\n' || c == '\r') return SPACE; |
|||
else if (std::string("()[]{}").find(c) != std::string::npos) return BRACK; |
|||
else if (c == '"') return DQUOTE; |
|||
else if (c == '\'') return SQUOTE; |
|||
else return SYMB; |
|||
} |
|||
|
|||
// "y = f(45,124)/3" -> [ "y", "f", "(", "45", ",", "124", ")", "/", "3"]
|
|||
std::vector<Node> tokenize(std::string inp, Metadata metadata, bool lispMode) { |
|||
int curtype = SPACE; |
|||
unsigned pos = 0; |
|||
int lastNewline = 0; |
|||
metadata.ch = 0; |
|||
std::string cur; |
|||
std::vector<Node> out; |
|||
|
|||
inp += " "; |
|||
while (pos < inp.length()) { |
|||
int headtype = chartype(inp[pos]); |
|||
if (lispMode) { |
|||
if (inp[pos] == '\'') headtype = ALPHANUM; |
|||
} |
|||
// Are we inside a quote?
|
|||
if (curtype == SQUOTE || curtype == DQUOTE) { |
|||
// Close quote
|
|||
if (headtype == curtype) { |
|||
cur += inp[pos]; |
|||
out.push_back(token(cur, metadata)); |
|||
cur = ""; |
|||
metadata.ch = pos - lastNewline; |
|||
curtype = SPACE; |
|||
pos += 1; |
|||
} |
|||
// eg. \xc3
|
|||
else if (inp.length() >= pos + 4 && inp.substr(pos, 2) == "\\x") { |
|||
cur += (std::string("0123456789abcdef").find(inp[pos+2]) * 16 |
|||
+ std::string("0123456789abcdef").find(inp[pos+3])); |
|||
pos += 4; |
|||
} |
|||
// Newline
|
|||
else if (inp.substr(pos, 2) == "\\n") { |
|||
cur += '\n'; |
|||
pos += 2; |
|||
} |
|||
// Backslash escape
|
|||
else if (inp.length() >= pos + 2 && inp[pos] == '\\') { |
|||
cur += inp[pos + 1]; |
|||
pos += 2; |
|||
} |
|||
// Normal character
|
|||
else { |
|||
cur += inp[pos]; |
|||
pos += 1; |
|||
} |
|||
} |
|||
else { |
|||
// Handle atoms ( '//', '#', brackets )
|
|||
for (int i = 0; i < numAtoms; i++) { |
|||
int split = cur.length() - atoms[i].length(); |
|||
if (split >= 0 && cur.substr(split) == atoms[i]) { |
|||
if (split > 0) { |
|||
out.push_back(token(cur.substr(0, split), metadata)); |
|||
} |
|||
metadata.ch += split; |
|||
out.push_back(token(cur.substr(split), metadata)); |
|||
metadata.ch = pos - lastNewline; |
|||
cur = ""; |
|||
curtype = SPACE; |
|||
} |
|||
} |
|||
// Special case the minus sign
|
|||
if (cur.length() > 1 && (cur.substr(cur.length() - 1) == "-" |
|||
|| cur.substr(cur.length() - 1) == "!")) { |
|||
out.push_back(token(cur.substr(0, cur.length() - 1), metadata)); |
|||
out.push_back(token(cur.substr(cur.length() - 1), metadata)); |
|||
cur = ""; |
|||
} |
|||
// Boundary between different char types
|
|||
if (headtype != curtype) { |
|||
if (curtype != SPACE && cur != "") { |
|||
out.push_back(token(cur, metadata)); |
|||
} |
|||
metadata.ch = pos - lastNewline; |
|||
cur = ""; |
|||
} |
|||
cur += inp[pos]; |
|||
curtype = headtype; |
|||
pos += 1; |
|||
} |
|||
if (inp[pos] == '\n') { |
|||
lastNewline = pos; |
|||
metadata.ch = 0; |
|||
metadata.ln += 1; |
|||
} |
|||
} |
|||
return out; |
|||
} |
|||
|
|||
|
@ -1,16 +0,0 @@ |
|||
#ifndef ETHSERP_TOKENIZE |
|||
#define ETHSERP_TOKENIZE |
|||
|
|||
#include <stdio.h> |
|||
#include <iostream> |
|||
#include <vector> |
|||
#include <map> |
|||
#include "util.h" |
|||
|
|||
int chartype(char c); |
|||
|
|||
std::vector<Node> tokenize(std::string inp, |
|||
Metadata meta=Metadata(), |
|||
bool lispMode=false); |
|||
|
|||
#endif |
@ -1,333 +0,0 @@ |
|||
#include <stdio.h> |
|||
#include <iostream> |
|||
#include <vector> |
|||
#include <map> |
|||
#include "util.h" |
|||
#include "bignum.h" |
|||
#include <fstream> |
|||
#include <cerrno> |
|||
|
|||
//Token or value node constructor
|
|||
Node token(std::string val, Metadata met) { |
|||
Node o; |
|||
o.type = 0; |
|||
o.val = val; |
|||
o.metadata = met; |
|||
return o; |
|||
} |
|||
|
|||
//AST node constructor
|
|||
Node astnode(std::string val, std::vector<Node> args, Metadata met) { |
|||
Node o; |
|||
o.type = 1; |
|||
o.val = val; |
|||
o.args = args; |
|||
o.metadata = met; |
|||
return o; |
|||
} |
|||
|
|||
//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) { |
|||
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); |
|||
} |
|||
|
|||
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
|
|||
std::string printTokens(std::vector<Node> tokens) { |
|||
std::string s = ""; |
|||
for (unsigned i = 0; i < tokens.size(); i++) { |
|||
s += tokens[i].val + " "; |
|||
} |
|||
return s; |
|||
} |
|||
|
|||
// Prints a lisp AST on one line
|
|||
std::string printSimple(Node ast) { |
|||
if (ast.type == TOKEN) return ast.val; |
|||
std::string o = "(" + ast.val; |
|||
std::vector<std::string> subs; |
|||
for (unsigned i = 0; i < ast.args.size(); i++) { |
|||
o += " " + printSimple(ast.args[i]); |
|||
} |
|||
return o + ")"; |
|||
} |
|||
|
|||
// Number of tokens in a tree
|
|||
int treeSize(Node prog) { |
|||
if (prog.type == TOKEN) return 1; |
|||
int o = 0; |
|||
for (unsigned i = 0; i < prog.args.size(); i++) o += treeSize(prog.args[i]); |
|||
return o; |
|||
} |
|||
|
|||
// Pretty-prints a lisp AST
|
|||
std::string printAST(Node ast, bool printMetadata) { |
|||
if (ast.type == TOKEN) return ast.val; |
|||
std::string o = "("; |
|||
if (printMetadata) { |
|||
o += ast.metadata.file + " "; |
|||
o += unsignedToDecimal(ast.metadata.ln) + " "; |
|||
o += unsignedToDecimal(ast.metadata.ch) + ": "; |
|||
} |
|||
o += ast.val; |
|||
std::vector<std::string> subs; |
|||
for (unsigned i = 0; i < ast.args.size(); i++) { |
|||
subs.push_back(printAST(ast.args[i], printMetadata)); |
|||
} |
|||
unsigned k = 0; |
|||
std::string out = " "; |
|||
// As many arguments as possible go on the same line as the function,
|
|||
// except when seq is used
|
|||
while (k < subs.size() && o != "(seq") { |
|||
if (subs[k].find("\n") != std::string::npos || (out + subs[k]).length() >= 80) break; |
|||
out += subs[k] + " "; |
|||
k += 1; |
|||
} |
|||
// All remaining arguments go on their own lines
|
|||
if (k < subs.size()) { |
|||
o += out + "\n"; |
|||
std::vector<std::string> subsSliceK; |
|||
for (unsigned i = k; i < subs.size(); i++) subsSliceK.push_back(subs[i]); |
|||
o += indentLines(joinLines(subsSliceK)); |
|||
o += "\n)"; |
|||
} |
|||
else { |
|||
o += out.substr(0, out.size() - 1) + ")"; |
|||
} |
|||
return o; |
|||
} |
|||
|
|||
// Splits text by line
|
|||
std::vector<std::string> splitLines(std::string s) { |
|||
unsigned pos = 0; |
|||
int lastNewline = 0; |
|||
std::vector<std::string> o; |
|||
while (pos < s.length()) { |
|||
if (s[pos] == '\n') { |
|||
o.push_back(s.substr(lastNewline, pos - lastNewline)); |
|||
lastNewline = pos + 1; |
|||
} |
|||
pos = pos + 1; |
|||
} |
|||
o.push_back(s.substr(lastNewline)); |
|||
return o; |
|||
} |
|||
|
|||
// Inverse of splitLines
|
|||
std::string joinLines(std::vector<std::string> lines) { |
|||
std::string o = "\n"; |
|||
for (unsigned i = 0; i < lines.size(); i++) { |
|||
o += lines[i] + "\n"; |
|||
} |
|||
return o.substr(1, o.length() - 2); |
|||
} |
|||
|
|||
// Indent all lines by 4 spaces
|
|||
std::string indentLines(std::string inp) { |
|||
std::vector<std::string> lines = splitLines(inp); |
|||
for (unsigned i = 0; i < lines.size(); i++) lines[i] = " "+lines[i]; |
|||
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
|
|||
std::string strToNumeric(std::string inp) { |
|||
std::string o = "0"; |
|||
if (inp == "") { |
|||
o = ""; |
|||
} |
|||
else if ((inp[0] == '"' && inp[inp.length()-1] == '"') |
|||
|| (inp[0] == '\'' && inp[inp.length()-1] == '\'')) { |
|||
for (unsigned i = 1; i < inp.length() - 1; i++) { |
|||
o = decimalAdd(decimalMul(o,"256"), unsignedToDecimal((unsigned char)inp[i])); |
|||
} |
|||
} |
|||
else if (inp.substr(0,2) == "0x") { |
|||
for (unsigned i = 2; i < inp.length(); i++) { |
|||
int dig = std::string("0123456789abcdef0123456789ABCDEF").find(inp[i]) % 16; |
|||
if (dig < 0) return ""; |
|||
o = decimalAdd(decimalMul(o,"16"), unsignedToDecimal(dig)); |
|||
} |
|||
} |
|||
else { |
|||
bool isPureNum = true; |
|||
for (unsigned i = 0; i < inp.length(); i++) { |
|||
isPureNum = isPureNum && inp[i] >= '0' && inp[i] <= '9'; |
|||
} |
|||
o = isPureNum ? inp : ""; |
|||
} |
|||
return o; |
|||
} |
|||
|
|||
// Does the node contain a number (eg. 124, 0xf012c, "george")
|
|||
bool isNumberLike(Node node) { |
|||
if (node.type == ASTNODE) return false; |
|||
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
|
|||
Node nodeToNumeric(Node node) { |
|||
std::string o = strToNumeric(node.val); |
|||
return token(o == "" ? node.val : o, node.metadata); |
|||
} |
|||
|
|||
Node tryNumberize(Node node) { |
|||
if (node.type == TOKEN && isNumberLike(node)) return nodeToNumeric(node); |
|||
return node; |
|||
} |
|||
|
|||
//Converts a value to an array of byte number nodes
|
|||
std::vector<Node> toByteArr(std::string val, Metadata metadata, int minLen) { |
|||
std::vector<Node> o; |
|||
int L = 0; |
|||
while (val != "0" || L < minLen) { |
|||
o.push_back(token(decimalMod(val, "256"), metadata)); |
|||
val = decimalDiv(val, "256"); |
|||
L++; |
|||
} |
|||
std::vector<Node> o2; |
|||
for (int i = o.size() - 1; i >= 0; i--) o2.push_back(o[i]); |
|||
return o2; |
|||
} |
|||
|
|||
int counter = 0; |
|||
|
|||
//Makes a unique token
|
|||
std::string mkUniqueToken() { |
|||
counter++; |
|||
return unsignedToDecimal(counter); |
|||
} |
|||
|
|||
//Does a file exist? http://stackoverflow.com/questions/12774207
|
|||
bool exists(std::string fileName) { |
|||
std::ifstream infile(fileName.c_str()); |
|||
return infile.good(); |
|||
} |
|||
|
|||
//Reads a file: http://stackoverflow.com/questions/2602013
|
|||
std::string get_file_contents(std::string filename) |
|||
{ |
|||
std::ifstream in(filename.c_str(), std::ios::in | std::ios::binary); |
|||
if (in) |
|||
{ |
|||
std::string contents; |
|||
in.seekg(0, std::ios::end); |
|||
contents.resize(in.tellg()); |
|||
in.seekg(0, std::ios::beg); |
|||
in.read(&contents[0], contents.size()); |
|||
in.close(); |
|||
return(contents); |
|||
} |
|||
throw(errno); |
|||
} |
|||
|
|||
//Report error
|
|||
void err(std::string errtext, Metadata met) { |
|||
std::string err = "Error (file \"" + met.file + "\", line " + |
|||
unsignedToDecimal(met.ln + 1) + ", char " + unsignedToDecimal(met.ch) + |
|||
"): " + errtext; |
|||
std::cerr << err << std::endl; |
|||
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 << std::endl; |
|||
} |
|||
|
|||
//Bin to hex
|
|||
std::string binToHex(std::string inp) { |
|||
std::string o = ""; |
|||
for (unsigned i = 0; i < inp.length(); i++) { |
|||
unsigned char v = inp[i]; |
|||
o += std::string("0123456789abcdef").substr(v/16, 1) |
|||
+ std::string("0123456789abcdef").substr(v%16, 1); |
|||
} |
|||
return o; |
|||
} |
|||
|
|||
//Hex to bin
|
|||
std::string hexToBin(std::string inp) { |
|||
std::string o = ""; |
|||
for (unsigned i = 0; i+1 < inp.length(); i+=2) { |
|||
char v = (char)(std::string("0123456789abcdef").find(inp[i]) * 16 + |
|||
std::string("0123456789abcdef").find(inp[i+1])); |
|||
o += v; |
|||
} |
|||
return o; |
|||
} |
|||
|
|||
//Lower to upper
|
|||
std::string upperCase(std::string inp) { |
|||
std::string o = ""; |
|||
for (unsigned i = 0; i < inp.length(); i++) { |
|||
if (inp[i] >= 97 && inp[i] <= 122) o += inp[i] - 32; |
|||
else o += inp[i]; |
|||
} |
|||
return o; |
|||
} |
|||
|
|||
//Three-int vector
|
|||
std::vector<int> triple(int a, int b, int c) { |
|||
std::vector<int> v; |
|||
v.push_back(a); |
|||
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; |
|||
} |
@ -1,137 +0,0 @@ |
|||
#ifndef ETHSERP_UTIL |
|||
#define ETHSERP_UTIL |
|||
|
|||
#include <stdio.h> |
|||
#include <iostream> |
|||
#include <vector> |
|||
#include <map> |
|||
#include <fstream> |
|||
#include <cerrno> |
|||
|
|||
const int TOKEN = 0, |
|||
ASTNODE = 1, |
|||
SPACE = 2, |
|||
BRACK = 3, |
|||
SQUOTE = 4, |
|||
DQUOTE = 5, |
|||
SYMB = 6, |
|||
ALPHANUM = 7, |
|||
LPAREN = 8, |
|||
RPAREN = 9, |
|||
COMMA = 10, |
|||
COLON = 11, |
|||
UNARY_OP = 12, |
|||
BINARY_OP = 13, |
|||
COMPOUND = 14, |
|||
TOKEN_SPLITTER = 15; |
|||
|
|||
// Stores metadata about each token
|
|||
class Metadata { |
|||
public: |
|||
Metadata(std::string File="main", int Ln=-1, int Ch=-1) { |
|||
file = File; |
|||
ln = Ln; |
|||
ch = Ch; |
|||
fixed = false; |
|||
} |
|||
std::string file; |
|||
int ln; |
|||
int ch; |
|||
bool fixed; |
|||
}; |
|||
|
|||
std::string mkUniqueToken(); |
|||
|
|||
// type can be TOKEN or ASTNODE
|
|||
class Node { |
|||
public: |
|||
int type; |
|||
std::string val; |
|||
std::vector<Node> args; |
|||
Metadata 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, 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()); |
|||
Node astnode(std::string val, Node a, Node b, |
|||
Node c, Node d, Metadata met=Metadata()); |
|||
|
|||
// Number of tokens in a tree
|
|||
int treeSize(Node prog); |
|||
|
|||
// Print token list
|
|||
std::string printTokens(std::vector<Node> tokens); |
|||
|
|||
// Prints a lisp AST on one line
|
|||
std::string printSimple(Node ast); |
|||
|
|||
// Pretty-prints a lisp AST
|
|||
std::string printAST(Node ast, bool printMetadata=false); |
|||
|
|||
// Splits text by line
|
|||
std::vector<std::string> splitLines(std::string s); |
|||
|
|||
// Inverse of splitLines
|
|||
std::string joinLines(std::vector<std::string> lines); |
|||
|
|||
// Indent all lines by 4 spaces
|
|||
std::string indentLines(std::string inp); |
|||
|
|||
// Converts binary to simple numeric format
|
|||
std::string binToNumeric(std::string inp); |
|||
|
|||
// Converts string to simple numeric format
|
|||
std::string strToNumeric(std::string inp); |
|||
|
|||
// Does the node contain a number (eg. 124, 0xf012c, "george")
|
|||
bool isNumberLike(Node node); |
|||
|
|||
//Normalizes number representations
|
|||
Node nodeToNumeric(Node node); |
|||
|
|||
//If a node is numeric, normalize its representation
|
|||
Node tryNumberize(Node node); |
|||
|
|||
//Converts a value to an array of byte number nodes
|
|||
std::vector<Node> toByteArr(std::string val, Metadata metadata, int minLen=1); |
|||
|
|||
//Reads a file
|
|||
std::string get_file_contents(std::string filename); |
|||
|
|||
//Does a file exist?
|
|||
bool exists(std::string fileName); |
|||
|
|||
//Report error
|
|||
void err(std::string errtext, Metadata met); |
|||
|
|||
//Report warning
|
|||
void warn(std::string errtext, Metadata met); |
|||
|
|||
//Bin to hex
|
|||
std::string binToHex(std::string inp); |
|||
|
|||
//Hex to bin
|
|||
std::string hexToBin(std::string inp); |
|||
|
|||
//Lower to upper
|
|||
std::string upperCase(std::string inp); |
|||
|
|||
//Three-int vector
|
|||
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 |
@ -1,12 +0,0 @@ |
|||
#!/bin/bash |
|||
|
|||
opwd="$PWD" |
|||
cd ../serpent |
|||
git stash |
|||
git pull |
|||
git stash pop |
|||
cp bignum.* compiler.* funcs.* lllparser.* opcodes.h parser.* rewriter.* tokenize.* util.* ../cpp-ethereum/libserpent/ |
|||
cp cmdline.* "$opwd/sc/" |
|||
cd "$opwd" |
|||
perl -i -p -e 's:include "funcs.h":include <libserpent/funcs.h>:gc' sc/* |
|||
|
@ -1,13 +0,0 @@ |
|||
include pysol/*.cpp |
|||
include *.py |
|||
include libdevcore/*cpp |
|||
include libdevcore/*h |
|||
include libdevcrypto/*cpp |
|||
include libdevcrypto/*h |
|||
include libethcore/*cpp |
|||
include libethcore/*h |
|||
include libsolidity/*cpp |
|||
include libsolidity/*h |
|||
include libevmcore/*cpp |
|||
include libevmcore/*h |
|||
include pysol/README.md |
@ -1,115 +0,0 @@ |
|||
#include <Python.h> |
|||
#include "structmember.h" |
|||
#include <stdlib.h> |
|||
#include <stdio.h> |
|||
#include <iostream> |
|||
#include <vector> |
|||
|
|||
#include "../libdevcore/CommonData.h" |
|||
|
|||
|
|||
#include <libsolidity/Compiler.h> |
|||
#include <libsolidity/CompilerStack.h> |
|||
#include <libsolidity/CompilerUtils.h> |
|||
#include <libsolidity/SourceReferenceFormatter.h> |
|||
|
|||
|
|||
std::string compile(std::string src) { |
|||
dev::solidity::CompilerStack compiler; |
|||
try |
|||
{ |
|||
std::vector<uint8_t> m_data = compiler.compile(src, false); |
|||
return std::string(m_data.begin(), m_data.end()); |
|||
} |
|||
catch (dev::Exception const& exception) |
|||
{ |
|||
std::ostringstream error; |
|||
dev::solidity::SourceReferenceFormatter::printExceptionInformation(error, exception, "Error", compiler); |
|||
std::string e = error.str(); |
|||
throw(e); |
|||
} |
|||
} |
|||
|
|||
|
|||
std::string mk_full_signature(std::string src) { |
|||
dev::solidity::CompilerStack compiler; |
|||
try |
|||
{ |
|||
compiler.compile(src); |
|||
return compiler.getInterface(""); |
|||
} |
|||
catch (dev::Exception const& exception) |
|||
{ |
|||
std::ostringstream error; |
|||
dev::solidity::SourceReferenceFormatter::printExceptionInformation(error, exception, "Error", compiler); |
|||
std::string e = error.str(); |
|||
throw(e); |
|||
} |
|||
} |
|||
|
|||
std::string bob(std::string src) { return src + src; } |
|||
|
|||
#define PYMETHOD(name, FROM, method, TO) \ |
|||
static PyObject * name(PyObject *, PyObject *args) { \ |
|||
try { \ |
|||
FROM(med) \ |
|||
return TO(method(med)); \ |
|||
} \ |
|||
catch (std::string e) { \ |
|||
PyErr_SetString(PyExc_Exception, e.c_str()); \ |
|||
return NULL; \ |
|||
} \ |
|||
} |
|||
|
|||
#define FROMSTR(v) \ |
|||
const char *command; \ |
|||
int len; \ |
|||
if (!PyArg_ParseTuple(args, "s#", &command, &len)) \ |
|||
return NULL; \ |
|||
std::string v = std::string(command, len); \ |
|||
|
|||
// Convert string into python wrapper form
|
|||
PyObject* pyifyString(std::string s) { |
|||
return Py_BuildValue("s#", s.c_str(), s.length()); |
|||
} |
|||
|
|||
// Convert integer into python wrapper form
|
|||
PyObject* pyifyInteger(unsigned int i) { |
|||
return Py_BuildValue("i", i); |
|||
} |
|||
|
|||
// Convert pyobject int into normal form
|
|||
int cppifyInt(PyObject* o) { |
|||
int out; |
|||
if (!PyArg_Parse(o, "i", &out)) |
|||
throw("Argument should be integer"); |
|||
return out; |
|||
} |
|||
|
|||
// Convert pyobject string into normal form
|
|||
std::string cppifyString(PyObject* o) { |
|||
const char *command; |
|||
if (!PyArg_Parse(o, "s", &command)) |
|||
throw("Argument should be string"); |
|||
return std::string(command); |
|||
} |
|||
|
|||
int fh(std::string i) { |
|||
return dev::fromHex(i[0]); |
|||
} |
|||
|
|||
PYMETHOD(ps_compile, FROMSTR, compile, pyifyString) |
|||
PYMETHOD(ps_mk_full_signature, FROMSTR, mk_full_signature, pyifyString) |
|||
|
|||
static PyMethodDef PyextMethods[] = { |
|||
{"compile", ps_compile, METH_VARARGS, |
|||
"Compile code."}, |
|||
{"mk_full_signature", ps_mk_full_signature, METH_VARARGS, |
|||
"Get the signature of a piece of code."}, |
|||
{NULL, NULL, 0, NULL} /* Sentinel */ |
|||
}; |
|||
|
|||
PyMODINIT_FUNC initsolidity(void) |
|||
{ |
|||
Py_InitModule( "solidity", PyextMethods ); |
|||
} |
@ -1,41 +0,0 @@ |
|||
import os |
|||
os.chdir('..') |
|||
|
|||
from setuptools import setup, Extension |
|||
|
|||
from distutils.sysconfig import get_config_vars |
|||
|
|||
(opt,) = get_config_vars('OPT') |
|||
os.environ['OPT'] = " ".join( |
|||
flag for flag in opt.split() if flag != '-Wstrict-prototypes' |
|||
) |
|||
|
|||
setup( |
|||
# Name of this package |
|||
name="ethereum-solidity", |
|||
|
|||
# Package version |
|||
version='1.8.0', |
|||
|
|||
description='Solidity compiler python wrapper', |
|||
maintainer='Vitalik Buterin', |
|||
maintainer_email='v@buterin.com', |
|||
license='WTFPL', |
|||
url='http://www.ethereum.org/', |
|||
|
|||
# Describes how to build the actual extension module from C source files. |
|||
ext_modules=[ |
|||
Extension( |
|||
'solidity', # Python name of the module |
|||
sources= ['libdevcore/Common.cpp', 'libdevcore/CommonData.cpp', 'libdevcore/CommonIO.cpp', 'libdevcore/FixedHash.cpp', 'libdevcore/Guards.cpp', 'libdevcore/Log.cpp', 'libdevcore/RangeMask.cpp', 'libdevcore/RLP.cpp', 'libdevcore/Worker.cpp', 'libdevcrypto/AES.cpp', 'libdevcrypto/Common.cpp', 'libdevcrypto/CryptoPP.cpp', 'libdevcrypto/ECDHE.cpp', 'libdevcrypto/FileSystem.cpp', 'libdevcrypto/MemoryDB.cpp', 'libdevcrypto/OverlayDB.cpp', 'libdevcrypto/SHA3.cpp', 'libdevcrypto/TrieCommon.cpp', 'libdevcrypto/TrieDB.cpp', 'libethcore/CommonEth.cpp', 'libethcore/CommonJS.cpp', 'libethcore/Exceptions.cpp', 'libsolidity/AST.cpp', 'libsolidity/ASTJsonConverter.cpp', 'libsolidity/ASTPrinter.cpp', 'libsolidity/CompilerContext.cpp', 'libsolidity/Compiler.cpp', 'libsolidity/CompilerStack.cpp', 'libsolidity/CompilerUtils.cpp', 'libsolidity/DeclarationContainer.cpp', 'libsolidity/ExpressionCompiler.cpp', 'libsolidity/GlobalContext.cpp', 'libsolidity/InterfaceHandler.cpp', 'libsolidity/NameAndTypeResolver.cpp', 'libsolidity/Parser.cpp', 'libsolidity/Scanner.cpp', 'libsolidity/SourceReferenceFormatter.cpp', 'libsolidity/Token.cpp', 'libsolidity/Types.cpp', 'libevmcore/Assembly.cpp', 'libevmcore/Instruction.cpp', 'pysol/pysolidity.cpp'], |
|||
libraries=['boost_python', 'boost_filesystem', 'boost_chrono', 'boost_thread', 'cryptopp', 'leveldb', 'jsoncpp'], |
|||
include_dirs=['/usr/include/boost', '..', '../..', '.'], |
|||
extra_compile_args=['--std=c++11', '-Wno-unknown-pragmas'] |
|||
)], |
|||
py_modules=[ |
|||
], |
|||
scripts=[ |
|||
], |
|||
entry_points={ |
|||
} |
|||
), |
@ -1,19 +0,0 @@ |
|||
cmake_policy(SET CMP0015 NEW) |
|||
|
|||
aux_source_directory(. SRC_LIST) |
|||
|
|||
include_directories(BEFORE ..) |
|||
|
|||
set(EXECUTABLE sc) |
|||
|
|||
add_executable(${EXECUTABLE} ${SRC_LIST}) |
|||
|
|||
if (NOT ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")) |
|||
target_link_libraries(${EXECUTABLE} serpent) |
|||
endif() |
|||
target_link_libraries(${EXECUTABLE} lll) |
|||
target_link_libraries(${EXECUTABLE} evmcore) |
|||
target_link_libraries(${EXECUTABLE} devcore) |
|||
|
|||
install( TARGETS ${EXECUTABLE} DESTINATION bin ) |
|||
|
@ -1,129 +0,0 @@ |
|||
#include <stdio.h> |
|||
#include <string> |
|||
#include <iostream> |
|||
#include <vector> |
|||
#include <map> |
|||
#include <libserpent/funcs.h> |
|||
|
|||
int main(int argv, char** argc) { |
|||
if (argv == 1) { |
|||
std::cerr << "Must provide a command and arguments! Try parse, rewrite, compile, assemble\n"; |
|||
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 command = argc[1]; |
|||
std::string input; |
|||
std::string secondInput; |
|||
if (std::string(argc[1]) == "-s") { |
|||
flag = command.substr(1); |
|||
command = argc[2]; |
|||
input = ""; |
|||
std::string line; |
|||
while (std::getline(std::cin, line)) { |
|||
input += line + "\n"; |
|||
} |
|||
secondInput = argv == 3 ? "" : argc[3]; |
|||
} |
|||
else { |
|||
if (argv == 2) { |
|||
std::cerr << "Not enough arguments for serpent cmdline\n"; |
|||
throw(0); |
|||
} |
|||
input = argc[2]; |
|||
secondInput = argv == 3 ? "" : argc[3]; |
|||
} |
|||
bool haveSec = secondInput.length() > 0; |
|||
if (command == "parse" || command == "parse_serpent") { |
|||
std::cout << printAST(parseSerpent(input), haveSec) << "\n"; |
|||
} |
|||
else if (command == "rewrite") { |
|||
std::cout << printAST(rewrite(parseLLL(input, true)), haveSec) << "\n"; |
|||
} |
|||
else if (command == "compile_to_lll") { |
|||
std::cout << printAST(compileToLLL(input), haveSec) << "\n"; |
|||
} |
|||
else if (command == "rewrite_chunk") { |
|||
std::cout << printAST(rewriteChunk(parseLLL(input, true)), haveSec) << "\n"; |
|||
} |
|||
else if (command == "compile_chunk_to_lll") { |
|||
std::cout << printAST(compileChunkToLLL(input), haveSec) << "\n"; |
|||
} |
|||
else if (command == "build_fragtree") { |
|||
std::cout << printAST(buildFragmentTree(parseLLL(input, true))) << "\n"; |
|||
} |
|||
else if (command == "compile_lll") { |
|||
std::cout << binToHex(compileLLL(parseLLL(input, true))) << "\n"; |
|||
} |
|||
else if (command == "dereference") { |
|||
std::cout << printTokens(dereference(parseLLL(input, true))) <<"\n"; |
|||
} |
|||
else if (command == "pretty_assemble") { |
|||
std::cout << printTokens(prettyAssemble(parseLLL(input, true))) <<"\n"; |
|||
} |
|||
else if (command == "pretty_compile_lll") { |
|||
std::cout << printTokens(prettyCompileLLL(parseLLL(input, true))) << "\n"; |
|||
} |
|||
else if (command == "pretty_compile") { |
|||
std::cout << printTokens(prettyCompile(input)) << "\n"; |
|||
} |
|||
else if (command == "pretty_compile_chunk") { |
|||
std::cout << printTokens(prettyCompileChunk(input)) << "\n"; |
|||
} |
|||
else if (command == "assemble") { |
|||
std::cout << assemble(parseLLL(input, true)) << "\n"; |
|||
} |
|||
else if (command == "serialize") { |
|||
std::cout << binToHex(serialize(tokenize(input, Metadata(), false))) << "\n"; |
|||
} |
|||
else if (command == "deserialize") { |
|||
std::cout << printTokens(deserialize(hexToBin(input))) << "\n"; |
|||
} |
|||
else if (command == "compile") { |
|||
std::cout << binToHex(compile(input)) << "\n"; |
|||
} |
|||
else if (command == "compile_chunk") { |
|||
std::cout << binToHex(compileChunk(input)) << "\n"; |
|||
} |
|||
else if (command == "encode_datalist") { |
|||
std::vector<Node> tokens = tokenize(input); |
|||
std::vector<std::string> o; |
|||
for (int i = 0; i < (int)tokens.size(); i++) { |
|||
o.push_back(tokens[i].val); |
|||
} |
|||
std::cout << binToHex(encodeDatalist(o)) << "\n"; |
|||
} |
|||
else if (command == "decode_datalist") { |
|||
std::vector<std::string> o = decodeDatalist(hexToBin(input)); |
|||
std::vector<Node> tokens; |
|||
for (int i = 0; i < (int)o.size(); i++) |
|||
tokens.push_back(token(o[i])); |
|||
std::cout << printTokens(tokens) << "\n"; |
|||
} |
|||
else if (command == "tokenize") { |
|||
std::cout << printTokens(tokenize(input)); |
|||
} |
|||
else if (command == "biject") { |
|||
if (argv == 3) |
|||
std::cerr << "Not enough arguments for biject\n"; |
|||
int pos = decimalToUnsigned(secondInput); |
|||
std::vector<Node> n = prettyCompile(input); |
|||
if (pos >= (int)n.size()) |
|||
std::cerr << "Code position too high\n"; |
|||
Metadata m = n[pos].metadata; |
|||
std::cout << "Opcode: " << n[pos].val << ", file: " << m.file << |
|||
", line: " << m.ln << ", char: " << m.ch << "\n"; |
|||
} |
|||
} |
Loading…
Reference in new issue