Skip to content

Commit

Permalink
Merge pull request #13 from jasonpaulos/restructure
Browse files Browse the repository at this point in the history
Internal restructure
  • Loading branch information
jasonpaulos committed Jul 29, 2020
2 parents 4088a5e + 5099333 commit fb9fa69
Show file tree
Hide file tree
Showing 26 changed files with 1,238 additions and 1,211 deletions.
1 change: 0 additions & 1 deletion __init__.py

This file was deleted.

4 changes: 3 additions & 1 deletion pyteal/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
from .ops import *
from .ast import *
from .errors import TealInternalError, TealTypeError, TealTypeMismatchError, TealInputError
from .util import execute
from .config import MAX_GROUP_SIZE
33 changes: 33 additions & 0 deletions pyteal/ast/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# abstract types
from .expr import Expr

# basic types
from .leafexpr import LeafExpr
from .addr import Addr
from .bytes import Bytes
from .err import Err
from .int import Int

# properties
from .arg import Arg
from .txn import Txn, TxnField
from .gtxn import Gtxn
from .global_ import Global

# meta
from .tmpl import Tmpl
from .nonce import Nonce

# unary ops
from .unaryexpr import UnaryExpr, Btoi, Itob, Len, Sha256, Sha512_256, Keccak256, Pop

# binary ops
from .binaryexpr import BinaryExpr, Add, Minus, Mul, Div, Mod, Eq, Lt, Le, Gt, Ge

# more ops
from .ed25519verify import Ed25519Verify
from .naryexpr import NaryExpr, And, Or

# control flow
from .if_ import If
from .cond import Cond
29 changes: 29 additions & 0 deletions pyteal/ast/addr.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
from typing import Union

from ..types import TealType, valid_address
from .leafexpr import LeafExpr
from .tmpl import Tmpl

class Addr(LeafExpr):
"""An expression that represents an Algorand address."""

def __init__(self, address: Union[str, Tmpl]) -> None:
"""Create a new Addr expression.
Args:
address: A string containing a valid base32 Algorand address, or a Tmpl object.
"""
if isinstance(address, Tmpl):
self.address = address.name
else:
valid_address(address)
self.address = address

def __teal__(self):
return [["addr", self.address]]

def __str__(self):
return "(address: {})".format(self.address)

def type_of(self):
return TealType.bytes
29 changes: 29 additions & 0 deletions pyteal/ast/arg.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
from ..types import TealType
from ..errors import TealInputError
from .leafexpr import LeafExpr

class Arg(LeafExpr):
"""An expression to get an argument."""

def __init__(self, index:int) -> None:
"""Get an argument for this program.
Args:
index: The integer index of the argument to get. Must be between 0 and 255 inclusive.
"""
if type(index) is not int:
raise TealInputError("invalid arg input type {}".format(type(index)))

if index < 0 or index > 255:
raise TealInputError("invalid arg index {}".format(index))

self.index = index

def __teal__(self):
return [["arg", str(self.index)]]

def __str__(self):
return "(arg {})".format(self.index)

def type_of(self):
return TealType.bytes
140 changes: 140 additions & 0 deletions pyteal/ast/binaryexpr.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
from ..types import TealType, require_type, types_match
from ..errors import TealTypeMismatchError
from .expr import Expr

class BinaryExpr(Expr):
"""An expression with two arguments."""

def __init__(self, op: str, inputType: TealType, outputType: TealType, argLeft: Expr, argRight: Expr) -> None:
require_type(argLeft.type_of(), inputType)
require_type(argRight.type_of(), inputType)
self.op = op
self.outputType = outputType
self.argLeft = argLeft
self.argRight = argRight

def __teal__(self):
teal = self.argLeft.__teal__() + self.argRight.__teal__()
teal.append([self.op])
return teal

def __str__(self):
return "({} {} {})".format(self.op, self.argLeft, self.argRight)

def type_of(self):
return self.outputType

def Add(left: Expr, right: Expr):
"""Add two numbers.
Produces left + right.
Args:
left: Must evaluate to uint64.
right: Must evaluate to uint64.
"""
return BinaryExpr("+", TealType.uint64, TealType.uint64, left, right)

def Minus(left: Expr, right: Expr):
"""Subtract two numbers.
Produces left - right.
Args:
left: Must evaluate to uint64.
right: Must evaluate to uint64.
"""
return BinaryExpr("-", TealType.uint64, TealType.uint64, left, right)

def Mul(left: Expr, right: Expr):
"""Multiply two numbers.
Produces left * right.
Args:
left: Must evaluate to uint64.
right: Must evaluate to uint64.
"""
return BinaryExpr("*", TealType.uint64, TealType.uint64, left, right)

def Div(left: Expr, right: Expr):
"""Divide two numbers.
Produces left / right.
Args:
left: Must evaluate to uint64.
right: Must evaluate to uint64.
"""
return BinaryExpr("/", TealType.uint64, TealType.uint64, left, right)

def Mod(left: Expr, right: Expr):
"""Modulo expression.
Produces left % right.
Args:
left: Must evaluate to uint64.
right: Must evaluate to uint64.
"""
return BinaryExpr("%", TealType.uint64, TealType.uint64, left, right)

def Eq(left: Expr, right: Expr):
"""Equality expression.
Checks if left == right.
Args:
left: A value to check.
right: The other value to check. Must evaluate to the same type as left.
"""
# a hack to make this op emit TealTypeMismatchError instead of TealTypeError
t1 = left.type_of()
t2 = right.type_of()
if not types_match(t1, t2):
raise TealTypeMismatchError(t1, t2)
return BinaryExpr("==", TealType.anytype, TealType.uint64, left, right)

def Lt(left: Expr, right: Expr):
"""Less than expression.
Checks if left < right.
Args:
left: Must evaluate to uint64.
right: Must evaluate to uint64.
"""
return BinaryExpr("<", TealType.uint64, TealType.uint64, left, right)

def Le(left: Expr, right: Expr):
"""Less than or equal to expression.
Checks if left <= right.
Args:
left: Must evaluate to uint64.
right: Must evaluate to uint64.
"""
return BinaryExpr("<=", TealType.uint64, TealType.uint64, left, right)

def Gt(left: Expr, right: Expr):
"""Greater than expression.
Checks if left > right.
Args:
left: Must evaluate to uint64.
right: Must evaluate to uint64.
"""
return BinaryExpr(">", TealType.uint64, TealType.uint64, left, right)

def Ge(left: Expr, right: Expr):
"""Greater than or equal to expression.
Checks if left >= right.
Args:
left: Must evaluate to uint64.
right: Must evaluate to uint64.
"""
return BinaryExpr(">=", TealType.uint64, TealType.uint64, left, right)
56 changes: 56 additions & 0 deletions pyteal/ast/bytes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
from typing import Union

from ..types import TealType, valid_base16, valid_base32, valid_base64
from ..errors import TealInputError
from .leafexpr import LeafExpr
from .tmpl import Tmpl

class Bytes(LeafExpr):
"""An expression that represents a byte string."""

def __init__(self, base: str, byte_str: Union[str, Tmpl]) -> None:
"""Create a new byte string.
Args:
base: The base type for this byte string. Must be one of base16, base32, or base64.
byte_string: The content of the byte string, encoding with the passed in base, or a Tmpl
object.
"""
if base == "base32":
self.base = base
if isinstance(byte_str, Tmpl):
self.byte_str = byte_str.name
else:
valid_base32(byte_str)
self.byte_str = byte_str
elif base == "base64":
self.base = base
if isinstance(byte_str, Tmpl):
self.byte_str = byte_str.name
else:
self.byte_str = byte_str
valid_base64(byte_str)
elif base == "base16":
self.base = base
if isinstance(byte_str, Tmpl):
self.byte_str = byte_str.name
elif byte_str.startswith("0x"):
self.byte_str = byte_str[2:]
valid_base16(self.byte_str)
else:
self.byte_str = byte_str
valid_base16(self.byte_str)
else:
raise TealInputError("invalid base {}, need to be base32, base64, or base16.".format(base))

def __teal__(self):
if self.base != "base16":
return [["byte", self.base, self.byte_str]]
else:
return [["byte", "0x" + self.byte_str]]

def __str__(self):
return "({} bytes: {})".format(self.base, self.byte_str)

def type_of(self):
return TealType.bytes
94 changes: 94 additions & 0 deletions pyteal/ast/cond.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
from typing import List

from ..types import TealType, require_type
from ..errors import TealInputError
from ..util import new_label
from .expr import Expr
from .err import Err
from .if_ import If

class Cond(Expr):
"""A chainable branching expression that supports an arbitrary number of conditions."""

def __init__(self, *argv: List[Expr]):
"""Create a new Cond expression.
At least one argument must be provided, and each argument must be a list with two elements.
The first element is a condition which evalutes to uint64, and the second is the body of the
condition, which will execute if that condition is true. All condition bodies must have the
same return type. During execution, each condition is tested in order, and the first
condition to evaluate to a true value will cause its associated body to execute and become
the value for this Cond expression. If no condition evalutes to a true value, the Cond
expression produces an error and the TEAL program terminates.
For example:
Cond([Global.group_size() == Int(5), bid],
[Global.group_size() == Int(4), redeem],
[Global.group_size() == Int(1), wrapup])
"""

if len(argv) < 1:
raise TealInputError("Cond requires at least one [condition, value]")

value_type = None

for arg in argv:
msg = "Cond should be in the form of Cond([cond1, value1], [cond2, value2], ...), error in {}"
if not isinstance(arg, list):
raise TealInputError(msg.format(arg))
if len(arg) != 2:
raise TealInputError(msg.format(arg))

require_type(arg[0].type_of(), TealType.uint64) # cond_n should be int

if value_type is None: # the types of all branches should be the same
value_type = arg[1].type_of()
else:
require_type(arg[1].type_of(), value_type)

self.value_type = value_type
self.args = argv

def __teal__(self):
teal = []

labels = []
for arg in self.args:
l = new_label()
cond = arg[0]

teal += cond.__teal__()
teal += [["bnz", l]]

labels.append(l)

# err if no conditions are met
teal += [["err"]]

# end label
labels.append(new_label())

for i, arg in enumerate(self.args):
label = labels[i] + ":"
branch = arg[1]

teal += [[label]]
teal += branch.__teal__()
if i + 1 != len(self.args):
teal += [["int", "1"], ["bnz", labels[-1]]]

endLabel = labels[-1] + ":"

teal += [[endLabel]]

return teal

def __str__(self):
ret_str = "(Cond"
for a in self.args:
ret_str += " [" + a[0].__str__() + ", " + a[1].__str__() + "]"
ret_str += ")"
return ret_str

def type_of(self):
return self.value_type

0 comments on commit fb9fa69

Please sign in to comment.