1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
//! # ezjs
//!
//! A light Javascript (ES5 version) runtime library , you can use Javascript as DSL in your project. 
//! Whole design is inspired from https://mujs.com/, including including a simple bytecode virtual machine.
//! You can added a extenstion hook type via Rust's generic features, and this hook type play key role in your DSL enviroment.
//!
//! All implementation including tokenlizer, AST builder, compiler, optimizer(TODO), and VM executer is simple and easy to debug, 
//! so I called this library *ezjs*.  
//! 
//! An completed example is a repl ( src/bin/repl.rs), which including a hookable extention struct. 

mod common;
mod token;
mod ast;
mod bytecode;

mod compile;
mod optimizer;

pub mod value;
pub mod runtime;
mod builtin;

use std::convert::TryFrom;
use std::collections::HashMap;
use crate::ast::*;
use crate::bytecode::*;
use crate::compile::*;

use crate::value::*;
use crate::runtime::*;
use crate::builtin::*;


/// The function compile string to bytecode of Virtual Machone.
/// If some erros happens, return it with errpr message in a Resut.
///
pub fn build_function_from_code(script: &str) -> Result<SharedFunction, String> {
    let ast = build_ast_from_script(script);
	if let Err(msg) = ast {
		return Err(msg);
	}
	let ast = ast.unwrap();

    let null = AstNode::null();
    let func = compile_func(&null, &null, &ast, true)?;
    return Ok(SharedFunction_new(func));
}

/// Print all bytecode of Function object, whith internal data.
///
///
pub fn dump_function(f: &VMFunction) {
    println!("-------------------------------");
    println!("script: {}", f.script);
    println!("functions: {}", f.func_tab.len());
    println!("---num----");
    for n in &f.num_tab {
        println!("{}", n);
    }
    println!("---str----");
    for n in &f.str_tab {
        println!("{}", n);
    }
    println!("---code----");
    
    let mut addr = 0;
    for i in &f.code {
        if let Ok(op) = OpcodeType::try_from(*i) {
			println!("{}\t\tOP: {:?} V: {}", addr, op, i);
		} else {
            println!("{}\t\tV: {}", addr, i);
        }
        addr = addr + 1;
    }

    println!("---functions---");
    for i in &f.func_tab {
        dump_function( i );
    }
    println!("----------END-----------");
}

///	Create an new JsRuntime struct of ezjs, which execute byte of VMFunction.
/// All internal members are open and defined in runtime.rs.  
///
pub fn new_runtime<T: Hookable>(root: T) -> JsRuntime<T> {	
	let prototypes = JsPrototype {
		object_prototype:		SharedObject_new(JsObject::new()),
		string_prototype:		SharedObject_new(JsObject::new()),
		array_prototype:		SharedObject_new(JsObject::new()),
		function_prototype:		SharedObject_new(JsObject::new()),
		exception_prototype:	SharedObject_new(JsObject::new()),
	};

	let genv = JsEnvironment::new();
	let cenv = genv.clone();

	let mut runtime = JsRuntime {
		builtins:	Vec::new(),
		prototypes:	prototypes,
		genv:		genv,
		cenv:		cenv,
		stack:		Vec::new(),

		hooks:		HashMap::new(),
		hooks_id:	0,
		root:		root,
	};

	// init prototypes
	prototypes_init(&mut runtime);
	builtin_init(&mut runtime);
	
	return runtime;
}

///	Run bytecode of VMFunction with a runtime, and return the result sharedvalue.
/// See more info via repl example.
///
pub fn run_script<T:Hookable>(rt: &mut JsRuntime<T>, vmf: SharedFunction) -> Result<SharedValue, String> {
	assert!( vmf.script == true);
	let fobj = SharedObject_new(JsObject::new_function(vmf, rt.genv.clone()));
	let thiz = rt.genv.borrow().target(); 

	rt.push_object(fobj);	// function object
	rt.push_object(thiz);	// this

	let result = jscall(rt, 0);
	if result.is_err() {
		let err_msg = format!("Exceptions: {:?}", result.err().unwrap());
		println!("{}", err_msg);
		rt.stack.clear();
		return Err(err_msg);
	}

	if rt.stack.len() != 1 {
		let err_msg = format!("stack len should be 1 but get {}", rt.stack.len());
		panic!(err_msg);
	}

	let value = rt.stack[0].clone();
	rt.stack.clear();
	return Ok(value);
}