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

Add print & REPL functionality to CLI #267

Merged
merged 6 commits into from
Mar 8, 2020
Merged
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
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,20 @@ see [CHANGELOG](./CHANGELOG.md)
- Run with `cargo run -- test.js` where `test.js` is an existing JS file
- If any JS doesn't work then it's a bug. Please raise an issue!

## Command-line Options

```
USAGE:
boa_cli [FILE]...

FLAGS:
-h, --help Prints help information
-V, --version Prints version information

ARGS:
<FILE>... The JavaScript file(s) to be evaluated
```

## Communication

Feel free to contact us on Discord https://discord.gg/tUFFk9Y
Expand Down
14 changes: 12 additions & 2 deletions boa/src/realm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
//!A realm is represented in this implementation as a Realm struct with the fields specified from the spec
use crate::{
builtins::{
array, boolean, console, function, json, math, number, object, regexp, string, symbol,
value::{Value, ValueData},
array, boolean, console, function,
function::NativeFunctionData,
json, math, number, object, regexp, string, symbol,
value::{ToValue, Value, ValueData},
},
environment::{
declarative_environment_record::DeclarativeEnvironmentRecord,
Expand Down Expand Up @@ -65,6 +67,14 @@ impl Realm {
global.set_field_slice("Symbol", symbol::create_constructor(global));
global.set_field_slice("console", console::create_constructor(global));
}

/// Utility to add a function to the global object
pub fn register_global_func(self, func_name: &str, func: NativeFunctionData) -> Self {
self.global_obj
.set_field(func_name.to_value(), func.to_value());

self
}
}

// Similar to new_global_environment in lexical_environment, except we need to return a GlobalEnvirionment
Expand Down
29 changes: 20 additions & 9 deletions boa_cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,27 @@
#![warn(clippy::perf)]
#![allow(clippy::cognitive_complexity)]

use boa::builtins::console::log;
use boa::{exec::Executor, forward_val, realm::Realm};
use std::io;
use std::{fs::read_to_string, path::PathBuf};
use structopt::StructOpt;

/// CLI configuration for Boa.
#[derive(Debug, StructOpt)]
#[structopt(author, about)]
struct Opt {
/// The javascript file to be evaluated.
#[structopt(name = "FILE", parse(from_os_str), default_value = "tests/js/test.js")]
/// The JavaScript file(s) to be evaluated.
#[structopt(name = "FILE", parse(from_os_str))]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So if i understand it by default boa_cli will now be a shell, and we need to pass the file if we want to keep the same functionality as before?
Im personally OK with that as it matches up with Node etc

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup! It should be functionally equivalent. I just tested running node locally to check and passing no arguments (just calling node) opens an interactive shell.

files: Vec<PathBuf>,
/// Open a boa shell (WIP).
#[structopt(short, long)]
shell: bool,
}

pub fn main() -> Result<(), std::io::Error> {
let args = Opt::from_args();

let realm = Realm::create();
let realm = Realm::create().register_global_func("print", log);

let mut engine = Executor::new(realm);

for file in args.files {
for file in &args.files {
let buffer = read_to_string(file)?;

match forward_val(&mut engine, &buffer) {
Expand All @@ -33,5 +31,18 @@ pub fn main() -> Result<(), std::io::Error> {
}
}

if args.files.is_empty() {
loop {
let mut buffer = String::new();

io::stdin().read_line(&mut buffer)?;

match forward_val(&mut engine, buffer.trim_end()) {
Ok(v) => println!("{}", v.to_string()),
Err(v) => eprintln!("{}", v.to_string()),
}
}
}

Ok(())
}
7 changes: 4 additions & 3 deletions docs/debugging.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
# Debugging

There are multiple ways to debug what Boa is doing. Or maybe you just want to know how it works under the hood. Or even test some javaScript.
There are multiple ways to debug what Boa is doing. Or maybe you just want to know how it works under the hood. Or even test some JavaScript.

The first thing i usually do is add some JS [here](../tests/js/test.js).
This file will be read if no arguments are provided. Then boa will begin to parse and execute that JS.
One way to do so is to create a file in the root of the repository. For example `test.js`. Then execute `cargo run -- test.js` to run the file with boa.

You can also run boa interactively by simply calling `cargo run` without any arguments to start a shell to execute JS.

These are added in order of how the code is read:

Expand Down