diff --git a/tealish/base.py b/tealish/base.py index 1e11f28..4e60c56 100644 --- a/tealish/base.py +++ b/tealish/base.py @@ -7,7 +7,7 @@ if TYPE_CHECKING: from . import TealWriter - from .nodes import Block, Node, Func + from .nodes import Block, Node, Func, Literal lang_spec = get_active_langspec() @@ -154,10 +154,10 @@ def lookup_func(self, name: str) -> "Func": def lookup_var(self, name: str) -> Any: return self.get_scope().lookup_var(name) - def lookup_const(self, name: str) -> Tuple["AVMType", ConstValue]: + def lookup_const(self, name: str) -> Tuple["AVMType", Union["Literal", ConstValue]]: return self.get_scope().lookup_const(name) - def lookup_avm_constant(self, name: str) -> Tuple["AVMType", Any]: + def lookup_avm_constant(self, name: str) -> Tuple["AVMType", ConstValue]: return lang_spec.lookup_avm_constant(name) # TODO: these attributes are only available on Node and other children types diff --git a/tealish/expression_nodes.py b/tealish/expression_nodes.py index 22f2e92..8bf26a4 100644 --- a/tealish/expression_nodes.py +++ b/tealish/expression_nodes.py @@ -4,11 +4,12 @@ from .errors import CompileError from .tealish_builtins import AVMType, get_struct from .langspec import Op, type_lookup +from .scope import ConstValue if TYPE_CHECKING: from . import TealWriter - from .nodes import Node, Func, GenericExpression + from .nodes import Node, Func, GenericExpression, Literal class Integer(BaseNode): @@ -66,9 +67,11 @@ def __init__(self, name: str, parent: Optional[BaseNode] = None) -> None: self.name = name self.type: AVMType = AVMType.none self.parent = parent + self.value: Union[ConstValue, "Literal"] def process(self) -> None: - type, value = None, None + type: AVMType = AVMType.none + value: Union[ConstValue, "Literal"] = 0 try: # user defined const type, value = self.lookup_const(self.name) @@ -87,10 +90,14 @@ def process(self) -> None: self.value = value def write_teal(self, writer: "TealWriter") -> None: - if self.type == AVMType.int: - writer.write(self, f"pushint {self.value} // {self.name}") # type: ignore - elif self.type == AVMType.bytes: + if isinstance(self.value, str) or isinstance(self.value, bytes): + assert self.type == AVMType.bytes writer.write(self, f"pushbytes {self.value} // {self.name}") # type: ignore + elif isinstance(self.value, int): + assert self.type == AVMType.int + writer.write(self, f"pushint {self.value} // {self.name}") + else: + self.value.write_teal(writer) def _tealish(self) -> str: return f"{self.name}" diff --git a/tealish/nodes.py b/tealish/nodes.py index b1b7353..8973b72 100644 --- a/tealish/nodes.py +++ b/tealish/nodes.py @@ -22,7 +22,9 @@ from .scope import Scope, VarType LITERAL_INT = r"[0-9]+" -LITERAL_BYTES = r'"(.+)"' +LITERAL_BYTE_STRING = r'"(.+)"' +LITERAL_BYTE_HEX = r"0x([a-fA-F0-9]+)" +LITERAL_BYTE_ADDR = r"([A-Z2-7]+)" VARIABLE_NAME = r"[a-z_][a-zA-Z0-9_]*" if TYPE_CHECKING: @@ -140,7 +142,12 @@ class Literal(Expression): @classmethod def parse(cls, line: str, parent: Node, compiler: "TealishCompiler") -> Node: - matchable: List[Type[Expression]] = [LiteralInt, LiteralBytes] + matchable: List[Type[Expression]] = [ + LiteralAddr, + LiteralHex, + LiteralInt, + LiteralBytes, + ] for expr in matchable: if expr.match(line): return expr(line, parent, compiler) @@ -162,7 +169,35 @@ def _tealish(self) -> str: class LiteralBytes(Literal): - pattern = rf"(?P{LITERAL_BYTES})$" + pattern = rf"(?P{LITERAL_BYTE_STRING})$" + value: str + + def write_teal(self, writer: "TealWriter") -> None: + writer.write(self, f"pushbytes {self.value}") + + def type(self) -> AVMType: + return AVMType.bytes + + def _tealish(self) -> str: + return f"{self.value}" + + +class LiteralAddr(Literal): + pattern = rf"addr\((?P{LITERAL_BYTE_ADDR})\)$" + value: str + + def write_teal(self, writer: "TealWriter") -> None: + writer.write(self, f"addr {self.value}") + + def type(self) -> AVMType: + return AVMType.bytes + + def _tealish(self) -> str: + return f"addr({self.value})" + + +class LiteralHex(Literal): + pattern = rf"(?P{LITERAL_BYTE_HEX})$" value: str def write_teal(self, writer: "TealWriter") -> None: @@ -379,7 +414,7 @@ class Const(LineStatement): def process(self) -> None: scope = self.get_current_scope() - scope.declare_const(self.name, (self.type, self.expression.value)) + scope.declare_const(self.name, (self.type, self.expression)) def write_teal(self, writer: "TealWriter") -> None: pass diff --git a/tealish/scope.py b/tealish/scope.py index a3619ca..ffd03ff 100644 --- a/tealish/scope.py +++ b/tealish/scope.py @@ -3,7 +3,7 @@ if TYPE_CHECKING: from .tealish_builtins import AVMType - from .nodes import Func, Block + from .nodes import Func, Block, Literal VarType = Union["AVMType", Tuple[str, str]] @@ -25,7 +25,7 @@ def __init__( slot_range if slot_range is not None else (0, 200) ) - self.consts: Dict[str, Tuple["AVMType", ConstValue]] = {} + self.consts: Dict[str, Tuple["AVMType", Union[ConstValue, "Literal"]]] = {} self.blocks: Dict[str, "Block"] = {} self.functions: Dict[str, "Func"] = {} @@ -63,11 +63,11 @@ def delete_var(self, name: str) -> None: del self.slots[name] def declare_const( - self, name: str, const_data: Tuple["AVMType", ConstValue] + self, name: str, const_data: Tuple["AVMType", Union["Literal", ConstValue]] ) -> None: self.consts[name] = const_data - def lookup_const(self, name: str) -> Tuple["AVMType", ConstValue]: + def lookup_const(self, name: str) -> Tuple["AVMType", Union["Literal", ConstValue]]: if name not in self.consts: raise KeyError(f'Const "{name}" not declared in current scope') return self.consts[name] diff --git a/tealish/tealish_expressions.tx b/tealish/tealish_expressions.tx index 1bea043..3af49d0 100644 --- a/tealish/tealish_expressions.tx +++ b/tealish/tealish_expressions.tx @@ -35,4 +35,6 @@ GroupIndex: NegativeGroupIndex | PositiveGroupIndex | Expression; NegativeGroupIndex: '-' index=INT; PositiveGroupIndex: '+' index=INT; Integer: value=/[0-9_]+/; -Bytes: value=STRING; +HexBytes: value=/0x([a-fA-F0-9]+)/; +AddrBytes: value='addr('/([A-Z2-7]+)/ ')'; +Bytes: value=STRING | HexBytes | AddrBytes; diff --git a/tests/everything.teal b/tests/everything.teal index 994ea7e..9119a02 100644 --- a/tests/everything.teal +++ b/tests/everything.teal @@ -12,69 +12,74 @@ // Assignments // int a = FOO [slot 0] -pushint 100 // FOO +pushint 100 store 0 // a // bytes b = BAR [slot 1] -pushbytes "bar" // BAR +pushbytes "bar" store 1 // b - +// bytes c = BAZ [slot 2] +pushbytes 0xDEADBEEF +store 2 // c +// bytes d = ADDR [slot 3] +addr RIKLQ5HEVXAOAWYSW2LGQFYGWVO4J6LIAQQ72ZRULHZ4KS5NRPCCKYPCUU +store 3 // d // Structs -// Item item1 = bzero(46) [slot 2] +// Item item1 = bzero(46) [slot 4] pushint 46 bzero -store 2 // item1 -// item1.id = 123 [slot 2] -load 2 // item1 +store 4 // item1 +// item1.id = 123 [slot 4] +load 4 // item1 pushint 123 itob replace 0 // item1.id -store 2 // item1 -// item1.name = "xyz" [slot 2] -load 2 // item1 +store 4 // item1 +// item1.name = "xyz" [slot 4] +load 4 // item1 pushbytes "xyz" replace 16 // item1.name -store 2 // item1 +store 4 // item1 // assert(item1.id > 0) -load 2 // item1 +load 4 // item1 pushint 0 extract_uint64 // id pushint 0 > assert // log(item1.name) -load 2 // item1 +load 4 // item1 extract 16 10 // name log // Delcaration without assignment -// int balance [slot 3] -// int exists [slot 4] +// int balance [slot 5] +// int exists [slot 6] // Multiple assignment // Opcode with immediate arg // exists, balance = asset_holding_get(AssetBalance, 0, 5) pushint 0 pushint 5 asset_holding_get AssetBalance -store 4 // exists -store 3 // balance +store 6 // exists +store 5 // balance // Use of _ to ignore a return value // _, balance = asset_holding_get(AssetBalance, 1, 5) pushint 1 pushint 5 asset_holding_get AssetBalance pop // discarding value for _ -store 3 // balance +store 5 // balance // if FOO > 1: - pushint 100 // FOO + pushint 100 pushint 1 > bz l0_else // then: // log(BAR) - pushbytes "bar" // BAR + pushbytes "bar" log b l0_end l0_else: @@ -116,20 +121,20 @@ store 3 // balance // Boxes -// box box1 = CreateBox("a") [slot 5] +// box box1 = CreateBox("a") [slot 7] pushbytes "a" dup pushint 26 box_create assert // assert created -store 5 // box1 +store 7 // box1 // box1.name = "xyz" [box] -load 5 // box key box1 +load 7 // box key box1 pushint 16 // offset pushbytes "xyz" box_replace // box1.name // assert(box1.id) -load 5 // box key box1 +load 7 // box key box1 pushint 0 // offset pushint 8 // size box_extract // box1.id @@ -151,14 +156,14 @@ b fail // else // block main main: - // int amount = sum(2, 3) [slot 6] + // int amount = sum(2, 3) [slot 8] pushint 2 pushint 3 callsub __func__sum - store 6 // amount + store 8 // amount // transfer(0, amount, Gtxn[-1].Sender, Txn.Accounts[1]) pushint 0 - load 6 // amount + load 8 // amount txn GroupIndex pushint 1 - @@ -166,36 +171,36 @@ main: txna Accounts 1 callsub __func__transfer - // int sum = teal_sum(2, 3) [slot 7] + // int sum = teal_sum(2, 3) [slot 9] pushint 2 pushint 3 callsub __func__teal_sum - store 7 // sum + store 9 // sum // assert(amount == sum) - load 6 // amount - load 7 // sum + load 8 // amount + load 9 // sum == assert - // int z = add_amount(5) [slot 8] + // int z = add_amount(5) [slot 10] pushint 5 callsub main__func__add_amount - store 8 // z + store 10 // z - // int i = 0 [slot 9] + // int i = 0 [slot 11] pushint 0 - store 9 // i + store 11 // i // while i < z: l3_while: - load 9 // i - load 8 // z + load 11 // i + load 10 // z < bz l3_end // i = i + 1 - load 9 // i + load 11 // i pushint 1 + - store 9 // i + store 11 // i b l3_while l3_end: // end @@ -207,10 +212,10 @@ main: == bnz l4_end // i = i + 1 - load 9 // i + load 11 // i pushint 1 + - store 9 // i + store 11 // i pushint 1 + dup @@ -220,60 +225,60 @@ main: // for x in 1:10: pushint 1 - store 10 // x + store 12 // x l5_for: - load 10 // x + load 12 // x pushint 10 == bnz l5_end // log(itob(x)) - load 10 // x + load 12 // x itob log - load 10 // x + load 12 // x pushint 1 + - store 10 // x + store 12 // x b l5_for l5_end: // end - // int first = 1 [slot 10] + // int first = 1 [slot 12] pushint 1 - store 10 // first - // int last = 5 + 5 [slot 11] + store 12 // first + // int last = 5 + 5 [slot 13] pushint 5 pushint 5 + - store 11 // last + store 13 // last // For loop with variables // for x in first:last: - load 10 // first - store 12 // x + load 12 // first + store 14 // x l6_for: - load 12 // x - load 11 // last + load 14 // x + load 13 // last == bnz l6_end // log(itob(x)) - load 12 // x + load 14 // x itob log - load 12 // x + load 14 // x pushint 1 + - store 12 // x + store 14 // x b l6_for l6_end: // end // Function with multiple return values - // int fx [slot 12] - // int fy [slot 13] + // int fx [slot 14] + // int fy [slot 15] // fx, fy = foo(1, 2) pushint 1 pushint 2 callsub __func__foo - store 12 // fx - store 13 // fy + store 14 // fx + store 15 // fy // exit(1) pushint 1 @@ -282,14 +287,14 @@ main: // Locally scoped function using variable from parent scope // func add_amount(x: int) int: main__func__add_amount: - store 14 // x - // int result = amount + x [slot 15] - load 6 // amount - load 14 // x + store 16 // x + // int result = amount + x [slot 17] + load 8 // amount + load 16 // x + - store 15 // result + store 17 // result // return result - load 15 // result + load 17 // result retsub @@ -357,12 +362,12 @@ retsub // Function with args but no return value // func transfer(asset_id: int, amount: int, sender: bytes, receiver: bytes): __func__transfer: -store 16 // receiver -store 17 // sender -store 18 // amount -store 19 // asset_id +store 18 // receiver +store 19 // sender +store 20 // amount +store 21 // asset_id // if asset_id == 0: - load 19 // asset_id + load 21 // asset_id pushint 0 == bz l8_else @@ -373,13 +378,13 @@ store 19 // asset_id pushint 1 // Pay itxn_field TypeEnum // Sender: sender - load 17 // sender + load 19 // sender itxn_field Sender // Receiver: receiver - load 16 // receiver + load 18 // receiver itxn_field Receiver // Amount: amount - load 18 // amount + load 20 // amount itxn_field Amount // Fee: 0 pushint 0 @@ -395,16 +400,16 @@ store 19 // asset_id pushint 4 // Axfer itxn_field TypeEnum // Sender: sender - load 17 // sender + load 19 // sender itxn_field Sender // AssetReceiver: receiver - load 16 // receiver + load 18 // receiver itxn_field AssetReceiver // AssetAmount: amount - load 18 // amount + load 20 // amount itxn_field AssetAmount // XferAsset: asset_id - load 19 // asset_id + load 21 // asset_id itxn_field XferAsset // Fee: 0 pushint 0 @@ -418,45 +423,45 @@ retsub // Function with return value // func sum(x: int, y: int) int: __func__sum: -store 20 // y -store 21 // x -// int result = x + y [slot 22] -load 21 // x -load 20 // y +store 22 // y +store 23 // x +// int result = x + y [slot 24] +load 23 // x +load 22 // y + -store 22 // result +store 24 // result // return result -load 22 // result +load 24 // result retsub // func teal_sum(x: int, y: int) int: __func__teal_sum: -store 23 // y -store 24 // x +store 25 // y +store 26 // x // push(x) -load 24 // x +load 26 // x // push // push(y) -load 23 // y +load 25 // y // push pop pop + -// int result = pop() [slot 25] +// int result = pop() [slot 27] // pop -store 25 // result +store 27 // result // return result -load 25 // result +load 27 // result retsub // Function with multiple return values // func foo(x: int, y: int) int, int: __func__foo: -store 26 // y -store 27 // x +store 28 // y +store 29 // x // return x, y -load 26 // y -load 27 // x +load 28 // y +load 29 // x retsub diff --git a/tests/everything.tl b/tests/everything.tl index 38d6a6c..baaeb77 100644 --- a/tests/everything.tl +++ b/tests/everything.tl @@ -16,11 +16,14 @@ end # Consts const int FOO = 100 const bytes BAR = "bar" +const bytes BAZ = 0xDEADBEEF +const bytes ADDR = addr(RIKLQ5HEVXAOAWYSW2LGQFYGWVO4J6LIAQQ72ZRULHZ4KS5NRPCCKYPCUU) # Assignments int a = FOO bytes b = BAR - +bytes c = BAZ +bytes d = ADDR # Structs Item item1 = bzero(46)