diff --git a/README.md b/README.md
index f3973d0..72ce205 100644
--- a/README.md
+++ b/README.md
@@ -9,49 +9,85 @@
Read the Docs:
[FFI](https://github.com/vkobinski/benda-main/tree/master/docs/FFI.md)
-This is in conceptual stage.
+This is in MVP stage.
## Example
```py
-from dataclasses import dataclass
-from benda import bjit, u24
-
-@dataclass
-class Leaf:
- value: u24 # native HVM machine integer
-
-@dataclass
-class Node:
- left: 'Tree'
- right: 'Tree'
-
-Tree = Node | Leaf
-
-# The `bjit` decorator will introspect and translate the function to HVM/Bend
-# code, replacing it with a wrapper that converts the Python-level types of the
-# inputs and result value, Numba-style.
-
-@bjit
-def sum_tree(tree: Tree) -> u24:
- match tree:
- case Leaf(value=value):
- return value
- case Node(left=left, right=right):
- return sum_tree(left) + sum_tree(right)
- case _:
- raise TypeError("Invalid type for tree")
-
-# Alternatively, you can opt to use Python big integers and other primitives,
-# they will be translated to the equivalent representations automatically.
-
-@dataclass
-class Leaf2:
- value: int
+import benda
+import random
+
+book = benda.load_book("""
+(Sort List/Nil) = List/Nil
+(Sort(List/Cons head tail)) =
+((Part head tail) λmin λmax
+ let lft=(Sort min)
+ let rgt=(Sort max)
+ (Concat lft(List/Cons head rgt)))
+
+# Partitions a list in two halves, less-than-p and greater-than-p
+(Part p List/Nil) = λt(t List/Nil List/Nil)
+(Part p(List/Cons head tail)) = (Push(> head p) head(Part p tail))
+
+# Pushes a value to the first or second list of a pair
+(Push 0 x pair) = (pair λmin λmax λp(p(List/Cons x min) max))
+(Push _ x pair) = (pair λmin λmax λp(p min(List/Cons x max)))
+
+(Concat List/Nil tail) = tail
+(Concat(List/Cons head tail) xs2) =
+(List/Cons head(Concat tail xs2))
+""")
+
+List = book.adts.List
+
+def gen_list(n: int, max_value: int = 0xffffff) -> list[int]:
+ result: list[int] = []
+ for _ in range(n):
+ result.append(random.randint(0, max_value))
+ return result
+
+
+def to_cons_list(xs: list[int]):
+ result = List.Nil()
+
+ hi = len(xs)
+ if hi == 0:
+ return result
+
+ while hi > 0:
+ hi -= 1
+ result = List.Cons(xs[hi], result)
+
+ return result
+
+def print_cons_list(list):
+ while True:
+ match list:
+ case List.Cons.type(value, tail):
+ print(value, end=", ")
+ list = tail
+ case List.Nil.type():
+ break
+
+
+data = gen_list(5, 1000)
+cons_list = to_cons_list(data)
+sorted_list = book.defs.Sort(cons_list)
+sorted_list = sorted_list.to_adt(book.adts.List)
+print_cons_list(sorted_list)
```
## Development
+Dependencies:
+
+- Python 3.11+
+- Rust
+- C compiler
+- maturin
+
+### Getting dependencies with Nix (optional)
+
- Install Nix with [Determinate Nix Installer]
```sh
@@ -61,8 +97,20 @@ class Leaf2:
- You can run `nix develop` to enter a shell with the dependencies installed.
-- You can use [`direnv`][direnv] to automatically load the environment when you
- enter the project directory.
+### Building
+
+- Create and activate a Python virtual environment.
+ - e.g. with
+ ```
+ python -m venv .venv
+ source .venv/bin/activate
+ ```
+
+- Run `make` to build the project and install the `benda` package in the virtual
+ environment.
+
+
[Determinate Nix Installer]: https://install.determinate.systems
[direnv]: https://direnv.net
diff --git a/crates/benda/src/benda_ffi/mod.rs b/crates/benda/src/benda_ffi/mod.rs
index afbe41d..28c39d0 100644
--- a/crates/benda/src/benda_ffi/mod.rs
+++ b/crates/benda/src/benda_ffi/mod.rs
@@ -2,6 +2,10 @@ use bend::diagnostics::{Diagnostics, DiagnosticsConfig};
use bend::fun::{Book, Term};
use bend::{CompileOpts, RunOpts};
+/**
+ * TODO: move to another module
+ * TODO: docstring
+ */
pub fn run(book: &Book) -> Option<(Term, String, Diagnostics)> {
let run_opts = RunOpts::default();
let compile_opts = CompileOpts::default().set_all();
diff --git a/crates/benda/src/lib.rs b/crates/benda/src/lib.rs
index 8d518bd..27d5bee 100644
--- a/crates/benda/src/lib.rs
+++ b/crates/benda/src/lib.rs
@@ -1,3 +1,5 @@
+use std::fs::{remove_dir, File};
+use std::io::Write;
use std::path::Path;
use num_traits::ToPrimitive;
@@ -18,21 +20,30 @@ fn switch() -> PyResult {
Ok("Ok".to_string())
}
+#[pyfunction]
+fn load_book(py: Python, code: Py) -> PyResult> {
+ let mut tmp_file = File::create_new("./tmp/bend_book.tmp")
+ .expect("Could not create temporary file.");
+
+ let _ = tmp_file.write_all(code.to_string().as_bytes());
+ let _ = tmp_file.flush();
+
+ let path = Path::new("./tmp/bend_book.tmp");
+ let bend_book = bend::load_file_to_book(path);
+
+ let _ = std::fs::remove_file(path);
+
+ let book = Book::new(&mut bend_book.unwrap());
+
+ Ok(Py::new(py, book).unwrap())
+}
+
#[pyfunction]
fn load_book_from_file(py: Python, path: Py) -> PyResult> {
let binding = path.to_string();
let new_path = Path::new(&binding);
let bend_book = bend::load_file_to_book(new_path);
- //let code = std::fs::read_to_string(new_path)
- // .map_err(|e| e.to_string())
- // .unwrap();
- //let bend_book = bend::fun::load_book::do_parse_book(
- // &code,
- // new_path,
- // BendBook::default(),
- //);
-
let book = Book::new(&mut bend_book.unwrap());
Ok(Py::new(py, book).unwrap())
@@ -138,6 +149,7 @@ impl PyBjit {
fn benda(_py: Python, m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_function(wrap_pyfunction!(switch, m)?)?;
m.add_function(wrap_pyfunction!(load_book_from_file, m)?)?;
+ m.add_function(wrap_pyfunction!(load_book, m)?)?;
m.add_class::()?;
m.add_class::()?;
m.add_class::()?;
diff --git a/crates/benda/src/parser/builtins.rs b/crates/benda/src/parser/builtins.rs
deleted file mode 100644
index e0d80ea..0000000
--- a/crates/benda/src/parser/builtins.rs
+++ /dev/null
@@ -1,3 +0,0 @@
-pub enum BuiltIn {
- Switch,
-}
\ No newline at end of file
diff --git a/crates/benda/src/types/book.rs b/crates/benda/src/types/book.rs
index 23d05f4..2ba38b4 100644
--- a/crates/benda/src/types/book.rs
+++ b/crates/benda/src/types/book.rs
@@ -71,7 +71,7 @@ macro_rules! generate_structs {
#[pyclass(name = $name)]
#[derive(Clone, Debug)]
pub struct $iden {
- entire_name: String,
+ full_name: String,
name: String,
fields: IndexMap>>,
}
@@ -105,10 +105,7 @@ macro_rules! generate_structs {
fn __str__(&self) -> String {
let mut out = String::new();
- out.push_str(
- format!("Bend ADT: {}", self.entire_name).as_str(),
- );
-
+ out.push_str(format!("", self.full_name).as_str());
out
}
@@ -136,19 +133,29 @@ macro_rules! generate_structs {
}
}
+ #[getter]
+ fn r#type(&self) -> PyResult {
+ Python::with_gil(|py| {
+ let ctr_type = $iden::type_object_bound(py).to_object(py);
+ Ok(ctr_type)
+ })
+ }
+
fn __getattr__(&self, object: Bound) -> PyResult {
let field = object.to_string();
let py = object.py();
- if field == "type" {
+ if field == "__variant" {
return Ok(
- PyString::new_bound(py, &self.entire_name).into_py(py)
+ PyString::new_bound(py, &self.full_name).into_py(py)
);
}
- if &object.to_string() == "name" {
- return Ok(PyString::new_bound(py, &self.name).into());
+ if field == "__ctr_type__" {
+ return Ok(
+ PyString::new_bound(py, &self.full_name).into_py(py)
+ );
}
if let Ok(val) = object.to_string().parse::() {
@@ -173,6 +180,9 @@ generate_structs!("Ctr2", Ctr2);
generate_structs!("Ctr3", Ctr3);
generate_structs!("Ctr4", Ctr4);
generate_structs!("Ctr5", Ctr5);
+generate_structs!("Ctr6", Ctr6);
+generate_structs!("Ctr7", Ctr7);
+generate_structs!("Ctr8", Ctr8);
#[pyclass(name = "Ctrs")]
#[derive(Clone, Debug, Default)]
@@ -183,6 +193,9 @@ pub struct Ctrs {
pub third: Option,
pub fourth: Option,
pub fifth: Option,
+ pub sixth: Option,
+ pub seventh: Option,
+ pub eighth: Option,
}
impl Ctrs {
@@ -208,13 +221,10 @@ impl Ctrs {
#[pymethods]
impl Ctrs {
- fn __getattr__(&self, object: Bound) -> PyResult {
- let py = object.py();
-
- if object.to_string().starts_with('t') {
- let b_name = object.to_string();
- let name = b_name.strip_prefix('t').unwrap();
+ fn __getattr__(&self, name: Bound) -> PyResult {
+ let py = name.py();
+ if let Some(name) = name.to_string().strip_suffix("_t") {
let index = self.fields.get_index_of(name).unwrap();
let res = match index {
@@ -233,10 +243,10 @@ impl Ctrs {
return Ok(res.to_object(py));
}
- if let Some(val) = self.fields.get(&object.to_string()) {
+ if let Some(val) = self.fields.get(&name.to_string()) {
Ok(val.clone())
} else {
- new_err(format!("Could not find attr {}", object))
+ new_err(format!("Could not find attr {}", name))
}
}
}
@@ -422,6 +432,9 @@ impl Book {
let mut third: Option = None;
let mut fourth: Option = None;
let mut fifth: Option = None;
+ let mut sixth: Option = None;
+ let mut seventh: Option = None;
+ let mut eighth: Option = None;
for (index, (ctr_name, ctr_fields)) in
bend_adt.ctrs.iter().enumerate()
@@ -432,7 +445,7 @@ impl Book {
0 => {
let mut ct = Ctr1 {
name: new_name.clone(),
- entire_name: ctr_name.to_string(),
+ full_name: ctr_name.to_string(),
fields: IndexMap::new(),
};
for c in ctr_fields {
@@ -446,7 +459,7 @@ impl Book {
1 => {
let mut ct = Ctr2 {
name: new_name.clone(),
- entire_name: ctr_name.to_string(),
+ full_name: ctr_name.to_string(),
fields: IndexMap::new(),
};
for c in ctr_fields {
@@ -460,7 +473,7 @@ impl Book {
2 => {
let mut ct = Ctr3 {
name: new_name.clone(),
- entire_name: ctr_name.to_string(),
+ full_name: ctr_name.to_string(),
fields: IndexMap::new(),
};
for c in ctr_fields {
@@ -474,7 +487,7 @@ impl Book {
3 => {
let mut ct = Ctr4 {
name: new_name.clone(),
- entire_name: ctr_name.to_string(),
+ full_name: ctr_name.to_string(),
fields: IndexMap::new(),
};
for c in ctr_fields {
@@ -489,7 +502,7 @@ impl Book {
4 => {
let mut ct = Ctr5 {
name: new_name.clone(),
- entire_name: ctr_name.to_string(),
+ full_name: ctr_name.to_string(),
fields: IndexMap::new(),
};
for c in ctr_fields {
@@ -501,6 +514,49 @@ impl Book {
.insert(new_name, ct.into_py(py).as_any().clone());
}
+ 5 => {
+ let mut ct = Ctr6 {
+ name: new_name.clone(),
+ full_name: ctr_name.to_string(),
+ fields: IndexMap::new(),
+ };
+ for c in ctr_fields {
+ ct.fields.insert(c.nam.to_string(), None);
+ }
+ sixth = Some(ct.clone());
+ all_ctrs
+ .fields
+ .insert(new_name, ct.into_py(py).as_any().clone());
+ }
+ 6 => {
+ let mut ct = Ctr7 {
+ name: new_name.clone(),
+ full_name: ctr_name.to_string(),
+ fields: IndexMap::new(),
+ };
+ for c in ctr_fields {
+ ct.fields.insert(c.nam.to_string(), None);
+ }
+ seventh = Some(ct.clone());
+ all_ctrs
+ .fields
+ .insert(new_name, ct.into_py(py).as_any().clone());
+ }
+
+ 7 => {
+ let mut ct = Ctr8 {
+ name: new_name.clone(),
+ full_name: ctr_name.to_string(),
+ fields: IndexMap::new(),
+ };
+ for c in ctr_fields {
+ ct.fields.insert(c.nam.to_string(), None);
+ }
+ eighth = Some(ct.clone());
+ all_ctrs
+ .fields
+ .insert(new_name, ct.into_py(py).as_any().clone());
+ }
_ => panic!("Type must have up to 5 Ctrs"),
});
}
@@ -510,6 +566,9 @@ impl Book {
all_ctrs.third = third;
all_ctrs.fourth = fourth;
all_ctrs.fifth = fifth;
+ all_ctrs.sixth = sixth;
+ all_ctrs.seventh = seventh;
+ all_ctrs.eighth = eighth;
adts.adts.insert(adt_name.to_string(), all_ctrs);
}
@@ -541,22 +600,25 @@ impl Book {
#[pymethods]
impl Book {
- fn __getattr__(&self, object: Bound) -> PyResult {
- let binding = object.to_string();
- let field = binding.as_str();
- let py = object.py();
+ #[getter]
+ fn adts(&self) -> PyResult {
+ Python::with_gil(|py| {
+ let adt = &self.adts;
+ Ok(adt.clone().into_py(py))
+ })
+ }
- match field {
- "adts" => {
- let adt = &self.adts;
- Ok(adt.clone().into_py(py))
- }
- "defs" => {
- let def = &self.defs;
- Ok(def.clone().into_py(py))
- }
+ #[getter]
+ fn defs(&self) -> PyResult {
+ Python::with_gil(|py| {
+ let defs = &self.defs;
+ Ok(defs.clone().into_py(py))
+ })
+ }
- _ => new_err(format!("Could not find attribute {}", object)),
- }
+ fn __getattr__(&self, attr_name: Bound) -> PyResult {
+ let attr_name = attr_name.to_string();
+
+ new_err(format!("Could not find attribute {}", attr_name))
}
}
diff --git a/crates/benda/src/types/mod.rs b/crates/benda/src/types/mod.rs
index eeb4be0..e38dce8 100644
--- a/crates/benda/src/types/mod.rs
+++ b/crates/benda/src/types/mod.rs
@@ -18,6 +18,9 @@ pub mod tree;
pub mod u24;
pub mod user_adt;
+/**
+ * TODO: document
+ */
pub trait BendType {
fn to_bend(&self) -> ToBendResult;
}
diff --git a/crates/benda/src/types/user_adt.rs b/crates/benda/src/types/user_adt.rs
index 5a4fd14..5243537 100644
--- a/crates/benda/src/types/user_adt.rs
+++ b/crates/benda/src/types/user_adt.rs
@@ -93,6 +93,9 @@ pub fn from_term_into_adt(term: &BTerm, def_adts: &Ctrs) -> Option {
2 => Some(Box::new(def_adts.third.clone().unwrap())),
3 => Some(Box::new(def_adts.fourth.clone().unwrap())),
4 => Some(Box::new(def_adts.fifth.clone().unwrap())),
+ 5 => Some(Box::new(def_adts.sixth.clone().unwrap())),
+ 6 => Some(Box::new(def_adts.seventh.clone().unwrap())),
+ 7 => Some(Box::new(def_adts.eighth.clone().unwrap())),
_ => panic!("ADT has more than 5 Ctrs"),
};
@@ -143,6 +146,9 @@ pub fn from_term_into_adt(term: &BTerm, def_adts: &Ctrs) -> Option {
}
}
+/**
+ * TODO: document
+ */
#[derive(Debug, Clone)]
pub struct UserAdt<'py> {
adt: BAdt,
@@ -162,7 +168,7 @@ impl<'py> UserAdt<'py> {
// return None;
//}
- if let Ok(binding) = data.getattr("type") {
+ if let Ok(binding) = data.getattr("__ctr_type__") {
for (nam, _ctr) in &book.ctrs {
let new_nam = nam.to_string();
let two_names = new_nam.split_once('/').unwrap();
diff --git a/examples/future/old_readme.py b/examples/future/old_readme.py
new file mode 100644
index 0000000..d5db25e
--- /dev/null
+++ b/examples/future/old_readme.py
@@ -0,0 +1,34 @@
+from dataclasses import dataclass
+from benda import bjit, u24
+
+@dataclass
+class Leaf:
+ value: u24 # native HVM machine integer
+
+@dataclass
+class Node:
+ left: 'Tree'
+ right: 'Tree'
+
+Tree = Node | Leaf
+
+# The `bjit` decorator will introspect and translate the function to HVM/Bend
+# code, replacing it with a wrapper that converts the Python-level types of the
+# inputs and result value, Numba-style.
+
+@bjit
+def sum_tree(tree: Tree) -> u24:
+ match tree:
+ case Leaf(value=value):
+ return value
+ case Node(left=left, right=right):
+ return sum_tree(left) + sum_tree(right)
+ case _:
+ raise TypeError("Invalid type for tree")
+
+# Alternatively, you can opt to use Python big integers and other primitives,
+# they will be translated to the equivalent representations automatically.
+
+@dataclass
+class Leaf2:
+ value: int