From b2b901f677ae81a60ce535485b722ed6477088fa Mon Sep 17 00:00:00 2001 From: Jason Williams Date: Wed, 10 Oct 2018 14:23:10 +0100 Subject: [PATCH] adding value.rs --- Cargo.lock | 64 ++++++ Cargo.toml | 6 +- src/bin/bin.rs | 14 +- src/lib/exec.rs | 332 +-------------------------- src/lib/js/mod.rs | 6 + src/lib/js/object.rs | 37 +++ src/lib/js/value.rs | 533 +++++++++++++++++++++++++++++++++++++++++++ src/lib/lib.rs | 5 + tests/lexer_test.rs | 10 +- 9 files changed, 666 insertions(+), 341 deletions(-) create mode 100644 src/lib/js/mod.rs create mode 100644 src/lib/js/object.rs create mode 100644 src/lib/js/value.rs diff --git a/Cargo.lock b/Cargo.lock index 1736f44bcc..85d33063e5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,4 +1,68 @@ [[package]] name = "Boa" version = "0.1.3" +dependencies = [ + "gc 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "gc_derive 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", +] +[[package]] +name = "gc" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "gc_derive" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", + "synstructure 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "quote" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "syn" +version = "0.11.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", + "synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "synom" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "synstructure" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "unicode-xid" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[metadata] +"checksum gc 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "630b15fafc2270fc89de904da9ebb8b950642a5ed7bd99ec3f95558b0831ea9a" +"checksum gc_derive 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2501c15cbaf28a0c2214617aa85351982a933161d7937fe6cd71c855364e0ea6" +"checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" +"checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad" +"checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6" +"checksum synstructure 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3a761d12e6d8dcb4dcf952a7a89b475e3a9d69e4a69307e01a470977642914bd" +"checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc" diff --git a/Cargo.toml b/Cargo.toml index 42715646b3..1ec85df179 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,11 +10,13 @@ license = "Unlicense OR MIT" exclude = [".vscode/*", "Dockerfile", "Makefile", ".editorConfig"] [dependencies] +gc = "0.3.2" +gc_derive = "*" [lib] -name = "js" +name = "boa" path = "src/lib/lib.rs" # [[bin]] # name = "js" -# path = "src/bin/bin.rs" +# path = "src/bin/bin.rs" \ No newline at end of file diff --git a/src/bin/bin.rs b/src/bin/bin.rs index 4635196a11..2ab9e65709 100644 --- a/src/bin/bin.rs +++ b/src/bin/bin.rs @@ -1,6 +1,6 @@ -extern crate js; -use js::syntax::lexer::Lexer; -use js::syntax::parser::Parser; +extern crate boa; +use boa::syntax::lexer::Lexer; +use boa::syntax::parser::Parser; use std::fs::read_to_string; pub fn main() { @@ -8,8 +8,8 @@ pub fn main() { let mut lexer = Lexer::new(&buffer); lexer.lex().unwrap(); let tokens = lexer.tokens; - match Parser::new(tokens).parse_all() { - Ok(e) => println!("{}", e), - Err(e) => println!("{:?}", e), - } + + // Setup executor + let expr = Parser::new(tokens).parse_all().unwrap(); + println!("{}", expr); } diff --git a/src/lib/exec.rs b/src/lib/exec.rs index e65ce8d26a..7ec7760339 100644 --- a/src/lib/exec.rs +++ b/src/lib/exec.rs @@ -1,339 +1,17 @@ -use ast::{ - CompEqual, CompGreaterThan, CompGreaterThanOrEqual, CompLessThan, CompLessThanOrEqual, - CompNotEqual, CompStrictEqual, CompStrictNotEqual, -}; -use ast::{LogAnd, LogOr}; -use ast::{OpAdd, OpDiv, OpMod, OpMul, OpSub}; -use ast::{UnaryMinus, UnaryNot}; -use collections::treemap::TreeMap; -use js::function::{RegularFunc, RegularFunction}; -use js::object::{ObjectData, INSTANCE_PROTOTYPE, PROTOTYPE}; -use js::value::{ - from_value, to_value, ResultValue, VBoolean, VFunction, VInteger, VNull, VNumber, VObject, - VString, VUndefined, Value, ValueData, -}; -use js::{array, console, error, function, json, math, number, object, string, uri}; -use std::cell::RefCell; -use std::gc::Gc; -use std::vec::Vec; -use syntax::ast::constant::Const; -use syntax::ast::expr::{Expr, ExprDef}; -use syntax::ast::op::{BinOp, BitOp, CompOp, LogOp, NumOp, Operator, UnaryOp}; -use syntax - +use js::object::{INSTANCE_PROTOTYPE, PROTOTYPE, ObjectData}; /// An execution engine pub trait Executor { /// Make a new execution engine fn new() -> Self; /// Set a global variable called `name` with the value `val` - fn set_global(&mut self, name: StrBuf, val: Value) -> Value; + // fn set_global(&mut self, name: String, val: Value) -> Value; /// Resolve the global variable `name` - fn get_global(&self, name: StrBuf) -> Value; + // fn get_global(&self, name: String) -> Value; /// Create a new scope and return it - fn make_scope(&mut self) -> Gc>; + // fn make_scope(&mut self) -> Gc>; /// Destroy the current scope fn destroy_scope(&mut self) -> (); /// Run an expression - fn run(&mut self, expr: &Expr) -> ResultValue; -} -#[deriving(Clone)] -/// A Javascript intepreter -pub struct Interpreter { - /// An object representing the global object - global: Value, - /// The variable scopes - scopes: Vec>>, -} -impl Executor for Interpreter { - fn new() -> Interpreter { - let global = ValueData::new_obj(None); - object::init(global); - console::init(global); - math::init(global); - array::init(global); - function::init(global); - json::init(global); - number::init(global); - error::init(global); - string::init(global); - uri::init(global); - Interpreter { - global: global, - scopes: Vec::new(), - } - } - fn set_global(&mut self, name: StrBuf, val: Value) -> Value { - self.global.borrow().set_field(name, val) - } - fn get_global(&self, name: StrBuf) -> Value { - self.global.borrow().get_field(name) - } - fn make_scope(&mut self) -> Gc> { - let value = Gc::new(RefCell::new(TreeMap::new())); - self.scopes.push(value.clone()); - value - } - fn destroy_scope(&mut self) -> () { - self.scopes.pop(); - } - fn run(&mut self, expr: &Expr) -> ResultValue { - match expr.def { - ConstExpr(CNull) => Ok(Gc::new(VNull)), - ConstExpr(CUndefined) => Ok(Gc::new(VUndefined)), - ConstExpr(CNum(num)) => Ok(to_value(num)), - ConstExpr(CInt(num)) => Ok(to_value(num)), - ConstExpr(CString(ref str)) => Ok(Gc::new(VString(StrBuf::from_str(str.as_slice())))), - ConstExpr(CBool(val)) => Ok(Gc::new(VBoolean(val))), - ConstExpr(CRegExp(_, _, _)) => Ok(Gc::new(VNull)), - BlockExpr(ref es) => { - let mut obj = Gc::new(VNull); - for e in es.iter() { - let val = try!(self.run(e)); - if e == es.last().unwrap() { - obj = val; - } - } - Ok(obj) - } - LocalExpr(ref name) => { - let mut value = Gc::new(VUndefined); - for scope in self.scopes.iter().rev() { - match scope.borrow().borrow().find(name) { - Some(v) => { - value = v.value.clone(); - break; - } - None => (), - } - } - Ok(if value.borrow().is_undefined() { - self.global.borrow().get_field(name.clone()) - } else { - value - }) - } - GetConstFieldExpr(ref obj, ref field) => { - let val_obj = try!(self.run(*obj)); - Ok(val_obj.borrow().get_field(field.clone())) - } - GetFieldExpr(ref obj, ref field) => { - let val_obj = try!(self.run(*obj)); - let val_field = try!(self.run(*field)); - Ok(val_obj.borrow().get_field(val_field.borrow().to_str())) - } - CallExpr(ref callee, ref args) => { - let (this, func) = match callee.def { - GetConstFieldExpr(ref obj, ref field) => { - let obj = try!(self.run(*obj)); - (obj, obj.borrow().get_field(field.clone())) - } - GetFieldExpr(ref obj, ref field) => { - let obj = try!(self.run(*obj)); - let field = try!(self.run(*field)); - (obj, obj.borrow().get_field(field.borrow().to_str())) - } - _ => (self.global.clone(), try!(self.run(callee.clone()))), - }; - let mut v_args = Vec::with_capacity(args.len()); - for arg in args.iter() { - v_args.push(try!(self.run(arg))); - } - match *func.borrow() { - VFunction(ref func) => func.borrow().call(self, this, Gc::new(VNull), v_args), - _ => Err(Gc::new(VUndefined)), - } - } - WhileLoopExpr(ref cond, ref expr) => { - let mut result = Gc::new(VUndefined); - while try!(self.run(*cond)).borrow().is_true() { - result = try!(self.run(*expr)); - } - Ok(result) - } - IfExpr(ref cond, ref expr, None) => Ok(if try!(self.run(*cond)).borrow().is_true() { - try!(self.run(*expr)) - } else { - Gc::new(VUndefined) - }), - IfExpr(ref cond, ref expr, Some(ref else_e)) => { - Ok(if try!(self.run(*cond)).borrow().is_true() { - try!(self.run(*expr)) - } else { - try!(self.run(*else_e)) - }) - } - SwitchExpr(ref val_e, ref vals, ref default) => { - let val = try!(self.run(*val_e)).borrow().clone(); - let mut result = Gc::new(VNull); - let mut matched = false; - for tup in vals.iter() { - let tup: &(Expr, Vec) = tup; - match *tup { - (ref cond, ref block) if (val == *try!(self.run(cond)).borrow()) => { - matched = true; - let last_expr = block.last().unwrap(); - for expr in block.iter() { - let e_result = try!(self.run(expr)); - if expr == last_expr { - result = e_result; - } - } - } - _ => (), - } - } - if !matched && default.is_some() { - result = try!(self.run(*default.as_ref().unwrap())); - } - Ok(result) - } - ObjectDeclExpr(ref map) => { - let obj = ValueData::new_obj(Some(self.global)); - for (key, val) in map.iter() { - obj.borrow().set_field(key.clone(), try!(self.run(val))); - } - Ok(obj) - } - ArrayDeclExpr(ref arr) => { - let arr_map = ValueData::new_obj(Some(self.global)); - let mut index: i32 = 0; - for val in arr.iter() { - let val = try!(self.run(val)); - arr_map.borrow().set_field(index.to_str(), val); - index += 1; - } - arr_map.borrow().set_field_slice( - INSTANCE_PROTOTYPE, - self.get_global("Array".into_strbuf()) - .borrow() - .get_field_slice(PROTOTYPE), - ); - arr_map.borrow().set_field_slice("length", to_value(index)); - Ok(arr_map) - } - FunctionDeclExpr(ref name, ref args, ref expr) => { - let function = RegularFunc(RegularFunction::new(*expr.clone(), args.clone())); - let val = Gc::new(VFunction(RefCell::new(function))); - if name.is_some() { - self.global.borrow().set_field(name.clone().unwrap(), val); - } - Ok(val) - } - ArrowFunctionDeclExpr(ref args, ref expr) => { - let function = RegularFunc(RegularFunction::new(*expr.clone(), args.clone())); - Ok(Gc::new(VFunction(RefCell::new(function)))) - } - BinOpExpr(BinNum(ref op), ref a, ref b) => { - let v_r_a = try!(self.run(*a)); - let v_r_b = try!(self.run(*b)); - let v_a = v_r_a.borrow(); - let v_b = v_r_b.borrow(); - Ok(Gc::new(match *op { - OpAdd => *v_a + *v_b, - OpSub => *v_a - *v_b, - OpMul => *v_a * *v_b, - OpDiv => *v_a / *v_b, - OpMod => *v_a % *v_b, - })) - } - UnaryOpExpr(ref op, ref a) => { - let v_r_a = try!(self.run(*a)); - let v_a = v_r_a.borrow(); - Ok(match *op { - UnaryMinus => to_value(-v_a.to_num()), - UnaryNot => Gc::new(!v_a), - _ => unreachable!(), - }) - } - BinOpExpr(BinBit(ref op), ref a, ref b) => { - let v_r_a = try!(self.run(*a)); - let v_r_b = try!(self.run(*b)); - let v_a = v_r_a.borrow(); - let v_b = v_r_b.borrow(); - Ok(Gc::new(match *op { - BitAnd => *v_a & *v_b, - BitOr => *v_a | *v_b, - BitXor => *v_a ^ *v_b, - BitShl => *v_a << *v_b, - BitShr => *v_a >> *v_b, - })) - } - BinOpExpr(BinComp(ref op), ref a, ref b) => { - let v_r_a = try!(self.run(*a)); - let v_r_b = try!(self.run(*b)); - let v_a = v_r_a.borrow(); - let v_b = v_r_b.borrow(); - Ok(to_value(match *op { - CompEqual if v_a.is_object() => v_r_a.ptr_eq(&v_r_b), - CompEqual => v_a == v_b, - CompNotEqual if v_a.is_object() => !v_r_a.ptr_eq(&v_r_b), - CompNotEqual => v_a != v_b, - CompStrictEqual if v_a.is_object() => v_r_a.ptr_eq(&v_r_b), - CompStrictEqual => v_a == v_b, - CompStrictNotEqual if v_a.is_object() => !v_r_a.ptr_eq(&v_r_b), - CompStrictNotEqual => v_a != v_b, - CompGreaterThan => v_a.to_num() > v_b.to_num(), - CompGreaterThanOrEqual => v_a.to_num() >= v_b.to_num(), - CompLessThan => v_a.to_num() < v_b.to_num(), - CompLessThanOrEqual => v_a.to_num() <= v_b.to_num(), - })) - } - BinOpExpr(BinLog(ref op), ref a, ref b) => { - let v_a = from_value::(try!(self.run(*a))).unwrap(); - let v_b = from_value::(try!(self.run(*b))).unwrap(); - Ok(match *op { - LogAnd => to_value(v_a && v_b), - LogOr => to_value(v_a || v_b), - }) - } - ConstructExpr(ref callee, ref args) => { - let func = try!(self.run(*callee)); - let mut v_args = Vec::with_capacity(args.len()); - for arg in args.iter() { - v_args.push(try!(self.run(arg))); - } - let this = Gc::new(VObject(RefCell::new(TreeMap::new()))); - this.borrow() - .set_field_slice(INSTANCE_PROTOTYPE, func.borrow().get_field_slice(PROTOTYPE)); - Ok(match *func.borrow() { - VFunction(ref func) => { - try!(func.borrow().call(self, this, Gc::new(VNull), v_args)); - this - } - _ => Gc::new(VUndefined), - }) - } - ReturnExpr(ref ret) => match *ret { - Some(ref v) => self.run(*v), - None => Ok(Gc::new(VUndefined)), - }, - ThrowExpr(ref ex) => Err(try!(self.run(*ex))), - AssignExpr(ref ref_e, ref val_e) => { - let val = try!(self.run(*val_e)); - match ref_e.def { - LocalExpr(ref name) => { - self.global.borrow().set_field(name.clone(), val); - } - GetConstFieldExpr(ref obj, ref field) => { - let val_obj = try!(self.run(*obj)); - val_obj.borrow().set_field(field.clone(), val); - } - _ => (), - } - Ok(val) - } - TypeOfExpr(ref val_e) => { - let val = try!(self.run(*val_e)); - Ok(to_value(match *val.borrow() { - VUndefined => "undefined", - VNull | VObject(_) => "object", - VBoolean(_) => "boolean", - VNumber(_) | VInteger(_) => "number", - VString(_) => "string", - VFunction(_) => "function", - })) - } - } - } + // fn run(&mut self, expr: &Expr) -> ResultValue; } diff --git a/src/lib/js/mod.rs b/src/lib/js/mod.rs new file mode 100644 index 0000000000..7c5ada0c57 --- /dev/null +++ b/src/lib/js/mod.rs @@ -0,0 +1,6 @@ +extern crate gc; + +/// The global `Object` object +pub mod object; +/// Javascript values, utility methods and conversion between Javascript values and Rust values +pub mod value; diff --git a/src/lib/js/object.rs b/src/lib/js/object.rs new file mode 100644 index 0000000000..dbcfef410a --- /dev/null +++ b/src/lib/js/object.rs @@ -0,0 +1,37 @@ +use gc::Gc; +use js::value::Value; +use std::collections::HashMap; +pub static PROTOTYPE: &'static str = "prototype"; +pub static INSTANCE_PROTOTYPE: &'static str = "__proto__"; + +pub type ObjectData = HashMap; + +/// A Javascript Property +pub struct Property { + /// If the type of this can be changed and this can be deleted + pub configurable: bool, + /// If the property shows up in enumeration of the object + pub enumerable: bool, + /// If this property can be changed with an assignment + pub writable: bool, + /// The value associated with the property + pub value: Value, + /// The function serving as getter + pub get: Value, + /// The function serving as setter + pub set: Value, +} + +impl Property { + /// Make a new property with the given value + pub fn new(value: Value) -> Property { + Property { + configurable: false, + enumerable: false, + writable: false, + value: value, + // get: Gc::new(VUndefined), + // set: Gc::new(VUndefined), + } + } +} diff --git a/src/lib/js/value.rs b/src/lib/js/value.rs new file mode 100644 index 0000000000..6b4f03ff9f --- /dev/null +++ b/src/lib/js/value.rs @@ -0,0 +1,533 @@ +use gc::Gc; +use js::object::{ObjectData, Property, INSTANCE_PROTOTYPE, PROTOTYPE}; +use std::cell::RefCell; +use std::collections::HashMap; +use std::str::FromStr; + +/// The result of a Javascript expression is represented like this so it can succeed (`Ok`) or fail (`Err`) +pub type ResultValue = Result; +/// A Garbage-collected Javascript value as represented in the interpreter +pub type Value = Gc; + +/// A Javascript value +pub enum ValueData { + /// `null` - A null value, for when a value doesn't exist + Null, + /// `undefined` - An undefined value, for when a field or index doesn't exist + Undefined, + /// `boolean` - A `true` / `false` value, for if a certain criteria is met + Boolean(bool), + /// `String` - A UTF-8 string, such as `"Hello, world"` + String(String), + /// `Number` - A 64-bit floating point number, such as `3.1415` + Number(f64), + /// `Number` - A 32-bit integer, such as `42` + Integer(i32), + /// `Object` - An object, such as `Math`, represented by a binary tree of string keys to Javascript values + Object(RefCell), + /// `Function` - A runnable block of code, such as `Math.sqrt`, which can take some variables and return a useful value or act upon an object + Function(RefCell), +} + +impl ValueData { + /// Returns a new empty object + pub fn new_obj(global: Option) -> Value { + let mut obj: ObjectData = HashMap::new(); + if global.is_some() { + let obj_proto = global + .unwrap() + .borrow() + .get_field_slice("Object") + .borrow() + .get_field_slice(PROTOTYPE); + obj.insert(INSTANCE_PROTOTYPE.into_String(), Property::new(obj_proto)); + } + Gc::new(ValueData::Object(RefCell::new(obj))) + } + /// Returns true if the value is an object + pub fn is_object(&self) -> bool { + return match *self { + ValueData::Object(_) => true, + _ => false, + }; + } + /// Returns true if the value is undefined + pub fn is_undefined(&self) -> bool { + return match *self { + ValueData::Undefined => true, + _ => false, + }; + } + /// Returns true if the value is null + pub fn is_null(&self) -> bool { + return match *self { + ValueData::Null => true, + _ => false, + }; + } + /// Returns true if the value is null or undefined + pub fn is_null_or_undefined(&self) -> bool { + return match *self { + ValueData::Null | ValueData::Undefined => true, + _ => false, + }; + } + /// Returns true if the value is a 64-bit floating-point number + pub fn is_double(&self) -> bool { + return match *self { + ValueData::Number(_) => true, + _ => false, + }; + } + /// Returns true if the value is true + pub fn is_true(&self) -> bool { + return match *self { + ValueData::Object(_) => true, + ValueData::String(ref s) if s.as_slice() == "1" => true, + ValueData::Number(n) if n >= 1.0 && n % 1.0 == 0.0 => true, + ValueData::Integer(n) if n > 1 => true, + ValueData::Boolean(v) => v, + _ => false, + }; + } + /// Converts the value into a 64-bit floating point number + pub fn to_num(&self) -> f64 { + return match *self { + ValueData::Object(_) | ValueData::Undefined | ValueData::Function(_) => f64::NAN, + ValueData::String(ref str) => match FromStr::from_str(str.as_slice()) { + Some(num) => num, + None => f64::NAN, + }, + ValueData::Number(num) => num, + ValueData::Boolean(true) => 1.0, + ValueData::Boolean(false) | ValueData::Null => 0.0, + ValueData::Integer(num) => num as f64, + }; + } + /// Converts the value into a 32-bit integer + pub fn to_int(&self) -> i32 { + return match *self { + ValueData::Object(_) + | ValueData::Undefined + | ValueData::Null + | ValueData::Boolean(false) + | ValueData::Function(_) => 0, + ValueData::String(ref str) => match FromStr::from_str(str.as_slice()) { + Some(num) => num, + None => 0, + }, + ValueData::Number(num) => num as i32, + ValueData::Boolean(true) => 1, + ValueData::Integer(num) => num, + }; + } + /// Resolve the property in the object + pub fn get_prop(&self, field: String) -> Option { + let obj: ObjectData = match *self { + ValueData::Object(ref obj) => obj.borrow().clone(), + ValueData::Function(ref func) => { + let func = func.borrow().clone(); + match func { + NativeFunc(f) => f.object.clone(), + RegularFunc(f) => f.object.clone(), + } + } + _ => return None, + }; + match obj.find(&field) { + Some(val) => Some(*val), + None => match obj.find(&PROTOTYPE.into_String()) { + Some(prop) => prop.value.borrow().get_prop(field), + None => None, + }, + } + } + /// 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(&self, field: String) -> Value { + match self.get_prop(field) { + Some(prop) => prop.value, + None => Gc::new(ValueData::Undefined), + } + } + /// 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<'t>(&self, field: &'t str) -> Value { + self.get_field(field.into_String()) + } + /// Set the field in the value + pub fn set_field(&self, field: String, val: Value) -> Value { + match *self { + ValueData::Object(ref obj) => { + obj.borrow_mut().insert(field.clone(), Property::new(val)); + } + ValueData::Function(ref func) => { + match *func.borrow_mut().deref_mut() { + NativeFunc(ref mut f) => f.object.insert(field.clone(), Property::new(val)), + RegularFunc(ref mut f) => f.object.insert(field.clone(), Property::new(val)), + }; + } + _ => (), + } + val + } + /// Set the field in the value + pub fn set_field_slice<'t>(&self, field: &'t str, val: Value) -> Value { + self.set_field(field.into_String(), val) + } + /// Set the property in the value + pub fn set_prop(&self, field: String, prop: Property) -> Property { + match *self { + ValueData::Object(ref obj) => { + obj.borrow_mut().insert(field.clone(), prop); + } + ValueData::Function(ref func) => { + match *func.borrow_mut().deref_mut() { + NativeFunc(ref mut f) => f.object.insert(field.clone(), prop), + RegularFunc(ref mut f) => f.object.insert(field.clone(), prop), + }; + } + _ => (), + } + prop + } + /// Set the property in the value + pub fn set_prop_slice<'t>(&self, field: &'t str, prop: Property) -> Property { + self.set_prop(field.into_String(), prop) + } + /// Convert from a JSON value to a JS value + pub fn from_json(json: Json) -> ValueData { + match json { + Number(v) => ValueData::Number(v), + String(v) => ValueData::String(v), + Boolean(v) => ValueData::Boolean(v), + List(vs) => { + let mut i = 0; + let mut data: ObjectData = FromIterator::from_iter(vs.iter().map(|json| { + i += 1; + ( + (i - 1).to_str().into_String(), + Property::new(to_value(json.clone())), + ) + })); + data.insert( + "length".into_String(), + Property::new(to_value(vs.len() as i32)), + ); + ValueData::Object(RefCell::new(data)) + } + Object(obj) => { + let data: ObjectData = FromIterator::from_iter( + obj.iter() + .map(|(key, json)| (key.clone(), Property::new(to_value(json.clone())))), + ); + ValueData::Object(RefCell::new(data)) + } + Null => ValueData::Null, + } + } +} +impl fmt::Show for ValueData { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + ValueData::Null => write!(f, "null"), + ValueData::Undefined => write!(f, "undefined"), + ValueData::Boolean(v) => write!(f, "{}", v), + ValueData::String(ref v) => write!(f, "{}", v), + ValueData::Number(v) => write!( + f, + "{}", + match v { + _ if v.is_nan() => "NaN".into_String(), + f64::INFINITY => "Infinity".into_String(), + f64::NEG_INFINITY => "-Infinity".into_String(), + _ => f64::to_str_digits(v, 15), + } + ), + ValueData::Object(ref v) => { + try!(write!(f, "{}", "{")); + match v.borrow().iter().last() { + Some((last_key, _)) => { + for (key, val) in v.borrow().iter() { + try!(write!(f, "{}: {}", key, val.value.borrow())); + if key != last_key { + try!(write!(f, "{}", ", ")); + } + } + } + None => (), + } + write!(f, "{}", "}") + } + ValueData::Integer(v) => write!(f, "{}", v), + ValueData::Function(ref v) => match v.borrow().clone() { + NativeFunc(_) => write!(f, "{}", "function() { [native code] }"), + RegularFunc(rf) => write!(f, "function({}){}", rf.args.connect(", "), rf.expr), + }, + } + } +} +impl PartialEq for ValueData { + fn eq(&self, other: &ValueData) -> bool { + match (self.clone(), other.clone()) { + (ref a, ref b) if a.is_null_or_undefined() && b.is_null_or_undefined() => true, + (ValueData::String(ref a), ValueData::String(ref b)) if a == b => true, + (ValueData::String(ref a), ref b) | (ref b, ValueData::String(ref a)) + if *a == b.to_str() => + { + true + } + (ValueData::Boolean(a), ValueData::Boolean(b)) if a == b => true, + (ValueData::Number(a), ValueData::Number(b)) + if a == b && !a.is_nan() && !b.is_nan() => + { + true + } + (ValueData::Number(a), ref b) | (ref b, ValueData::Number(a)) if a == b.to_num() => { + true + } + (ValueData::Integer(a), ValueData::Integer(b)) if a == b => true, + _ => false, + } + } +} +impl ToJson for ValueData { + fn to_json(&self) -> Json { + match *self { + ValueData::Null | ValueData::Undefined => Null, + ValueData::Boolean(b) => Boolean(b), + ValueData::Object(ref obj) => { + let mut nobj = HashMap::new(); + for (k, v) in obj.borrow().iter() { + if k.as_slice() != INSTANCE_PROTOTYPE.as_slice() { + nobj.insert(k.clone(), v.value.borrow().to_json()); + } + } + Object(box nobj) + } + ValueData::String(ref str) => String(str.clone()), + ValueData::Number(num) => Number(num), + ValueData::Integer(val) => Number(val as f64), + ValueData::Function(_) => Null, + } + } +} +impl Add for ValueData { + fn add(&self, other: &ValueData) -> ValueData { + return match (self.clone(), other.clone()) { + (ValueData::String(s), other) | (other, ValueData::String(s)) => { + ValueData::String(s.clone().append(other.to_str().as_slice())) + } + (_, _) => ValueData::Number(self.to_num() + other.to_num()), + }; + } +} +impl Sub for ValueData { + fn sub(&self, other: &ValueData) -> ValueData { + ValueData::Number(self.to_num() - other.to_num()) + } +} +impl Mul for ValueData { + fn mul(&self, other: &ValueData) -> ValueData { + ValueData::Number(self.to_num() * other.to_num()) + } +} +impl Div for ValueData { + fn div(&self, other: &ValueData) -> ValueData { + ValueData::Number(self.to_num() / other.to_num()) + } +} +impl Rem for ValueData { + fn rem(&self, other: &ValueData) -> ValueData { + ValueData::Number(self.to_num() % other.to_num()) + } +} +impl BitAnd for ValueData { + fn bitand(&self, other: &ValueData) -> ValueData { + ValueData::Integer(self.to_int() & other.to_int()) + } +} +impl BitOr for ValueData { + fn bitor(&self, other: &ValueData) -> ValueData { + ValueData::Integer(self.to_int() | other.to_int()) + } +} +impl BitXor for ValueData { + fn bitxor(&self, other: &ValueData) -> ValueData { + ValueData::Integer(self.to_int() ^ other.to_int()) + } +} +impl Shl for ValueData { + fn shl(&self, other: &ValueData) -> ValueData { + ValueData::Integer(self.to_int() << other.to_int()) + } +} +impl Shr for ValueData { + fn shr(&self, other: &ValueData) -> ValueData { + ValueData::Integer(self.to_int() >> other.to_int()) + } +} +impl Not for ValueData { + fn not(&self) -> ValueData { + ValueData::Boolean(!self.is_true()) + } +} +/// Conversion to Javascript values from Rust values +pub trait ToValue { + /// Convert this value to a Rust value + fn to_value(&self) -> Value; +} +/// Conversion to Rust values from Javascript values +pub trait FromValue { + /// Convert this value to a Javascript value + fn from_value(value: Value) -> Result; +} +impl ToValue for String { + fn to_value(&self) -> Value { + Gc::new(ValueData::String(self.clone())) + } +} +impl FromValue for String { + fn from_value(v: Value) -> Result { + Ok(v.borrow().to_str()) + } +} +impl<'s> ToValue for &'s str { + fn to_value(&self) -> Value { + Gc::new(ValueData::String(String::from_str(*self))) + } +} +impl ToValue for char { + fn to_value(&self) -> Value { + Gc::new(ValueData::String(String::from_char(1, *self))) + } +} +impl FromValue for char { + fn from_value(v: Value) -> Result { + Ok(v.borrow().to_str().as_slice().char_at(0)) + } +} +impl ToValue for f64 { + fn to_value(&self) -> Value { + Gc::new(ValueData::Number(self.clone())) + } +} +impl FromValue for f64 { + fn from_value(v: Value) -> Result { + Ok(v.borrow().to_num()) + } +} +impl ToValue for i32 { + fn to_value(&self) -> Value { + Gc::new(ValueData::Integer(self.clone())) + } +} +impl FromValue for i32 { + fn from_value(v: Value) -> Result { + Ok(v.borrow().to_int()) + } +} +impl ToValue for bool { + fn to_value(&self) -> Value { + Gc::new(ValueData::Boolean(self.clone())) + } +} +impl FromValue for bool { + fn from_value(v: Value) -> Result { + Ok(v.borrow().is_true()) + } +} +impl<'s, T: ToValue> ToValue for &'s [T] { + fn to_value(&self) -> Value { + let mut arr = HashMap::new(); + let mut i = 0; + for item in self.iter() { + arr.insert(i.to_str().into_String(), Property::new(item.to_value())); + i += 1; + } + to_value(arr) + } +} +impl ToValue for Vec { + fn to_value(&self) -> Value { + let mut arr = HashMap::new(); + let mut i = 0; + for item in self.iter() { + arr.insert(i.to_str().into_String(), Property::new(item.to_value())); + i += 1; + } + to_value(arr) + } +} +impl FromValue for Vec { + fn from_value(v: Value) -> Result, &'static str> { + let len = v.borrow().get_field_slice("length").borrow().to_int(); + let mut vec = Vec::with_capacity(len as uint); + for i in range(0, len) { + vec.push(try!(from_value(v.borrow().get_field(i.to_str())))) + } + Ok(vec) + } +} +impl ToValue for NativeFunctionData { + fn to_value(&self) -> Value { + Gc::new(ValueData::Function(RefCell::new(NativeFunc( + NativeFunction::new(*self), + )))) + } +} +impl FromValue for NativeFunctionData { + fn from_value(v: Value) -> Result { + match *v.borrow() { + ValueData::Function(ref func) => match *func.borrow() { + NativeFunc(ref data) => Ok(data.data), + _ => Err("Value is not a native function"), + }, + _ => Err("Value is not a function"), + } + } +} +impl ToValue for ObjectData { + fn to_value(&self) -> Value { + Gc::new(ValueData::Object(RefCell::new(self.clone()))) + } +} +impl FromValue for ObjectData { + fn from_value(v: Value) -> Result { + match *v.borrow() { + ValueData::Object(ref obj) => Ok(obj.clone().borrow().deref().clone()), + ValueData::Function(ref func) => Ok(match *func.borrow().deref() { + NativeFunc(ref data) => data.object.clone(), + RegularFunc(ref data) => data.object.clone(), + }), + _ => Err("Value is not a valid object"), + } + } +} +impl ToValue for Json { + fn to_value(&self) -> Value { + Gc::new(ValueData::from_json(self.clone())) + } +} +impl FromValue for Json { + fn from_value(v: Value) -> Result { + Ok(v.borrow().to_json()) + } +} +impl ToValue for () { + fn to_value(&self) -> Value { + Gc::new(ValueData::Null) + } +} +impl FromValue for () { + fn from_value(_: Value) -> Result<(), &'static str> { + Ok(()) + } +} +/// A utility function that just calls FromValue::from_value +pub fn from_value(v: Value) -> Result { + FromValue::from_value(v) +} + +/// A utility function that just calls ToValue::to_value +pub fn to_value(v: A) -> Value { + v.to_value() +} diff --git a/src/lib/lib.rs b/src/lib/lib.rs index 4a39d2cfa8..8962564faf 100644 --- a/src/lib/lib.rs +++ b/src/lib/lib.rs @@ -1 +1,6 @@ +extern crate gc; +extern crate gc_derive; + +pub mod exec; +pub mod js; pub mod syntax; diff --git a/tests/lexer_test.rs b/tests/lexer_test.rs index 4e980b0b3f..02bfba47a3 100644 --- a/tests/lexer_test.rs +++ b/tests/lexer_test.rs @@ -1,8 +1,8 @@ -extern crate js; -use js::syntax::ast::keyword::Keyword; -use js::syntax::ast::punc::Punctuator; -use js::syntax::ast::token::TokenData; -use js::syntax::lexer::Lexer; +extern crate boa; +use boa::syntax::ast::keyword::Keyword; +use boa::syntax::ast::punc::Punctuator; +use boa::syntax::ast::token::TokenData; +use boa::syntax::lexer::Lexer; #[test] /// Check basic variable definition tokens