Browse Source

adding value.rs

pull/5/head
Jason Williams 6 years ago
parent
commit
b2b901f677
  1. 64
      Cargo.lock
  2. 6
      Cargo.toml
  3. 14
      src/bin/bin.rs
  4. 332
      src/lib/exec.rs
  5. 6
      src/lib/js/mod.rs
  6. 37
      src/lib/js/object.rs
  7. 533
      src/lib/js/value.rs
  8. 5
      src/lib/lib.rs
  9. 10
      tests/lexer_test.rs

64
Cargo.lock generated

@ -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"

6
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"

14
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);
}

332
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<RefCell<ObjectData>>;
// fn make_scope(&mut self) -> Gc<RefCell<ObjectData>>;
/// 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<Gc<RefCell<ObjectData>>>,
}
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<RefCell<ObjectData>> {
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<Expr>) = 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::<bool>(try!(self.run(*a))).unwrap();
let v_b = from_value::<bool>(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;
}

6
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;

37
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<String, Property>;
/// 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),
}
}
}

533
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<Value, Value>;
/// A Garbage-collected Javascript value as represented in the interpreter
pub type Value = Gc<ValueData>;
/// 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<ObjectData>),
/// `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<Function>),
}
impl ValueData {
/// Returns a new empty object
pub fn new_obj(global: Option<Value>) -> 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<Property> {
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<ValueData, ValueData> 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<ValueData, ValueData> for ValueData {
fn sub(&self, other: &ValueData) -> ValueData {
ValueData::Number(self.to_num() - other.to_num())
}
}
impl Mul<ValueData, ValueData> for ValueData {
fn mul(&self, other: &ValueData) -> ValueData {
ValueData::Number(self.to_num() * other.to_num())
}
}
impl Div<ValueData, ValueData> for ValueData {
fn div(&self, other: &ValueData) -> ValueData {
ValueData::Number(self.to_num() / other.to_num())
}
}
impl Rem<ValueData, ValueData> for ValueData {
fn rem(&self, other: &ValueData) -> ValueData {
ValueData::Number(self.to_num() % other.to_num())
}
}
impl BitAnd<ValueData, ValueData> for ValueData {
fn bitand(&self, other: &ValueData) -> ValueData {
ValueData::Integer(self.to_int() & other.to_int())
}
}
impl BitOr<ValueData, ValueData> for ValueData {
fn bitor(&self, other: &ValueData) -> ValueData {
ValueData::Integer(self.to_int() | other.to_int())
}
}
impl BitXor<ValueData, ValueData> for ValueData {
fn bitxor(&self, other: &ValueData) -> ValueData {
ValueData::Integer(self.to_int() ^ other.to_int())
}
}
impl Shl<ValueData, ValueData> for ValueData {
fn shl(&self, other: &ValueData) -> ValueData {
ValueData::Integer(self.to_int() << other.to_int())
}
}
impl Shr<ValueData, ValueData> for ValueData {
fn shr(&self, other: &ValueData) -> ValueData {
ValueData::Integer(self.to_int() >> other.to_int())
}
}
impl Not<ValueData> 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<Self, &'static str>;
}
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<String, &'static str> {
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<char, &'static str> {
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<f64, &'static str> {
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<i32, &'static str> {
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<bool, &'static str> {
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<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_str().into_String(), Property::new(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.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<NativeFunctionData, &'static str> {
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<ObjectData, &'static str> {
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<Json, &'static str> {
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<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()
}

5
src/lib/lib.rs

@ -1 +1,6 @@
extern crate gc;
extern crate gc_derive;
pub mod exec;
pub mod js;
pub mod syntax;

10
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

Loading…
Cancel
Save