Skip to content

Commit

Permalink
wasm support
Browse files Browse the repository at this point in the history
  • Loading branch information
brassy-endomorph committed May 18, 2021
1 parent b035164 commit 3b6e04c
Show file tree
Hide file tree
Showing 8 changed files with 66 additions and 8 deletions.
10 changes: 7 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,29 +18,33 @@ readme = "README.md"
name = "ketos"
path = "src/bin/repl.rs"
doc = false
required-features = ["repl"]

[lib]
name = "ketos"
path = "src/ketos/lib.rs"

[dependencies]
byteorder = "1.3"
dirs = "2.0"
gumdrop = "0.7"
ketos_derive = { version = "0.12", path = "ketos_derive", optional = true }
linefeed = "0.6"
num = "0.2"
rand = "0.7"
serde = { version = "1.0", optional = true }
# Used only in `tests/value_derive.rs`
serde_derive = { version = "1.0", optional = true }

[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
dirs = { version = "2.0", optional = true }
linefeed = { version = "0.6", optional = true }

[dev-dependencies]
assert_matches = "1.0"
ketos_derive = { version = "0.12", path = "ketos_derive" }

[features]
default = []
default = ["repl"]
derive = ["ketos_derive"]
repl = ["dirs", "linefeed"]

[workspace]
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,16 @@ Build optimized executable:
`ketos` can be run as an interpreter to execute Ketos code files (`.ket`)
or run as an interactive read-eval-print loop.

## WebAssembly

This library supports compiling to WebAssembly, but some internal APIs are only
available on the nightly compiler. Include `ketos` in your `Cargo.toml` like so:

```toml
[dependencies]
ketos = { version = "x.y.z", default-features = false }
```

## License

Ketos is distributed under the terms of both the MIT license and the
Expand Down
24 changes: 22 additions & 2 deletions src/ketos/interpreter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
use std::cell::RefCell;
use std::fs::File;
use std::io::{stderr, Read, Write};
use std::path::{Path, PathBuf};
use std::path::Path;
#[cfg(not(target_arch = "wasm32"))]
use std::path::PathBuf;
use std::rc::Rc;

use crate::bytecode::Code;
Expand All @@ -12,7 +14,9 @@ use crate::error::Error;
use crate::exec::{call_function, execute, Context, ExecError};
use crate::io::{GlobalIo, IoError, IoMode};
use crate::lexer::{CodeMap, Lexer};
use crate::module::{BuiltinModuleLoader, FileModuleLoader, ModuleLoader, ModuleRegistry};
use crate::module::{BuiltinModuleLoader, ModuleLoader, ModuleRegistry};
#[cfg(not(target_arch = "wasm32"))]
use crate::module::{FileModuleLoader};
use crate::name::{debug_names, display_names, NameStore};
use crate::parser::{ParseError, Parser};
use crate::restrict::RestrictConfig;
Expand Down Expand Up @@ -48,6 +52,7 @@ pub struct Builder {
io: Option<Rc<GlobalIo>>,
struct_defs: Option<Rc<RefCell<StructDefMap>>>,
module_loader: Option<Box<dyn ModuleLoader>>,
#[cfg(not(target_arch = "wasm32"))]
search_paths: Option<Vec<PathBuf>>,
}

Expand All @@ -70,6 +75,7 @@ impl Builder {
io: None,
struct_defs: None,
module_loader: None,
#[cfg(not(target_arch = "wasm32"))]
search_paths: None,
}
}
Expand All @@ -90,6 +96,7 @@ impl Builder {
exclude!(self.restrict, "context", "restrict");
exclude!(self.io, "context", "io");
exclude!(self.module_loader, "context", "module_loader");
#[cfg(not(target_arch = "wasm32"))]
exclude!(self.search_paths, "context", "search_paths");

self.context = Some(ctx);
Expand All @@ -110,6 +117,7 @@ impl Builder {
exclude!(self.context, "scope", "context");
exclude!(self.io, "scope", "io");
exclude!(self.module_loader, "scope", "module_loader");
#[cfg(not(target_arch = "wasm32"))]
exclude!(self.search_paths, "scope", "search_paths");

self.scope = Some(scope);
Expand Down Expand Up @@ -138,13 +146,15 @@ impl Builder {
pub fn module_loader(mut self, loader: Box<dyn ModuleLoader>) -> Self {
exclude!(self.context, "module_loader", "context");
exclude!(self.scope, "module_loader", "scope");
#[cfg(not(target_arch = "wasm32"))]
exclude!(self.search_paths, "module_loader", "search_paths");

self.module_loader = Some(loader);
self
}

/// Sets the search paths for a new `FileModuleLoader`.
#[cfg(not(target_arch = "wasm32"))]
pub fn search_paths(mut self, paths: Vec<PathBuf>) -> Self {
exclude!(self.context, "search_paths", "context");
exclude!(self.scope, "search_paths", "scope");
Expand Down Expand Up @@ -186,6 +196,7 @@ impl Builder {
Rc::new(GlobalScope::new(name, names, codemap, modules, io, defs))
}

#[cfg(not(target_arch = "wasm32"))]
fn build_loader(&mut self) -> Box<dyn ModuleLoader> {
match (self.module_loader.take(), self.search_paths.take()) {
(Some(loader), _) => loader,
Expand All @@ -195,6 +206,14 @@ impl Builder {
(None, None) => Box::new(BuiltinModuleLoader)
}
}

#[cfg(target_arch = "wasm32")]
fn build_loader(&mut self) -> Box<dyn ModuleLoader> {
match self.module_loader.take() {
Some(loader) => loader,
None => Box::new(BuiltinModuleLoader)
}
}
}

/// Provides a context in which to compile and execute code.
Expand Down Expand Up @@ -250,6 +269,7 @@ impl Interpreter {

/// Creates a new `Interpreter` that searches for module files in a given
/// series of directories.
#[cfg(not(target_arch = "wasm32"))]
pub fn with_search_paths(paths: Vec<PathBuf>) -> Interpreter {
Interpreter::with_loader(Box::new(
BuiltinModuleLoader.chain(FileModuleLoader::with_search_paths(paths))))
Expand Down
4 changes: 3 additions & 1 deletion src/ketos/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,9 @@ pub use crate::function::Arity;
pub use crate::interpreter::{Builder, Interpreter};
pub use crate::integer::{Integer, Ratio};
pub use crate::io::{File, GlobalIo, IoError, SharedWrite};
pub use crate::module::{BuiltinModuleLoader, FileModuleLoader, Module, ModuleBuilder, ModuleLoader};
pub use crate::module::{BuiltinModuleLoader, Module, ModuleBuilder, ModuleLoader};
#[cfg(not(target_arch = "wasm32"))]
pub use crate::module::{FileModuleLoader};
pub use crate::name::{Name, NameStore};
pub use crate::parser::{ParseError, ParseErrorKind};
pub use crate::restrict::{RestrictConfig, RestrictError};
Expand Down
20 changes: 19 additions & 1 deletion src/ketos/module.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,29 @@
//! Implements loading named values from code modules.

use std::cell::RefCell;
#[cfg(not(target_arch = "wasm32"))]
use std::fs::{File, Metadata};
#[cfg(not(target_arch = "wasm32"))]
use std::io::{stderr, Read, Write};
#[cfg(not(target_arch = "wasm32"))]
use std::path::{Path, PathBuf};
use std::rc::Rc;

use crate::bytecode::Code;
use crate::compile::{compile, CompileError};
use crate::compile::CompileError;
#[cfg(not(target_arch = "wasm32"))]
use crate::compile::compile;
#[cfg(not(target_arch = "wasm32"))]
use crate::encode::{DecodeError, read_bytecode_file, write_bytecode_file};
use crate::error::Error;
use crate::exec::{Context, execute};
use crate::function::{Arity, Function, FunctionImpl, Lambda, SystemFn};
#[cfg(not(target_arch = "wasm32"))]
use crate::io::{IoError, IoMode};
#[cfg(not(target_arch = "wasm32"))]
use crate::lexer::Lexer;
use crate::name::{Name, NameMap, NameSetSlice};
#[cfg(not(target_arch = "wasm32"))]
use crate::parser::Parser;
use crate::scope::{GlobalScope, ImportSet, Scope};
use crate::value::Value;
Expand Down Expand Up @@ -341,6 +350,7 @@ fn load_builtin_module(name: Name, scope: &Scope) -> Result<Module, Error> {
}

/// Loads modules from source files and compiled bytecode files.
#[cfg(not(target_arch = "wasm32"))]
pub struct FileModuleLoader {
/// Tracks import chains to prevent infinite recursion
chain: RefCell<Vec<PathBuf>>,
Expand All @@ -358,6 +368,7 @@ pub const FILE_EXTENSION: &str = "ket";
/// File extension for `ketos` compiled bytecode files.
pub const COMPILED_FILE_EXTENSION: &str = "ketc";

#[cfg(not(target_arch = "wasm32"))]
impl FileModuleLoader {
/// Creates a new `FileModuleLoader` that will search the current
/// directory for modules.
Expand Down Expand Up @@ -407,6 +418,7 @@ impl FileModuleLoader {
}
}

#[cfg(not(target_arch = "wasm32"))]
impl ModuleLoader for FileModuleLoader {
fn load_module(&self, name: Name, ctx: Context) -> Result<Module, Error> {
let (src_fname, code_fname) = ctx.scope().with_name(name, |name_str| {
Expand Down Expand Up @@ -474,12 +486,14 @@ impl ModuleLoader for FileModuleLoader {
}

#[derive(Copy, Clone)]
#[cfg(not(target_arch = "wasm32"))]
enum ModuleFileResult {
NotFound,
UseCode,
UseSource,
}

#[cfg(not(target_arch = "wasm32"))]
fn find_module_file(src_path: &Path, code_path: &Path) -> Result<ModuleFileResult, Error> {
match (code_path.exists(), src_path.exists()) {
(true, true) if is_younger(code_path, src_path)? =>
Expand All @@ -490,6 +504,7 @@ fn find_module_file(src_path: &Path, code_path: &Path) -> Result<ModuleFileResul
}
}

#[cfg(not(target_arch = "wasm32"))]
fn find_source_file(src_path: &Path) -> ModuleFileResult {
if src_path.exists() {
ModuleFileResult::UseSource
Expand All @@ -498,6 +513,7 @@ fn find_source_file(src_path: &Path) -> ModuleFileResult {
}
}

#[cfg(not(target_arch = "wasm32"))]
fn is_younger(a: &Path, b: &Path) -> Result<bool, Error> {
let ma = a.metadata()
.map_err(|e| IoError::new(IoMode::Stat, a, e))?;
Expand All @@ -519,6 +535,7 @@ fn is_younger_impl(ma: &Metadata, mb: &Metadata) -> bool {
ma.last_write_time() > mb.last_write_time()
}

#[cfg(not(target_arch = "wasm32"))]
fn load_module_from_file(ctx: Context, name: Name,
src_path: &Path, code_path: Option<&Path>) -> Result<Module, Error> {
let mut file = File::open(src_path)
Expand Down Expand Up @@ -611,6 +628,7 @@ fn process_imports(ctx: &Context, imports: &[ImportSet]) -> Result<(), Error> {
Ok(())
}

#[cfg(not(target_arch = "wasm32"))]
fn check_exports(scope: &Scope, mod_name: Name) -> Result<(), CompileError> {
scope.with_exports(|exports| {
for name in exports {
Expand Down
2 changes: 2 additions & 0 deletions tests/encode.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![cfg(not(target_arch = "wasm32"))]

#[macro_use] extern crate assert_matches;
extern crate ketos;

Expand Down
2 changes: 2 additions & 0 deletions tests/run.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![cfg(not(target_arch = "wasm32"))]

extern crate ketos;

use ketos::{
Expand Down
2 changes: 1 addition & 1 deletion tests/value_encode.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#![cfg(all(feature = "serde", feature = "serde_derive"))]
#![cfg(all(feature = "serde", feature = "serde_derive", not(target_arch = "wasm32")))]

extern crate ketos;
extern crate serde;
Expand Down

0 comments on commit 3b6e04c

Please sign in to comment.