Skip to content

Commit

Permalink
Merge up
Browse files Browse the repository at this point in the history
  • Loading branch information
prozacchiwawa committed Sep 14, 2023
2 parents d4f039f + 308a98f commit 5d23e94
Show file tree
Hide file tree
Showing 26 changed files with 7,731 additions and 103 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/npm-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ jobs:
- name: Test wasm
run: node wasm/tests/index.js

- name: Test clvm-js like wasm interface
run: |
cd wasm/tests/clvm-tools-interface && npm install && yarn test
- name: Upload npm pkg artifacts
uses: actions/upload-artifact@v3
with:
Expand Down
3 changes: 3 additions & 0 deletions src/classic/clvm_tools/clvmc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ pub fn compile_clvm_text_maybe_opt(
let dialect = detect_modern(allocator, assembled_sexp);
// Now the stepping is optional (None for classic) but we may communicate
// other information in dialect as well.
//
// I think stepping is a good name for the number below as dialect is going
// to get more members that are somewhat independent.
if let Some(stepping) = dialect.stepping {
let runner = Rc::new(DefaultProgramRunner::new());
let opts = opts
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -479,7 +479,6 @@ fn compile_call(
),
));

let runner = context.runner();
let compile_atom_head = |al: Srcloc, an: &Vec<u8>| {
let tl = call.args.iter().skip(1).cloned().collect();
get_callable(
Expand Down Expand Up @@ -585,6 +584,7 @@ fn compile_call(
);

let mut unused_symbol_table = HashMap::new();
let runner = context.runner();
updated_opts
.compile_program(
context.allocator(),
Expand Down
49 changes: 49 additions & 0 deletions src/compiler/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,28 @@ pub struct BasicCompileContext {
}

impl BasicCompileContext {
/// Get a mutable allocator reference from this compile context. The
/// allocator is used any time we need to execute pure CLVM operators, such
/// as when evaluating macros or constant folding any chialisp expression.
fn allocator(&mut self) -> &mut Allocator {
&mut self.allocator
}

/// Get the runner this compile context carries. This is used with the
/// allocator above to execute pure CLVM when needed either on behalf of a
/// macro or constant folding.
fn runner(&self) -> Rc<dyn TRunProgram> {
self.runner.clone()
}

/// Get the mutable symbol store this compile context carries. During
/// compilation, the compiler records the relationships between objects in
/// the source code and emitted CLVM expressions, along with other useful
/// information.
///
/// There are times when we're in a subcompile (such as mod expressions when
/// the compile context needs to do swap in or out symbols or transform them
/// on behalf of the child.
fn symbols(&mut self) -> &mut HashMap<String, String> {
&mut self.symbols
}
Expand Down Expand Up @@ -171,6 +187,10 @@ impl BasicCompileContext {
)
}

/// Given allocator, runner and symbols, move the mutable objects into this
/// BasicCompileContext so it can own them and pass a single mutable
/// reference to itself down the stack. This allows these objects to be
/// queried and used by appropriate machinery.
pub fn new(
allocator: Allocator,
runner: Rc<dyn TRunProgram>,
Expand All @@ -186,13 +206,35 @@ impl BasicCompileContext {
}
}

/// A wrapper that owns a BasicCompileContext and remembers a mutable reference
/// to an allocator and symbols. It is used as a container to swap out these
/// objects for new ones used in an inner compile context. This is used when
/// a subcompile occurs such as when a macro is compiled to CLVM to be executed
/// or an inner mod is compiled.
pub struct CompileContextWrapper<'a> {
pub allocator: &'a mut Allocator,
pub symbols: &'a mut HashMap<String, String>,
pub context: BasicCompileContext,
}

impl<'a> CompileContextWrapper<'a> {
/// Given an allocator, runner and symbols, hold the mutable references from
/// the code above, swapping content into a new BasicCompileContext this
/// object contains.
///
/// The new and drop methods both rely on the object's private 'switch' method
/// which swaps the mutable reference to allocator and symbols that the caller
/// holds with the new empty allocator and hashmap held by the inner
/// BasicCompileContext. This allows us to pin the mutable references here,
/// ensuring that this object is the only consumer of these objects when in
/// use, while allowing a new BasicCompileContext to be passed down. The user
/// may inspect, copy, modify etc the inner context before allowing the
/// CompileContextWrapper object to be dropped, which will put the modified
/// objects back in the mutable references given by the user.
///
/// This object does more in the current (nightly) code, such as carrying the
/// optimizer, which is modified when an inner compile has a different sigil
/// and must be optimized differently.
pub fn new(
allocator: &'a mut Allocator,
runner: Rc<dyn TRunProgram>,
Expand All @@ -214,12 +256,19 @@ impl<'a> CompileContextWrapper<'a> {
wrapper
}

/// Swap allocator and symbols with the ones in self.context. This has the
/// effect of making the inner context hold the same information that would
/// have been passed down in these members had it come from the caller's
/// perspective. Useful when compile context has more fields and needs
/// to change for a consumer down the stack.
fn switch(&mut self) {
swap(self.allocator, &mut self.context.allocator);
swap(self.symbols, &mut self.context.symbols);
}
}

/// Drop CompileContextWrapper reverts the contained objects back to the ones
/// owned by the caller.
impl<'a> Drop for CompileContextWrapper<'a> {
fn drop(&mut self) {
self.switch();
Expand Down
2 changes: 2 additions & 0 deletions wasm/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions wasm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,5 @@ clvmr = { version = "0.3.0", features = ["pre-eval"] }
wasm-bindgen = "=0.2.83"
wasm-bindgen-test = "=0.3.25"
js-sys = "0.3.60"
num-bigint = "0.4.0"
num-traits = "0.2.15"
115 changes: 115 additions & 0 deletions wasm/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,118 @@ Prerequisite:
# Make sure you're at <clvm_tools_rs root>/wasm
node ./tests/index.js
```

Program
===

Program is exported by ```clvm_tools_rs``` and contains a ```to``` function
among a few others. Its use is very like Program.to in the python code and
similar to chiaminejp's ```clvm_tools``` library. It produces a value that
can be used together with other such values, can be curried (and uncurried)
converted to the hex representation and run.

```Program.to(javascript_value)```

Converts javascript values to SExp objects which can be used together and run
as CLVM programs or used as program arguments. This conversion follows simple
conventions that were established in ```clvm_tools```.

- There's a tuple object returned by the ```t``` function (2 arguments) which
produces a cons.

- javascript arrays are treated as linear proper lists. Each element appears
as the first of a cons with the rest of the converted list as its tail. The
list is terminated by a nil.

- an object which has a serialize method treats the result of ```o.serialize()```
as an array-like object which specifies the byte values of the object's atom
representation. This covers bls primitives such as G1Element and G2Element.

- javascript numbers, bignums, strings and bools are treated as atoms.

- javascript objects which contain an array-like ```pair``` member are treated
the same as tuple objects above.

```Program.from_hex(hex_str)```

Converts a string of pairs of hex digits into the CLVM deserialized form of the
object.

```Program.null()```

Returns a null object.

The returned objects have these methods:

SExp methods
===

```SExp.toString()```

Convert the object to its hex representation.

```SExp.as_pair()```

If it is a cons, return a tuple-compatible object containing a ```pair``` array
with 2 elements, otherwise null.

```SExp.listp()```

Return true if the object is a cons.

```SExp.nullp()```

Return true if the object is falsey.

```SExp.as_int()```

Returns a javascript number that fits within the 32-bit integers representing the object's atom value, or throw.

```SExp.as_bigint()```

Returns a javascript big number representing the value of the given atom or throw.

```SExp.first()```

If the object is a cons, return its first or left component, or throw if not.

```SExp.rest()```

If the object is a cons, return its rest or right component, or throw if not.

```SExp.cons(other)```

Creates an SExp which is a cons of this sexp and other.

```SExp.run(env)```

Runs the indicated SExp as a program with the given environment.

```SExp.as_bin()```

Serialize the object into an array of byte values.

```SExp.list_len()```

Give the number of conses one needs to traverse until reaching a non-cons rest.
For a proper list, this gives the list's length.

```SExp.as_javascript()```

Return a javascript value that allows the given SExp to be inspected via
javascript.

```SExp.curry(a, b, c ...)```

Given a number of positional arguments, build a curried application that provides
values for the left arguments of some runnable CLVM code, giving code that can
be correctly called with fewer arguments. This is common for providing values to
the upper case parameters of chialisp programs, such as coin puzzles.

```SExp.uncurry(program) -> [inner_program, [args...]]```
```SExp.uncurry_error(program)```

Uncurry returns an array with the inner program and the retrievable arguments
separated out, or the original program and null. uncurry_error throws instead
of returning a value if the object wasn't a curried program.

Loading

0 comments on commit 5d23e94

Please sign in to comment.