feat: works
This commit is contained in:
173
__wasm/wit-bindgen-sample/engine/boa/boa_cli/src/helper.rs
Normal file
173
__wasm/wit-bindgen-sample/engine/boa/boa_cli/src/helper.rs
Normal file
@@ -0,0 +1,173 @@
|
||||
use colored::{Color, Colorize};
|
||||
use phf::{phf_set, Set};
|
||||
use regex::{Captures, Regex};
|
||||
use rustyline::{
|
||||
error::ReadlineError,
|
||||
highlight::Highlighter,
|
||||
validate::{MatchingBracketValidator, ValidationContext, ValidationResult, Validator},
|
||||
};
|
||||
use rustyline_derive::{Completer, Helper, Hinter};
|
||||
use std::borrow::Cow;
|
||||
|
||||
const STRING_COLOR: Color = Color::Green;
|
||||
const KEYWORD_COLOR: Color = Color::Yellow;
|
||||
const PROPERTY_COLOR: Color = Color::Magenta;
|
||||
const OPERATOR_COLOR: Color = Color::TrueColor {
|
||||
r: 214,
|
||||
g: 95,
|
||||
b: 26,
|
||||
};
|
||||
const UNDEFINED_COLOR: Color = Color::TrueColor {
|
||||
r: 100,
|
||||
g: 100,
|
||||
b: 100,
|
||||
};
|
||||
const NUMBER_COLOR: Color = Color::TrueColor {
|
||||
r: 26,
|
||||
g: 214,
|
||||
b: 175,
|
||||
};
|
||||
const IDENTIFIER_COLOR: Color = Color::TrueColor {
|
||||
r: 26,
|
||||
g: 160,
|
||||
b: 214,
|
||||
};
|
||||
|
||||
#[allow(clippy::upper_case_acronyms)]
|
||||
#[derive(Completer, Helper, Hinter)]
|
||||
pub(crate) struct RLHelper {
|
||||
highlighter: LineHighlighter,
|
||||
validator: MatchingBracketValidator,
|
||||
}
|
||||
|
||||
impl RLHelper {
|
||||
#[inline]
|
||||
pub(crate) fn new() -> Self {
|
||||
Self {
|
||||
highlighter: LineHighlighter,
|
||||
validator: MatchingBracketValidator::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Validator for RLHelper {
|
||||
fn validate(
|
||||
&self,
|
||||
context: &mut ValidationContext<'_>,
|
||||
) -> Result<ValidationResult, ReadlineError> {
|
||||
self.validator.validate(context)
|
||||
}
|
||||
|
||||
fn validate_while_typing(&self) -> bool {
|
||||
self.validator.validate_while_typing()
|
||||
}
|
||||
}
|
||||
|
||||
impl Highlighter for RLHelper {
|
||||
fn highlight_hint<'h>(&self, hint: &'h str) -> Cow<'h, str> {
|
||||
hint.into()
|
||||
}
|
||||
|
||||
fn highlight<'l>(&self, line: &'l str, pos: usize) -> Cow<'l, str> {
|
||||
self.highlighter.highlight(line, pos)
|
||||
}
|
||||
|
||||
fn highlight_candidate<'c>(
|
||||
&self,
|
||||
candidate: &'c str,
|
||||
_completion: rustyline::CompletionType,
|
||||
) -> Cow<'c, str> {
|
||||
self.highlighter.highlight(candidate, 0)
|
||||
}
|
||||
|
||||
fn highlight_char(&self, line: &str, _: usize) -> bool {
|
||||
!line.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
static KEYWORDS: Set<&'static str> = phf_set! {
|
||||
"break",
|
||||
"case",
|
||||
"catch",
|
||||
"class",
|
||||
"const",
|
||||
"continue",
|
||||
"default",
|
||||
"delete",
|
||||
"do",
|
||||
"else",
|
||||
"export",
|
||||
"extends",
|
||||
"finally",
|
||||
"for",
|
||||
"function",
|
||||
"if",
|
||||
"import",
|
||||
"instanceof",
|
||||
"new",
|
||||
"return",
|
||||
"super",
|
||||
"switch",
|
||||
"this",
|
||||
"throw",
|
||||
"try",
|
||||
"typeof",
|
||||
"var",
|
||||
"void",
|
||||
"while",
|
||||
"with",
|
||||
"yield",
|
||||
"await",
|
||||
"enum",
|
||||
"let",
|
||||
};
|
||||
|
||||
struct LineHighlighter;
|
||||
|
||||
impl Highlighter for LineHighlighter {
|
||||
fn highlight<'l>(&self, line: &'l str, _: usize) -> Cow<'l, str> {
|
||||
let mut coloured = line.to_string();
|
||||
|
||||
let reg = Regex::new(
|
||||
r#"(?x)
|
||||
(?P<identifier>\b[$_\p{ID_Start}][$_\p{ID_Continue}\u{200C}\u{200D}]*\b) |
|
||||
(?P<string_double_quote>"([^"\\]|\\.)*") |
|
||||
(?P<string_single_quote>'([^'\\]|\\.)*') |
|
||||
(?P<template_literal>`([^`\\]|\\.)*`) |
|
||||
(?P<op>[+\-/*%~^!&|=<>;:]) |
|
||||
(?P<number>0[bB][01](_?[01])*n?|0[oO][0-7](_?[0-7])*n?|0[xX][0-9a-fA-F](_?[0-9a-fA-F])*n?|(([0-9](_?[0-9])*\.([0-9](_?[0-9])*)?)|(([0-9](_?[0-9])*)?\.[0-9](_?[0-9])*)|([0-9](_?[0-9])*))([eE][+-]?[0-9](_?[0-9])*)?n?)"#,
|
||||
)
|
||||
.expect("could not compile regular expression");
|
||||
|
||||
coloured = reg
|
||||
.replace_all(&coloured, |caps: &Captures<'_>| {
|
||||
if let Some(cap) = caps.name("identifier") {
|
||||
match cap.as_str() {
|
||||
"true" | "false" | "null" | "Infinity" | "globalThis" => {
|
||||
cap.as_str().color(PROPERTY_COLOR).to_string()
|
||||
}
|
||||
"undefined" => cap.as_str().color(UNDEFINED_COLOR).to_string(),
|
||||
identifier if KEYWORDS.contains(identifier) => {
|
||||
cap.as_str().color(KEYWORD_COLOR).bold().to_string()
|
||||
}
|
||||
_ => cap.as_str().color(IDENTIFIER_COLOR).to_string(),
|
||||
}
|
||||
} else if let Some(cap) = caps.name("string_double_quote") {
|
||||
cap.as_str().color(STRING_COLOR).to_string()
|
||||
} else if let Some(cap) = caps.name("string_single_quote") {
|
||||
cap.as_str().color(STRING_COLOR).to_string()
|
||||
} else if let Some(cap) = caps.name("template_literal") {
|
||||
cap.as_str().color(STRING_COLOR).to_string()
|
||||
} else if let Some(cap) = caps.name("op") {
|
||||
cap.as_str().color(OPERATOR_COLOR).to_string()
|
||||
} else if let Some(cap) = caps.name("number") {
|
||||
cap.as_str().color(NUMBER_COLOR).to_string()
|
||||
} else {
|
||||
caps[0].to_string()
|
||||
}
|
||||
})
|
||||
.to_string();
|
||||
|
||||
coloured.into()
|
||||
}
|
||||
}
|
||||
264
__wasm/wit-bindgen-sample/engine/boa/boa_cli/src/main.rs
Normal file
264
__wasm/wit-bindgen-sample/engine/boa/boa_cli/src/main.rs
Normal file
@@ -0,0 +1,264 @@
|
||||
#![doc(
|
||||
html_logo_url = "https://raw.githubusercontent.com/boa-dev/boa/main/assets/logo.svg",
|
||||
html_favicon_url = "https://raw.githubusercontent.com/boa-dev/boa/main/assets/logo.svg"
|
||||
)]
|
||||
#![cfg_attr(not(test), deny(clippy::unwrap_used))]
|
||||
#![warn(
|
||||
clippy::perf,
|
||||
clippy::single_match_else,
|
||||
clippy::dbg_macro,
|
||||
clippy::doc_markdown,
|
||||
clippy::wildcard_imports,
|
||||
clippy::struct_excessive_bools,
|
||||
clippy::doc_markdown,
|
||||
clippy::semicolon_if_nothing_returned,
|
||||
clippy::pedantic
|
||||
)]
|
||||
#![deny(
|
||||
clippy::all,
|
||||
clippy::cast_lossless,
|
||||
clippy::redundant_closure_for_method_calls,
|
||||
clippy::use_self,
|
||||
clippy::unnested_or_patterns,
|
||||
clippy::trivially_copy_pass_by_ref,
|
||||
clippy::needless_pass_by_value,
|
||||
clippy::match_wildcard_for_single_variants,
|
||||
clippy::map_unwrap_or,
|
||||
unused_qualifications,
|
||||
unused_import_braces,
|
||||
unused_lifetimes,
|
||||
unreachable_pub,
|
||||
trivial_numeric_casts,
|
||||
// rustdoc,
|
||||
missing_debug_implementations,
|
||||
missing_copy_implementations,
|
||||
deprecated_in_future,
|
||||
meta_variable_misuse,
|
||||
non_ascii_idents,
|
||||
rust_2018_compatibility,
|
||||
rust_2018_idioms,
|
||||
future_incompatible,
|
||||
nonstandard_style,
|
||||
)]
|
||||
#![allow(
|
||||
clippy::module_name_repetitions,
|
||||
clippy::cast_possible_truncation,
|
||||
clippy::cast_sign_loss,
|
||||
clippy::cast_precision_loss,
|
||||
clippy::cast_possible_wrap,
|
||||
clippy::cast_ptr_alignment,
|
||||
clippy::missing_panics_doc,
|
||||
clippy::too_many_lines,
|
||||
clippy::unreadable_literal,
|
||||
clippy::missing_inline_in_public_items,
|
||||
clippy::cognitive_complexity,
|
||||
clippy::must_use_candidate,
|
||||
clippy::missing_errors_doc,
|
||||
clippy::as_conversions,
|
||||
clippy::let_unit_value,
|
||||
rustdoc::missing_doc_code_examples
|
||||
)]
|
||||
|
||||
use boa_engine::{syntax::ast::node::StatementList, Context};
|
||||
use clap::{ArgEnum, Parser};
|
||||
use colored::{Color, Colorize};
|
||||
use rustyline::{config::Config, error::ReadlineError, EditMode, Editor};
|
||||
use std::{fs::read, io, path::PathBuf};
|
||||
mod helper;
|
||||
|
||||
#[cfg(all(target_arch = "x86_64", target_os = "linux", target_env = "gnu"))]
|
||||
#[cfg_attr(
|
||||
all(target_arch = "x86_64", target_os = "linux", target_env = "gnu"),
|
||||
global_allocator
|
||||
)]
|
||||
static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
|
||||
|
||||
/// CLI configuration for Boa.
|
||||
static CLI_HISTORY: &str = ".boa_history";
|
||||
|
||||
const READLINE_COLOR: Color = Color::Cyan;
|
||||
|
||||
// Added #[allow(clippy::option_option)] because to StructOpt an Option<Option<T>>
|
||||
// is an optional argument that optionally takes a value ([--opt=[val]]).
|
||||
// https://docs.rs/structopt/0.3.11/structopt/#type-magic
|
||||
#[allow(clippy::option_option)]
|
||||
#[derive(Debug, Parser)]
|
||||
#[clap(author, about, name = "boa")]
|
||||
struct Opt {
|
||||
/// The JavaScript file(s) to be evaluated.
|
||||
#[clap(name = "FILE", parse(from_os_str))]
|
||||
files: Vec<PathBuf>,
|
||||
|
||||
/// Dump the AST to stdout with the given format.
|
||||
#[clap(long, short = 'a', value_name = "FORMAT", ignore_case = true, arg_enum)]
|
||||
dump_ast: Option<Option<DumpFormat>>,
|
||||
|
||||
/// Dump the AST to stdout with the given format.
|
||||
#[clap(long = "trace", short = 't')]
|
||||
trace: bool,
|
||||
|
||||
/// Use vi mode in the REPL
|
||||
#[clap(long = "vi")]
|
||||
vi_mode: bool,
|
||||
}
|
||||
|
||||
impl Opt {
|
||||
/// Returns whether a dump flag has been used.
|
||||
fn has_dump_flag(&self) -> bool {
|
||||
self.dump_ast.is_some()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, ArgEnum)]
|
||||
enum DumpFormat {
|
||||
/// The different types of format available for dumping.
|
||||
///
|
||||
// NOTE: This can easily support other formats just by
|
||||
// adding a field to this enum and adding the necessary
|
||||
// implementation. Example: Toml, Html, etc.
|
||||
//
|
||||
// NOTE: The fields of this enum are not doc comments because
|
||||
// arg_enum! macro does not support it.
|
||||
|
||||
// This is the default format that you get from std::fmt::Debug.
|
||||
Debug,
|
||||
|
||||
// This is a minified json format.
|
||||
Json,
|
||||
|
||||
// This is a pretty printed json format.
|
||||
JsonPretty,
|
||||
}
|
||||
|
||||
/// Parses the the token stream into an AST and returns it.
|
||||
///
|
||||
/// Returns a error of type String with a message,
|
||||
/// if the token stream has a parsing error.
|
||||
fn parse_tokens<S>(src: S, context: &mut Context) -> Result<StatementList, String>
|
||||
where
|
||||
S: AsRef<[u8]>,
|
||||
{
|
||||
use boa_engine::syntax::parser::Parser;
|
||||
|
||||
let src_bytes = src.as_ref();
|
||||
Parser::new(src_bytes)
|
||||
.parse_all(context)
|
||||
.map_err(|e| format!("ParsingError: {e}"))
|
||||
}
|
||||
|
||||
/// Dumps the AST to stdout with format controlled by the given arguments.
|
||||
///
|
||||
/// Returns a error of type String with a error message,
|
||||
/// if the source has a syntax or parsing error.
|
||||
fn dump<S>(src: S, args: &Opt, context: &mut Context) -> Result<(), String>
|
||||
where
|
||||
S: AsRef<[u8]>,
|
||||
{
|
||||
if let Some(ref arg) = args.dump_ast {
|
||||
let ast = parse_tokens(src, context)?;
|
||||
|
||||
match arg {
|
||||
Some(format) => match format {
|
||||
DumpFormat::Debug => println!("{ast:#?}"),
|
||||
DumpFormat::Json => println!(
|
||||
"{}",
|
||||
serde_json::to_string(&ast).expect("could not convert AST to a JSON string")
|
||||
),
|
||||
DumpFormat::JsonPretty => {
|
||||
println!(
|
||||
"{}",
|
||||
serde_json::to_string_pretty(&ast)
|
||||
.expect("could not convert AST to a pretty JSON string")
|
||||
);
|
||||
}
|
||||
},
|
||||
// Default ast dumping format.
|
||||
None => println!("{ast:#?}"),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn main() -> Result<(), std::io::Error> {
|
||||
let args = Opt::parse();
|
||||
|
||||
let mut context = Context::default();
|
||||
|
||||
// Trace Output
|
||||
context.set_trace(args.trace);
|
||||
|
||||
for file in &args.files {
|
||||
let buffer = read(file)?;
|
||||
|
||||
if args.has_dump_flag() {
|
||||
if let Err(e) = dump(&buffer, &args, &mut context) {
|
||||
eprintln!("{e}");
|
||||
}
|
||||
} else {
|
||||
match context.eval(&buffer) {
|
||||
Ok(v) => println!("{}", v.display()),
|
||||
Err(v) => eprintln!("Uncaught {}", v.display()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if args.files.is_empty() {
|
||||
let config = Config::builder()
|
||||
.keyseq_timeout(1)
|
||||
.edit_mode(if args.vi_mode {
|
||||
EditMode::Vi
|
||||
} else {
|
||||
EditMode::Emacs
|
||||
})
|
||||
.build();
|
||||
|
||||
let mut editor = Editor::with_config(config);
|
||||
editor.load_history(CLI_HISTORY).map_err(|err| match err {
|
||||
ReadlineError::Io(e) => e,
|
||||
e => io::Error::new(io::ErrorKind::Other, e),
|
||||
})?;
|
||||
editor.set_helper(Some(helper::RLHelper::new()));
|
||||
|
||||
let readline = ">> ".color(READLINE_COLOR).bold().to_string();
|
||||
|
||||
loop {
|
||||
match editor.readline(&readline) {
|
||||
Ok(line) if line == ".exit" => break,
|
||||
Err(ReadlineError::Interrupted | ReadlineError::Eof) => break,
|
||||
|
||||
Ok(line) => {
|
||||
editor.add_history_entry(&line);
|
||||
|
||||
if args.has_dump_flag() {
|
||||
if let Err(e) = dump(&line, &args, &mut context) {
|
||||
eprintln!("{e}");
|
||||
}
|
||||
} else {
|
||||
match context.eval(line.trim_end()) {
|
||||
Ok(v) => println!("{}", v.display()),
|
||||
Err(v) => {
|
||||
eprintln!(
|
||||
"{}: {}",
|
||||
"Uncaught".red(),
|
||||
v.display().to_string().red()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Err(err) => {
|
||||
eprintln!("Unknown error: {err:?}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
editor
|
||||
.save_history(CLI_HISTORY)
|
||||
.expect("could not save CLI history");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user