Browse Source

building up value

pull/5/head
Jason Williams 6 years ago
parent
commit
47271d5f91
  1. 1
      src/lib/js/mod.rs
  2. 2
      src/lib/js/object.rs
  3. 363
      src/lib/js/value.rs
  4. 1
      src/lib/lib.rs
  5. 2
      src/lib/syntax/lexer.rs
  6. 1
      tests/lexer_test.rs

1
src/lib/js/mod.rs

@ -1,4 +1,3 @@
extern crate serde_json;
/// The global `Function` object and function value representations /// The global `Function` object and function value representations
pub mod function; pub mod function;
// /// The global `JSON` object // /// The global `JSON` object

2
src/lib/js/object.rs

@ -1,4 +1,4 @@
use js::value::{Value, ValueData}; use js::value::Value;
use std::collections::HashMap; use std::collections::HashMap;
pub static PROTOTYPE: &'static str = "prototype"; pub static PROTOTYPE: &'static str = "prototype";
pub static INSTANCE_PROTOTYPE: &'static str = "__proto__"; pub static INSTANCE_PROTOTYPE: &'static str = "__proto__";

363
src/lib/js/value.rs

@ -1,7 +1,13 @@
use gc::{Gc, GcCell}; use gc::{Gc, GcCell};
use js::function::Function; use js::function::Function;
use js::object::{ObjectData, Property, INSTANCE_PROTOTYPE, PROTOTYPE}; use js::object::{ObjectData, Property, INSTANCE_PROTOTYPE, PROTOTYPE};
use serde_json::map::Map;
use serde_json::Number as JSONNumber;
use serde_json::Value as JSONValue;
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt::{Display, Formatter, Result as FmtResult};
use std::iter::FromIterator;
use std::ops::Deref;
use std::str::FromStr; use std::str::FromStr;
/// 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`)
@ -183,6 +189,104 @@ impl Value {
self.get_field(field.to_string()) self.get_field(field.to_string())
} }
/// Set the field in the value
pub fn set_field(&self, field: String, val: Value) -> Value {
match *self.ptr {
ValueData::Object(ref obj) => {
obj.borrow_mut()
.insert(field.clone(), Property::from_value(val.clone()));
}
ValueData::Function(ref func) => {
func.borrow_mut()
.object
.insert(field.clone(), Property::from_value(val.clone()));
}
_ => (),
}
val
}
/// Set the field in the value
pub fn set_field_slice<'a>(&self, field: &'a str, val: Value) -> Value {
self.set_field(field.to_string(), val)
}
/// Set the property in the value
pub fn set_prop(&self, field: String, prop: Property) -> Property {
match *self.ptr {
ValueData::Object(ref obj) => {
obj.borrow_mut().insert(field.clone(), prop.clone());
}
ValueData::Function(ref func) => {
func.borrow_mut().object.insert(field.clone(), prop.clone());
}
_ => (),
}
prop
}
/// Convert from a JSON value to a JS value
pub fn from_json(json: JSONValue) -> ValueData {
match json {
JSONValue::Number(v) => ValueData::Number(v.as_f64().unwrap()),
JSONValue::String(v) => ValueData::String(v),
JSONValue::Bool(v) => ValueData::Boolean(v),
JSONValue::Array(vs) => {
let mut i = 0;
let mut data: ObjectData = FromIterator::from_iter(vs.iter().map(|json| {
i += 1;
(
(i - 1).to_string().to_string(),
Property::from_value(to_value(json.clone())),
)
}));
data.insert(
"length".to_string(),
Property::from_value(to_value(vs.len() as i32)),
);
ValueData::Object(GcCell::new(data))
}
JSONValue::Object(obj) => {
let data: ObjectData = FromIterator::from_iter(obj.iter().map(|(key, json)| {
(key.clone(), Property::from_value(to_value(json.clone())))
}));
ValueData::Object(GcCell::new(data))
}
JSONValue::Null => ValueData::Null,
}
}
fn to_json(&self) -> JSONValue {
match *self.ptr {
ValueData::Null | ValueData::Undefined => JSONValue::Null,
ValueData::Boolean(b) => JSONValue::Bool(b),
ValueData::Object(ref obj) => {
let mut nobj = Map::new();
for (k, v) in obj.borrow().iter() {
if k != INSTANCE_PROTOTYPE {
nobj.insert(k.clone(), v.value.to_json());
}
}
JSONValue::Object(nobj)
}
ValueData::String(ref str) => JSONValue::String(str.clone()),
ValueData::Number(num) => JSONValue::Number(JSONNumber::from_f64(num).unwrap()),
ValueData::Integer(val) => JSONValue::Number(JSONNumber::from(val)),
ValueData::Function(_) => JSONValue::Null,
}
}
/// Get the type of the value
pub fn get_type(&self) -> &'static str {
match *self.ptr {
ValueData::Number(_) | ValueData::Integer(_) => "number",
ValueData::String(_) => "string",
ValueData::Boolean(_) => "boolean",
ValueData::Null => "null",
ValueData::Undefined => "undefined",
_ => "object",
}
}
/// Get the value for undefined /// Get the value for undefined
pub fn undefined() -> Value { pub fn undefined() -> Value {
Value { Value {
@ -190,3 +294,262 @@ impl Value {
} }
} }
} }
impl Display for Value {
fn fmt(&self, f: &mut Formatter) -> FmtResult {
match *self.ptr {
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 {
// https://tc39.github.io/ecma262/#sec-tostring-applied-to-the-number-type
_ if v.is_nan() => "NaN".to_string(),
_ if v.is_infinite() && v.is_sign_positive() => "Infinity".to_string(),
_ if v.is_infinite() && v.is_sign_negative() => "-Infinity".to_string(),
_ => v.to_string(),
}
),
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));
if key != last_key {
try!(write!(f, "{}", ", "));
}
}
}
None => (),
}
write!(f, "{}", "}")
}
ValueData::Integer(v) => write!(f, "{}", v),
ValueData::Function(ref v) => write!(f, "function({})", v.borrow().args.join(", ")),
}
}
}
impl PartialEq for Value {
fn eq(&self, other: &Value) -> bool {
match (self.ptr.clone().deref(), other.ptr.clone().deref()) {
// TODO: fix this
// _ if self.ptr.to_inner() == &other.ptr.to_inner() => true,
_ if self.is_null_or_undefined() && other.is_null_or_undefined() => true,
(ValueData::String(_), _) | (_, ValueData::String(_)) => {
self.to_string() == other.to_string()
}
(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(ref a), _) if a == other.to_num() => true,
(_, ValueData::Number(ref a)) if a == self.to_num() => true,
(ValueData::Integer(a), ValueData::Integer(b)) if a == b => true,
_ => false,
}
}
}
/// 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<Self, &'static str>
where
Self: Sized;
}
impl ToValue for String {
fn to_value(&self) -> Value {
Value {
ptr: Gc::new(ValueData::String(self.clone())),
}
}
}
impl FromValue for String {
fn from_value(v: Value) -> Result<String, &'static str> {
Ok(v.to_string())
}
}
impl<'s> ToValue for &'s str {
fn to_value(&self) -> Value {
Value {
ptr: Gc::new(ValueData::String(String::from_str(*self).unwrap())),
}
}
}
impl ToValue for char {
fn to_value(&self) -> Value {
Value {
ptr: Gc::new(ValueData::String(self.to_string())),
}
}
}
impl FromValue for char {
fn from_value(v: Value) -> Result<char, &'static str> {
Ok(v.to_string().chars().next().unwrap())
}
}
impl ToValue for f64 {
fn to_value(&self) -> Value {
Value {
ptr: Gc::new(ValueData::Number(*self)),
}
}
}
impl FromValue for f64 {
fn from_value(v: Value) -> Result<f64, &'static str> {
Ok(v.to_num())
}
}
impl ToValue for i32 {
fn to_value(&self) -> Value {
Value {
ptr: Gc::new(ValueData::Integer(*self)),
}
}
}
impl FromValue for i32 {
fn from_value(v: Value) -> Result<i32, &'static str> {
Ok(v.to_int())
}
}
impl ToValue for bool {
fn to_value(&self) -> Value {
Value {
ptr: Gc::new(ValueData::Boolean(*self)),
}
}
}
impl FromValue for bool {
fn from_value(v: Value) -> Result<bool, &'static str> {
Ok(v.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_string(), Property::from_value(item.to_value()));
i += 1;
}
to_value(arr)
}
}
impl<T: ToValue> ToValue for Vec<T> {
fn to_value(&self) -> Value {
let mut arr = HashMap::new();
let mut i = 0;
for item in self.iter() {
arr.insert(i.to_string(), Property::from_value(item.to_value()));
i += 1;
}
to_value(arr)
}
}
impl<T: FromValue> FromValue for Vec<T> {
fn from_value(v: Value) -> Result<Vec<T>, &'static str> {
let len = v.get_field_slice("length").to_int();
let mut vec = Vec::with_capacity(len as usize);
for i in 0..len {
vec.push(try!(from_value(v.get_field(i.to_string()))))
}
Ok(vec)
}
}
impl ToValue for ObjectData {
fn to_value(&self) -> Value {
Value {
ptr: Gc::new(ValueData::Object(GcCell::new(self.clone()))),
}
}
}
impl FromValue for ObjectData {
fn from_value(v: Value) -> Result<ObjectData, &'static str> {
match *v.ptr {
ValueData::Object(ref obj) => {
let objData = obj.clone().into_inner();
Ok(objData)
}
ValueData::Function(ref func) => Ok(func.borrow().object.clone()),
_ => Err("Value is not a valid object"),
}
}
}
impl ToValue for JSONValue {
fn to_value(&self) -> Value {
Value {
ptr: Gc::new(Value::from_json(self.clone())),
}
}
}
impl FromValue for JSONValue {
fn from_value(v: Value) -> Result<JSONValue, &'static str> {
Ok(v.to_json())
}
}
impl ToValue for () {
fn to_value(&self) -> Value {
Value {
ptr: Gc::new(ValueData::Null),
}
}
}
impl FromValue for () {
fn from_value(_: Value) -> Result<(), &'static str> {
Ok(())
}
}
impl<T: ToValue> ToValue for Option<T> {
fn to_value(&self) -> Value {
match *self {
Some(ref v) => v.to_value(),
None => Value {
ptr: Gc::new(ValueData::Null),
},
}
}
}
impl<T: FromValue> FromValue for Option<T> {
fn from_value(value: Value) -> Result<Option<T>, &'static str> {
Ok(if value.is_null_or_undefined() {
None
} else {
Some(try!(FromValue::from_value(value)))
})
}
}
/// A utility function that just calls FromValue::from_value
pub fn from_value<A: FromValue>(v: Value) -> Result<A, &'static str> {
FromValue::from_value(v)
}
/// A utility function that just calls ToValue::to_value
pub fn to_value<A: ToValue>(v: A) -> Value {
v.to_value()
}

1
src/lib/lib.rs

@ -1,4 +1,5 @@
extern crate gc; extern crate gc;
extern crate serde_json;
#[macro_use] #[macro_use]
extern crate gc_derive; extern crate gc_derive;

2
src/lib/syntax/lexer.rs

@ -113,7 +113,7 @@ impl<'a> Lexer<'a> {
/// ///
/// ```rust,no_run /// ```rust,no_run
/// let buffer = std::fs::read_to_string("yourSourceCode.js").unwrap(); /// let buffer = std::fs::read_to_string("yourSourceCode.js").unwrap();
/// let lexer = js::syntax::lexer::Lexer::new(&buffer); /// let lexer = boa::syntax::lexer::Lexer::new(&buffer);
/// ``` /// ```
pub fn new(buffer: &'a str) -> Lexer<'a> { pub fn new(buffer: &'a str) -> Lexer<'a> {
Lexer { Lexer {

1
tests/lexer_test.rs

@ -1,4 +1,5 @@
extern crate boa; extern crate boa;
use boa::syntax::ast::keyword::Keyword; use boa::syntax::ast::keyword::Keyword;
use boa::syntax::ast::punc::Punctuator; use boa::syntax::ast::punc::Punctuator;
use boa::syntax::ast::token::TokenData; use boa::syntax::ast::token::TokenData;

Loading…
Cancel
Save