Skip to content
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
12 changes: 8 additions & 4 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 }
gumdrop = "0.7"
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
3 changes: 2 additions & 1 deletion src/ketos/compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,8 @@ impl<'a> Compiler<'a> {
}
}

replace(&mut self.blocks, new_blocks);
// drop the old value
let _ = replace(&mut self.blocks, new_blocks);

for block in &mut self.blocks {
if let Some((_, dest)) = block.jump {
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: 1 addition & 1 deletion src/ketos/structs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ impl<T: StructValue> ForeignStructDef<T> {
fn get_rc(&self, value: Value) -> Rc<T> {
match value {
Value::Foreign(fv) => {
ForeignValue::downcast_rc::<T>(fv)
<dyn ForeignValue>::downcast_rc::<T>(fv)
.expect("invalid foreign value for ForeignStructDef")
}
_ => panic!("invalid value for ForeignStructDef")
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