tinymanorg / tealish Goto Github PK
View Code? Open in Web Editor NEWTealish: A readable language for Algorand
Home Page: https://tealish.tinyman.org
License: MIT License
Tealish: A readable language for Algorand
Home Page: https://tealish.tinyman.org
License: MIT License
The switch
control flow is not documented in Language Guide.
Just wondering if ABI not being supported (as stated in FAQ explicitly) is something that is planned for long term (giving more 'flexibility' in some sense) so people deal with abi compliant methods themselves or its just that tealish was developed (started development) before ABI was introduced?
Would be an icing completing the cake if it gets added to tealish as well ;)
AVM 8+ has support for frame pointers which allow for more efficient stack & slot usage for subroutines (functions in Tealish).
Tealish can generate Teal that uses proto
at the start of function definitions instead of store
s. frame_dig
can be used to access the function args from the stack without having to store them in slots.
Teal example for reference:
pushbytes "ab"
pushbytes "cd"
// log(myfunc("ab", "cd"))
callsub myfunc
log
pushint 1
return
// myfunc(x: bytes, y: bytes) bytes:
myfunc:
// specify that our function takes 2 arguments and has one return value
proto 2 1 // internally puts the first 2 values from the stack into the frame
frame_dig -2 // first arg
frame_dig -1 // second arg
concat
retsub
Since Statements and Expressions both inherit from BaseNode
some issues arise in accessing attributes that are not defined in one or the other. We can change the inheritance structure to split the different Node types for better type information and prevent issues with inferring attribute availability.
See this comment for more: #27 (review)
Currently base types (int
,bytes
,any
,none
) are strings that we match against.
A new class that subclasses Enum
type, and possibly also str
would be helpful to prevent us from doing string comparison and provide better type hints in containing classes
See this comment for a bit more: #27 (comment)
Describe the bug
{box_name}.{field_name} = {value}
. replaces the field {field_name}
of box {box_name}
created from a struct without considering the size of {value}
. This issue tracks the bug when parts of the replaced value remain after replacing it with a byte array of a smaller size than the current value.
To Reproduce
#pragma version 8
struct UserInfo:
favourite_colour: bytes[20]
end
if Txn.ApplicationID == 0:
exit(1)
end
box<UserInfo> info_box = CreateBox("foo")
info_box.favourite_colour = "yellow"
info_box.favourite_colour = "red"
exit(1)
Expected behavior
Field favourite_colour
of box with key "foo" should have a value of "red" but is instead "redlow".
Additional context
Related issue: #75
I noticed that for
loops always expect to increment between the start
and end
values, only breaking when var
equals exactly the end
value (which could be a problem if it somehow skips over it). However if you wanted to loop from a higher value to a lower value (e.g. swap the order of for n in 1:5:
to for n in 5:1:
) it results in TEAL that will hit an AVM error when evaluated ("cost budget exceeded"), since it never breaks.
At first I thought maybe I could quickly modify the tealish/__init__.py
file to check if start
was greater than end
, then write "-" instead of "+"... And then I realised they don't have to be defined values at all, and may be unkonwn until runtime.
int x = btoi(Txn.ApplicationArgs[0])
int y = btoi(Txn.ApplicationArgs[1])
for n in x:y:
pop(n)
end
Should there be an easy way to define or flip the default direction of a for
loop without having to write additional logic to work with an incrementing value?
My infra works with raw teal, so I use beaker, pyteal, etc. to generate TEAL that's checked-in and built directly into binaries or part of a bootstrapping process.
That raw teal includes 'template' substitutions that are replaced with values specific to each environment (testnet / mainnet contract IDs, or parameter values for eg).
In PyTeal, a reference to a templated int (same w/ Bytes) might be like:
xxx.store( Itob(Tmpl.Int("TMPL_XXX")) )
which might become:
int TMPL_XXXX
itob
store 50
in TEAL. My code converts that prior to compilation, replacing the templates in descending length order.
This is one of the showstoppers for me in using Tealish.
Describe the bug
Order of return vars in code and docs mixed up
To Reproduce
e.g.
box_get(A: bytes) โ bytes, int
in https://tealish.tinyman.org/en/latest/avm.html
but in code:
int, bytes = box_get(key)
Expected behavior
box_get(A: bytes) โ int, bytes
in docs
xor
bytes, int = box_get(key)
in code
Additional context
this is true for some other opcodes as well
Tealish could allow the developer to define a schema for Global & Local states. The schema would serve two purposes:
The schema should also allow for dynamically named state fields.
The syntax below is just an idea at this stage
Static state keys could be accessed using a field lookup syntax just like structs & Boxes. e.g
assert(Txn.Sender == GlobalState.manager_address)
Local state keys could use something like this:
assert(Txn.Sender == LocalState[account].manager_address)
When dynamic keys are required the existing opcodes (app_global_get
& app_local_get
) should be used unless a good argument can be made for having additional syntactic sugar for these.
The schemas would be queryable from the Tealish language model of the program to allow other tools to export/consume them.
Needs more fleshing out before implementation.
@barnjamin, do correct me if I have mangled your idea :) And please add any further detail you have in mind at this stage.
Describe the bug
Using tealish format converts the following code in wrong way
To Reproduce
bytes frc_proofs = Txn.ApplicationArgs[4]
bytes rewards_proof = extract((index * 32), 32, frc_proofs)
with something with this run
tealish format approval.tl
and outputs this
bytes frc_proofs = Txn.ApplicationArgs[4]
bytes rewards_proof = extract(<tealish.expression_nodes.Group object at 0x7f8a4e7ec790> 32, frc_proofs)
Expected behavior
Just format the code not change the code
Please see the Tealish Roadmap #64 post in Discussions for the current project roadmap.
Describe the bug
{box_name}.{field_name} = {value}
. replaces the field {field_name}
of box {box_name}
created from a struct without considering the size of {value}
. This issue tracks the bug when setting the value of a field to a byte array that is larger than the size defined overflowing into the next field.
To Reproduce
#pragma version 8
struct UserInfo:
birthday: bytes[10]
favourite_colour: bytes[20]
end
if Txn.ApplicationID == 0:
exit(1)
end
box<UserInfo> info_box = CreateBox("foo")
info_box.birthday = "06/02/2023OVERFLOW"
exit(1)
Expected behavior
Field favourite_colour
of box with key "foo" should have an empty value ("") but is instead "OVERFLOW". You could also argue that info_box.birthday = "06/02/2023OVERFLOW"
should fail outright (assigning a value larger than the size defined in the struct).
Additional context
Related issue: #74
Is your feature request related to a problem? Please describe.
By the best practicies it is better to write teal code which supports ABI calls.
Describe the solution you'd like
Please implement ABI support to tealish.
Describe alternatives you've considered
All other pyteal compilers support abi. Pyteal, beaker, ...
Additional context
I like idea of tealish that you copy the comments to the teal or that you use the same opcode names as in teal, but it is quite crucial for new projects to use the abi. Soon there will be release of shared resources and this will drive adoption of single abi calls which might be more complex.
Is it possible to have this extension in the VS market place?
open source it?
Update readme to include the link instead of the dropbox link?
I just learned about tealish. This is very exciting. Nice work! Thank you.
The structs
dictionary provides little information about the types it holds.
A new class should be created to provide information about a given Struct type.
See this comment for more: #27 (comment)
Describe the bug
With this code the compiler don't compiles the compiled get stuck.
https://gist.github.com/helderjnpinto/2416f4d5e3f0dc3349ee07d976e15536
To Reproduce
Tealish compile Test.tl
Expected behavior
code compile
Additional context
I think the problem here is in the if statement if returns (line 72 ) something compiles and if is removed compiles as well
https://gist.github.com/helderjnpinto/2416f4d5e3f0dc3349ee07d976e15536#file-test-bug-tealish-L71-L73
with this case compiles:
func calc(from_round: int) int:
int end_round = Global.Round
int test = 1
if end_round > 10:
test = 10
end
return end_round - from_round
end
tealish compile
on https://github.com/tinymanorg/tealish/blob/main/examples/boxes_with_structs.tlError: Pattern ((?P<names>([a-z_][a-zA-Z0-9_]*,?\s*)+) = (?P<expression>.*)$) does not match for Assignment for line "box<Item> item1 = CreateBox("a")"
I believe the latest merge with boxes wasn't propagated to pypi - perhaps its somehow related as well or some bug in the regex itsefl
Describe the bug
Multiple struct
with same field names cause wrong TEAL
(confused) code
struct StructA:
fieldname: int
q: int
end
struct StructB:
r: int
fieldname: int
end
block main:
StructA a = itob(1)
int x = a.fieldname
end
results in
// block main
main:
// StructA a = itob(1) [slot 0]
pushint 1
itob
store 0 // a
// int x = a.fieldname [slot 1]
load 0 // a
pushint 8
extract_uint64 // fieldname
store 1 // x
because fieldname
is in a different place in the different structs
(the pushint 8
is wrong in the TEAL code, should be pushint 0
since fieldname
is the first field in StructA
To Reproduce
code above
Expected behavior
perhaps field names need the struct name as a prefix to differentiate
Additional context
Add any other context about the problem here.
A statement like:
if a == 2 && b ==3:
will yield an error like:
Error: Cannot parse "if a == 2 && b ==3" as Expression at line 100
I had a more involved if statement in my particular case but I fought for quite a bit of time trying to figure out what was wrong (and there's no examples in the repo of an && expression like above) until I realized it wanted
if (a==2) && (b==3):
Tealish should make it extremely clear that the evaluation order isn't defined and needs to be. The error instead reads almost like a generic 'something's wrong' - ie: a syntax error.
Perhaps rename constants
to builtins
?
See #27 (comment) for more.
Thank you so much, this project is amazing! Welcome to Costco, I love you. I didn't look really thoroughly but from a glance, I am really hoping to use just about ALL of the things you mentioned in FUTURE.md
. It would be amazing if vim or VSCode could eventually have autocomplete for calling public methods in an external tealish contract (module) (maybe autogenerated app call wrappers from the contract JSON?).
Anyway, thanks for all the great work.
The inner transaction block doesn't support comments.
Problem:
inner_txn:
# This is a comment
TypeEnum: Axfer
...
end
Possible workaround:
# This is a comment
inner_txn:
TypeEnum: Axfer
...
end
algorand/go-algorand#4737 is introducing support for macros in TEAL.
One useful thing to do with macros is use them to define the constants Tealish supports.
I believe the following patch will enable that:
diff --git a/tealish/expression_nodes.py b/tealish/expression_nodes.py
index 4b365ac..409daca 100644
--- a/tealish/expression_nodes.py
+++ b/tealish/expression_nodes.py
@@ -78,9 +78,9 @@ class Constant(BaseNode):
def write_teal(self, writer):
if self.type == "int":
- writer.write(self, f"pushint {self.value} // {self.name}")
+ writer.write(self, f"pushint {self.name} // {self.value}")
elif self.type == "bytes":
- writer.write(self, f"pushbytes {self.value} // {self.name}")
+ writer.write(self, f"pushbytes {self.name} // {self.value}")
def _tealish(self, formatter=None):
return f"{self.name}"
diff --git a/tealish/nodes.py b/tealish/nodes.py
index 62f9ce1..d5a53d9 100644
--- a/tealish/nodes.py
+++ b/tealish/nodes.py
@@ -334,6 +334,7 @@ class Const(LineStatement):
scope["consts"][self.name] = [self.type, self.expression.value]
def write_teal(self, writer):
+ writer.write (self, f"#define {self.name} {self.expression.value}")
pass
def _tealish(self, formatter=None):
this will result in tealish that goes from
#pragma version 8
struct Item:
id: int
foo: int
name: bytes[10]
end
const int KNOWN_ID = 64738
const bytes BOX_NAME = "tealishbox"
const bytes KNOWN_ITEM = "fearghal"
box<Item> item1 = CreateBox(BOX_NAME)
item1.id = KNOWN_ID
item1.foo = KNOWN_ID + 1
item1.name = KNOWN_ITEM
log(itob(item1.id))
log(itob(item1.foo))
log(item1.name)
to TEAL
#pragma version 8
#define KNOWN_ID 64738
#define BOX_NAME "tealishbox"
#define KNOWN_ITEM "fearghal"
// box<Item> item1 = CreateBox(BOX_NAME) [slot 0]
pushbytes BOX_NAME // "tealishbox"
dup
pushint 26
box_create
assert // assert created
store 0 // item1
// item1.id = KNOWN_ID [box]
load 0 // box key item1
pushint 0 // offset
pushint KNOWN_ID // 64738
itob
box_replace // item1.id
// item1.foo = KNOWN_ID + 1 [box]
load 0 // box key item1
pushint 8 // offset
pushint KNOWN_ID // 64738
pushint 1
+
itob
box_replace // item1.foo
// item1.name = KNOWN_ITEM [box]
load 0 // box key item1
pushint 16 // offset
pushbytes KNOWN_ITEM // "fearghal"
box_replace // item1.name
// log(itob(item1.id))
load 0 // box key item1
pushint 0 // offset
pushint 8 // size
box_extract // item1.id
btoi
itob
log
// log(itob(item1.foo))
load 0 // box key item1
pushint 8 // offset
pushint 8 // size
box_extract // item1.foo
btoi
itob
log
// log(item1.name)
load 0 // box key item1
pushint 16 // offset
pushint 10 // size
box_extract // item1.name
log
We should consider adding a new type bigint
that is backed by bytes and type check for that on b+,b*,b-, ...
Maybe here? maybe not if we want to keep that isolated to just the actual AVM types
https://github.com/tinymanorg/tealish/blob/main/tealish/tealish_builtins.py#L8
I have many helper/utility functions that are shared between multiple related contracts. Whether its shared constants, or common helper functions (like beaker/pyteal utils functions), Tealish currently forces everything to be in a single file.
This is forcing copy-paste duplication of large chunks of code in some cases.
A completely fleshed out import mechanism isn't particularly necessary, but a simple C like #include that injected from other files would address this problem well enough.
Currently the scope
on Nodes is a Dict[str, Any]
but this doesn't provide much information about what it contains.
A new class should be defined to provide better type information for scope and the nodes should be updated to use it.
PyTeal suports Bytes syntax variants like:
Bytes("base16", "0x052001018008010203040506")
I use this to construct TEAL bytcode that's manipulate for LSIG references inside a contract. I don't see any way to do this in Tealish at the moment.
tealish compile
should allow output to stdout. The output file or directory should be customizable (probably remove the default of build/
.
tealish build
should allow/require specifying the build directory instead of forcing build/
.
Describe the bug
int x = 1
const int X = 1
int y = 1
# next line is ok
_ = x * y
# next line is NOT ok
_ = X * y
Error: Cannot parse "X * y" as Expression
To Reproduce
Steps to reproduce the behavior.
Expected behavior
both lines should compile
Additional context
Add any other context about the problem here.
Currently, # comments are only allowed on a line by themself.
They should be allowed after lines as well:
const int MY_CONST = 42 # Magic constant
Variations of this same issue are also referenced in #45 and #15
Comments should also be allowed before the #pragma version XX line (ie: templated copyright lines for eg)
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.