Browse Source

Fixed a bunch of Clippy issues (#59)

* Fixed a bunch of Clippy issues

* Fixed bug with numbers

* Added clippy checks in the CI
pull/62/head
Iban Eguia 5 years ago committed by Jason Williams
parent
commit
71340e6bec
  1. 3
      .travis.yml
  2. 30
      src/bin/bin.rs
  3. 45
      src/lib/environment/declerative_environment_record.rs
  4. 29
      src/lib/environment/environment_record_trait.rs
  5. 62
      src/lib/environment/function_environment_record.rs
  6. 71
      src/lib/environment/global_environment_record.rs
  7. 27
      src/lib/environment/lexical_environment.rs
  8. 54
      src/lib/environment/object_environment_record.rs
  9. 34
      src/lib/exec.rs
  10. 47
      src/lib/js/array.rs
  11. 17
      src/lib/js/console.rs
  12. 2
      src/lib/js/error.rs
  13. 23
      src/lib/js/function.rs
  14. 4
      src/lib/js/json.rs
  15. 102
      src/lib/js/math.rs
  16. 22
      src/lib/js/object.rs
  17. 116
      src/lib/js/string.rs
  18. 215
      src/lib/js/value.rs
  19. 36
      src/lib/lib.rs
  20. 4
      src/lib/syntax/ast/constant.rs
  21. 32
      src/lib/syntax/ast/expr.rs
  22. 162
      src/lib/syntax/ast/keyword.rs
  23. 10
      src/lib/syntax/ast/pos.rs
  24. 3
      src/lib/syntax/ast/punc.rs
  25. 28
      src/lib/syntax/ast/token.rs
  26. 129
      src/lib/syntax/lexer.rs
  27. 16
      src/lib/syntax/parser.rs
  28. 9
      tests/js/test.js

3
.travis.yml

@ -4,7 +4,8 @@ rust:
- beta - beta
cache: cargo cache: cargo
before_script: before_script:
- rustup component add rustfmt - rustup component add rustfmt clippy
script: script:
- cargo fmt --verbose -- --check - cargo fmt --verbose -- --check
- cargo clippy --verbose
- cargo test --verbose - cargo test --verbose

30
src/bin/bin.rs

@ -1,8 +1,28 @@
extern crate boa; #![forbid(
warnings,
anonymous_parameters,
unused_extern_crates,
unused_import_braces,
missing_copy_implementations,
//trivial_casts,
variant_size_differences,
missing_debug_implementations,
trivial_numeric_casts
)]
// Debug trait derivation will show an error if forbidden.
#![deny(unused_qualifications, unsafe_code)]
#![deny(clippy::all)]
#![warn(clippy::pedantic)]
#![allow(
missing_docs,
clippy::many_single_char_names,
clippy::unreadable_literal,
clippy::excessive_precision,
clippy::module_name_repetitions
)]
use boa::exec; use boa::exec;
use std::env; use std::{env, fs::read_to_string, process::exit};
use std::fs::read_to_string;
use std::process::exit;
fn print_usage() { fn print_usage() {
println!( println!(
@ -34,6 +54,6 @@ pub fn main() -> Result<(), std::io::Error> {
} }
let buffer = read_to_string(read_file)?; let buffer = read_to_string(read_file)?;
dbg!(exec(buffer)); dbg!(exec(&buffer));
Ok(()) Ok(())
} }

45
src/lib/environment/declerative_environment_record.rs

@ -26,14 +26,14 @@ pub struct DeclerativeEnvironmentRecordBinding {
/// A declarative Environment Record binds the set of identifiers defined by the /// A declarative Environment Record binds the set of identifiers defined by the
/// declarations contained within its scope. /// declarations contained within its scope.
#[derive(Trace, Finalize, Clone)] #[derive(Debug, Trace, Finalize, Clone)]
pub struct DeclerativeEnvironmentRecord { pub struct DeclerativeEnvironmentRecord {
pub env_rec: HashMap<String, DeclerativeEnvironmentRecordBinding>, pub env_rec: HashMap<String, DeclerativeEnvironmentRecordBinding>,
pub outer_env: Option<Environment>, pub outer_env: Option<Environment>,
} }
impl EnvironmentRecordTrait for DeclerativeEnvironmentRecord { impl EnvironmentRecordTrait for DeclerativeEnvironmentRecord {
fn has_binding(&self, name: &String) -> bool { fn has_binding(&self, name: &str) -> bool {
self.env_rec.contains_key(name) self.env_rec.contains_key(name)
} }
@ -66,14 +66,13 @@ impl EnvironmentRecordTrait for DeclerativeEnvironmentRecord {
value: None, value: None,
can_delete: true, can_delete: true,
mutable: false, mutable: false,
strict: strict, strict,
}, },
); );
} }
fn initialize_binding(&mut self, name: String, value: Value) { fn initialize_binding(&mut self, name: &str, value: Value) {
match self.env_rec.get_mut(&name) { if let Some(ref mut record) = self.env_rec.get_mut(name) {
Some(ref mut record) => {
match record.value { match record.value {
Some(_) => { Some(_) => {
// TODO: change this when error handling comes into play // TODO: change this when error handling comes into play
@ -82,23 +81,21 @@ impl EnvironmentRecordTrait for DeclerativeEnvironmentRecord {
None => record.value = Some(value), None => record.value = Some(value),
} }
} }
None => {}
}
} }
fn set_mutable_binding(&mut self, name: String, value: Value, mut strict: bool) { fn set_mutable_binding(&mut self, name: &str, value: Value, mut strict: bool) {
if self.env_rec.get(&name).is_none() { if self.env_rec.get(name).is_none() {
if strict == true { if strict {
// TODO: change this when error handling comes into play // TODO: change this when error handling comes into play
panic!("Reference Error: Cannot set mutable binding for {}", name); panic!("Reference Error: Cannot set mutable binding for {}", name);
} }
self.create_mutable_binding(name.clone(), true); self.create_mutable_binding(name.to_owned(), true);
self.initialize_binding(name.clone(), value); self.initialize_binding(name, value);
return; return;
} }
let record: &mut DeclerativeEnvironmentRecordBinding = self.env_rec.get_mut(&name).unwrap(); let record: &mut DeclerativeEnvironmentRecordBinding = self.env_rec.get_mut(name).unwrap();
if record.strict { if record.strict {
strict = true strict = true
} }
@ -109,17 +106,15 @@ impl EnvironmentRecordTrait for DeclerativeEnvironmentRecord {
if record.mutable { if record.mutable {
record.value = Some(value); record.value = Some(value);
} else { } else if strict {
if strict {
// TODO: change this when error handling comes into play // TODO: change this when error handling comes into play
panic!("TypeError: Cannot mutate an immutable binding {}", name); panic!("TypeError: Cannot mutate an immutable binding {}", name);
} }
} }
}
fn get_binding_value(&self, name: String, _strict: bool) -> Value { fn get_binding_value(&self, name: &str, _strict: bool) -> Value {
if self.env_rec.get(&name).is_some() && self.env_rec.get(&name).unwrap().value.is_some() { if self.env_rec.get(name).is_some() && self.env_rec.get(name).unwrap().value.is_some() {
let record: &DeclerativeEnvironmentRecordBinding = self.env_rec.get(&name).unwrap(); let record: &DeclerativeEnvironmentRecordBinding = self.env_rec.get(name).unwrap();
record.value.as_ref().unwrap().clone() record.value.as_ref().unwrap().clone()
} else { } else {
// TODO: change this when error handling comes into play // TODO: change this when error handling comes into play
@ -127,10 +122,10 @@ impl EnvironmentRecordTrait for DeclerativeEnvironmentRecord {
} }
} }
fn delete_binding(&mut self, name: String) -> bool { fn delete_binding(&mut self, name: &str) -> bool {
if self.env_rec.get(&name).is_some() { if self.env_rec.get(name).is_some() {
if self.env_rec.get(&name).unwrap().can_delete { if self.env_rec.get(name).unwrap().can_delete {
self.env_rec.remove(&name); self.env_rec.remove(name);
true true
} else { } else {
false false
@ -161,7 +156,7 @@ impl EnvironmentRecordTrait for DeclerativeEnvironmentRecord {
} }
fn get_environment_type(&self) -> EnvironmentType { fn get_environment_type(&self) -> EnvironmentType {
return EnvironmentType::Declerative; EnvironmentType::Declerative
} }
fn get_global_object(&self) -> Option<Value> { fn get_global_object(&self) -> Option<Value> {

29
src/lib/environment/environment_record_trait.rs

@ -1,24 +1,27 @@
//! # Environment Records //! # Environment Records
//! //!
//! https://tc39.github.io/ecma262/#sec-environment-records //! <https://tc39.github.io/ecma262/#sec-environment-records>
//! https://tc39.github.io/ecma262/#sec-lexical-environments //! <https://tc39.github.io/ecma262/#sec-lexical-environments>
//! //!
//! Some environments are stored as JSObjects. This is for GC, i.e we want to keep an environment if a variable is closed-over (a closure is returned). //! Some environments are stored as `JSObjects`. This is for GC, i.e we want to keep an environment if a variable is closed-over (a closure is returned).
//! All of the logic to handle scope/environment records are stored in here. //! All of the logic to handle scope/environment records are stored in here.
//! //!
//! There are 5 Environment record kinds. They all have methods in common, these are implemented as a the `EnvironmentRecordTrait` //! There are 5 Environment record kinds. They all have methods in common, these are implemented as a the `EnvironmentRecordTrait`
//! //!
use crate::environment::lexical_environment::{Environment, EnvironmentType}; use crate::{
use crate::js::value::Value; environment::lexical_environment::{Environment, EnvironmentType},
js::value::Value,
};
use gc::{Finalize, Trace}; use gc::{Finalize, Trace};
use std::fmt::Debug;
/// https://tc39.github.io/ecma262/#sec-environment-records /// <https://tc39.github.io/ecma262/#sec-environment-records>
/// ///
/// In the ECMAScript specification Environment Records are hierachical and have a base class with abstract methods. /// In the ECMAScript specification Environment Records are hierachical and have a base class with abstract methods.
/// In this implementation we have a trait which represents the behaviour of all EnvironmentRecord types. /// In this implementation we have a trait which represents the behaviour of all `EnvironmentRecord` types.
pub trait EnvironmentRecordTrait: Trace + Finalize { pub trait EnvironmentRecordTrait: Debug + Trace + Finalize {
/// Determine if an Environment Record has a binding for the String value N. Return true if it does and false if it does not. /// Determine if an Environment Record has a binding for the String value N. Return true if it does and false if it does not.
fn has_binding(&self, name: &String) -> bool; fn has_binding(&self, name: &str) -> bool;
/// Create a new but uninitialized mutable binding in an Environment Record. The String value N is the text of the bound name. /// Create a new but uninitialized mutable binding in an Environment Record. The String value N is the text of the bound name.
/// If the Boolean argument deletion is true the binding may be subsequently deleted. /// If the Boolean argument deletion is true the binding may be subsequently deleted.
@ -33,25 +36,25 @@ pub trait EnvironmentRecordTrait: Trace + Finalize {
/// Set the value of an already existing but uninitialized binding in an Environment Record. /// Set the value of an already existing but uninitialized binding in an Environment Record.
/// The String value N is the text of the bound name. /// The String value N is the text of the bound name.
/// V is the value for the binding and is a value of any ECMAScript language type. /// V is the value for the binding and is a value of any ECMAScript language type.
fn initialize_binding(&mut self, name: String, value: Value); fn initialize_binding(&mut self, name: &str, value: Value);
/// Set the value of an already existing mutable binding in an Environment Record. /// Set the value of an already existing mutable binding in an Environment Record.
/// The String value `name` is the text of the bound name. /// The String value `name` is the text of the bound name.
/// value is the `value` for the binding and may be a value of any ECMAScript language type. S is a Boolean flag. /// value is the `value` for the binding and may be a value of any ECMAScript language type. S is a Boolean flag.
/// If `strict` is true and the binding cannot be set throw a TypeError exception. /// If `strict` is true and the binding cannot be set throw a TypeError exception.
fn set_mutable_binding(&mut self, name: String, value: Value, strict: bool); fn set_mutable_binding(&mut self, name: &str, value: Value, strict: bool);
/// Returns the value of an already existing binding from an Environment Record. /// Returns the value of an already existing binding from an Environment Record.
/// The String value N is the text of the bound name. /// The String value N is the text of the bound name.
/// S is used to identify references originating in strict mode code or that /// S is used to identify references originating in strict mode code or that
/// otherwise require strict mode reference semantics. /// otherwise require strict mode reference semantics.
fn get_binding_value(&self, name: String, strict: bool) -> Value; fn get_binding_value(&self, name: &str, strict: bool) -> Value;
/// Delete a binding from an Environment Record. /// Delete a binding from an Environment Record.
/// The String value name is the text of the bound name. /// The String value name is the text of the bound name.
/// If a binding for name exists, remove the binding and return true. /// If a binding for name exists, remove the binding and return true.
/// If the binding exists but cannot be removed return false. If the binding does not exist return true. /// If the binding exists but cannot be removed return false. If the binding does not exist return true.
fn delete_binding(&mut self, name: String) -> bool; fn delete_binding(&mut self, name: &str) -> bool;
/// Determine if an Environment Record establishes a this binding. /// Determine if an Environment Record establishes a this binding.
/// Return true if it does and false if it does not. /// Return true if it does and false if it does not.

62
src/lib/environment/function_environment_record.rs

@ -1,12 +1,12 @@
//! # Function Environment Records //! # Function Environment Records
//! //!
//! A function Environment Record is a declarative Environment Record that is used to represent //! A function Environment Record is a declarative Environment Record that is used to represent
//! the top-level scope of a function and, if the function is not an ArrowFunction, //! the top-level scope of a function and, if the function is not an `ArrowFunction`,
//! provides a `this` binding. //! provides a `this` binding.
//! If a function is not an ArrowFunction function and references super, //! If a function is not an `ArrowFunction` function and references super,
//! its function Environment Record also contains the state that is used to perform super method invocations //! its function Environment Record also contains the state that is used to perform super method invocations
//! from within the function. //! from within the function.
//! More info: https://tc39.github.io/ecma262/#sec-function-environment-records //! More info: <https://tc39.github.io/ecma262/#sec-function-environment-records>
use crate::environment::declerative_environment_record::DeclerativeEnvironmentRecordBinding; use crate::environment::declerative_environment_record::DeclerativeEnvironmentRecordBinding;
use crate::environment::environment_record_trait::EnvironmentRecordTrait; use crate::environment::environment_record_trait::EnvironmentRecordTrait;
@ -27,8 +27,8 @@ pub enum BindingStatus {
Uninitialized, Uninitialized,
} }
/// https://tc39.github.io/ecma262/#table-16 /// <https://tc39.github.io/ecma262/#table-16>
#[derive(Trace, Finalize, Clone)] #[derive(Debug, Trace, Finalize, Clone)]
pub struct FunctionEnvironmentRecord { pub struct FunctionEnvironmentRecord {
pub env_rec: HashMap<String, DeclerativeEnvironmentRecordBinding>, pub env_rec: HashMap<String, DeclerativeEnvironmentRecordBinding>,
/// This is the this value used for this invocation of the function. /// This is the this value used for this invocation of the function.
@ -90,7 +90,7 @@ impl FunctionEnvironmentRecord {
impl EnvironmentRecordTrait for FunctionEnvironmentRecord { impl EnvironmentRecordTrait for FunctionEnvironmentRecord {
// TODO: get_super_base can't implement until GetPrototypeof is implemented on object // TODO: get_super_base can't implement until GetPrototypeof is implemented on object
fn has_binding(&self, name: &String) -> bool { fn has_binding(&self, name: &str) -> bool {
self.env_rec.contains_key(name) self.env_rec.contains_key(name)
} }
@ -123,14 +123,13 @@ impl EnvironmentRecordTrait for FunctionEnvironmentRecord {
value: None, value: None,
can_delete: true, can_delete: true,
mutable: false, mutable: false,
strict: strict, strict,
}, },
); );
} }
fn initialize_binding(&mut self, name: String, value: Value) { fn initialize_binding(&mut self, name: &str, value: Value) {
match self.env_rec.get_mut(&name) { if let Some(ref mut record) = self.env_rec.get_mut(name) {
Some(ref mut record) => {
match record.value { match record.value {
Some(_) => { Some(_) => {
// TODO: change this when error handling comes into play // TODO: change this when error handling comes into play
@ -139,23 +138,21 @@ impl EnvironmentRecordTrait for FunctionEnvironmentRecord {
None => record.value = Some(value), None => record.value = Some(value),
} }
} }
None => {}
}
} }
fn set_mutable_binding(&mut self, name: String, value: Value, mut strict: bool) { fn set_mutable_binding(&mut self, name: &str, value: Value, mut strict: bool) {
if self.env_rec.get(&name).is_none() { if self.env_rec.get(name).is_none() {
if strict == true { if strict {
// TODO: change this when error handling comes into play // TODO: change this when error handling comes into play
panic!("Reference Error: Cannot set mutable binding for {}", name); panic!("Reference Error: Cannot set mutable binding for {}", name);
} }
self.create_mutable_binding(name.clone(), true); self.create_mutable_binding(name.to_owned(), true);
self.initialize_binding(name.clone(), value); self.initialize_binding(name, value);
return; return;
} }
let record: &mut DeclerativeEnvironmentRecordBinding = self.env_rec.get_mut(&name).unwrap(); let record: &mut DeclerativeEnvironmentRecordBinding = self.env_rec.get_mut(name).unwrap();
if record.strict { if record.strict {
strict = true strict = true
} }
@ -167,17 +164,15 @@ impl EnvironmentRecordTrait for FunctionEnvironmentRecord {
if record.mutable { if record.mutable {
record.value = Some(value); record.value = Some(value);
} else { } else if strict {
if strict {
// TODO: change this when error handling comes into play // TODO: change this when error handling comes into play
panic!("TypeError: Cannot mutate an immutable binding {}", name); panic!("TypeError: Cannot mutate an immutable binding {}", name);
} }
} }
}
fn get_binding_value(&self, name: String, _strict: bool) -> Value { fn get_binding_value(&self, name: &str, _strict: bool) -> Value {
if self.env_rec.get(&name).is_some() && self.env_rec.get(&name).unwrap().value.is_some() { if self.env_rec.get(name).is_some() && self.env_rec.get(name).unwrap().value.is_some() {
let record: &DeclerativeEnvironmentRecordBinding = self.env_rec.get(&name).unwrap(); let record: &DeclerativeEnvironmentRecordBinding = self.env_rec.get(name).unwrap();
record.value.as_ref().unwrap().clone() record.value.as_ref().unwrap().clone()
} else { } else {
// TODO: change this when error handling comes into play // TODO: change this when error handling comes into play
@ -185,10 +180,10 @@ impl EnvironmentRecordTrait for FunctionEnvironmentRecord {
} }
} }
fn delete_binding(&mut self, name: String) -> bool { fn delete_binding(&mut self, name: &str) -> bool {
if self.env_rec.get(&name).is_some() { if self.env_rec.get(name).is_some() {
if self.env_rec.get(&name).unwrap().can_delete { if self.env_rec.get(name).unwrap().can_delete {
self.env_rec.remove(&name); self.env_rec.remove(name);
true true
} else { } else {
false false
@ -199,15 +194,10 @@ impl EnvironmentRecordTrait for FunctionEnvironmentRecord {
} }
fn has_super_binding(&self) -> bool { fn has_super_binding(&self) -> bool {
match self.this_binding_status { if let BindingStatus::Lexical = self.this_binding_status {
BindingStatus::Lexical => false,
_ => {
if self.home_object.is_undefined() {
false false
} else { } else {
true !self.home_object.is_undefined()
}
}
} }
} }
@ -234,7 +224,7 @@ impl EnvironmentRecordTrait for FunctionEnvironmentRecord {
} }
fn get_environment_type(&self) -> EnvironmentType { fn get_environment_type(&self) -> EnvironmentType {
return EnvironmentType::Function; EnvironmentType::Function
} }
fn get_global_object(&self) -> Option<Value> { fn get_global_object(&self) -> Option<Value> {

71
src/lib/environment/global_environment_record.rs

@ -5,7 +5,7 @@
//! A global Environment Record provides the bindings for built-in globals (clause 18), //! A global Environment Record provides the bindings for built-in globals (clause 18),
//! properties of the global object, and for all top-level declarations (13.2.8, 13.2.10) //! properties of the global object, and for all top-level declarations (13.2.8, 13.2.10)
//! that occur within a Script. //! that occur within a Script.
//! More info: https://tc39.github.io/ecma262/#sec-global-environment-records //! More info: <https://tc39.github.io/ecma262/#sec-global-environment-records>
use crate::environment::declerative_environment_record::DeclerativeEnvironmentRecord; use crate::environment::declerative_environment_record::DeclerativeEnvironmentRecord;
use crate::environment::environment_record_trait::EnvironmentRecordTrait; use crate::environment::environment_record_trait::EnvironmentRecordTrait;
@ -15,7 +15,7 @@ use crate::js::value::{Value, ValueData};
use gc::Gc; use gc::Gc;
use std::collections::HashSet; use std::collections::HashSet;
#[derive(Trace, Finalize, Clone)] #[derive(Debug, Trace, Finalize, Clone)]
pub struct GlobalEnvironmentRecord { pub struct GlobalEnvironmentRecord {
pub object_record: Box<ObjectEnvironmentRecord>, pub object_record: Box<ObjectEnvironmentRecord>,
pub global_this_binding: Value, pub global_this_binding: Value,
@ -25,23 +25,23 @@ pub struct GlobalEnvironmentRecord {
impl GlobalEnvironmentRecord { impl GlobalEnvironmentRecord {
pub fn get_this_binding(&self) -> Value { pub fn get_this_binding(&self) -> Value {
return self.global_this_binding.clone(); self.global_this_binding.clone()
} }
pub fn has_var_decleration(&self, name: &String) -> bool { pub fn has_var_decleration(&self, name: &str) -> bool {
return self.var_names.contains(name); self.var_names.contains(name)
} }
pub fn has_lexical_decleration(&self, name: &String) -> bool { pub fn has_lexical_decleration(&self, name: &str) -> bool {
self.declerative_record.has_binding(name) self.declerative_record.has_binding(name)
} }
pub fn has_restricted_global_property(&self, name: &String) -> bool { pub fn has_restricted_global_property(&self, name: &str) -> bool {
let global_object = &self.object_record.bindings; let global_object = &self.object_record.bindings;
let existing_prop = global_object.get_prop(name.clone()); let existing_prop = global_object.get_prop(name);
match existing_prop { match existing_prop {
Some(prop) => { Some(prop) => {
if prop.value.is_undefined() || prop.configurable == true { if prop.value.is_undefined() || prop.configurable {
return false; return false;
} }
true true
@ -53,24 +53,23 @@ impl GlobalEnvironmentRecord {
pub fn create_global_var_binding(&mut self, name: String, deletion: bool) { pub fn create_global_var_binding(&mut self, name: String, deletion: bool) {
let obj_rec = &mut self.object_record; let obj_rec = &mut self.object_record;
let global_object = &obj_rec.bindings; let global_object = &obj_rec.bindings;
let has_property = global_object.has_field(name.clone()); let has_property = global_object.has_field(&name);
let extensible = global_object.is_extensible(); let extensible = global_object.is_extensible();
if !has_property && extensible { if !has_property && extensible {
obj_rec.create_mutable_binding(name.clone(), deletion); obj_rec.create_mutable_binding(name.clone(), deletion);
obj_rec.initialize_binding(name.clone(), Gc::new(ValueData::Undefined)); obj_rec.initialize_binding(&name, Gc::new(ValueData::Undefined));
} }
let var_declared_names = &mut self.var_names; let var_declared_names = &mut self.var_names;
if !var_declared_names.contains(&name) { if !var_declared_names.contains(&name) {
var_declared_names.insert(name.clone()); var_declared_names.insert(name);
} }
} }
pub fn create_global_function_binding(&mut self, name: String, value: Value, deletion: bool) { pub fn create_global_function_binding(&mut self, name: &str, value: Value, deletion: bool) {
let global_object = &mut self.object_record.bindings; let global_object = &mut self.object_record.bindings;
let existing_prop = global_object.get_prop(name.clone()); let existing_prop = global_object.get_prop(&name);
match existing_prop { if let Some(prop) = existing_prop {
Some(prop) => {
if prop.value.is_undefined() || prop.configurable { if prop.value.is_undefined() || prop.configurable {
global_object.update_prop( global_object.update_prop(
name, name,
@ -80,22 +79,14 @@ impl GlobalEnvironmentRecord {
Some(deletion), Some(deletion),
); );
} }
} } else {
None => { global_object.update_prop(name, Some(value), Some(true), Some(true), Some(deletion));
global_object.update_prop(
name,
Some(value),
Some(true),
Some(true),
Some(deletion),
);
}
} }
} }
} }
impl EnvironmentRecordTrait for GlobalEnvironmentRecord { impl EnvironmentRecordTrait for GlobalEnvironmentRecord {
fn has_binding(&self, name: &String) -> bool { fn has_binding(&self, name: &str) -> bool {
if self.declerative_record.has_binding(name) { if self.declerative_record.has_binding(name) {
return true; return true;
} }
@ -121,18 +112,16 @@ impl EnvironmentRecordTrait for GlobalEnvironmentRecord {
.create_immutable_binding(name.clone(), strict) .create_immutable_binding(name.clone(), strict)
} }
fn initialize_binding(&mut self, name: String, value: Value) { fn initialize_binding(&mut self, name: &str, value: Value) {
if self.declerative_record.has_binding(&name) { if self.declerative_record.has_binding(&name) {
// TODO: assert binding is in the object environment record // TODO: assert binding is in the object environment record
return self return self.declerative_record.initialize_binding(name, value);
.declerative_record
.initialize_binding(name.clone(), value);
} }
panic!("Should not initialized binding without creating first."); panic!("Should not initialized binding without creating first.");
} }
fn set_mutable_binding(&mut self, name: String, value: Value, strict: bool) { fn set_mutable_binding(&mut self, name: &str, value: Value, strict: bool) {
if self.declerative_record.has_binding(&name) { if self.declerative_record.has_binding(&name) {
return self return self
.declerative_record .declerative_record
@ -141,25 +130,25 @@ impl EnvironmentRecordTrait for GlobalEnvironmentRecord {
self.object_record.set_mutable_binding(name, value, strict) self.object_record.set_mutable_binding(name, value, strict)
} }
fn get_binding_value(&self, name: String, strict: bool) -> Value { fn get_binding_value(&self, name: &str, strict: bool) -> Value {
if self.declerative_record.has_binding(&name) { if self.declerative_record.has_binding(&name) {
return self.declerative_record.get_binding_value(name, strict); return self.declerative_record.get_binding_value(name, strict);
} }
return self.object_record.get_binding_value(name, strict); self.object_record.get_binding_value(name, strict)
} }
fn delete_binding(&mut self, name: String) -> bool { fn delete_binding(&mut self, name: &str) -> bool {
if self.declerative_record.has_binding(&name) { if self.declerative_record.has_binding(&name) {
return self.declerative_record.delete_binding(name.clone()); return self.declerative_record.delete_binding(name);
} }
let global: &Value = &self.object_record.bindings; let global: &Value = &self.object_record.bindings;
if global.has_field(name.clone()) { if global.has_field(name) {
let status = self.object_record.delete_binding(name.clone()); let status = self.object_record.delete_binding(name);
if status { if status {
let var_names = &mut self.var_names; let var_names = &mut self.var_names;
if var_names.contains(&name) { if var_names.contains(name) {
var_names.remove(&name); var_names.remove(name);
return status; return status;
} }
} }
@ -188,7 +177,7 @@ impl EnvironmentRecordTrait for GlobalEnvironmentRecord {
} }
fn get_environment_type(&self) -> EnvironmentType { fn get_environment_type(&self) -> EnvironmentType {
return EnvironmentType::Global; EnvironmentType::Global
} }
fn get_global_object(&self) -> Option<Value> { fn get_global_object(&self) -> Option<Value> {

27
src/lib/environment/lexical_environment.rs

@ -1,6 +1,6 @@
//! # Lexical Environment //! # Lexical Environment
//! //!
//! https://tc39.github.io/ecma262/#sec-lexical-environment-operations //! <https://tc39.github.io/ecma262/#sec-lexical-environment-operations>
//! //!
//! The following operations are used to operate upon lexical environments //! The following operations are used to operate upon lexical environments
//! This is the entrypoint to lexical environments. //! This is the entrypoint to lexical environments.
@ -24,7 +24,7 @@ pub type Environment = Gc<GcCell<Box<dyn EnvironmentRecordTrait>>>;
/// Give each environment an easy way to declare its own type /// Give each environment an easy way to declare its own type
/// This helps with comparisons /// This helps with comparisons
#[derive(Debug)] #[derive(Debug, Clone, Copy)]
pub enum EnvironmentType { pub enum EnvironmentType {
Declerative, Declerative,
Function, Function,
@ -32,6 +32,7 @@ pub enum EnvironmentType {
Object, Object,
} }
#[derive(Debug)]
pub struct LexicalEnvironment { pub struct LexicalEnvironment {
environment_stack: VecDeque<Environment>, environment_stack: VecDeque<Environment>,
} }
@ -43,8 +44,8 @@ pub struct EnvironmentError {
} }
impl EnvironmentError { impl EnvironmentError {
pub fn new(msg: &str) -> EnvironmentError { pub fn new(msg: &str) -> Self {
EnvironmentError { Self {
details: msg.to_string(), details: msg.to_string(),
} }
} }
@ -68,9 +69,9 @@ impl error::Error for EnvironmentError {
} }
impl LexicalEnvironment { impl LexicalEnvironment {
pub fn new(global: Value) -> LexicalEnvironment { pub fn new(global: Value) -> Self {
let global_env = new_global_environment(global.clone(), global.clone()); let global_env = new_global_environment(global.clone(), global);
let mut lexical_env = LexicalEnvironment { let mut lexical_env = Self {
environment_stack: VecDeque::new(), environment_stack: VecDeque::new(),
}; };
@ -106,12 +107,12 @@ impl LexicalEnvironment {
.create_immutable_binding(name, deletion) .create_immutable_binding(name, deletion)
} }
pub fn set_mutable_binding(&mut self, name: String, value: Value, strict: bool) { pub fn set_mutable_binding(&mut self, name: &str, value: Value, strict: bool) {
let env = self.get_current_environment(); let env = self.get_current_environment();
env.borrow_mut().set_mutable_binding(name, value, strict); env.borrow_mut().set_mutable_binding(name, value, strict);
} }
pub fn initialize_binding(&mut self, name: String, value: Value) { pub fn initialize_binding(&mut self, name: &str, value: Value) {
let env = self.get_current_environment(); let env = self.get_current_environment();
env.borrow_mut().initialize_binding(name, value); env.borrow_mut().initialize_binding(name, value);
} }
@ -131,10 +132,10 @@ impl LexicalEnvironment {
self.environment_stack.back_mut().unwrap() self.environment_stack.back_mut().unwrap()
} }
pub fn get_binding_value(&mut self, name: String) -> Value { pub fn get_binding_value(&mut self, name: &str) -> Value {
let env: Environment = self.get_current_environment().clone(); let env: Environment = self.get_current_environment().clone();
let borrowed_env = env.borrow(); let borrowed_env = env.borrow();
let result = borrowed_env.has_binding(&name); let result = borrowed_env.has_binding(name);
if result { if result {
return borrowed_env.get_binding_value(name, false); return borrowed_env.get_binding_value(name, false);
} }
@ -172,10 +173,10 @@ pub fn new_function_environment(
debug_assert!(new_target.is_object() || new_target.is_undefined()); debug_assert!(new_target.is_object() || new_target.is_undefined());
Gc::new(GcCell::new(Box::new(FunctionEnvironmentRecord { Gc::new(GcCell::new(Box::new(FunctionEnvironmentRecord {
env_rec: HashMap::new(), env_rec: HashMap::new(),
function_object: f.clone(), function_object: f,
this_binding_status: BindingStatus::Uninitialized, // hardcoding to unitialized for now until short functions are properly supported this_binding_status: BindingStatus::Uninitialized, // hardcoding to unitialized for now until short functions are properly supported
home_object: Gc::new(ValueData::Undefined), home_object: Gc::new(ValueData::Undefined),
new_target: new_target, new_target,
outer_env: outer, // this will come from Environment set as a private property of F - https://tc39.github.io/ecma262/#sec-ecmascript-function-objects outer_env: outer, // this will come from Environment set as a private property of F - https://tc39.github.io/ecma262/#sec-ecmascript-function-objects
this_value: Gc::new(ValueData::Undefined), // TODO: this_value should start as an Option as its not always there to begin with this_value: Gc::new(ValueData::Undefined), // TODO: this_value should start as an Option as its not always there to begin with
}))) })))

54
src/lib/environment/object_environment_record.rs

@ -3,7 +3,7 @@
//! Each object Environment Record is associated with an object called its binding object. //! Each object Environment Record is associated with an object called its binding object.
//! An object Environment Record binds the set of string identifier names that directly //! An object Environment Record binds the set of string identifier names that directly
//! correspond to the property names of its binding object. //! correspond to the property names of its binding object.
//! Property keys that are not strings in the form of an IdentifierName are not included in the set of bound identifiers. //! Property keys that are not strings in the form of an `IdentifierName` are not included in the set of bound identifiers.
//! More info: [Object Records](https://tc39.github.io/ecma262/#sec-object-environment-records) //! More info: [Object Records](https://tc39.github.io/ecma262/#sec-object-environment-records)
use crate::environment::environment_record_trait::EnvironmentRecordTrait; use crate::environment::environment_record_trait::EnvironmentRecordTrait;
@ -12,7 +12,7 @@ use crate::js::object::Property;
use crate::js::value::{Value, ValueData}; use crate::js::value::{Value, ValueData};
use gc::Gc; use gc::Gc;
#[derive(Trace, Finalize, Clone)] #[derive(Debug, Trace, Finalize, Clone)]
pub struct ObjectEnvironmentRecord { pub struct ObjectEnvironmentRecord {
pub bindings: Value, pub bindings: Value,
pub with_environment: bool, pub with_environment: bool,
@ -20,16 +20,15 @@ pub struct ObjectEnvironmentRecord {
} }
impl EnvironmentRecordTrait for ObjectEnvironmentRecord { impl EnvironmentRecordTrait for ObjectEnvironmentRecord {
fn has_binding(&self, name: &String) -> bool { fn has_binding(&self, name: &str) -> bool {
if !self.bindings.has_field(name.to_string()) { if self.bindings.has_field(name) {
return false; if self.with_environment {
}
if !self.with_environment {
return true;
}
// TODO: implement unscopables // TODO: implement unscopables
}
true true
} else {
false
}
} }
fn create_mutable_binding(&mut self, name: String, deletion: bool) { fn create_mutable_binding(&mut self, name: String, deletion: bool) {
@ -48,37 +47,35 @@ impl EnvironmentRecordTrait for ObjectEnvironmentRecord {
unimplemented!() unimplemented!()
} }
fn initialize_binding(&mut self, name: String, value: Value) { fn initialize_binding(&mut self, name: &str, value: Value) {
// We should never need to check if a binding has been created, // We should never need to check if a binding has been created,
// As all calls to create_mutable_binding are followed by initialized binding // As all calls to create_mutable_binding are followed by initialized binding
// The below is just a check. // The below is just a check.
debug_assert!(self.has_binding(&name)); debug_assert!(self.has_binding(&name));
return self.set_mutable_binding(name, value, false); self.set_mutable_binding(name, value, false)
} }
fn set_mutable_binding(&mut self, name: String, value: Value, strict: bool) { fn set_mutable_binding(&mut self, name: &str, value: Value, strict: bool) {
debug_assert!(value.is_object() || value.is_function()); debug_assert!(value.is_object() || value.is_function());
let bindings = &mut self.bindings; let bindings = &mut self.bindings;
bindings.update_prop(name, Some(value.clone()), None, None, Some(strict)); bindings.update_prop(name, Some(value.clone()), None, None, Some(strict));
} }
fn get_binding_value(&self, name: String, strict: bool) -> Value { fn get_binding_value(&self, name: &str, strict: bool) -> Value {
if self.bindings.has_field(name.clone()) { if self.bindings.has_field(name) {
return self.bindings.get_field(name); self.bindings.get_field(name)
} } else {
if strict {
if !strict {
return Gc::new(ValueData::Undefined);
}
// TODO: throw error here // TODO: throw error here
// Error handling not implemented yet // Error handling not implemented yet
}
Gc::new(ValueData::Undefined) Gc::new(ValueData::Undefined)
} }
}
fn delete_binding(&mut self, name: String) -> bool { fn delete_binding(&mut self, name: &str) -> bool {
self.bindings.remove_prop(&name); self.bindings.remove_prop(name);
true true
} }
@ -112,13 +109,14 @@ impl EnvironmentRecordTrait for ObjectEnvironmentRecord {
} }
fn get_environment_type(&self) -> EnvironmentType { fn get_environment_type(&self) -> EnvironmentType {
return EnvironmentType::Function; EnvironmentType::Function
} }
fn get_global_object(&self) -> Option<Value> { fn get_global_object(&self) -> Option<Value> {
match &self.outer_env { if let Some(outer) = &self.outer_env {
Some(outer) => outer.borrow().get_global_object(), outer.borrow().get_global_object()
None => None, } else {
None
} }
} }
} }

34
src/lib/exec.rs

@ -19,13 +19,14 @@ pub trait Executor {
} }
/// A Javascript intepreter /// A Javascript intepreter
#[derive(Debug)]
pub struct Interpreter { pub struct Interpreter {
/// An object representing the global object /// An object representing the global object
environment: LexicalEnvironment, environment: LexicalEnvironment,
} }
impl Executor for Interpreter { impl Executor for Interpreter {
fn new() -> Interpreter { fn new() -> Self {
let global = ValueData::new_obj(None); let global = ValueData::new_obj(None);
object::init(&global); object::init(&global);
console::init(&global); console::init(&global);
@ -34,11 +35,12 @@ impl Executor for Interpreter {
function::init(&global); function::init(&global);
json::init(&global); json::init(&global);
string::init(&global); string::init(&global);
Interpreter { Self {
environment: LexicalEnvironment::new(global.clone()), environment: LexicalEnvironment::new(global.clone()),
} }
} }
#[allow(clippy::match_same_arms)]
fn run(&mut self, expr: &Expr) -> ResultValue { fn run(&mut self, expr: &Expr) -> ResultValue {
match expr.def { match expr.def {
ExprDef::ConstExpr(Const::Null) => Ok(to_value(None::<()>)), ExprDef::ConstExpr(Const::Null) => Ok(to_value(None::<()>)),
@ -62,30 +64,30 @@ impl Executor for Interpreter {
Ok(obj) Ok(obj)
} }
ExprDef::LocalExpr(ref name) => { ExprDef::LocalExpr(ref name) => {
let val = self.environment.get_binding_value(name.to_string()); let val = self.environment.get_binding_value(name);
Ok(val) Ok(val)
} }
ExprDef::GetConstFieldExpr(ref obj, ref field) => { ExprDef::GetConstFieldExpr(ref obj, ref field) => {
let val_obj = self.run(obj)?; let val_obj = self.run(obj)?;
Ok(val_obj.borrow().get_field(field.clone())) Ok(val_obj.borrow().get_field(field))
} }
ExprDef::GetFieldExpr(ref obj, ref field) => { ExprDef::GetFieldExpr(ref obj, ref field) => {
let val_obj = self.run(obj)?; let val_obj = self.run(obj)?;
let val_field = self.run(field)?; let val_field = self.run(field)?;
Ok(val_obj.borrow().get_field(val_field.borrow().to_string())) Ok(val_obj.borrow().get_field(&val_field.borrow().to_string()))
} }
ExprDef::CallExpr(ref callee, ref args) => { ExprDef::CallExpr(ref callee, ref args) => {
let (this, func) = match callee.def { let (this, func) = match callee.def {
ExprDef::GetConstFieldExpr(ref obj, ref field) => { ExprDef::GetConstFieldExpr(ref obj, ref field) => {
let obj = self.run(obj)?; let obj = self.run(obj)?;
(obj.clone(), obj.borrow().get_field(field.clone())) (obj.clone(), obj.borrow().get_field(field))
} }
ExprDef::GetFieldExpr(ref obj, ref field) => { ExprDef::GetFieldExpr(ref obj, ref field) => {
let obj = self.run(obj)?; let obj = self.run(obj)?;
let field = self.run(field)?; let field = self.run(field)?;
( (
obj.clone(), obj.clone(),
obj.borrow().get_field(field.borrow().to_string()), obj.borrow().get_field(&field.borrow().to_string()),
) )
} }
_ => ( _ => (
@ -116,8 +118,7 @@ impl Executor for Interpreter {
let name = data.args.get(i).unwrap(); let name = data.args.get(i).unwrap();
let expr = v_args.get(i).unwrap(); let expr = v_args.get(i).unwrap();
self.environment.create_mutable_binding(name.clone(), false); self.environment.create_mutable_binding(name.clone(), false);
self.environment self.environment.initialize_binding(name, expr.to_owned());
.initialize_binding(name.clone(), expr.to_owned());
} }
let result = self.run(&data.expr); let result = self.run(&data.expr);
self.environment.pop(); self.environment.pop();
@ -192,7 +193,7 @@ impl Executor for Interpreter {
arr_map.borrow().set_field_slice( arr_map.borrow().set_field_slice(
INSTANCE_PROTOTYPE, INSTANCE_PROTOTYPE,
self.environment self.environment
.get_binding_value("Array".to_string()) .get_binding_value("Array")
.borrow() .borrow()
.get_field_slice(PROTOTYPE), .get_field_slice(PROTOTYPE),
); );
@ -207,7 +208,7 @@ impl Executor for Interpreter {
self.environment self.environment
.create_mutable_binding(name.clone().unwrap(), false); .create_mutable_binding(name.clone().unwrap(), false);
self.environment self.environment
.initialize_binding(name.clone().unwrap(), val.clone()) .initialize_binding(name.as_ref().unwrap(), val.clone())
} }
Ok(val) Ok(val)
} }
@ -312,7 +313,7 @@ impl Executor for Interpreter {
let name = data.args.get(i).unwrap(); let name = data.args.get(i).unwrap();
let expr = v_args.get(i).unwrap(); let expr = v_args.get(i).unwrap();
env.create_mutable_binding(name.clone(), false); env.create_mutable_binding(name.clone(), false);
env.initialize_binding(name.clone(), expr.to_owned()); env.initialize_binding(name, expr.to_owned());
} }
let result = self.run(&data.expr); let result = self.run(&data.expr);
self.environment.pop(); self.environment.pop();
@ -332,8 +333,7 @@ impl Executor for Interpreter {
match ref_e.def { match ref_e.def {
ExprDef::LocalExpr(ref name) => { ExprDef::LocalExpr(ref name) => {
self.environment.create_mutable_binding(name.clone(), false); self.environment.create_mutable_binding(name.clone(), false);
self.environment self.environment.initialize_binding(name, val.clone());
.initialize_binding(name.clone(), val.clone());
} }
ExprDef::GetConstFieldExpr(ref obj, ref field) => { ExprDef::GetConstFieldExpr(ref obj, ref field) => {
let val_obj = self.run(obj)?; let val_obj = self.run(obj)?;
@ -351,7 +351,7 @@ impl Executor for Interpreter {
None => Gc::new(ValueData::Null), None => Gc::new(ValueData::Null),
}; };
self.environment.create_mutable_binding(name.clone(), false); self.environment.create_mutable_binding(name.clone(), false);
self.environment.initialize_binding(name, val); self.environment.initialize_binding(&name, val);
} }
Ok(Gc::new(ValueData::Undefined)) Ok(Gc::new(ValueData::Undefined))
} }
@ -363,7 +363,7 @@ impl Executor for Interpreter {
None => Gc::new(ValueData::Null), None => Gc::new(ValueData::Null),
}; };
self.environment.create_mutable_binding(name.clone(), false); self.environment.create_mutable_binding(name.clone(), false);
self.environment.initialize_binding(name, val); self.environment.initialize_binding(&name, val);
} }
Ok(Gc::new(ValueData::Undefined)) Ok(Gc::new(ValueData::Undefined))
} }
@ -376,7 +376,7 @@ impl Executor for Interpreter {
}; };
self.environment self.environment
.create_immutable_binding(name.clone(), false); .create_immutable_binding(name.clone(), false);
self.environment.initialize_binding(name, val); self.environment.initialize_binding(&name, val);
} }
Ok(Gc::new(ValueData::Undefined)) Ok(Gc::new(ValueData::Undefined))
} }

47
src/lib/js/array.rs

@ -3,8 +3,8 @@ use crate::js::object::{Property, PROTOTYPE};
use crate::js::value::{from_value, to_value, ResultValue, Value, ValueData}; use crate::js::value::{from_value, to_value, ResultValue, Value, ValueData};
use gc::Gc; use gc::Gc;
/// Utility function for creating array objects: array_obj can be any array with /// Utility function for creating array objects: `array_obj` can be any array with
/// prototype already set (it will be wiped and recreated from array_contents) /// prototype already set (it will be wiped and recreated from `array_contents`)
fn create_array_object(array_obj: Value, array_contents: Vec<Value>) -> ResultValue { fn create_array_object(array_obj: Value, array_contents: Vec<Value>) -> ResultValue {
let array_obj_ptr = array_obj.clone(); let array_obj_ptr = array_obj.clone();
@ -14,11 +14,10 @@ fn create_array_object(array_obj: Value, array_contents: Vec<Value>) -> ResultVa
array_obj_ptr.remove_prop(&n.to_string()); array_obj_ptr.remove_prop(&n.to_string());
} }
for (n, value) in array_contents.iter().enumerate() {
array_obj_ptr.set_field(n.to_string(), value.clone());
}
array_obj_ptr.set_field_slice("length", to_value(array_contents.len() as i32)); array_obj_ptr.set_field_slice("length", to_value(array_contents.len() as i32));
for (n, value) in array_contents.into_iter().enumerate() {
array_obj_ptr.set_field(n.to_string(), value);
}
Ok(array_obj_ptr) Ok(array_obj_ptr)
} }
@ -41,7 +40,7 @@ fn add_to_array_object(array_ptr: Value, add_values: Vec<Value>) -> ResultValue
pub fn make_array(this: Value, _: Value, args: Vec<Value>) -> ResultValue { pub fn make_array(this: Value, _: Value, args: Vec<Value>) -> ResultValue {
// Make a new Object which will internally represent the Array (mapping // Make a new Object which will internally represent the Array (mapping
// between indices and values): this creates an Object with no prototype // between indices and values): this creates an Object with no prototype
this.set_field_slice("length", to_value(0i32)); this.set_field_slice("length", to_value(0_i32));
match args.len() { match args.len() {
0 => create_array_object(this, Vec::new()), 0 => create_array_object(this, Vec::new()),
1 => { 1 => {
@ -66,11 +65,11 @@ pub fn get_array_length(this: Value, _: Value, _: Vec<Value>) -> ResultValue {
/// When the concat method is called with zero or more arguments, it returns an /// When the concat method is called with zero or more arguments, it returns an
/// array containing the array elements of the object followed by the array /// array containing the array elements of the object followed by the array
/// elements of each argument in order. /// elements of each argument in order.
/// https://tc39.es/ecma262/#sec-array.prototype.concat /// <https://tc39.es/ecma262/#sec-array.prototype.concat>
pub fn concat(this: Value, _: Value, args: Vec<Value>) -> ResultValue { pub fn concat(this: Value, _: Value, args: Vec<Value>) -> ResultValue {
if args.len() == 0 { if args.is_empty() {
// If concat is called with no arguments, it returns the original array // If concat is called with no arguments, it returns the original array
return Ok(this.clone()); return Ok(this);
} }
// Make a new array (using this object as the prototype basis for the new // Make a new array (using this object as the prototype basis for the new
@ -79,13 +78,13 @@ pub fn concat(this: Value, _: Value, args: Vec<Value>) -> ResultValue {
let this_length: i32 = from_value(this.get_field_slice("length")).unwrap(); let this_length: i32 = from_value(this.get_field_slice("length")).unwrap();
for n in 0..this_length { for n in 0..this_length {
new_values.push(this.get_field(n.to_string())); new_values.push(this.get_field(&n.to_string()));
} }
for concat_array in args { for concat_array in args {
let concat_length: i32 = from_value(concat_array.get_field_slice("length")).unwrap(); let concat_length: i32 = from_value(concat_array.get_field_slice("length")).unwrap();
for n in 0..concat_length { for n in 0..concat_length {
new_values.push(concat_array.get_field(n.to_string())); new_values.push(concat_array.get_field(&n.to_string()));
} }
} }
@ -97,7 +96,7 @@ pub fn concat(this: Value, _: Value, args: Vec<Value>) -> ResultValue {
/// The arguments are appended to the end of the array, in the order in which /// The arguments are appended to the end of the array, in the order in which
/// they appear. The new length of the array is returned as the result of the /// they appear. The new length of the array is returned as the result of the
/// call. /// call.
/// https://tc39.es/ecma262/#sec-array.prototype.push /// <https://tc39.es/ecma262/#sec-array.prototype.push>
pub fn push(this: Value, _: Value, args: Vec<Value>) -> ResultValue { pub fn push(this: Value, _: Value, args: Vec<Value>) -> ResultValue {
let new_array = add_to_array_object(this, args)?; let new_array = add_to_array_object(this, args)?;
Ok(new_array.get_field_slice("length")) Ok(new_array.get_field_slice("length"))
@ -106,7 +105,7 @@ pub fn push(this: Value, _: Value, args: Vec<Value>) -> ResultValue {
/// Array.prototype.pop ( ) /// Array.prototype.pop ( )
/// ///
/// The last element of the array is removed from the array and returned. /// The last element of the array is removed from the array and returned.
/// https://tc39.es/ecma262/#sec-array.prototype.pop /// <https://tc39.es/ecma262/#sec-array.prototype.pop>
pub fn pop(this: Value, _: Value, _: Vec<Value>) -> ResultValue { pub fn pop(this: Value, _: Value, _: Vec<Value>) -> ResultValue {
let curr_length: i32 = from_value(this.get_field_slice("length")).unwrap(); let curr_length: i32 = from_value(this.get_field_slice("length")).unwrap();
if curr_length < 1 { if curr_length < 1 {
@ -115,7 +114,7 @@ pub fn pop(this: Value, _: Value, _: Vec<Value>) -> ResultValue {
)); ));
} }
let pop_index = curr_length - 1; let pop_index = curr_length - 1;
let pop_value: Value = this.get_field(pop_index.to_string()); let pop_value: Value = this.get_field(&pop_index.to_string());
this.remove_prop(&pop_index.to_string()); this.remove_prop(&pop_index.to_string());
this.set_field_slice("length", to_value(pop_index)); this.set_field_slice("length", to_value(pop_index));
Ok(pop_value) Ok(pop_value)
@ -126,20 +125,18 @@ pub fn pop(this: Value, _: Value, _: Vec<Value>) -> ResultValue {
/// The elements of the array are converted to Strings, and these Strings are /// The elements of the array are converted to Strings, and these Strings are
/// then concatenated, separated by occurrences of the separator. If no /// then concatenated, separated by occurrences of the separator. If no
/// separator is provided, a single comma is used as the separator. /// separator is provided, a single comma is used as the separator.
/// https://tc39.es/ecma262/#sec-array.prototype.join /// <https://tc39.es/ecma262/#sec-array.prototype.join>
pub fn join(this: Value, _: Value, args: Vec<Value>) -> ResultValue { pub fn join(this: Value, _: Value, args: Vec<Value>) -> ResultValue {
let separator: String; let separator = if args.is_empty() {
String::from(",")
if args.len() > 0 {
separator = args[0].to_string();
} else { } else {
separator = ",".to_string(); args[0].to_string()
} };
let mut elem_strs: Vec<String> = Vec::new(); let mut elem_strs: Vec<String> = Vec::new();
let length: i32 = from_value(this.get_field_slice("length")).unwrap(); let length: i32 = from_value(this.get_field_slice("length")).unwrap();
for n in 0..length { for n in 0..length {
let elem_str: String = this.get_field(n.to_string()).to_string(); let elem_str: String = this.get_field(&n.to_string()).to_string();
elem_strs.push(elem_str); elem_strs.push(elem_str);
} }
@ -160,10 +157,10 @@ pub fn _create(global: &Value) -> Value {
}; };
proto.set_prop_slice("length", length); proto.set_prop_slice("length", length);
let concat_func = to_value(concat as NativeFunctionData); let concat_func = to_value(concat as NativeFunctionData);
concat_func.set_field_slice("length", to_value(1 as i32)); concat_func.set_field_slice("length", to_value(1_i32));
proto.set_field_slice("concat", concat_func); proto.set_field_slice("concat", concat_func);
let push_func = to_value(push as NativeFunctionData); let push_func = to_value(push as NativeFunctionData);
push_func.set_field_slice("length", to_value(1 as i32)); push_func.set_field_slice("length", to_value(1_i32));
proto.set_field_slice("push", push_func); proto.set_field_slice("push", push_func);
proto.set_field_slice("pop", to_value(pop as NativeFunctionData)); proto.set_field_slice("pop", to_value(pop as NativeFunctionData));
proto.set_field_slice("join", to_value(join as NativeFunctionData)); proto.set_field_slice("join", to_value(join as NativeFunctionData));

17
src/lib/js/console.rs

@ -7,20 +7,19 @@ use std::fmt::Write;
use std::iter::FromIterator; use std::iter::FromIterator;
/// Print a javascript value to the standard output stream /// Print a javascript value to the standard output stream
/// https://console.spec.whatwg.org/#logger /// <https://console.spec.whatwg.org/#logger>
pub fn log(_: Value, _: Value, args: Vec<Value>) -> ResultValue { pub fn log(_: Value, _: Value, args: Vec<Value>) -> ResultValue {
let args: Vec<String> = FromIterator::from_iter(args.iter().map(|x| { let args: Vec<String> = FromIterator::from_iter(args.iter().map(|x| {
// Welcome to console.log! The output here is what the developer sees, so its best matching through value types and stringifying to the correct output // Welcome to console.log! The output here is what the developer sees, so its best matching through value types and stringifying to the correct output
// The input is a vector of Values, we generate a vector of strings then pass them to println! // The input is a vector of Values, we generate a vector of strings then pass them to println!
return match *x.clone() { match *x.clone() {
// We don't want to print private (compiler) or prototype properties // We don't want to print private (compiler) or prototype properties
ValueData::Object(ref v, _) => { ValueData::Object(ref v, _) => {
// Create empty formatted string to start writing to // Create empty formatted string to start writing to
// TODO: once constructor is set on objects, we can do specific output for Strings, Numbers etc // TODO: once constructor is set on objects, we can do specific output for Strings, Numbers etc
let mut s = String::new(); let mut s = String::new();
write!(s, "{}", "{").unwrap(); write!(s, "{{").unwrap();
match v.borrow().iter().last() { if let Some((last_key, _)) = v.borrow().iter().last() {
Some((last_key, _)) => {
for (key, val) in v.borrow().iter() { for (key, val) in v.borrow().iter() {
// Don't print prototype properties // Don't print prototype properties
if key == INSTANCE_PROTOTYPE { if key == INSTANCE_PROTOTYPE {
@ -28,18 +27,16 @@ pub fn log(_: Value, _: Value, args: Vec<Value>) -> ResultValue {
} }
write!(s, "{}: {}", key, val.value.clone()).unwrap(); write!(s, "{}: {}", key, val.value.clone()).unwrap();
if key != last_key { if key != last_key {
write!(s, "{}", ", ").unwrap(); write!(s, ", ").unwrap();
} }
} }
} }
None => (), write!(s, "}}").unwrap();
}
write!(s, "{}", "}").unwrap();
s s
} }
_ => from_value::<String>(x.clone()).unwrap(), _ => from_value::<String>(x.clone()).unwrap(),
}; }
// from_value::<String>(x.clone()).unwrap() // from_value::<String>(x.clone()).unwrap()
})); }));

2
src/lib/js/error.rs

@ -5,7 +5,7 @@ use gc::Gc;
/// Create a new error /// Create a new error
pub fn make_error(this: Value, _: Value, args: Vec<Value>) -> ResultValue { pub fn make_error(this: Value, _: Value, args: Vec<Value>) -> ResultValue {
if args.len() >= 1 { if !args.is_empty() {
this.set_field_slice("message", to_value(args.get(0).unwrap().to_string())); this.set_field_slice("message", to_value(args.get(0).unwrap().to_string()));
} }
Ok(Gc::new(ValueData::Undefined)) Ok(Gc::new(ValueData::Undefined))

23
src/lib/js/function.rs

@ -9,7 +9,7 @@ pub type NativeFunctionData = fn(Value, Value, Vec<Value>) -> ResultValue;
/// A Javascript function /// A Javascript function
/// A member of the Object type that may be invoked as a subroutine /// A member of the Object type that may be invoked as a subroutine
/// https://tc39.github.io/ecma262/#sec-terms-and-definitions-function /// <https://tc39.github.io/ecma262/#sec-terms-and-definitions-function>
/// In our implementation, Function is extending Object by holding an object field which some extra data /// In our implementation, Function is extending Object by holding an object field which some extra data
/// A Javascript function /// A Javascript function
@ -34,17 +34,13 @@ pub struct RegularFunction {
impl RegularFunction { impl RegularFunction {
/// Make a new regular function /// Make a new regular function
pub fn new(expr: Expr, args: Vec<String>) -> RegularFunction { pub fn new(expr: Expr, args: Vec<String>) -> Self {
let mut obj = HashMap::new(); let mut object = HashMap::new();
obj.insert( object.insert(
"arguments".to_string(), "arguments".to_string(),
Property::new(Gc::new(ValueData::Integer(args.len() as i32))), Property::new(Gc::new(ValueData::Integer(args.len() as i32))),
); );
RegularFunction { Self { object, expr, args }
object: obj,
expr: expr,
args: args,
}
} }
} }
@ -58,12 +54,9 @@ pub struct NativeFunction {
} }
impl NativeFunction { impl NativeFunction {
/// Make a new native function with the given function data /// Make a new native function with the given function data
pub fn new(data: NativeFunctionData) -> NativeFunction { pub fn new(data: NativeFunctionData) -> Self {
let obj = HashMap::new(); let object = HashMap::new();
NativeFunction { Self { object, data }
object: obj,
data: data,
}
} }
} }

4
src/lib/js/json.rs

@ -1,11 +1,11 @@
use crate::js::function::NativeFunctionData; use crate::js::function::NativeFunctionData;
/// The JSON Object /// The JSON Object
/// https://tc39.github.io/ecma262/#sec-json-object /// <https://tc39.github.io/ecma262/#sec-json-object>
use crate::js::value::{to_value, ResultValue, Value, ValueData}; use crate::js::value::{to_value, ResultValue, Value, ValueData};
use serde_json::{self, to_string_pretty, Value as JSONValue}; use serde_json::{self, to_string_pretty, Value as JSONValue};
/// Parse a JSON string into a Javascript object /// Parse a JSON string into a Javascript object
/// https://tc39.github.io/ecma262/#sec-json.parse /// <https://tc39.github.io/ecma262/#sec-json.parse>
pub fn parse(_: Value, _: Value, args: Vec<Value>) -> ResultValue { pub fn parse(_: Value, _: Value, args: Vec<Value>) -> ResultValue {
match serde_json::from_str::<JSONValue>(&args.get(0).unwrap().clone().to_string()) { match serde_json::from_str::<JSONValue>(&args.get(0).unwrap().clone().to_string()) {
Ok(json) => Ok(to_value(json)), Ok(json) => Ok(to_value(json)),

102
src/lib/js/math.rs

@ -1,122 +1,124 @@
use crate::js::function::NativeFunctionData; use crate::js::{
use crate::js::value::{from_value, to_value, ResultValue, Value, ValueData}; function::NativeFunctionData,
value::{from_value, to_value, ResultValue, Value, ValueData},
};
use rand::random; use rand::random;
use std::f64; use std::f64;
/// Get the absolute value of a number /// Get the absolute value of a number
pub fn abs(_: Value, _: Value, args: Vec<Value>) -> ResultValue { pub fn abs(_: Value, _: Value, args: Vec<Value>) -> ResultValue {
Ok(to_value(if args.len() >= 1 { Ok(to_value(if args.is_empty() {
f64::NAN
} else {
from_value::<f64>(args.get(0).unwrap().clone()) from_value::<f64>(args.get(0).unwrap().clone())
.unwrap() .unwrap()
.abs() .abs()
} else {
f64::NAN
})) }))
} }
/// Get the arccos of a number /// Get the arccos of a number
pub fn acos(_: Value, _: Value, args: Vec<Value>) -> ResultValue { pub fn acos(_: Value, _: Value, args: Vec<Value>) -> ResultValue {
Ok(to_value(if args.len() >= 1 { Ok(to_value(if args.is_empty() {
f64::NAN
} else {
from_value::<f64>(args.get(0).unwrap().clone()) from_value::<f64>(args.get(0).unwrap().clone())
.unwrap() .unwrap()
.acos() .acos()
} else {
f64::NAN
})) }))
} }
/// Get the arcsine of a number /// Get the arcsine of a number
pub fn asin(_: Value, _: Value, args: Vec<Value>) -> ResultValue { pub fn asin(_: Value, _: Value, args: Vec<Value>) -> ResultValue {
Ok(to_value(if args.len() >= 1 { Ok(to_value(if args.is_empty() {
f64::NAN
} else {
from_value::<f64>(args.get(0).unwrap().clone()) from_value::<f64>(args.get(0).unwrap().clone())
.unwrap() .unwrap()
.asin() .asin()
} else {
f64::NAN
})) }))
} }
/// Get the arctangent of a number /// Get the arctangent of a number
pub fn atan(_: Value, _: Value, args: Vec<Value>) -> ResultValue { pub fn atan(_: Value, _: Value, args: Vec<Value>) -> ResultValue {
Ok(to_value(if args.len() >= 1 { Ok(to_value(if args.is_empty() {
f64::NAN
} else {
from_value::<f64>(args.get(0).unwrap().clone()) from_value::<f64>(args.get(0).unwrap().clone())
.unwrap() .unwrap()
.atan() .atan()
} else {
f64::NAN
})) }))
} }
/// Get the arctangent of a numbers /// Get the arctangent of a numbers
pub fn atan2(_: Value, _: Value, args: Vec<Value>) -> ResultValue { pub fn atan2(_: Value, _: Value, args: Vec<Value>) -> ResultValue {
Ok(to_value(if args.len() >= 1 { Ok(to_value(if args.is_empty() {
f64::NAN
} else {
from_value::<f64>(args.get(0).unwrap().clone()) from_value::<f64>(args.get(0).unwrap().clone())
.unwrap() .unwrap()
.atan2(args.get(1).unwrap().to_num()) .atan2(args.get(1).unwrap().to_num())
} else {
f64::NAN
})) }))
} }
/// Get the cubic root of a number /// Get the cubic root of a number
pub fn cbrt(_: Value, _: Value, args: Vec<Value>) -> ResultValue { pub fn cbrt(_: Value, _: Value, args: Vec<Value>) -> ResultValue {
Ok(to_value(if args.len() >= 1 { Ok(to_value(if args.is_empty() {
f64::NAN
} else {
from_value::<f64>(args.get(0).unwrap().clone()) from_value::<f64>(args.get(0).unwrap().clone())
.unwrap() .unwrap()
.cbrt() .cbrt()
} else {
f64::NAN
})) }))
} }
/// Get lowest integer above a number /// Get lowest integer above a number
pub fn ceil(_: Value, _: Value, args: Vec<Value>) -> ResultValue { pub fn ceil(_: Value, _: Value, args: Vec<Value>) -> ResultValue {
Ok(to_value(if args.len() >= 1 { Ok(to_value(if args.is_empty() {
f64::NAN
} else {
from_value::<f64>(args.get(0).unwrap().clone()) from_value::<f64>(args.get(0).unwrap().clone())
.unwrap() .unwrap()
.ceil() .ceil()
} else {
f64::NAN
})) }))
} }
/// Get the cosine of a number /// Get the cosine of a number
pub fn cos(_: Value, _: Value, args: Vec<Value>) -> ResultValue { pub fn cos(_: Value, _: Value, args: Vec<Value>) -> ResultValue {
Ok(to_value(if args.len() >= 1 { Ok(to_value(if args.is_empty() {
f64::NAN
} else {
from_value::<f64>(args.get(0).unwrap().clone()) from_value::<f64>(args.get(0).unwrap().clone())
.unwrap() .unwrap()
.cos() .cos()
} else {
f64::NAN
})) }))
} }
/// Get the power to raise the natural logarithm to get the number /// Get the power to raise the natural logarithm to get the number
pub fn exp(_: Value, _: Value, args: Vec<Value>) -> ResultValue { pub fn exp(_: Value, _: Value, args: Vec<Value>) -> ResultValue {
Ok(to_value(if args.len() >= 1 { Ok(to_value(if args.is_empty() {
f64::NAN
} else {
from_value::<f64>(args.get(0).unwrap().clone()) from_value::<f64>(args.get(0).unwrap().clone())
.unwrap() .unwrap()
.exp() .exp()
} else {
f64::NAN
})) }))
} }
/// Get the highest integer below a number /// Get the highest integer below a number
pub fn floor(_: Value, _: Value, args: Vec<Value>) -> ResultValue { pub fn floor(_: Value, _: Value, args: Vec<Value>) -> ResultValue {
Ok(to_value(if args.len() >= 1 { Ok(to_value(if args.is_empty() {
f64::NAN
} else {
from_value::<f64>(args.get(0).unwrap().clone()) from_value::<f64>(args.get(0).unwrap().clone())
.unwrap() .unwrap()
.floor() .floor()
} else {
f64::NAN
})) }))
} }
/// Get the natural logarithm of a number /// Get the natural logarithm of a number
pub fn log(_: Value, _: Value, args: Vec<Value>) -> ResultValue { pub fn log(_: Value, _: Value, args: Vec<Value>) -> ResultValue {
Ok(to_value(if args.len() >= 1 { Ok(to_value(if args.is_empty() {
f64::NAN
} else {
from_value::<f64>(args.get(0).unwrap().clone()) from_value::<f64>(args.get(0).unwrap().clone())
.unwrap() .unwrap()
.log(f64::consts::E) .log(f64::consts::E)
} else {
f64::NAN
})) }))
} }
/// Get the maximum of several numbers /// Get the maximum of several numbers
pub fn max(_: Value, _: Value, args: Vec<Value>) -> ResultValue { pub fn max(_: Value, _: Value, args: Vec<Value>) -> ResultValue {
let mut max = f64::NEG_INFINITY; let mut max = f64::NEG_INFINITY;
for arg in args.iter() { for arg in &args {
let num = arg.to_num(); let num = arg.to_num();
max = max.max(num); max = max.max(num);
} }
@ -125,7 +127,7 @@ pub fn max(_: Value, _: Value, args: Vec<Value>) -> ResultValue {
/// Get the minimum of several numbers /// Get the minimum of several numbers
pub fn min(_: Value, _: Value, args: Vec<Value>) -> ResultValue { pub fn min(_: Value, _: Value, args: Vec<Value>) -> ResultValue {
let mut max = f64::INFINITY; let mut max = f64::INFINITY;
for arg in args.iter() { for arg in &args {
let num = arg.to_num(); let num = arg.to_num();
max = max.min(num); max = max.min(num);
} }
@ -147,42 +149,42 @@ pub fn _random(_: Value, _: Value, _args: Vec<Value>) -> ResultValue {
} }
/// Round a number to the nearest integer /// Round a number to the nearest integer
pub fn round(_: Value, _: Value, args: Vec<Value>) -> ResultValue { pub fn round(_: Value, _: Value, args: Vec<Value>) -> ResultValue {
Ok(to_value(if args.len() >= 1 { Ok(to_value(if args.is_empty() {
f64::NAN
} else {
from_value::<f64>(args.get(0).unwrap().clone()) from_value::<f64>(args.get(0).unwrap().clone())
.unwrap() .unwrap()
.round() .round()
} else {
f64::NAN
})) }))
} }
/// Get the sine of a number /// Get the sine of a number
pub fn sin(_: Value, _: Value, args: Vec<Value>) -> ResultValue { pub fn sin(_: Value, _: Value, args: Vec<Value>) -> ResultValue {
Ok(to_value(if args.len() >= 1 { Ok(to_value(if args.is_empty() {
f64::NAN
} else {
from_value::<f64>(args.get(0).unwrap().clone()) from_value::<f64>(args.get(0).unwrap().clone())
.unwrap() .unwrap()
.sin() .sin()
} else {
f64::NAN
})) }))
} }
/// Get the square root of a number /// Get the square root of a number
pub fn sqrt(_: Value, _: Value, args: Vec<Value>) -> ResultValue { pub fn sqrt(_: Value, _: Value, args: Vec<Value>) -> ResultValue {
Ok(to_value(if args.len() >= 1 { Ok(to_value(if args.is_empty() {
f64::NAN
} else {
from_value::<f64>(args.get(0).unwrap().clone()) from_value::<f64>(args.get(0).unwrap().clone())
.unwrap() .unwrap()
.sqrt() .sqrt()
} else {
f64::NAN
})) }))
} }
/// Get the tangent of a number /// Get the tangent of a number
pub fn tan(_: Value, _: Value, args: Vec<Value>) -> ResultValue { pub fn tan(_: Value, _: Value, args: Vec<Value>) -> ResultValue {
Ok(to_value(if args.len() >= 1 { Ok(to_value(if args.is_empty() {
f64::NAN
} else {
from_value::<f64>(args.get(0).unwrap().clone()) from_value::<f64>(args.get(0).unwrap().clone())
.unwrap() .unwrap()
.tan() .tan()
} else {
f64::NAN
})) }))
} }
/// Create a new `Math` object /// Create a new `Math` object
@ -193,7 +195,7 @@ pub fn _create(global: &Value) -> Value {
math.set_field_slice("LN10", to_value(f64::consts::LN_10)); math.set_field_slice("LN10", to_value(f64::consts::LN_10));
math.set_field_slice("LOG2E", to_value(f64::consts::LOG2_E)); math.set_field_slice("LOG2E", to_value(f64::consts::LOG2_E));
math.set_field_slice("LOG10E", to_value(f64::consts::LOG10_E)); math.set_field_slice("LOG10E", to_value(f64::consts::LOG10_E));
math.set_field_slice("SQRT1_2", to_value(0.5f64.sqrt())); math.set_field_slice("SQRT1_2", to_value(0.5_f64.sqrt()));
math.set_field_slice("SQRT2", to_value(f64::consts::SQRT_2)); math.set_field_slice("SQRT2", to_value(f64::consts::SQRT_2));
math.set_field_slice("PI", to_value(f64::consts::PI)); math.set_field_slice("PI", to_value(f64::consts::PI));
math.set_field_slice("abs", to_value(abs as NativeFunctionData)); math.set_field_slice("abs", to_value(abs as NativeFunctionData));

22
src/lib/js/object.rs

@ -1,5 +1,7 @@
use crate::js::function::NativeFunctionData; use crate::js::{
use crate::js::value::{from_value, to_value, FromValue, ResultValue, ToValue, Value, ValueData}; function::NativeFunctionData,
value::{from_value, to_value, FromValue, ResultValue, ToValue, Value, ValueData},
};
use gc::Gc; use gc::Gc;
use std::collections::HashMap; use std::collections::HashMap;
@ -34,12 +36,12 @@ pub struct Property {
impl Property { impl Property {
/// Make a new property with the given value /// Make a new property with the given value
pub fn new(value: Value) -> Property { pub fn new(value: Value) -> Self {
Property { Self {
configurable: false, configurable: false,
enumerable: false, enumerable: false,
writable: false, writable: false,
value: value, value,
get: Gc::new(ValueData::Undefined), get: Gc::new(ValueData::Undefined),
set: Gc::new(ValueData::Undefined), set: Gc::new(ValueData::Undefined),
} }
@ -60,8 +62,8 @@ impl ToValue for Property {
} }
impl FromValue for Property { impl FromValue for Property {
fn from_value(v: Value) -> Result<Property, &'static str> { fn from_value(v: Value) -> Result<Self, &'static str> {
Ok(Property { Ok(Self {
configurable: from_value(v.get_field_slice("configurable")).unwrap(), configurable: from_value(v.get_field_slice("configurable")).unwrap(),
enumerable: from_value(v.get_field_slice("enumerable")).unwrap(), enumerable: from_value(v.get_field_slice("enumerable")).unwrap(),
writable: from_value(v.get_field_slice("writable")).unwrap(), writable: from_value(v.get_field_slice("writable")).unwrap(),
@ -107,13 +109,13 @@ pub fn to_string(this: Value, _: Value, _: Vec<Value>) -> ResultValue {
/// Check if it has a property /// Check if it has a property
pub fn has_own_prop(this: Value, _: Value, args: Vec<Value>) -> ResultValue { pub fn has_own_prop(this: Value, _: Value, args: Vec<Value>) -> ResultValue {
let prop = if args.len() == 0 { let prop = if args.is_empty() {
None None
} else { } else {
from_value::<String>(args.get(0).unwrap().clone()).ok() from_value::<String>(args.get(0).unwrap().clone()).ok()
}; };
Ok(to_value( Ok(to_value(
prop.is_some() && this.get_prop(prop.unwrap()).is_some(), prop.is_some() && this.get_prop(&prop.unwrap()).is_some(),
)) ))
} }
@ -126,7 +128,7 @@ pub fn _create(global: &Value) -> Value {
to_value(has_own_prop as NativeFunctionData), to_value(has_own_prop as NativeFunctionData),
); );
prototype.set_field_slice("toString", to_value(to_string as NativeFunctionData)); prototype.set_field_slice("toString", to_value(to_string as NativeFunctionData));
object.set_field_slice("length", to_value(1i32)); object.set_field_slice("length", to_value(1_i32));
object.set_field_slice(PROTOTYPE, prototype); object.set_field_slice(PROTOTYPE, prototype);
object.set_field_slice( object.set_field_slice(
"setPrototypeOf", "setPrototypeOf",

116
src/lib/js/string.rs

@ -1,12 +1,16 @@
use crate::js::function::NativeFunctionData; use crate::js::{
use crate::js::object::{Property, PROTOTYPE}; function::NativeFunctionData,
use crate::js::value::{from_value, to_value, ResultValue, Value, ValueData}; object::{Property, PROTOTYPE},
value::{from_value, to_value, ResultValue, Value, ValueData},
};
use gc::Gc; use gc::Gc;
use std::cmp::{max, min}; use std::{
use std::f64::NAN; cmp::{max, min},
f64::NAN,
};
/// Create new string /// Create new string
/// https://searchfox.org/mozilla-central/source/js/src/vm/StringObject.h#19 /// <https://searchfox.org/mozilla-central/source/js/src/vm/StringObject.h#19>
// This gets called when a new String() is created, it's called by exec:346 // This gets called when a new String() is created, it's called by exec:346
pub fn make_string(this: Value, _: Value, args: Vec<Value>) -> ResultValue { pub fn make_string(this: Value, _: Value, args: Vec<Value>) -> ResultValue {
// If we're constructing a string, we should set the initial length // If we're constructing a string, we should set the initial length
@ -20,73 +24,77 @@ pub fn make_string(this: Value, _: Value, args: Vec<Value>) -> ResultValue {
/// Get a string's length /// Get a string's length
pub fn get_string_length(this: Value, _: Value, _: Vec<Value>) -> ResultValue { pub fn get_string_length(this: Value, _: Value, _: Vec<Value>) -> ResultValue {
let this_str: String = let this_str: String = from_value(this.get_private_field("PrimitiveValue")).unwrap();
from_value(this.get_private_field(String::from("PrimitiveValue"))).unwrap();
Ok(to_value::<i32>(this_str.len() as i32)) Ok(to_value::<i32>(this_str.len() as i32))
} }
/// Get the string value to a primitive string /// Get the string value to a primitive string
pub fn to_string(this: Value, _: Value, _: Vec<Value>) -> ResultValue { pub fn to_string(this: Value, _: Value, _: Vec<Value>) -> ResultValue {
// Get String from String Object and send it back as a new value // Get String from String Object and send it back as a new value
let primitive_val = this.get_private_field(String::from("PrimitiveValue")); let primitive_val = this.get_private_field("PrimitiveValue");
Ok(to_value(format!("{}", primitive_val).to_string())) Ok(to_value(format!("{}", primitive_val).to_string()))
} }
/// Returns a single element String containing the code unit at index pos within the String value resulting from converting this object to a String. If there is no element at that index, the result is the empty String. The result is a String value, not a String object. /// Returns a single element String containing the code unit at index pos within the String value
/// https://tc39.github.io/ecma262/#sec-string.prototype.charat /// resulting from converting this object to a String. If there is no element at that index, the
/// result is the empty String. The result is a String value, not a String object.
/// <https://tc39.github.io/ecma262/#sec-string.prototype.charat>
pub fn char_at(this: Value, _: Value, args: Vec<Value>) -> ResultValue { pub fn char_at(this: Value, _: Value, args: Vec<Value>) -> ResultValue {
// ^^ represents instance ^^ represents arguments (we only care about the first one in this case) // ^^ represents instance ^^ represents arguments (we only care about the first one in this case)
// First we get it the actual string a private field stored on the object only the engine has access to. // First we get it the actual string a private field stored on the object only the engine has access to.
// Then we convert it into a Rust String by wrapping it in from_value // Then we convert it into a Rust String by wrapping it in from_value
let primitive_val: String = let primitive_val: String = from_value(this.get_private_field("PrimitiveValue")).unwrap();
from_value(this.get_private_field(String::from("PrimitiveValue"))).unwrap(); let pos: i32 = from_value(args[0].clone()).unwrap();
let pos = from_value(args[0].clone()).unwrap();
// Calling .len() on a string would give the wrong result, as they are bytes not the number of unicode code points // Calling .len() on a string would give the wrong result, as they are bytes not the number of
// Note that this is an O(N) operation (because UTF-8 is complex) while getting the number of bytes is an O(1) operation. // unicode code points
// Note that this is an O(N) operation (because UTF-8 is complex) while getting the number of
// bytes is an O(1) operation.
let length = primitive_val.chars().count(); let length = primitive_val.chars().count();
// We should return an empty string is pos is out of range // We should return an empty string is pos is out of range
if pos >= length || pos < 0 as usize { if pos >= length as i32 || pos < 0 {
return Ok(to_value::<String>(String::new())); return Ok(to_value::<String>(String::new()));
} }
Ok(to_value::<char>(primitive_val.chars().nth(pos).unwrap())) Ok(to_value::<char>(
primitive_val.chars().nth(pos as usize).unwrap(),
))
} }
/// Returns a Number (a nonnegative integer less than 216) that is the numeric value of the code unit at index pos within the String resulting from converting this object to a String. If there is no element at that index, the result is NaN. /// Returns a Number (a nonnegative integer less than 216) that is the numeric value of the code
/// https://tc39.github.io/ecma262/#sec-string.prototype.charcodeat /// unit at index pos within the String resulting from converting this object to a String. If there
/// is no element at that index, the result is NaN.
/// <https://tc39.github.io/ecma262/#sec-string.prototype.charcodeat>
pub fn char_code_at(this: Value, _: Value, args: Vec<Value>) -> ResultValue { pub fn char_code_at(this: Value, _: Value, args: Vec<Value>) -> ResultValue {
// ^^ represents instance ^^ represents arguments (we only care about the first one in this case) // ^^ represents instance ^^ represents arguments (we only care about the first one in this case)
// First we get it the actual string a private field stored on the object only the engine has access to. // First we get it the actual string a private field stored on the object only the engine has access to.
// Then we convert it into a Rust String by wrapping it in from_value // Then we convert it into a Rust String by wrapping it in from_value
let primitive_val: String = let primitive_val: String = from_value(this.get_private_field("PrimitiveValue")).unwrap();
from_value(this.get_private_field(String::from("PrimitiveValue"))).unwrap();
// Calling .len() on a string would give the wrong result, as they are bytes not the number of unicode code points // Calling .len() on a string would give the wrong result, as they are bytes not the number of unicode code points
// Note that this is an O(N) operation (because UTF-8 is complex) while getting the number of bytes is an O(1) operation. // Note that this is an O(N) operation (because UTF-8 is complex) while getting the number of bytes is an O(1) operation.
let length = primitive_val.chars().count(); let length = primitive_val.chars().count();
let pos = from_value(args[0].clone()).unwrap(); let pos: i32 = from_value(args[0].clone()).unwrap();
if pos >= length || pos < 0 as usize { if pos >= length as i32 || pos < 0 {
return Ok(to_value(NAN)); return Ok(to_value(NAN));
} }
let utf16_val = primitive_val.encode_utf16().nth(pos).unwrap(); let utf16_val = primitive_val.encode_utf16().nth(pos as usize).unwrap();
// If there is no element at that index, the result is NaN // If there is no element at that index, the result is NaN
// TODO: We currently don't have NaN // TODO: We currently don't have NaN
Ok(to_value(utf16_val as f64)) Ok(to_value(f64::from(utf16_val)))
} }
/// Returns a String that is the result of concatenating this String and all strings provided as /// Returns a String that is the result of concatenating this String and all strings provided as
/// arguments /// arguments
/// https://tc39.github.io/ecma262/#sec-string.prototype.concat /// <https://tc39.github.io/ecma262/#sec-string.prototype.concat>
pub fn concat(this: Value, _: Value, args: Vec<Value>) -> ResultValue { pub fn concat(this: Value, _: Value, args: Vec<Value>) -> ResultValue {
// ^^ represents instance ^^ represents arguments // ^^ represents instance ^^ represents arguments
// First we get it the actual string a private field stored on the object only the engine has access to. // First we get it the actual string a private field stored on the object only the engine has access to.
// Then we convert it into a Rust String by wrapping it in from_value // Then we convert it into a Rust String by wrapping it in from_value
let primitive_val: String = let primitive_val: String = from_value(this.get_private_field("PrimitiveValue")).unwrap();
from_value(this.get_private_field(String::from("PrimitiveValue"))).unwrap();
let mut new_str = primitive_val.clone(); let mut new_str = primitive_val.clone();
@ -100,13 +108,12 @@ pub fn concat(this: Value, _: Value, args: Vec<Value>) -> ResultValue {
/// Returns a String that is the result of repeating this String the number of times given by the /// Returns a String that is the result of repeating this String the number of times given by the
/// first argument /// first argument
/// https://tc39.github.io/ecma262/#sec-string.prototype.repeat /// <https://tc39.github.io/ecma262/#sec-string.prototype.repeat>
pub fn repeat(this: Value, _: Value, args: Vec<Value>) -> ResultValue { pub fn repeat(this: Value, _: Value, args: Vec<Value>) -> ResultValue {
// ^^ represents instance ^^ represents arguments (only care about the first one in this case) // ^^ represents instance ^^ represents arguments (only care about the first one in this case)
// First we get it the actual string a private field stored on the object only the engine has access to. // First we get it the actual string a private field stored on the object only the engine has access to.
// Then we convert it into a Rust String by wrapping it in from_value // Then we convert it into a Rust String by wrapping it in from_value
let primitive_val: String = let primitive_val: String = from_value(this.get_private_field("PrimitiveValue")).unwrap();
from_value(this.get_private_field(String::from("PrimitiveValue"))).unwrap();
let repeat_times: usize = from_value(args[0].clone()).unwrap(); let repeat_times: usize = from_value(args[0].clone()).unwrap();
Ok(to_value(primitive_val.repeat(repeat_times))) Ok(to_value(primitive_val.repeat(repeat_times)))
@ -114,13 +121,12 @@ pub fn repeat(this: Value, _: Value, args: Vec<Value>) -> ResultValue {
/// Returns a String which contains the slice of the JS String from character at "start" index up /// Returns a String which contains the slice of the JS String from character at "start" index up
/// to but not including character at "end" index /// to but not including character at "end" index
/// https://tc39.github.io/ecma262/#sec-string.prototype.slice /// <https://tc39.github.io/ecma262/#sec-string.prototype.slice>
pub fn slice(this: Value, _: Value, args: Vec<Value>) -> ResultValue { pub fn slice(this: Value, _: Value, args: Vec<Value>) -> ResultValue {
// ^^ represents instance ^^ represents arguments) // ^^ represents instance ^^ represents arguments)
// First we get it the actual string a private field stored on the object only the engine has access to. // First we get it the actual string a private field stored on the object only the engine has access to.
// Then we convert it into a Rust String by wrapping it in from_value // Then we convert it into a Rust String by wrapping it in from_value
let primitive_val: String = let primitive_val: String = from_value(this.get_private_field("PrimitiveValue")).unwrap();
from_value(this.get_private_field(String::from("PrimitiveValue"))).unwrap();
let start: i32 = from_value(args[0].clone()).unwrap(); let start: i32 = from_value(args[0].clone()).unwrap();
let end: i32 = from_value(args[1].clone()).unwrap(); let end: i32 = from_value(args[1].clone()).unwrap();
@ -152,13 +158,12 @@ pub fn slice(this: Value, _: Value, args: Vec<Value>) -> ResultValue {
/// Returns a Boolean indicating whether the sequence of code units of the /// Returns a Boolean indicating whether the sequence of code units of the
/// "search string" is the same as the corresponding code units of this string /// "search string" is the same as the corresponding code units of this string
/// starting at index "position" /// starting at index "position"
/// https://tc39.github.io/ecma262/#sec-string.prototype.startswith /// <https://tc39.github.io/ecma262/#sec-string.prototype.startswith>
pub fn starts_with(this: Value, _: Value, args: Vec<Value>) -> ResultValue { pub fn starts_with(this: Value, _: Value, args: Vec<Value>) -> ResultValue {
// ^^ represents instance ^^ represents arguments) // ^^ represents instance ^^ represents arguments)
// First we get it the actual string a private field stored on the object only the engine has access to. // First we get it the actual string a private field stored on the object only the engine has access to.
// Then we convert it into a Rust String by wrapping it in from_value // Then we convert it into a Rust String by wrapping it in from_value
let primitive_val: String = let primitive_val: String = from_value(this.get_private_field("PrimitiveValue")).unwrap();
from_value(this.get_private_field(String::from("PrimitiveValue"))).unwrap();
// TODO: Should throw TypeError if pattern is regular expression // TODO: Should throw TypeError if pattern is regular expression
let search_string: String = from_value(args[0].clone()).unwrap(); let search_string: String = from_value(args[0].clone()).unwrap();
@ -188,13 +193,12 @@ pub fn starts_with(this: Value, _: Value, args: Vec<Value>) -> ResultValue {
/// Returns a Boolean indicating whether the sequence of code units of the /// Returns a Boolean indicating whether the sequence of code units of the
/// "search string" is the same as the corresponding code units of this string /// "search string" is the same as the corresponding code units of this string
/// starting at position "end position" - length /// starting at position "end position" - length
/// https://tc39.github.io/ecma262/#sec-string.prototype.endswith /// <https://tc39.github.io/ecma262/#sec-string.prototype.endswith>
pub fn ends_with(this: Value, _: Value, args: Vec<Value>) -> ResultValue { pub fn ends_with(this: Value, _: Value, args: Vec<Value>) -> ResultValue {
// ^^ represents instance ^^ represents arguments) // ^^ represents instance ^^ represents arguments)
// First we get it the actual string a private field stored on the object only the engine has access to. // First we get it the actual string a private field stored on the object only the engine has access to.
// Then we convert it into a Rust String by wrapping it in from_value // Then we convert it into a Rust String by wrapping it in from_value
let primitive_val: String = let primitive_val: String = from_value(this.get_private_field("PrimitiveValue")).unwrap();
from_value(this.get_private_field(String::from("PrimitiveValue"))).unwrap();
// TODO: Should throw TypeError if search_string is regular expression // TODO: Should throw TypeError if search_string is regular expression
let search_string: String = from_value(args[0].clone()).unwrap(); let search_string: String = from_value(args[0].clone()).unwrap();
@ -226,13 +230,12 @@ pub fn ends_with(this: Value, _: Value, args: Vec<Value>) -> ResultValue {
/// the result of converting this object to a String, at one or more indices /// the result of converting this object to a String, at one or more indices
/// that are greater than or equal to position. If position is undefined, 0 is /// that are greater than or equal to position. If position is undefined, 0 is
/// assumed, so as to search all of the String. /// assumed, so as to search all of the String.
/// https://tc39.github.io/ecma262/#sec-string.prototype.includes /// <https://tc39.github.io/ecma262/#sec-string.prototype.includes>
pub fn includes(this: Value, _: Value, args: Vec<Value>) -> ResultValue { pub fn includes(this: Value, _: Value, args: Vec<Value>) -> ResultValue {
// ^^ represents instance ^^ represents arguments) // ^^ represents instance ^^ represents arguments)
// First we get it the actual string a private field stored on the object only the engine has access to. // First we get it the actual string a private field stored on the object only the engine has access to.
// Then we convert it into a Rust String by wrapping it in from_value // Then we convert it into a Rust String by wrapping it in from_value
let primitive_val: String = let primitive_val: String = from_value(this.get_private_field("PrimitiveValue")).unwrap();
from_value(this.get_private_field(String::from("PrimitiveValue"))).unwrap();
// TODO: Should throw TypeError if search_string is regular expression // TODO: Should throw TypeError if search_string is regular expression
let search_string: String = from_value(args[0].clone()).unwrap(); let search_string: String = from_value(args[0].clone()).unwrap();
@ -259,13 +262,12 @@ pub fn includes(this: Value, _: Value, args: Vec<Value>) -> ResultValue {
/// position, then the smallest such index is returned; otherwise, -1 is /// position, then the smallest such index is returned; otherwise, -1 is
/// returned. If position is undefined, 0 is assumed, so as to search all of the /// returned. If position is undefined, 0 is assumed, so as to search all of the
/// String. /// String.
/// https://tc39.github.io/ecma262/#sec-string.prototype.includes /// <https://tc39.github.io/ecma262/#sec-string.prototype.includes>
pub fn index_of(this: Value, _: Value, args: Vec<Value>) -> ResultValue { pub fn index_of(this: Value, _: Value, args: Vec<Value>) -> ResultValue {
// ^^ represents instance ^^ represents arguments) // ^^ represents instance ^^ represents arguments)
// First we get it the actual string a private field stored on the object only the engine has access to. // First we get it the actual string a private field stored on the object only the engine has access to.
// Then we convert it into a Rust String by wrapping it in from_value // Then we convert it into a Rust String by wrapping it in from_value
let primitive_val: String = let primitive_val: String = from_value(this.get_private_field("PrimitiveValue")).unwrap();
from_value(this.get_private_field(String::from("PrimitiveValue"))).unwrap();
// TODO: Should throw TypeError if search_string is regular expression // TODO: Should throw TypeError if search_string is regular expression
let search_string: String = from_value(args[0].clone()).unwrap(); let search_string: String = from_value(args[0].clone()).unwrap();
@ -293,7 +295,7 @@ pub fn index_of(this: Value, _: Value, args: Vec<Value>) -> ResultValue {
} }
} }
// Didn't find a match, so return -1 // Didn't find a match, so return -1
Ok(to_value(-1 as i32)) Ok(to_value(-1))
} }
//// If searchString appears as a substring of the result of converting this //// If searchString appears as a substring of the result of converting this
@ -301,13 +303,12 @@ pub fn index_of(this: Value, _: Value, args: Vec<Value>) -> ResultValue {
/// position, then the greatest such index is returned; otherwise, -1 is /// position, then the greatest such index is returned; otherwise, -1 is
/// returned. If position is undefined, the length of the String value is /// returned. If position is undefined, the length of the String value is
/// assumed, so as to search all of the String. /// assumed, so as to search all of the String.
/// https://tc39.github.io/ecma262/#sec-string.prototype.lastindexof /// <https://tc39.github.io/ecma262/#sec-string.prototype.lastindexof>
pub fn last_index_of(this: Value, _: Value, args: Vec<Value>) -> ResultValue { pub fn last_index_of(this: Value, _: Value, args: Vec<Value>) -> ResultValue {
// ^^ represents instance ^^ represents arguments) // ^^ represents instance ^^ represents arguments)
// First we get it the actual string a private field stored on the object only the engine has access to. // First we get it the actual string a private field stored on the object only the engine has access to.
// Then we convert it into a Rust String by wrapping it in from_value // Then we convert it into a Rust String by wrapping it in from_value
let primitive_val: String = let primitive_val: String = from_value(this.get_private_field("PrimitiveValue")).unwrap();
from_value(this.get_private_field(String::from("PrimitiveValue"))).unwrap();
// TODO: Should throw TypeError if search_string is regular expression // TODO: Should throw TypeError if search_string is regular expression
let search_string: String = from_value(args[0].clone()).unwrap(); let search_string: String = from_value(args[0].clone()).unwrap();
@ -348,9 +349,9 @@ fn is_trimmable_whitespace(c: char) -> bool {
// '\u{FEFF}' (zero width non-breaking space) // '\u{FEFF}' (zero width non-breaking space)
match c { match c {
// Explicit whitespace: https://tc39.es/ecma262/#sec-white-space // Explicit whitespace: https://tc39.es/ecma262/#sec-white-space
'\u{0009}' | '\u{000B}' | '\u{000C}' | '\u{0020}' | '\u{00A0}' | '\u{FEFF}' => true, '\u{0009}' | '\u{000B}' | '\u{000C}' | '\u{0020}' | '\u{00A0}' | '\u{FEFF}' |
// Unicode Space_Seperator category // Unicode Space_Seperator category
'\u{1680}' | '\u{2000}'..='\u{200A}' | '\u{202F}' | '\u{205F}' | '\u{3000}' => true, '\u{1680}' | '\u{2000}'..='\u{200A}' | '\u{202F}' | '\u{205F}' | '\u{3000}' |
// Line terminators: https://tc39.es/ecma262/#sec-line-terminators // Line terminators: https://tc39.es/ecma262/#sec-line-terminators
'\u{000A}' | '\u{000D}' | '\u{2028}' | '\u{2029}' => true, '\u{000A}' | '\u{000D}' | '\u{2028}' | '\u{2029}' => true,
_ => false, _ => false,
@ -358,22 +359,19 @@ fn is_trimmable_whitespace(c: char) -> bool {
} }
pub fn trim(this: Value, _: Value, _: Vec<Value>) -> ResultValue { pub fn trim(this: Value, _: Value, _: Vec<Value>) -> ResultValue {
let this_str: String = let this_str: String = from_value(this.get_private_field("PrimitiveValue")).unwrap();
from_value(this.get_private_field(String::from("PrimitiveValue"))).unwrap();
Ok(to_value(this_str.trim_matches(is_trimmable_whitespace))) Ok(to_value(this_str.trim_matches(is_trimmable_whitespace)))
} }
pub fn trim_start(this: Value, _: Value, _: Vec<Value>) -> ResultValue { pub fn trim_start(this: Value, _: Value, _: Vec<Value>) -> ResultValue {
let this_str: String = let this_str: String = from_value(this.get_private_field("PrimitiveValue")).unwrap();
from_value(this.get_private_field(String::from("PrimitiveValue"))).unwrap();
Ok(to_value( Ok(to_value(
this_str.trim_start_matches(is_trimmable_whitespace), this_str.trim_start_matches(is_trimmable_whitespace),
)) ))
} }
pub fn trim_end(this: Value, _: Value, _: Vec<Value>) -> ResultValue { pub fn trim_end(this: Value, _: Value, _: Vec<Value>) -> ResultValue {
let this_str: String = let this_str: String = from_value(this.get_private_field("PrimitiveValue")).unwrap();
from_value(this.get_private_field(String::from("PrimitiveValue"))).unwrap();
Ok(to_value(this_str.trim_end_matches(is_trimmable_whitespace))) Ok(to_value(this_str.trim_end_matches(is_trimmable_whitespace)))
} }

215
src/lib/js/value.rs

@ -1,18 +1,17 @@
use crate::js::function::{Function, NativeFunction, NativeFunctionData}; use crate::js::{
use crate::js::object::{ObjectData, Property, INSTANCE_PROTOTYPE, PROTOTYPE}; function::{Function, NativeFunction, NativeFunctionData},
object::{ObjectData, Property, INSTANCE_PROTOTYPE, PROTOTYPE},
};
use gc::{Gc, GcCell}; use gc::{Gc, GcCell};
use serde_json::map::Map; use serde_json::{map::Map, Number as JSONNumber, Value as JSONValue};
use serde_json::Number as JSONNumber; use std::{
use serde_json::Value as JSONValue; collections::HashMap,
use std::collections::HashMap; f64::NAN,
use std::f64::NAN; fmt::{self, Display},
use std::fmt; iter::FromIterator,
use std::fmt::Display; ops::{Add, BitAnd, BitOr, BitXor, Deref, DerefMut, Div, Mul, Not, Rem, Shl, Shr, Sub},
use std::iter::FromIterator; str::FromStr,
use std::ops::Deref; };
use std::ops::DerefMut;
use std::ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Not, Rem, Shl, Shr, Sub};
use std::str::FromStr;
#[must_use] #[must_use]
/// The result of a Javascript expression is represented like this so it can succeed (`Ok`) or fail (`Err`) /// The result of a Javascript expression is represented like this so it can succeed (`Ok`) or fail (`Err`)
@ -160,7 +159,7 @@ impl ValueData {
ValueData::Number(num) => num, ValueData::Number(num) => num,
ValueData::Boolean(true) => 1.0, ValueData::Boolean(true) => 1.0,
ValueData::Boolean(false) | ValueData::Null => 0.0, ValueData::Boolean(false) | ValueData::Null => 0.0,
ValueData::Integer(num) => num as f64, ValueData::Integer(num) => f64::from(num),
} }
} }
@ -184,7 +183,7 @@ impl ValueData {
/// remove_prop removes a property from a Value object. /// remove_prop removes a property from a Value object.
/// It will return a boolean based on if the value was removed, if there was no value to remove false is returned /// It will return a boolean based on if the value was removed, if there was no value to remove false is returned
pub fn remove_prop(&self, field: &String) { pub fn remove_prop(&self, field: &str) {
match *self { match *self {
ValueData::Object(ref obj, _) => obj.borrow_mut().deref_mut().remove(field), ValueData::Object(ref obj, _) => obj.borrow_mut().deref_mut().remove(field),
// Accesing .object on borrow() seems to automatically dereference it, so we don't need the * // Accesing .object on borrow() seems to automatically dereference it, so we don't need the *
@ -198,7 +197,7 @@ impl ValueData {
/// Resolve the property in the object /// Resolve the property in the object
/// Returns a copy of the Property /// Returns a copy of the Property
pub fn get_prop(&self, field: String) -> Option<Property> { pub fn get_prop(&self, field: &str) -> Option<Property> {
// Spidermonkey has its own GetLengthProperty: https://searchfox.org/mozilla-central/source/js/src/vm/Interpreter-inl.h#154 // Spidermonkey has its own GetLengthProperty: https://searchfox.org/mozilla-central/source/js/src/vm/Interpreter-inl.h#154
// This is only for primitive strings, String() objects have their lengths calculated in string.rs // This is only for primitive strings, String() objects have their lengths calculated in string.rs
if self.is_string() && field == "length" { if self.is_string() && field == "length" {
@ -222,7 +221,7 @@ impl ValueData {
_ => return None, _ => return None,
}; };
match obj.get(&field) { match obj.get(field) {
Some(val) => Some(val.clone()), Some(val) => Some(val.clone()),
None => match obj.get(&INSTANCE_PROTOTYPE.to_string()) { None => match obj.get(&INSTANCE_PROTOTYPE.to_string()) {
Some(prop) => prop.value.get_prop(field), Some(prop) => prop.value.get_prop(field),
@ -236,7 +235,7 @@ impl ValueData {
/// Mostly used internally for now /// Mostly used internally for now
pub fn update_prop( pub fn update_prop(
&self, &self,
field: String, field: &str,
value: Option<Value>, value: Option<Value>,
enumerable: Option<bool>, enumerable: Option<bool>,
writable: Option<bool>, writable: Option<bool>,
@ -252,27 +251,20 @@ impl ValueData {
_ => None, _ => None,
}; };
if obj.is_none() { if let Some(mut hashmap) = obj {
return ();
}
let mut hashmap = obj.unwrap();
// Use value, or walk up the prototype chain // Use value, or walk up the prototype chain
match hashmap.get_mut(&field) { if let Some(ref mut prop) = hashmap.get_mut(field) {
Some(ref mut prop) => { prop.value = value.unwrap_or_else(|| prop.value.clone());
prop.value = value.unwrap_or(prop.value.clone());
prop.enumerable = enumerable.unwrap_or(prop.enumerable); prop.enumerable = enumerable.unwrap_or(prop.enumerable);
prop.writable = writable.unwrap_or(prop.writable); prop.writable = writable.unwrap_or(prop.writable);
prop.configurable = configurable.unwrap_or(prop.configurable); prop.configurable = configurable.unwrap_or(prop.configurable);
} }
// Try again with next prop up the chain
None => (),
} }
} }
/// Resolve the property in the object /// Resolve the property in the object
/// Returns a copy of the Property /// Returns a copy of the Property
pub fn get_private_prop(&self, field: String) -> Option<Property> { pub fn get_private_prop(&self, field: &str) -> Option<Property> {
let obj: ObjectData = match *self { let obj: ObjectData = match *self {
ValueData::Object(_, ref obj) => { ValueData::Object(_, ref obj) => {
let hash = obj.clone(); let hash = obj.clone();
@ -281,7 +273,7 @@ impl ValueData {
_ => return None, _ => return None,
}; };
match obj.get(&field) { match obj.get(field) {
Some(val) => Some(val.clone()), Some(val) => Some(val.clone()),
None => match obj.get(&INSTANCE_PROTOTYPE.to_string()) { None => match obj.get(&INSTANCE_PROTOTYPE.to_string()) {
Some(prop) => prop.value.get_prop(field), Some(prop) => prop.value.get_prop(field),
@ -292,7 +284,7 @@ impl ValueData {
/// Resolve the property in the object and get its value, or undefined if this is not an object or the field doesn't exist /// Resolve the property in the object and get its value, or undefined if this is not an object or the field doesn't exist
/// get_field recieves a Property from get_prop(). It should then return the [[Get]] result value if that's set, otherwise fall back to [[Value]] /// get_field recieves a Property from get_prop(). It should then return the [[Get]] result value if that's set, otherwise fall back to [[Value]]
pub fn get_private_field(&self, field: String) -> Value { pub fn get_private_field(&self, field: &str) -> Value {
match self.get_private_prop(field) { match self.get_private_prop(field) {
Some(prop) => prop.value.clone(), Some(prop) => prop.value.clone(),
None => Gc::new(ValueData::Undefined), None => Gc::new(ValueData::Undefined),
@ -301,7 +293,7 @@ impl ValueData {
/// Resolve the property in the object and get its value, or undefined if this is not an object or the field doesn't exist /// Resolve the property in the object and get its value, or undefined if this is not an object or the field doesn't exist
/// get_field recieves a Property from get_prop(). It should then return the [[Get]] result value if that's set, otherwise fall back to [[Value]] /// get_field recieves a Property from get_prop(). It should then return the [[Get]] result value if that's set, otherwise fall back to [[Value]]
pub fn get_field(&self, field: String) -> Value { pub fn get_field(&self, field: &str) -> Value {
match self.get_prop(field) { match self.get_prop(field) {
Some(prop) => { Some(prop) => {
// If the Property has [[Get]] set to a function, we should run that and return the Value // If the Property has [[Get]] set to a function, we should run that and return the Value
@ -334,32 +326,28 @@ impl ValueData {
} }
/// Check to see if the Value has the field, mainly used by environment records /// Check to see if the Value has the field, mainly used by environment records
pub fn has_field(&self, field: String) -> bool { pub fn has_field(&self, field: &str) -> bool {
match self.get_prop(field) { self.get_prop(field).is_some()
Some(_) => true,
None => false,
}
} }
/// Resolve the property in the object and get its value, or undefined if this is not an object or the field doesn't exist /// Resolve the property in the object and get its value, or undefined if this is not an object or the field doesn't exist
pub fn get_field_slice<'a>(&self, field: &'a str) -> Value { pub fn get_field_slice(&self, field: &str) -> Value {
self.get_field(field.to_string()) self.get_field(field)
} }
/// Set the field in the value /// Set the field in the value
pub fn set_field(&self, field: String, val: Value) -> Value { pub fn set_field(&self, field: String, val: Value) -> Value {
match *self { match *self {
ValueData::Object(ref obj, _) => { ValueData::Object(ref obj, _) => {
obj.borrow_mut() obj.borrow_mut().insert(field, Property::new(val.clone()));
.insert(field.clone(), Property::new(val.clone()));
} }
ValueData::Function(ref func) => { ValueData::Function(ref func) => {
match *func.borrow_mut().deref_mut() { match *func.borrow_mut().deref_mut() {
Function::NativeFunc(ref mut f) => { Function::NativeFunc(ref mut f) => {
f.object.insert(field.clone(), Property::new(val.clone())) f.object.insert(field, Property::new(val.clone()))
} }
Function::RegularFunc(ref mut f) => { Function::RegularFunc(ref mut f) => {
f.object.insert(field.clone(), Property::new(val.clone())) f.object.insert(field, Property::new(val.clone()))
} }
}; };
} }
@ -375,12 +363,8 @@ impl ValueData {
/// Set the private field in the value /// Set the private field in the value
pub fn set_private_field(&self, field: String, val: Value) -> Value { pub fn set_private_field(&self, field: String, val: Value) -> Value {
match *self { if let ValueData::Object(_, ref obj) = *self {
ValueData::Object(_, ref obj) => { obj.borrow_mut().insert(field, Property::new(val.clone()));
obj.borrow_mut()
.insert(field.clone(), Property::new(val.clone()));
}
_ => (),
} }
val val
} }
@ -394,14 +378,12 @@ impl ValueData {
pub fn set_prop(&self, field: String, prop: Property) -> Property { pub fn set_prop(&self, field: String, prop: Property) -> Property {
match *self { match *self {
ValueData::Object(ref obj, _) => { ValueData::Object(ref obj, _) => {
obj.borrow_mut().insert(field.clone(), prop.clone()); obj.borrow_mut().insert(field, prop.clone());
} }
ValueData::Function(ref func) => { ValueData::Function(ref func) => {
match *func.borrow_mut().deref_mut() { match *func.borrow_mut().deref_mut() {
Function::NativeFunc(ref mut f) => f.object.insert(field.clone(), prop.clone()), Function::NativeFunc(ref mut f) => f.object.insert(field, prop.clone()),
Function::RegularFunc(ref mut f) => { Function::RegularFunc(ref mut f) => f.object.insert(field, prop.clone()),
f.object.insert(field.clone(), prop.clone())
}
}; };
} }
_ => (), _ => (),
@ -415,7 +397,7 @@ impl ValueData {
} }
/// Convert from a JSON value to a JS value /// Convert from a JSON value to a JS value
pub fn from_json(json: JSONValue) -> ValueData { pub fn from_json(json: JSONValue) -> Self {
match json { match json {
JSONValue::Number(v) => ValueData::Number(v.as_f64().unwrap()), JSONValue::Number(v) => ValueData::Number(v.as_f64().unwrap()),
JSONValue::String(v) => ValueData::String(v), JSONValue::String(v) => ValueData::String(v),
@ -453,13 +435,13 @@ impl ValueData {
ValueData::Null | ValueData::Undefined => JSONValue::Null, ValueData::Null | ValueData::Undefined => JSONValue::Null,
ValueData::Boolean(b) => JSONValue::Bool(b), ValueData::Boolean(b) => JSONValue::Bool(b),
ValueData::Object(ref obj, _) => { ValueData::Object(ref obj, _) => {
let mut nobj = Map::new(); let mut new_obj = Map::new();
for (k, v) in obj.borrow().iter() { for (k, v) in obj.borrow().iter() {
if k != INSTANCE_PROTOTYPE { if k != INSTANCE_PROTOTYPE {
nobj.insert(k.clone(), v.value.to_json()); new_obj.insert(k.clone(), v.value.to_json());
} }
} }
JSONValue::Object(nobj) JSONValue::Object(new_obj)
} }
ValueData::String(ref str) => JSONValue::String(str.clone()), ValueData::String(ref str) => JSONValue::String(str.clone()),
ValueData::Number(num) => JSONValue::Number(JSONNumber::from_f64(num).unwrap()), ValueData::Number(num) => JSONValue::Number(JSONNumber::from_f64(num).unwrap()),
@ -499,37 +481,31 @@ impl Display for ValueData {
} }
), ),
ValueData::Object(ref v, ref p) => { ValueData::Object(ref v, ref p) => {
write!(f, "{}", "{")?; write!(f, "{{")?;
// Print public properties // Print public properties
match v.borrow().iter().last() { if let Some((last_key, _)) = v.borrow().iter().last() {
Some((last_key, _)) => {
for (key, val) in v.borrow().iter() { for (key, val) in v.borrow().iter() {
write!(f, "{}: {}", key, val.value.clone())?; write!(f, "{}: {}", key, val.value.clone())?;
if key != last_key { if key != last_key {
write!(f, "{}", ", ")?; write!(f, ", ")?;
}
} }
} }
None => (),
}; };
// Print private properties // Print private properties
match p.borrow().iter().last() { if let Some((last_key, _)) = p.borrow().iter().last() {
Some((last_key, _)) => {
for (key, val) in p.borrow().iter() { for (key, val) in p.borrow().iter() {
write!(f, "(Private) {}: {}", key, val.value.clone())?; write!(f, "(Private) {}: {}", key, val.value.clone())?;
if key != last_key { if key != last_key {
write!(f, "{}", ", ")?; write!(f, ", ")?;
}
} }
} }
None => (),
}; };
write!(f, "{}", "}") write!(f, "}}")
} }
ValueData::Integer(v) => write!(f, "{}", v), ValueData::Integer(v) => write!(f, "{}", v),
ValueData::Function(ref v) => match *v.borrow() { ValueData::Function(ref v) => match *v.borrow() {
Function::NativeFunc(_) => write!(f, "{}", "function() { [native code] }"), Function::NativeFunc(_) => write!(f, "function() {{ [native code] }}"),
Function::RegularFunc(ref rf) => { Function::RegularFunc(ref rf) => {
write!(f, "function({}){}", rf.args.join(", "), rf.expr) write!(f, "function({}){}", rf.args.join(", "), rf.expr)
} }
@ -539,7 +515,7 @@ impl Display for ValueData {
} }
impl PartialEq for ValueData { impl PartialEq for ValueData {
fn eq(&self, other: &ValueData) -> bool { fn eq(&self, other: &Self) -> bool {
match (self.clone(), other.clone()) { match (self.clone(), other.clone()) {
// TODO: fix this // TODO: fix this
// _ if self.ptr.to_inner() == &other.ptr.to_inner() => true, // _ if self.ptr.to_inner() == &other.ptr.to_inner() => true,
@ -562,73 +538,72 @@ impl PartialEq for ValueData {
} }
impl Add for ValueData { impl Add for ValueData {
type Output = ValueData; type Output = Self;
fn add(self, other: ValueData) -> ValueData { fn add(self, other: Self) -> Self {
return match (self.clone(), other.clone()) { match (self, other) {
(ValueData::String(ref s), ref other) | (ref other, ValueData::String(ref s)) => { (ValueData::String(ref s), ref o) => ValueData::String(s.clone() + &o.to_string()),
ValueData::String(s.clone() + &other.to_string()) (ref s, ValueData::String(ref o)) => ValueData::String(s.to_string() + o),
(ref s, ref o) => ValueData::Number(s.to_num() + o.to_num()),
} }
(_, _) => ValueData::Number(self.to_num() + other.to_num()),
};
} }
} }
impl Sub for ValueData { impl Sub for ValueData {
type Output = ValueData; type Output = Self;
fn sub(self, other: ValueData) -> ValueData { fn sub(self, other: Self) -> Self {
ValueData::Number(self.to_num() - other.to_num()) ValueData::Number(self.to_num() - other.to_num())
} }
} }
impl Mul for ValueData { impl Mul for ValueData {
type Output = ValueData; type Output = Self;
fn mul(self, other: ValueData) -> ValueData { fn mul(self, other: Self) -> Self {
ValueData::Number(self.to_num() * other.to_num()) ValueData::Number(self.to_num() * other.to_num())
} }
} }
impl Div for ValueData { impl Div for ValueData {
type Output = ValueData; type Output = Self;
fn div(self, other: ValueData) -> ValueData { fn div(self, other: Self) -> Self {
ValueData::Number(self.to_num() / other.to_num()) ValueData::Number(self.to_num() / other.to_num())
} }
} }
impl Rem for ValueData { impl Rem for ValueData {
type Output = ValueData; type Output = Self;
fn rem(self, other: ValueData) -> ValueData { fn rem(self, other: Self) -> Self {
ValueData::Number(self.to_num() % other.to_num()) ValueData::Number(self.to_num() % other.to_num())
} }
} }
impl BitAnd for ValueData { impl BitAnd for ValueData {
type Output = ValueData; type Output = Self;
fn bitand(self, other: ValueData) -> ValueData { fn bitand(self, other: Self) -> Self {
ValueData::Integer(self.to_int() & other.to_int()) ValueData::Integer(self.to_int() & other.to_int())
} }
} }
impl BitOr for ValueData { impl BitOr for ValueData {
type Output = ValueData; type Output = Self;
fn bitor(self, other: ValueData) -> ValueData { fn bitor(self, other: Self) -> Self {
ValueData::Integer(self.to_int() | other.to_int()) ValueData::Integer(self.to_int() | other.to_int())
} }
} }
impl BitXor for ValueData { impl BitXor for ValueData {
type Output = ValueData; type Output = Self;
fn bitxor(self, other: ValueData) -> ValueData { fn bitxor(self, other: Self) -> Self {
ValueData::Integer(self.to_int() ^ other.to_int()) ValueData::Integer(self.to_int() ^ other.to_int())
} }
} }
impl Shl for ValueData { impl Shl for ValueData {
type Output = ValueData; type Output = Self;
fn shl(self, other: ValueData) -> ValueData { fn shl(self, other: Self) -> Self {
ValueData::Integer(self.to_int() << other.to_int()) ValueData::Integer(self.to_int() << other.to_int())
} }
} }
impl Shr for ValueData { impl Shr for ValueData {
type Output = ValueData; type Output = Self;
fn shr(self, other: ValueData) -> ValueData { fn shr(self, other: Self) -> Self {
ValueData::Integer(self.to_int() >> other.to_int()) ValueData::Integer(self.to_int() >> other.to_int())
} }
} }
impl Not for ValueData { impl Not for ValueData {
type Output = ValueData; type Output = Self;
fn not(self) -> ValueData { fn not(self) -> Self {
ValueData::Boolean(!self.is_true()) ValueData::Boolean(!self.is_true())
} }
} }
@ -653,7 +628,7 @@ impl ToValue for String {
} }
impl FromValue for String { impl FromValue for String {
fn from_value(v: Value) -> Result<String, &'static str> { fn from_value(v: Value) -> Result<Self, &'static str> {
Ok(v.to_string()) Ok(v.to_string())
} }
} }
@ -670,7 +645,7 @@ impl ToValue for char {
} }
} }
impl FromValue for char { impl FromValue for char {
fn from_value(v: Value) -> Result<char, &'static str> { fn from_value(v: Value) -> Result<Self, &'static str> {
Ok(v.to_string().chars().next().unwrap()) Ok(v.to_string().chars().next().unwrap())
} }
} }
@ -681,7 +656,7 @@ impl ToValue for f64 {
} }
} }
impl FromValue for f64 { impl FromValue for f64 {
fn from_value(v: Value) -> Result<f64, &'static str> { fn from_value(v: Value) -> Result<Self, &'static str> {
Ok(v.to_num()) Ok(v.to_num())
} }
} }
@ -692,7 +667,7 @@ impl ToValue for i32 {
} }
} }
impl FromValue for i32 { impl FromValue for i32 {
fn from_value(v: Value) -> Result<i32, &'static str> { fn from_value(v: Value) -> Result<Self, &'static str> {
Ok(v.to_int()) Ok(v.to_int())
} }
} }
@ -703,7 +678,7 @@ impl ToValue for usize {
} }
} }
impl FromValue for usize { impl FromValue for usize {
fn from_value(v: Value) -> Result<usize, &'static str> { fn from_value(v: Value) -> Result<Self, &'static str> {
Ok(v.to_int() as usize) Ok(v.to_int() as usize)
} }
} }
@ -714,7 +689,7 @@ impl ToValue for bool {
} }
} }
impl FromValue for bool { impl FromValue for bool {
fn from_value(v: Value) -> Result<bool, &'static str> { fn from_value(v: Value) -> Result<Self, &'static str> {
Ok(v.is_true()) Ok(v.is_true())
} }
} }
@ -722,10 +697,8 @@ impl FromValue for bool {
impl<'s, T: ToValue> ToValue for &'s [T] { impl<'s, T: ToValue> ToValue for &'s [T] {
fn to_value(&self) -> Value { fn to_value(&self) -> Value {
let mut arr = HashMap::new(); let mut arr = HashMap::new();
let mut i = 0; for (i, item) in self.iter().enumerate() {
for item in self.iter() {
arr.insert(i.to_string(), Property::new(item.to_value())); arr.insert(i.to_string(), Property::new(item.to_value()));
i += 1;
} }
to_value(arr) to_value(arr)
} }
@ -733,21 +706,19 @@ impl<'s, T: ToValue> ToValue for &'s [T] {
impl<T: ToValue> ToValue for Vec<T> { impl<T: ToValue> ToValue for Vec<T> {
fn to_value(&self) -> Value { fn to_value(&self) -> Value {
let mut arr = HashMap::new(); let mut arr = HashMap::new();
let mut i = 0; for (i, item) in self.iter().enumerate() {
for item in self.iter() {
arr.insert(i.to_string(), Property::new(item.to_value())); arr.insert(i.to_string(), Property::new(item.to_value()));
i += 1;
} }
to_value(arr) to_value(arr)
} }
} }
impl<T: FromValue> FromValue for Vec<T> { impl<T: FromValue> FromValue for Vec<T> {
fn from_value(v: Value) -> Result<Vec<T>, &'static str> { fn from_value(v: Value) -> Result<Self, &'static str> {
let len = v.get_field_slice("length").to_int(); let len = v.get_field_slice("length").to_int();
let mut vec = Vec::with_capacity(len as usize); let mut vec = Self::with_capacity(len as usize);
for i in 0..len { for i in 0..len {
vec.push(from_value(v.get_field(i.to_string()))?) vec.push(from_value(v.get_field(&i.to_string()))?)
} }
Ok(vec) Ok(vec)
} }
@ -755,7 +726,7 @@ impl<T: FromValue> FromValue for Vec<T> {
impl ToValue for ObjectData { impl ToValue for ObjectData {
fn to_value(&self) -> Value { fn to_value(&self) -> Value {
let private_obj: ObjectData = HashMap::new(); let private_obj: Self = HashMap::new();
Gc::new(ValueData::Object( Gc::new(ValueData::Object(
GcCell::new(self.clone()), GcCell::new(self.clone()),
GcCell::new(private_obj), GcCell::new(private_obj),
@ -764,7 +735,7 @@ impl ToValue for ObjectData {
} }
impl FromValue for ObjectData { impl FromValue for ObjectData {
fn from_value(v: Value) -> Result<ObjectData, &'static str> { fn from_value(v: Value) -> Result<Self, &'static str> {
match *v { match *v {
ValueData::Object(ref obj, _) => Ok(obj.clone().into_inner()), ValueData::Object(ref obj, _) => Ok(obj.clone().into_inner()),
ValueData::Function(ref func) => Ok(match *func.borrow().deref() { ValueData::Function(ref func) => Ok(match *func.borrow().deref() {
@ -783,7 +754,7 @@ impl ToValue for JSONValue {
} }
impl FromValue for JSONValue { impl FromValue for JSONValue {
fn from_value(v: Value) -> Result<JSONValue, &'static str> { fn from_value(v: Value) -> Result<Self, &'static str> {
Ok(v.to_json()) Ok(v.to_json())
} }
} }
@ -808,7 +779,7 @@ impl<T: ToValue> ToValue for Option<T> {
} }
} }
impl<T: FromValue> FromValue for Option<T> { impl<T: FromValue> FromValue for Option<T> {
fn from_value(value: Value) -> Result<Option<T>, &'static str> { fn from_value(value: Value) -> Result<Self, &'static str> {
Ok(if value.is_null_or_undefined() { Ok(if value.is_null_or_undefined() {
None None
} else { } else {
@ -825,7 +796,7 @@ impl ToValue for NativeFunctionData {
} }
} }
impl FromValue for NativeFunctionData { impl FromValue for NativeFunctionData {
fn from_value(v: Value) -> Result<NativeFunctionData, &'static str> { fn from_value(v: Value) -> Result<Self, &'static str> {
match *v { match *v {
ValueData::Function(ref func) => match *func.borrow() { ValueData::Function(ref func) => match *func.borrow() {
Function::NativeFunc(ref data) => Ok(data.data), Function::NativeFunc(ref data) => Ok(data.data),
@ -836,12 +807,12 @@ impl FromValue for NativeFunctionData {
} }
} }
/// A utility function that just calls FromValue::from_value /// A utility function that just calls `FromValue::from_value`
pub fn from_value<A: FromValue>(v: Value) -> Result<A, &'static str> { pub fn from_value<A: FromValue>(v: Value) -> Result<A, &'static str> {
FromValue::from_value(v) FromValue::from_value(v)
} }
/// A utility function that just calls ToValue::to_value /// A utility function that just calls `ToValue::to_value`
pub fn to_value<A: ToValue>(v: A) -> Value { pub fn to_value<A: ToValue>(v: A) -> Value {
v.to_value() v.to_value()
} }

36
src/lib/lib.rs

@ -1,7 +1,27 @@
extern crate chrono; #![forbid(
extern crate gc; //missing_docs,
extern crate rand; //warnings,
extern crate serde_json; anonymous_parameters,
unused_extern_crates,
unused_import_braces,
missing_copy_implementations,
//trivial_casts,
variant_size_differences,
missing_debug_implementations,
trivial_numeric_casts
)]
// Debug trait derivation will show an error if forbidden.
#![deny(unused_qualifications, unsafe_code)]
#![deny(clippy::all)]
#![warn(clippy::pedantic)]
#![allow(
clippy::many_single_char_names,
clippy::unreadable_literal,
clippy::excessive_precision,
clippy::module_name_repetitions,
clippy::pub_enum_variant_names,
clippy::cognitive_complexity
)]
#[macro_use] #[macro_use]
extern crate gc_derive; extern crate gc_derive;
@ -24,8 +44,8 @@ extern "C" {
fn log(s: &str); fn log(s: &str);
} }
pub fn exec(src: String) -> String { pub fn exec(src: &str) -> String {
let mut lexer = Lexer::new(&src); let mut lexer = Lexer::new(src);
lexer.lex().unwrap(); lexer.lex().unwrap();
let tokens = lexer.tokens; let tokens = lexer.tokens;
@ -36,7 +56,7 @@ pub fn exec(src: String) -> String {
let result = engine.run(&expr); let result = engine.run(&expr);
match result { match result {
Ok(v) => v.to_string(), Ok(v) => v.to_string(),
Err(v) => String::from(format!("{}: {}", "Error", v.to_string())), Err(v) => format!("{}: {}", "Error", v.to_string()),
} }
} }
@ -69,7 +89,7 @@ pub fn evaluate(src: &str) -> String {
Ok(v) => v.to_string(), Ok(v) => v.to_string(),
Err(v) => { Err(v) => {
log(&format!("{} {}", "asudihsiu", v.to_string())); log(&format!("{} {}", "asudihsiu", v.to_string()));
String::from(format!("{}: {}", "error", v.to_string())) format!("{}: {}", "error", v.to_string())
} }
} }
} }

4
src/lib/syntax/ast/constant.rs

@ -21,7 +21,7 @@ pub enum Const {
impl Display for Const { impl Display for Const {
fn fmt(&self, f: &mut Formatter) -> Result { fn fmt(&self, f: &mut Formatter) -> Result {
return match *self { match *self {
Const::String(ref st) => write!(f, "\"{}\"", st), Const::String(ref st) => write!(f, "\"{}\"", st),
Const::RegExp(ref reg, _, _) => write!(f, "~/{}/", reg), Const::RegExp(ref reg, _, _) => write!(f, "~/{}/", reg),
Const::Num(num) => write!(f, "{}", num), Const::Num(num) => write!(f, "{}", num),
@ -29,6 +29,6 @@ impl Display for Const {
Const::Bool(v) => write!(f, "{}", v), Const::Bool(v) => write!(f, "{}", v),
Const::Null => write!(f, "null"), Const::Null => write!(f, "null"),
Const::Undefined => write!(f, "undefined"), Const::Undefined => write!(f, "undefined"),
}; }
} }
} }

32
src/lib/syntax/ast/expr.rs

@ -1,7 +1,11 @@
use crate::syntax::ast::constant::Const; use crate::syntax::ast::{
use crate::syntax::ast::op::{BinOp, Operator, UnaryOp}; constant::Const,
use std::collections::btree_map::BTreeMap; op::{BinOp, Operator, UnaryOp},
use std::fmt::{Display, Formatter, Result}; };
use std::{
collections::btree_map::BTreeMap,
fmt::{Display, Formatter, Result},
};
#[derive(Clone, Trace, Finalize, Debug, PartialEq)] #[derive(Clone, Trace, Finalize, Debug, PartialEq)]
pub struct Expr { pub struct Expr {
@ -11,8 +15,8 @@ pub struct Expr {
impl Expr { impl Expr {
/// Create a new expression with a starting and ending position /// Create a new expression with a starting and ending position
pub fn new(def: ExprDef) -> Expr { pub fn new(def: ExprDef) -> Self {
Expr { def: def } Self { def }
} }
} }
@ -108,21 +112,21 @@ impl Operator for ExprDef {
impl Display for ExprDef { impl Display for ExprDef {
fn fmt(&self, f: &mut Formatter) -> Result { fn fmt(&self, f: &mut Formatter) -> Result {
return match *self { match *self {
ExprDef::ConstExpr(ref c) => write!(f, "{}", c), ExprDef::ConstExpr(ref c) => write!(f, "{}", c),
ExprDef::BlockExpr(ref block) => { ExprDef::BlockExpr(ref block) => {
write!(f, "{}", "{")?; write!(f, "{{")?;
for expr in block.iter() { for expr in block.iter() {
write!(f, "{};", expr)?; write!(f, "{};", expr)?;
} }
write!(f, "{}", "}") write!(f, "}}")
} }
ExprDef::LocalExpr(ref s) => write!(f, "{}", s), ExprDef::LocalExpr(ref s) => write!(f, "{}", s),
ExprDef::GetConstFieldExpr(ref ex, ref field) => write!(f, "{}.{}", ex, field), ExprDef::GetConstFieldExpr(ref ex, ref field) => write!(f, "{}.{}", ex, field),
ExprDef::GetFieldExpr(ref ex, ref field) => write!(f, "{}[{}]", ex, field), ExprDef::GetFieldExpr(ref ex, ref field) => write!(f, "{}[{}]", ex, field),
ExprDef::CallExpr(ref ex, ref args) => { ExprDef::CallExpr(ref ex, ref args) => {
write!(f, "{}(", ex)?; write!(f, "{}(", ex)?;
let arg_strs: Vec<String> = args.iter().map(|arg| arg.to_string()).collect(); let arg_strs: Vec<String> = args.iter().map(ToString::to_string).collect();
write!(f, "{})", arg_strs.join(",")) write!(f, "{})", arg_strs.join(","))
} }
ExprDef::ConstructExpr(ref func, ref args) => { ExprDef::ConstructExpr(ref func, ref args) => {
@ -185,7 +189,7 @@ impl Display for ExprDef {
ExprDef::BinOpExpr(ref op, ref a, ref b) => write!(f, "{} {} {}", a, op, b), ExprDef::BinOpExpr(ref op, ref a, ref b) => write!(f, "{} {} {}", a, op, b),
ExprDef::UnaryOpExpr(ref op, ref a) => write!(f, "{}{}", op, a), ExprDef::UnaryOpExpr(ref op, ref a) => write!(f, "{}{}", op, a),
ExprDef::ReturnExpr(Some(ref ex)) => write!(f, "return {}", ex), ExprDef::ReturnExpr(Some(ref ex)) => write!(f, "return {}", ex),
ExprDef::ReturnExpr(None) => write!(f, "{}", "return"), ExprDef::ReturnExpr(None) => write!(f, "return"),
ExprDef::ThrowExpr(ref ex) => write!(f, "throw {}", ex), ExprDef::ThrowExpr(ref ex) => write!(f, "throw {}", ex),
ExprDef::AssignExpr(ref ref_e, ref val) => write!(f, "{} = {}", ref_e, val), ExprDef::AssignExpr(ref ref_e, ref val) => write!(f, "{} = {}", ref_e, val),
ExprDef::VarDeclExpr(ref vars) ExprDef::VarDeclExpr(ref vars)
@ -201,12 +205,12 @@ impl Display for ExprDef {
f.write_str("") f.write_str("")
} }
ExprDef::TypeOfExpr(ref e) => write!(f, "typeof {}", e), ExprDef::TypeOfExpr(ref e) => write!(f, "typeof {}", e),
}; }
} }
} }
/// join_expr - Utility to join multiple Expressions into a single string /// `join_expr` - Utility to join multiple Expressions into a single string
fn join_expr(f: &mut Formatter, expr: &Vec<Expr>) -> Result { fn join_expr(f: &mut Formatter, expr: &[Expr]) -> Result {
let mut first = true; let mut first = true;
for e in expr.iter() { for e in expr.iter() {
if !first { if !first {

162
src/lib/syntax/ast/keyword.rs

@ -1,12 +1,12 @@
use crate::syntax::ast::keyword::Keyword::*; use std::{
use std::error; error,
use std::fmt::Error; fmt::{Display, Error, Formatter},
use std::fmt::{Display, Formatter}; str::FromStr,
use std::str::FromStr; };
#[derive(Clone, PartialEq, Debug)] #[derive(Clone, Copy, PartialEq, Debug)]
/// A Javascript Keyword /// A Javascript Keyword
/// As specificed by https://www.ecma-international.org/ecma-262/#sec-keywords /// As specificed by <https://www.ecma-international.org/ecma-262/#sec-keywords>
pub enum Keyword { pub enum Keyword {
/// The `await` keyword /// The `await` keyword
Await, Await,
@ -82,7 +82,7 @@ pub enum Keyword {
Yield, Yield,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone, Copy)]
pub struct KeywordError; pub struct KeywordError;
impl Display for KeywordError { impl Display for KeywordError {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
@ -103,44 +103,44 @@ impl error::Error for KeywordError {
} }
impl FromStr for Keyword { impl FromStr for Keyword {
type Err = KeywordError; type Err = KeywordError;
fn from_str(s: &str) -> Result<Keyword, Self::Err> { fn from_str(s: &str) -> Result<Self, Self::Err> {
match s { match s {
"await" => Ok(Await), "await" => Ok(Keyword::Await),
"break" => Ok(Break), "break" => Ok(Keyword::Break),
"case" => Ok(Case), "case" => Ok(Keyword::Case),
"catch" => Ok(Catch), "catch" => Ok(Keyword::Catch),
"class" => Ok(Class), "class" => Ok(Keyword::Class),
"continue" => Ok(Continue), "continue" => Ok(Keyword::Continue),
"const" => Ok(Const), "const" => Ok(Keyword::Const),
"debugger" => Ok(Debugger), "debugger" => Ok(Keyword::Debugger),
"default" => Ok(Default), "default" => Ok(Keyword::Default),
"delete" => Ok(Delete), "delete" => Ok(Keyword::Delete),
"do" => Ok(Do), "do" => Ok(Keyword::Do),
"else" => Ok(Else), "else" => Ok(Keyword::Else),
"enum" => Ok(Enum), "enum" => Ok(Keyword::Enum),
"extends" => Ok(Extends), "extends" => Ok(Keyword::Extends),
"export" => Ok(Export), "export" => Ok(Keyword::Export),
"finally" => Ok(Finally), "finally" => Ok(Keyword::Finally),
"for" => Ok(For), "for" => Ok(Keyword::For),
"function" => Ok(Function), "function" => Ok(Keyword::Function),
"if" => Ok(If), "if" => Ok(Keyword::If),
"in" => Ok(In), "in" => Ok(Keyword::In),
"instanceof" => Ok(InstanceOf), "instanceof" => Ok(Keyword::InstanceOf),
"import" => Ok(Import), "import" => Ok(Keyword::Import),
"let" => Ok(Let), "let" => Ok(Keyword::Let),
"new" => Ok(New), "new" => Ok(Keyword::New),
"return" => Ok(Return), "return" => Ok(Keyword::Return),
"super" => Ok(Super), "super" => Ok(Keyword::Super),
"switch" => Ok(Switch), "switch" => Ok(Keyword::Switch),
"this" => Ok(This), "this" => Ok(Keyword::This),
"throw" => Ok(Throw), "throw" => Ok(Keyword::Throw),
"try" => Ok(Try), "try" => Ok(Keyword::Try),
"typeof" => Ok(TypeOf), "typeof" => Ok(Keyword::TypeOf),
"var" => Ok(Var), "var" => Ok(Keyword::Var),
"void" => Ok(Void), "void" => Ok(Keyword::Void),
"while" => Ok(While), "while" => Ok(Keyword::While),
"with" => Ok(With), "with" => Ok(Keyword::With),
"yield" => Ok(Yield), "yield" => Ok(Keyword::Yield),
_ => Err(KeywordError), _ => Err(KeywordError),
} }
} }
@ -151,42 +151,42 @@ impl Display for Keyword {
f, f,
"{}", "{}",
match *self { match *self {
Await => "await", Keyword::Await => "await",
Break => "break", Keyword::Break => "break",
Case => "case", Keyword::Case => "case",
Catch => "catch", Keyword::Catch => "catch",
Class => "class", Keyword::Class => "class",
Continue => "continue", Keyword::Continue => "continue",
Const => "const", Keyword::Const => "const",
Debugger => "debugger", Keyword::Debugger => "debugger",
Default => "default", Keyword::Default => "default",
Delete => "delete", Keyword::Delete => "delete",
Do => "do", Keyword::Do => "do",
Else => "else", Keyword::Else => "else",
Enum => "enum", Keyword::Enum => "enum",
Extends => "extends", Keyword::Extends => "extends",
Export => "export", Keyword::Export => "export",
Finally => "finally", Keyword::Finally => "finally",
For => "for", Keyword::For => "for",
Function => "function", Keyword::Function => "function",
If => "if", Keyword::If => "if",
In => "in", Keyword::In => "in",
InstanceOf => "instanceof", Keyword::InstanceOf => "instanceof",
Import => "import", Keyword::Import => "import",
Let => "let", Keyword::Let => "let",
New => "new", Keyword::New => "new",
Return => "return", Keyword::Return => "return",
Super => "super", Keyword::Super => "super",
Switch => "switch", Keyword::Switch => "switch",
This => "this", Keyword::This => "this",
Throw => "throw", Keyword::Throw => "throw",
Try => "try", Keyword::Try => "try",
TypeOf => "typeof", Keyword::TypeOf => "typeof",
Var => "var", Keyword::Var => "var",
Void => "void", Keyword::Void => "void",
While => "while", Keyword::While => "while",
With => "with", Keyword::With => "with",
Yield => "yield", Keyword::Yield => "yield",
} }
) )
} }

10
src/lib/syntax/ast/pos.rs

@ -1,4 +1,4 @@
#[derive(Clone, PartialEq, Debug)] #[derive(Clone, Copy, PartialEq, Debug)]
/// A position in the Javascript source code /// A position in the Javascript source code
/// Stores both the column number and the line number /// Stores both the column number and the line number
/// ///
@ -20,10 +20,10 @@ impl Position {
/// ///
/// * `line_number` - The line number the token starts at /// * `line_number` - The line number the token starts at
/// * `column_number` - The column number the token starts at /// * `column_number` - The column number the token starts at
pub fn new(line_number: u64, column_number: u64) -> Position { pub fn new(line_number: u64, column_number: u64) -> Self {
Position { Self {
line_number: line_number, line_number,
column_number: column_number, column_number,
} }
} }
} }

3
src/lib/syntax/ast/punc.rs

@ -1,5 +1,6 @@
use std::fmt::{Display, Error, Formatter}; use std::fmt::{Display, Error, Formatter};
#[derive(PartialEq, Clone, Debug)]
#[derive(PartialEq, Clone, Copy, Debug)]
/// Punctuation /// Punctuation
pub enum Punctuator { pub enum Punctuator {
/// `+` /// `+`

28
src/lib/syntax/ast/token.rs

@ -1,6 +1,4 @@
use crate::syntax::ast::keyword::Keyword; use crate::syntax::ast::{keyword::Keyword, pos::Position, punc::Punctuator};
use crate::syntax::ast::pos::Position;
use crate::syntax::ast::punc::Punctuator;
use std::fmt::{Debug, Display, Formatter, Result}; use std::fmt::{Debug, Display, Formatter, Result};
#[derive(Clone, PartialEq)] #[derive(Clone, PartialEq)]
@ -15,9 +13,9 @@ pub struct Token {
impl Token { impl Token {
/// Create a new detailed token from the token data, line number and column number /// Create a new detailed token from the token data, line number and column number
pub fn new(data: TokenData, line_number: u64, column_number: u64) -> Token { pub fn new(data: TokenData, line_number: u64, column_number: u64) -> Self {
Token { Self {
data: data, data,
pos: Position::new(line_number, column_number), pos: Position::new(line_number, column_number),
} }
} }
@ -68,17 +66,17 @@ pub enum TokenData {
impl Display for TokenData { impl Display for TokenData {
fn fmt(&self, f: &mut Formatter) -> Result { fn fmt(&self, f: &mut Formatter) -> Result {
match self.clone() { match *self {
TokenData::BooleanLiteral(val) => write!(f, "{}", val), TokenData::BooleanLiteral(ref val) => write!(f, "{}", val),
TokenData::EOF => write!(f, "end of file"), TokenData::EOF => write!(f, "end of file"),
TokenData::Identifier(ident) => write!(f, "{}", ident), TokenData::Identifier(ref ident) => write!(f, "{}", ident),
TokenData::Keyword(word) => write!(f, "{}", word), TokenData::Keyword(ref word) => write!(f, "{}", word),
TokenData::NullLiteral => write!(f, "null"), TokenData::NullLiteral => write!(f, "null"),
TokenData::NumericLiteral(num) => write!(f, "{}", num), TokenData::NumericLiteral(ref num) => write!(f, "{}", num),
TokenData::Punctuator(punc) => write!(f, "{}", punc), TokenData::Punctuator(ref punc) => write!(f, "{}", punc),
TokenData::StringLiteral(lit) => write!(f, "{}", lit), TokenData::StringLiteral(ref lit) => write!(f, "{}", lit),
TokenData::RegularExpression(reg) => write!(f, "{}", reg), TokenData::RegularExpression(ref reg) => write!(f, "{}", reg),
TokenData::Comment(comm) => write!(f, "/*{}*/", comm), TokenData::Comment(ref comm) => write!(f, "/*{}*/", comm),
} }
} }
} }

129
src/lib/syntax/lexer.rs

@ -2,16 +2,18 @@
//! //!
//! The Lexer splits its input source code into a sequence of input elements called tokens, represented by the [Token](../ast/token/struct.Token.html) structure. //! The Lexer splits its input source code into a sequence of input elements called tokens, represented by the [Token](../ast/token/struct.Token.html) structure.
//! It also removes whitespace and comments and attaches them to the next token. //! It also removes whitespace and comments and attaches them to the next token.
use crate::syntax::ast::punc::Punctuator; use crate::syntax::ast::{
use crate::syntax::ast::token::{Token, TokenData}; punc::Punctuator,
use std::char::{decode_utf16, from_u32}; token::{Token, TokenData},
use std::error; };
use std::fmt;
use std::iter::Peekable; use std::{
use std::str::Chars; char::{decode_utf16, from_u32},
use std::str::FromStr; error, fmt,
iter::Peekable,
#[allow(unused)] str::{Chars, FromStr},
};
macro_rules! vop { macro_rules! vop {
($this:ident, $assign_op:expr, $op:expr) => ({ ($this:ident, $assign_op:expr, $op:expr) => ({
let preview = $this.preview_next().unwrap(); let preview = $this.preview_next().unwrap();
@ -71,8 +73,8 @@ pub struct LexerError {
} }
impl LexerError { impl LexerError {
fn new(msg: &str) -> LexerError { fn new(msg: &str) -> Self {
LexerError { Self {
details: msg.to_string(), details: msg.to_string(),
} }
} }
@ -96,6 +98,7 @@ impl error::Error for LexerError {
} }
/// A lexical analyzer for JavaScript source code /// A lexical analyzer for JavaScript source code
#[derive(Debug)]
pub struct Lexer<'a> { pub struct Lexer<'a> {
// The list fo tokens generated so far // The list fo tokens generated so far
pub tokens: Vec<Token>, pub tokens: Vec<Token>,
@ -143,7 +146,7 @@ impl<'a> Lexer<'a> {
/// next fetches the next token and return it, or a LexerError if there are no more. /// next fetches the next token and return it, or a LexerError if there are no more.
fn next(&mut self) -> Result<char, LexerError> { fn next(&mut self) -> Result<char, LexerError> {
match self.buffer.next() { match self.buffer.next() {
Some(char) => Ok(char), Some(ch) => Ok(ch),
None => Err(LexerError::new("finished")), None => Err(LexerError::new("finished")),
} }
} }
@ -168,11 +171,7 @@ impl<'a> Lexer<'a> {
/// Preview the next character but don't actually increment /// Preview the next character but don't actually increment
fn preview_next(&mut self) -> Option<char> { fn preview_next(&mut self) -> Option<char> {
// No need to return a reference, we can return a copy self.buffer.peek().copied()
match self.buffer.peek() {
Some(v) => Some(*v),
None => None,
}
} }
/// Utility Function, while ``f(char)`` is true, read chars and move curser. /// Utility Function, while ``f(char)`` is true, read chars and move curser.
@ -191,10 +190,7 @@ impl<'a> Lexer<'a> {
/// next_is compares the character passed in to the next character, if they match true is returned and the buffer is incremented /// next_is compares the character passed in to the next character, if they match true is returned and the buffer is incremented
fn next_is(&mut self, peek: char) -> bool { fn next_is(&mut self, peek: char) -> bool {
let result = match self.preview_next() { let result = self.preview_next() == Some(peek);
Some(v) => v == peek,
None => false,
};
if result { if result {
self.buffer.next(); self.buffer.next();
} }
@ -204,12 +200,9 @@ impl<'a> Lexer<'a> {
pub fn lex(&mut self) -> Result<(), LexerError> { pub fn lex(&mut self) -> Result<(), LexerError> {
loop { loop {
// Check if we've reached the end // Check if we've reached the end
match self.preview_next() { if self.preview_next().is_none() {
Some(_) => (), // If there are still characters, carry on
None => {
return Ok(()); return Ok(());
} }
}
self.column_number += 1; self.column_number += 1;
let ch = self.next()?; let ch = self.next()?;
match ch { match ch {
@ -235,7 +228,7 @@ impl<'a> Lexer<'a> {
'0' => '\0', '0' => '\0',
'x' => { 'x' => {
let mut nums = String::with_capacity(2); let mut nums = String::with_capacity(2);
for _ in 0u8..2 { for _ in 0_u8..2 {
nums.push(self.next()?); nums.push(self.next()?);
} }
self.column_number += 2; self.column_number += 2;
@ -260,7 +253,7 @@ impl<'a> Lexer<'a> {
// Support \u{X..X} (Unicode Codepoint) // Support \u{X..X} (Unicode Codepoint)
if self.next_is('{') { if self.next_is('{') {
let s = self let s = self
.take_char_while(|c| c.is_alphanumeric()) .take_char_while(char::is_alphanumeric)
.unwrap(); .unwrap();
// We know this is a single unicode codepoint, convert to u32 // We know this is a single unicode codepoint, convert to u32
@ -279,7 +272,7 @@ impl<'a> Lexer<'a> {
loop { loop {
// Collect each character after \u e.g \uD83D will give "D83D" // Collect each character after \u e.g \uD83D will give "D83D"
let s = self let s = self
.take_char_while(|c| c.is_alphanumeric()) .take_char_while(char::is_alphanumeric)
.unwrap(); .unwrap();
// Convert to u16 // Convert to u16
@ -300,12 +293,10 @@ impl<'a> Lexer<'a> {
// codepoints length should either be 1 (unicode codepoint) or 2 (surrogate codepoint). // codepoints length should either be 1 (unicode codepoint) or 2 (surrogate codepoint).
// Rust's decode_utf16 will deal with it regardless // Rust's decode_utf16 will deal with it regardless
let c = decode_utf16(codepoints.iter().cloned()) decode_utf16(codepoints.iter().cloned())
.next() .next()
.unwrap() .unwrap()
.unwrap(); .unwrap()
c
} }
} }
'\'' | '"' => escape, '\'' | '"' => escape,
@ -317,7 +308,7 @@ impl<'a> Lexer<'a> {
buf.push(escaped_ch); buf.push(escaped_ch);
} }
} }
ch => buf.push(ch), next_ch => buf.push(next_ch),
} }
} }
let str_length = buf.len() as u64; let str_length = buf.len() as u64;
@ -330,40 +321,30 @@ impl<'a> Lexer<'a> {
'0' => { '0' => {
let mut buf = String::new(); let mut buf = String::new();
let num = if self.next_is('x') { let num = if self.next_is('x') {
loop { while let Some(ch) = self.preview_next() {
match self.preview_next() {
Some(ch) => {
if ch.is_digit(16) { if ch.is_digit(16) {
buf.push(self.next()?); buf.push(self.next()?);
} else { } else {
break; break;
} }
} }
None => break,
};
}
u64::from_str_radix(&buf, 16).unwrap() u64::from_str_radix(&buf, 16).unwrap()
} else if self.next_is('b') { } else if self.next_is('b') {
loop { while let Some(ch) = self.preview_next() {
match self.preview_next() {
Some(ch) => {
if ch.is_digit(2) { if ch.is_digit(2) {
buf.push(self.next()?); buf.push(self.next()?);
} else { } else {
break; break;
} }
} }
None => break,
}
}
u64::from_str_radix(&buf, 2).unwrap() u64::from_str_radix(&buf, 2).unwrap()
} else { } else {
let mut gone_decimal = false; let mut gone_decimal = false;
loop { loop {
let ch = self.preview_next().unwrap_or('_'); let next_ch = self.preview_next().unwrap_or('_');
match ch { match next_ch {
ch if ch.is_digit(8) => { next_ch if next_ch.is_digit(8) => {
buf.push(ch); buf.push(next_ch);
self.next()?; self.next()?;
} }
'O' | 'o' => { 'O' | 'o' => {
@ -371,7 +352,7 @@ impl<'a> Lexer<'a> {
} }
'8' | '9' | '.' => { '8' | '9' | '.' => {
gone_decimal = true; gone_decimal = true;
buf.push(ch); buf.push(next_ch);
self.next()?; self.next()?;
} }
_ => break, _ => break,
@ -379,24 +360,17 @@ impl<'a> Lexer<'a> {
} }
if gone_decimal { if gone_decimal {
u64::from_str(&buf).unwrap() u64::from_str(&buf).unwrap()
} else { } else if buf.is_empty() {
if buf.is_empty() {
0 0
} else { } else {
u64::from_str_radix(&buf, 8).unwrap() u64::from_str_radix(&buf, 8).unwrap()
} }
}
}; };
self.push_token(TokenData::NumericLiteral(num as f64)) self.push_token(TokenData::NumericLiteral(num as f64))
} }
_ if ch.is_digit(10) => { _ if ch.is_digit(10) => {
let mut buf = ch.to_string(); let mut buf = ch.to_string();
'digitloop: loop { 'digitloop: while let Some(ch) = self.preview_next() {
// There might not be a next character
let ch = match self.preview_next() {
Some(c) => c,
None => break,
};
match ch { match ch {
'.' => loop { '.' => loop {
buf.push(self.next()?); buf.push(self.next()?);
@ -424,30 +398,26 @@ impl<'a> Lexer<'a> {
} }
_ if ch.is_alphabetic() || ch == '$' || ch == '_' => { _ if ch.is_alphabetic() || ch == '$' || ch == '_' => {
let mut buf = ch.to_string(); let mut buf = ch.to_string();
loop { while let Some(ch) = self.preview_next() {
let ch = match self.preview_next() { if ch.is_alphabetic() || ch.is_digit(10) || ch == '_' {
Some(ch) => ch,
None => break,
};
match ch {
_ if ch.is_alphabetic() || ch.is_digit(10) || ch == '_' => {
buf.push(self.next()?); buf.push(self.next()?);
} } else {
_ => {
break; break;
} }
} }
}
// Match won't compare &String to &str so i need to convert it :( // Match won't compare &String to &str so i need to convert it :(
let buf_compare: &str = &buf; let buf_compare: &str = &buf;
self.push_token(match buf_compare { self.push_token(match buf_compare {
"true" => TokenData::BooleanLiteral(true), "true" => TokenData::BooleanLiteral(true),
"false" => TokenData::BooleanLiteral(false), "false" => TokenData::BooleanLiteral(false),
"null" => TokenData::NullLiteral, "null" => TokenData::NullLiteral,
slice => match FromStr::from_str(slice) { slice => {
Ok(keyword) => TokenData::Keyword(keyword), if let Ok(keyword) = FromStr::from_str(slice) {
Err(_) => TokenData::Identifier(buf.clone()), TokenData::Keyword(keyword)
}, } else {
TokenData::Identifier(buf.clone())
}
}
}); });
// Move position forward the length of keyword // Move position forward the length of keyword
self.column_number += (buf_compare.len() - 1) as u64; self.column_number += (buf_compare.len() - 1) as u64;
@ -476,11 +446,7 @@ impl<'a> Lexer<'a> {
'?' => self.push_punc(Punctuator::Question), '?' => self.push_punc(Punctuator::Question),
// Comments // Comments
'/' => { '/' => {
let ch = match self.preview_next() { if let Some(ch) = self.preview_next() {
Some(ch) => ch,
None => return Err(LexerError::new("Expecting Token /,*,=")),
};
let token = match ch { let token = match ch {
// Matched comment // Matched comment
'/' => { '/' => {
@ -498,7 +464,7 @@ impl<'a> Lexer<'a> {
buf.push('*') buf.push('*')
} }
} }
ch => buf.push(ch), next_ch => buf.push(next_ch),
} }
} }
TokenData::Comment(buf) TokenData::Comment(buf)
@ -507,6 +473,9 @@ impl<'a> Lexer<'a> {
_ => TokenData::Punctuator(Punctuator::Div), _ => TokenData::Punctuator(Punctuator::Div),
}; };
self.push_token(token) self.push_token(token)
} else {
return Err(LexerError::new("Expecting Token /,*,="));
}
} }
'*' => op!(self, Punctuator::AssignMul, Punctuator::Mul, { '*' => op!(self, Punctuator::AssignMul, Punctuator::Mul, {
'*' => vop!(self, Punctuator::AssignPow, Punctuator::Pow) '*' => vop!(self, Punctuator::AssignPow, Punctuator::Pow)

16
src/lib/syntax/parser.rs

@ -17,7 +17,7 @@ macro_rules! mk (
}; };
); );
/// ParseError is an enum which represents errors encounted during parsing an expression /// `ParseError` is an enum which represents errors encounted during parsing an expression
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum ParseError { pub enum ParseError {
/// When it expected a certain kind of token, but got another as part of something /// When it expected a certain kind of token, but got another as part of something
@ -32,6 +32,7 @@ pub enum ParseError {
pub type ParseResult = Result<Expr, ParseError>; pub type ParseResult = Result<Expr, ParseError>;
#[derive(Debug)]
pub struct Parser { pub struct Parser {
/// The tokens being input /// The tokens being input
tokens: Vec<Token>, tokens: Vec<Token>,
@ -41,11 +42,8 @@ pub struct Parser {
impl Parser { impl Parser {
/// Create a new parser, using `tokens` as input /// Create a new parser, using `tokens` as input
pub fn new(tokens: Vec<Token>) -> Parser { pub fn new(tokens: Vec<Token>) -> Self {
Parser { Self { tokens, pos: 0 }
tokens: tokens,
pos: 0,
}
} }
/// Parse all expressions in the token array /// Parse all expressions in the token array
@ -744,10 +742,10 @@ impl Parser {
fn expect(&mut self, tk: TokenData, routine: &'static str) -> Result<(), ParseError> { fn expect(&mut self, tk: TokenData, routine: &'static str) -> Result<(), ParseError> {
self.pos += 1; self.pos += 1;
let curr_tk = self.get_token(self.pos - 1)?; let curr_tk = self.get_token(self.pos - 1)?;
if curr_tk.data != tk { if curr_tk.data == tk {
Err(ParseError::Expected(vec![tk], curr_tk, routine))
} else {
Ok(()) Ok(())
} else {
Err(ParseError::Expected(vec![tk], curr_tk, routine))
} }
} }

9
tests/js/test.js

@ -1,7 +1,4 @@
let a = 5; let a = "b";
let b = 6; let c = 5;
let c = 8;
let d = 5;
let res = a + d * (b - 3) + 1; c + a;
res;
Loading…
Cancel
Save