Browse Source

Serpent compiler fix.

cl-refactor
Gav Wood 11 years ago
parent
commit
d2f9d6b285
  1. 2
      alethzero/MainWin.cpp
  2. 37
      serpent/compiler.py
  3. 25
      serpent/parser.py
  4. 4
      serpent_cli.py

2
alethzero/MainWin.cpp

@ -126,7 +126,7 @@ bytes compileSerpent(string const& _code)
postream& os = c.get_stdin();
pistream& is = c.get_stdout();
os << _code;
os << _code << "\n";
os.close();
string hex;

37
serpent/compiler.py

@ -35,9 +35,9 @@ funtable = [
['>=', 2, 1, ['<1>', '<0>', 'LT', 'NOT']],
['!', 1, 1, ['<0>', 'NOT']],
['or', 2, 1, ['<1>', '<0>', 'DUP', 4, 'PC',
'ADD', 'JMPI', 'POP', 'SWAP', 'POP']],
'ADD', 'JUMPI', 'POP', 'SWAP', 'POP']],
['||', 2, 1, ['<1>', '<0>', 'DUP', 4, 'PC',
'ADD', 'JMPI', 'POP', 'SWAP', 'POP']],
'ADD', 'JUMPI', 'POP', 'SWAP', 'POP']],
['and', 2, 1, ['<1>', '<0>', 'NOT', 'NOT', 'MUL']],
['&&', 2, 1, ['<1>', '<0>', 'NOT', 'NOT', 'MUL']],
['xor', 2, 1, ['<1>', '<0>', 'XOR']],
@ -49,6 +49,8 @@ funtable = [
['access', 2, 1, ['<0>', '<1>', 32, 'MUL', 'ADD', 'MLOAD']],
# arr, ind, val
['arrset', 3, 0, ['<2>', '<0>', '<1>', 32, 'MUL', 'ADD', 'MSTORE']],
# val, pointer -> pointer+32
['set_and_inc', 2, 1, ['<1>', 'DUP', '<0>', 'SWAP', 'MSTORE', 32, 'ADD']],
# len (32 MUL) len*32 (MSIZE) len*32 MSIZE (SWAP) MSIZE len*32 (MSIZE ADD)
# MSIZE MSIZE+len*32 (1) MSIZE MSIZE+len*32 1 (SWAP SUB) MSIZE
# MSIZE+len*32-1 (0 SWAP MSTORE8) MSIZE
@ -69,9 +71,9 @@ funtable = [
# <3> <2> <1> <0> (CALL) MSIZE FLAG (POP) MSIZE (MLOAD) RESULT
['msg', 5, 1, ['MSIZE', 0, 'MSIZE', 'MSTORE', 'DUP', 32, 'SWAP', '<4>', 32, 'MUL', '<3>',
'<2>', '<1>', '<0>', 'CALL', 'POP', 'MLOAD']], # to, value, gas, data, datasize -> out32
# <5> MSIZE (SWAP) MSIZE <5> (MSIZE SWAP) MSIZE MSIZE <5> (32 MUL) MSIZE MSIZE <5>*32 (DUP ADD 1 SWAP SUB) MSIZE MSIZE <6>*32 MEND (MSTORE8) MSIZE MSIZE <6>*32 (... CALL)
['msg', 6, 0, ['<5>', 'MSIZE', 'SWAP', 'MSIZE', 'SWAP', 32, 'MUL', 'DUP', 'ADD', 1, 'SWAP', 'SUB', 'MSTORE8',
'<4>', '<3>', '<2>', '<1>', '<0>', 'CALL', 'POP']], # to, value, gas, data, datasize, outsize -> out
# <5>*32 (MSIZE SWAP MSIZE SWAP) MSIZE MSIZE <5>*32 (DUP MSIZE ADD) MSIZE MSIZE <5>*32 MEND+1 (1 SWAP SUB) MSIZE MSIZE <5>*32 MEND (0 SWAP MSTORE8) MSIZE MSIZE <5>*32 (SWAP) MSIZE <5>*32 MSIZE
['msg', 6, 1, ['<5>', 32, 'MUL', 'MSIZE', 'SWAP', 'MSIZE', 'SWAP', 'DUP', 'MSIZE', 'ADD', 1, 'SWAP', 'SUB', 0, 'SWAP', 'MSTORE8', 'SWAP',
'<4>', 32, 'MUL', '<3>', '<2>', '<1>', '<0>', 'CALL', 'POP']], # to, value, gas, data, datasize, outsize -> out
# value, gas, data, datasize
['create', 4, 1, ['<3>', '<2>', '<1>', '<0>', 'CREATE']],
['sha3', 1, 1, [32, 'MSIZE', '<0>', 'MSIZE', 'MSTORE', 'SHA3']],
@ -153,11 +155,13 @@ def rewrite(ast):
elif ast[1] == 'contract.storage':
return ['sload', rewrite(ast[2])]
elif ast[0] == 'array_lit':
tempvar = mklabel('_temp')
o1 = ['set', tempvar, ['array', str(len(ast[1:]))]]
of = map(
lambda i: ['arrset', tempvar, str(i), rewrite(ast[i + 1])], range(0, len(ast[1:])))
return ['seq'] + [o1] + of + [tempvar]
o = ['array', str(len(ast[1:]))]
for a in ast[1:]:
o = ['set_and_inc', rewrite(a), o]
return ['-', o, str(len(ast[1:])*32)]
elif ast[0] == 'return':
if len(ast) == 2 and ast[1][0] == 'array_lit':
return ['return', rewrite(ast[1]), str(len(ast[1][1:]))]
return map(rewrite, ast)
@ -245,6 +249,7 @@ def compile_expr(ast, varhash, lc=[0]):
# Functions and operations
for f in funtable:
if ast[0] == f[0] and len(ast[1:]) == f[1]:
# If arity of all args is 1
if reduce(lambda x, y: x * arity(y), ast[1:], 1):
iq = f[3][:]
oq = []
@ -301,9 +306,9 @@ def optimize(c):
multipop(oq, 3).append(ntok)
if oq[-1] == 'NOT' and len(oq) >= 2 and oq[-2] == 'NOT':
multipop(oq, 2)
if oq[-1] == 'ADD' and len(oq) >= 3 and oq[-2] == 0:
if oq[-1] == 'ADD' and len(oq) >= 3 and oq[-2] == 0 and is_numberlike(oq[-3]):
multipop(oq, 2)
if oq[-1] in ['SUB', 'ADD'] and len(oq) >= 3 and oq[-3] == 0:
if oq[-1] in ['SUB', 'ADD'] and len(oq) >= 3 and oq[-3] == 0 and is_numberlike(oq[-2]):
ntok = oq[-2]
multipop(oq, 3).append(ntok)
return oq
@ -423,7 +428,13 @@ def encode_datalist(vals):
return 1
elif n is False or n is None:
return 0
return ''.join(map(enc, vals))
if isinstance(vals, (tuple, list)):
return ''.join(map(enc, vals))
elif vals == '':
return ''
else:
# Assume you're getting in numbers or 0x...
return ''.join(map(enc, map(numberize, vals.split(' '))))
def decode_datalist(arr):

25
serpent/parser.py

@ -10,6 +10,13 @@ def spaces(ln):
def parse(document):
return parse_lines(document.split('\n'))
def strip_line(ln):
ln2 = ln.strip()
if '//' in ln2:
return ln2[:ln2.find('//')]
else:
return ln2
# Parse the statement-level structure, including if and while statements
def parse_lines(lns):
o = []
@ -22,16 +29,20 @@ def parse_lines(lns):
continue
if spaces(main) > 0:
raise Exception("Line "+str(i)+" indented too much!")
main = strip_line(main)
# Grab the child block of an if statement
start_child_block = i+1
indent = 99999999
i += 1
child_lns = []
while i < len(lns):
sp = spaces(lns[i])
if sp == 0: break
indent = min(sp,indent)
if len(strip_line(lns[i])) > 0:
sp = spaces(lns[i])
if sp == 0: break
indent = min(sp,indent)
child_lns.append(lns[i])
i += 1
child_block = map(lambda x:x[indent:],lns[start_child_block:i])
child_block = map(lambda x:x[indent:],child_lns)
# Calls parse_line to parse the individual line
out = parse_line(main)
# Include the child block into the parsed expression
@ -79,8 +90,6 @@ def tokenize(ln):
o = []
global cur
cur = ''
# Comments
if '//' in ln: ln = ln[:ln.find('//')]
# Finish a token and start a new one
def nxt():
global cur
@ -158,7 +167,7 @@ def toktype(token):
elif token in ['!']: return 'unary_operation'
elif not isinstance(token,str): return 'compound'
elif token in precedence: return 'binary_operation'
elif re.match('^[0-9a-z\-\.]*$',token): return 'alphanum'
elif re.match('^[0-9a-zA-Z\-\.]*$',token): return 'alphanum'
elif token[0] in ['"',"'"] and token[0] == token[-1]: return 'alphanum'
else: raise Exception("Invalid token: "+token)
@ -226,7 +235,7 @@ def shunting_yard(tokens):
elif typ == 'left_paren':
# Handle cases like 3 * (2 + 5) by using 'id' as a default function
# name
if toktype(prev) != 'alphanum' and toktype(prev) != 'rparen':
if toktype(prev) != 'alphanum' and toktype(prev) != 'right_paren':
oq.append('id')
# Say the statement is "... f(45...". At the start, we would have f
# as the last item on the oq. So we move it onto the stack, put the

4
serpent_cli.py

@ -26,8 +26,10 @@ def main():
else:
cmd = sys.argv[1]
args = sys.argv[2:]
if args[0] in os.listdir(os.getcwd()):
try:
args[0] = open(args[0]).read()
except:
pass
o = getattr(serpent, cmd)(*args)
if isinstance(o, (list, dict)):
print json.dumps(o)

Loading…
Cancel
Save