Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Restructure the examples section #87

Open
wants to merge 1 commit into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 12 additions & 30 deletions eopsin/tests/test_misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ def test_assert_sum_contract_fail(self):
b=st.integers(min_value=0, max_value=10),
)
def test_mult_for(self, a: int, b: int):
input_file = "examples/mult_for.py"
input_file = "examples/b_mult_for.py"
with open(input_file) as fp:
source_code = fp.read()
ast = compiler.parse(source_code)
Expand All @@ -76,7 +76,7 @@ def test_mult_for(self, a: int, b: int):
b=st.integers(min_value=0, max_value=10),
)
def test_mult_while(self, a: int, b: int):
input_file = "examples/mult_while.py"
input_file = "examples/c_mult_while.py"
with open(input_file) as fp:
source_code = fp.read()
ast = compiler.parse(source_code)
Expand All @@ -89,26 +89,8 @@ def test_mult_while(self, a: int, b: int):
ret = uplc_eval(f)
self.assertEqual(ret, uplc.PlutusInteger(a * b))

@given(
a=st.integers(),
b=st.integers(),
)
def test_sum(self, a: int, b: int):
input_file = "examples/sum.py"
with open(input_file) as fp:
source_code = fp.read()
ast = compiler.parse(source_code)
code = compiler.compile(ast)
code = code.compile()
f = code.term
# UPLC lambdas may only take one argument at a time, so we evaluate by repeatedly applying
for d in [uplc.PlutusInteger(a), uplc.PlutusInteger(b)]:
f = uplc.Apply(f, d)
ret = uplc_eval(f)
self.assertEqual(ret, uplc.PlutusInteger(a + b))

def test_complex_datum_correct_vals(self):
input_file = "examples/complex_datum.py"
input_file = "examples/a_complex_datum.py"
with open(input_file) as fp:
source_code = fp.read()
ast = compiler.parse(source_code)
Expand All @@ -135,7 +117,7 @@ def test_complex_datum_correct_vals(self):
)

def test_hello_world(self):
input_file = "examples/hello_world.py"
input_file = "examples/a_introduction/a_hello_world.py"
with open(input_file) as fp:
source_code = fp.read()
ast = compiler.parse(source_code)
Expand All @@ -148,7 +130,7 @@ def test_hello_world(self):
ret = uplc_eval(f)

def test_list_datum_correct_vals(self):
input_file = "examples/list_datum.py"
input_file = "examples/d_list_datum.py"
with open(input_file) as fp:
source_code = fp.read()
ast = compiler.parse(source_code)
Expand All @@ -165,7 +147,7 @@ def test_list_datum_correct_vals(self):
)

def test_showcase(self):
input_file = "examples/showcase.py"
input_file = "examples/c_showcase.py"
with open(input_file) as fp:
source_code = fp.read()
ast = compiler.parse(source_code)
Expand All @@ -183,7 +165,7 @@ def test_showcase(self):

@given(n=st.integers(min_value=0, max_value=5))
def test_fib_iter(self, n):
input_file = "examples/fib_iter.py"
input_file = "examples/a_introduction/d_fib_iter.py"
with open(input_file) as fp:
source_code = fp.read()
ast = compiler.parse(source_code)
Expand All @@ -201,7 +183,7 @@ def test_fib_iter(self, n):

@given(n=st.integers(min_value=0, max_value=5))
def test_fib_rec(self, n):
input_file = "examples/fib_rec.py"
input_file = "examples/e_fib_rec.py"
with open(input_file) as fp:
source_code = fp.read()
ast = compiler.parse(source_code)
Expand Down Expand Up @@ -333,7 +315,7 @@ def a(x: int) -> int:
self.assertEqual(uplc.PlutusInteger(100), ret)

def test_datum_cast(self):
input_file = "examples/datum_cast.py"
input_file = "examples/b_datum_cast.py"
with open(input_file) as fp:
source_code = fp.read()
ast = compiler.parse(source_code)
Expand Down Expand Up @@ -416,7 +398,7 @@ def test_parameterized_compile(self):
f = code.term

def test_dict_datum(self):
input_file = "examples/dict_datum.py"
input_file = "examples/c_dict_datum.py"
with open(input_file) as fp:
source_code = fp.read()
ast = compiler.parse(source_code)
Expand Down Expand Up @@ -563,7 +545,7 @@ def validator(_: None) -> SomeOutputDatum:
)

def test_list_comprehension_even(self):
input_file = "examples/list_comprehensions.py"
input_file = "examples/a_list_comprehensions.py"
with open(input_file) as fp:
source_code = fp.read()
ast = compiler.parse(source_code)
Expand All @@ -584,7 +566,7 @@ def test_list_comprehension_even(self):
)

def test_list_comprehension_all(self):
input_file = "examples/list_comprehensions.py"
input_file = "examples/a_list_comprehensions.py"
with open(input_file) as fp:
source_code = fp.read()
ast = compiler.parse(source_code)
Expand Down
Empty file added examples/__init__.py
Empty file.
Empty file.
22 changes: 22 additions & 0 deletions examples/a_introduction/a_hello_world.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
"""
A very basic eopsin program
it does nothing, takes only one parameter and returns nothing

When executed, it will print "Hello World!" to the console!

Try it out!

```bash
(venv) $ eopsin eval examples/a_introduction/a_hello_world.py '{"int": 0}'
> Starting execution
> ------------------
> Hello world!
> ------------------
> None
```
"""


def validator(_: None) -> None:
# print a string into the debug console
print("Hello world!")
35 changes: 35 additions & 0 deletions examples/a_introduction/b_mult_for.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
"""
A re-implementation of a * b
This program will loop through all the numbers from 0 to b
and sum the values up

Try it out!

```bash
(venv) $ eopsin eval examples/a_introduction/b_mult_for.py '{"int": 3}' '{"int": 4}'
> Starting execution
> Starting execution
> ------------------
> 0
> 1
> 2
> 3
> ------------------
> 12
```
"""


def validator(a: int, b: int) -> int:
# trivial implementation of c = a * b

# We initialize the variable with a default
c = 0
# Now loop through all numbers from 0 to b (range creates the list [0, 1, 2, ...])
for i in range(b):
# for debugging purposes, you can print the number
# Note: we convert the `int` to a `str` by calling `str(...)` - print only prints strings!
print(str(i))
# and we update the value of c in every iteration
c += a
return c
24 changes: 24 additions & 0 deletions examples/a_introduction/c_mult_while.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
"""
A re-implementation of a * b, this time with a while loop.

Try it out!

```bash
(venv) $ eopsin eval examples/a_introduction/c_mult_while.py '{"int": 3}' '{"int": 4}'
> Starting execution
> ------------------
> ------------------
> 12
```
"""


def validator(a: int, b: int) -> int:
# trivial implementation of c = a * b
c = 0
# in a while-loop we check the condition and execute the inner body while it evaluates to True
while 0 < b:
# we update the values of c and b here
c += a
b -= 1
return c
28 changes: 28 additions & 0 deletions examples/a_introduction/d_fib_iter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
"""
A more complicated example of a program
This program will loop through all the numbers from 0 to n
and compute the fibonacci numbers on the way.

Try it out!

```bash
(venv) $ eopsin eval examples/a_introduction/d_fib_iter.py '{"int": 3}'
> Starting execution
> ------------------
> 0
> 1
> 2
> ------------------
> 2
```
"""


def validator(n: int) -> int:
a, b = 0, 1
for i in range(n):
# due to strict typing, we need to first convert the integer to a string by calling str(..)
print(str(i))
# this updates and re-assigns values in a tuple-like fashion
a, b = b, a + b
return a
32 changes: 32 additions & 0 deletions examples/a_introduction/e_fib_rec.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
"""
A more complicated example of a program
This program will loop through all the numbers from 0 to n
and compute the fibonacci numbers on the way.

Try it out!

```bash
(venv) $ eopsin eval examples/a_introduction/d_fib_iter.py '{"int": 3}'
> Starting execution
> ------------------
> 0
> 1
> 2
> ------------------
> 2
```
"""


def fib(n: int) -> int:
if n == 0:
res = 0
elif n == 1:
res = 1
else:
res = fib(n - 1) + fib(n - 2)
return res


def validator(n: int) -> int:
return fib(n)
28 changes: 28 additions & 0 deletions examples/a_introduction/f_branching.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
"""
We can branch the control flow using if/else
You can pass boolean values into the contract by passing integers with 0/1

Try it out!

```bash
(venv) $ eopsin eval examples/a_introduction/f_branching.py '{"int": 3}' '{"int": 0}'
> Starting execution
> ------------------
> ------------------
> 9
(venv) $ eopsin eval examples/a_introduction/f_branching.py '{"int": 3}' '{"int": 1}'
> Starting execution
> ------------------
> ------------------
> 6

```
"""


def validator(n: int, even: bool) -> int:
if even:
res = 2 * n
else:
res = 3 * n
return res
27 changes: 27 additions & 0 deletions examples/a_introduction/g_strict_typing.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
"""
eopsin is strictly typed! Take care that your types match. The compiler will guide you there.

Try it out!

```bash
(venv) $ eopsin eval examples/a_introduction/g_strict_typing.py '{"int": 3}'
Traceback (most recent call last):
File "/git/eopsin-lang/venv/bin/eopsin", line 33, in <module>
sys.exit(load_entry_point('eopsin-lang', 'console_scripts', 'eopsin')())
File "/git/eopsin-lang/eopsin/__main__.py", line 140, in main
raise SyntaxError(
File "examples/a_introduction/g_strict_typing.py", line 14
if "s" == 4:
^
NotImplementedError: Comparison Eq for StringType and IntegerType is not implemented. This is likely intended because it would always evaluate to False.
Note that eopsin errors may be overly restrictive as they aim to prevent code with unintended consequences.

```
"""


def validator(value: int) -> bytes:
# eopsin is strictly typed! This comparison is invalid, strings ant integers can not be compared
if "s" == value:
print("test")
return b""
Empty file added examples/b_datums/__init__.py
Empty file.
58 changes: 58 additions & 0 deletions examples/b_datums/a_complex_datum.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
"""
You can define more complex datums using PyCardanos notation.
Every datum/object is a class that inherits from PlutusData and implements dataclass.
If you don't understand what this means, don't worry. Just stick to the way that datums are defined
in these examples.

If something can be either object A or object B, you can create Union[A, B] as a new type.
You can distinguish between A and B using isinstance(x, A) and isinstance(x, B).
IMPORTANT: Make sure that all objects in a Union have different CONSTR_ID values.
The exact value does not matter too much actually, but they need to be different.

Try it out!

```bash
(venv) $ eopsin eval examples/b_datums/a_complex_datum.py '{ "constructor": 0, "fields": [ { "bytes": "0000000000000000000000000000000000" }, { "constructor": 1, "fields": [ { "int": 1000000 }, { "int": 20 } ] } ] }'
> Starting execution
> ------------------
> ------------------
> 20
```
"""
# We import the prelude now, it will bring all values for classes and types
from eopsin.prelude import *


@dataclass()
class Deposit(PlutusData):
CONSTR_ID = 0
minimum_lp: int


@dataclass()
class Withdraw(PlutusData):
CONSTR_ID = 1
minimum_coin_a: int
minimum_coin_b: int


OrderStep = Union[Deposit, Withdraw]

# inspired by https://github.com/MuesliSwapTeam/muesliswap-cardano-pool-contracts/blob/main/dex/src/MuesliSwapPools/BatchOrder/Types.hs
@dataclass()
class BatchOrder(PlutusData):
owner: PubKeyHash
action: Union[Deposit, Withdraw]


# If some parameter might be ommited, just Union with Nothing and check for the instance at runtime!
def validator(datum: BatchOrder) -> int:
action = datum.action
if isinstance(action, Deposit):
res = action.minimum_lp
elif isinstance(action, Withdraw):
res = action.minimum_coin_b
# note we never initialized res. This means this will
# throw a NameError if the instances don't match -
# this is fine, it means that the contract was not invoked correctly!
return res
Loading