#include "immutable_node.h"
static VALUE cNode;
static void deallocate(ImmutableNodeContext* context)
{
if (context->pc)
{
js_FinishParseContext(context->js, context->pc);
free(context->pc);
}
JS_DestroyContext(context->js);
JS_DestroyRuntime(context->runtime);
free(context);
}
static VALUE allocate(VALUE klass)
{
ImmutableNodeContext * context = calloc(1, sizeof(ImmutableNodeContext));
VALUE self = Data_Wrap_Struct(klass, 0, deallocate, context);
assert(context->runtime = JS_NewRuntime(0x100000));
assert(context->js = JS_NewContext(context->runtime, 8192));
return self;
}
/*
* call-seq:
* parse_io(stream, filename=nil, line_number=0)
*
* Parses an IO object, returning a native spidermonkey parse tree.
*/
static VALUE parse_io(int argc, VALUE *argv, VALUE klass) {
VALUE self = allocate(klass);
VALUE stream, filename, linenum;
ImmutableNodeContext* context;
Data_Get_Struct(self, ImmutableNodeContext, context);
assert(context->pc = calloc(1, sizeof(JSParseContext)));
rb_scan_args( argc, argv, "12", &stream, &filename, &linenum );
VALUE file_contents = rb_funcall(stream, rb_intern("read"), 0);
size_t length = NUM2INT(rb_funcall(file_contents, rb_intern("length"), 0));
jschar* chars;
assert(chars = js_InflateString(context->js, StringValuePtr(file_contents), &length));
if(!filename && rb_respond_to(stream, rb_intern("path"))) {
filename = rb_funcall(stream, rb_intern("path"), 0);
}
char* filenamez = RTEST(filename) ? StringValueCStr(filename) : NULL;
int linenumi = RTEST(linenum) ? NUM2INT(linenum) : 1;
assert(js_InitParseContext(context->js, context->pc,
NULL,
chars,
length,
NULL, filenamez, linenumi));
context->node = js_ParseScript(context->js,
JS_NewObject(context->js, NULL, NULL, NULL),
context->pc);
if(JS_IsExceptionPending(context->js)) {
jsval exception, message, file_name, line_number;
JS_GetPendingException(context->js, &exception);
JS_GetProperty(context->js, JSVAL_TO_OBJECT(exception), "message",&message);
JS_GetProperty(context->js, JSVAL_TO_OBJECT(exception), "fileName",&file_name);
JS_GetProperty(context->js, JSVAL_TO_OBJECT(exception), "lineNumber",&line_number);
JS_ClearPendingException(context->js);
rb_funcall( self,
rb_intern("raise_parse_error"),
3,
message == JSVAL_NULL ?
Qnil :
rb_str_new2(JS_GetStringBytes(JSVAL_TO_STRING(message))),
rb_str_new2(JS_GetStringBytes(JSVAL_TO_STRING(file_name))),
INT2NUM(JSVAL_TO_INT(line_number))
);
}
return self;
}
/*
* call-seq:
* line
*
* Returns the line number of the node.
*/
static VALUE line(VALUE self) {
ImmutableNodeContext * ctx;
Data_Get_Struct(self, ImmutableNodeContext, ctx);
return INT2NUM(ctx->node->pn_pos.begin.lineno);
}
/*
* call-seq:
* index
*
* Returns the column number of the node.
*/
static VALUE begin_index(VALUE self) {
ImmutableNodeContext * ctx;
Data_Get_Struct(self, ImmutableNodeContext, ctx);
return INT2NUM(ctx->node->pn_pos.begin.index);
}
/*
* call-seq:
* pn_arity
*
* Returns the arity of the node as a symbol.
*/
static VALUE pn_arity(VALUE self) {
ImmutableNodeContext * ctx;
Data_Get_Struct(self, ImmutableNodeContext, ctx);
switch(ctx->node->pn_arity) {
case PN_FUNC:
return ID2SYM(rb_intern("pn_func"));
case PN_LIST:
return ID2SYM(rb_intern("pn_list"));
case PN_TERNARY:
return ID2SYM(rb_intern("pn_ternary"));
case PN_BINARY:
return ID2SYM(rb_intern("pn_binary"));
case PN_UNARY:
return ID2SYM(rb_intern("pn_unary"));
case PN_NAME:
return ID2SYM(rb_intern("pn_name"));
case PN_NULLARY:
return ID2SYM(rb_intern("pn_nullary"));
}
return Qnil;
}
/*
* call-seq:
* pn_type
*
* Returns the type of the node as a symbol.
*/
static VALUE pn_type(VALUE self) {
ImmutableNodeContext * ctx;
Data_Get_Struct(self, ImmutableNodeContext, ctx);
switch(ctx->node->pn_type) {
<% tokens.each do |token| %>
case <%= token %>: return ID2SYM(rb_intern("<%= token.downcase %>"));
<% end %>
}
return INT2NUM(ctx->node->pn_type);
}
/*
* call-seq:
* pn_expr
*
* Returns the parse node expression as an ImmutableNode.
*/
static VALUE data_pn_expr(VALUE self) {
ImmutableNodeContext * ctx;
Data_Get_Struct(self, ImmutableNodeContext, ctx);
if(ctx->node->pn_expr) {
ImmutableNodeContext *roc;
VALUE node = Data_Make_Struct(cNode, ImmutableNodeContext, NULL, NULL, roc);
roc->js = ctx->js;
roc->node = ctx->node->pn_expr;
return node;
}
return Qnil;
}
/*
* call-seq:
* pn_kid
*
* Returns the child ImmutableNode.
*/
static VALUE data_pn_kid(VALUE self) {
ImmutableNodeContext * ctx;
Data_Get_Struct(self, ImmutableNodeContext, ctx);
if(ctx->node->pn_kid) {
ImmutableNodeContext *roc;
VALUE node = Data_Make_Struct(cNode, ImmutableNodeContext, NULL, NULL, roc);
roc->js = ctx->js;
roc->node = ctx->node->pn_kid;
return node;
}
return Qnil;
}
/*
* call-seq:
* pn_kid1
*
* Returns the first child ImmutableNode.
*/
static VALUE data_pn_kid1(VALUE self) {
ImmutableNodeContext * ctx;
Data_Get_Struct(self, ImmutableNodeContext, ctx);
if(ctx->node->pn_kid1) {
ImmutableNodeContext *roc;
VALUE node = Data_Make_Struct(cNode, ImmutableNodeContext, NULL, NULL, roc);
roc->js = ctx->js;
roc->node = ctx->node->pn_kid1;
return node;
}
return Qnil;
}
/*
* call-seq:
* pn_kid2
*
* Returns the second child ImmutableNode.
*/
static VALUE data_pn_kid2(VALUE self) {
ImmutableNodeContext * ctx;
Data_Get_Struct(self, ImmutableNodeContext, ctx);
if(ctx->node->pn_kid2) {
ImmutableNodeContext *roc;
VALUE node = Data_Make_Struct(cNode, ImmutableNodeContext, NULL, NULL, roc);
roc->js = ctx->js;
roc->node = ctx->node->pn_kid2;
return node;
}
return Qnil;
}
/*
* call-seq:
* pn_kid3
*
* Returns the third child ImmutableNode.
*/
static VALUE data_pn_kid3(VALUE self) {
ImmutableNodeContext * ctx;
Data_Get_Struct(self, ImmutableNodeContext, ctx);
if(ctx->node->pn_kid3) {
ImmutableNodeContext *roc;
VALUE node = Data_Make_Struct(cNode, ImmutableNodeContext, NULL, NULL, roc);
roc->js = ctx->js;
roc->node = ctx->node->pn_kid3;
return node;
}
return Qnil;
}
/*
* call-seq:
* pn_dval
*
* Returns the numeric value of the node.
*/
static VALUE data_pn_dval(VALUE self) {
ImmutableNodeContext * ctx;
Data_Get_Struct(self, ImmutableNodeContext, ctx);
if(JSVAL_IS_NUMBER(ATOM_KEY(ctx->node->pn_atom))) {
return rb_float_new(ctx->node->pn_dval);
} else {
return INT2NUM((int)(ctx->node->pn_dval));
}
}
/*
* call-seq:
* pn_op
*
* Returns the op code for the node as a symbol.
*/
static VALUE data_pn_op(VALUE self) {
ImmutableNodeContext * ctx;
Data_Get_Struct(self, ImmutableNodeContext, ctx);
switch(ctx->node->pn_op) {
<% jsops.each do |jsop| %>
case <%= jsop %>: return ID2SYM(rb_intern("<%= jsop.downcase %>"));
<% end %>
}
return INT2NUM(ctx->node->pn_op);
}
/*
* call-seq:
* pn_left
*
* Returns the left side ImmutableNode.
*/
static VALUE data_pn_left(VALUE self) {
ImmutableNodeContext * ctx;
Data_Get_Struct(self, ImmutableNodeContext, ctx);
if(ctx->node->pn_left) {
ImmutableNodeContext *roc;
VALUE node = Data_Make_Struct(cNode, ImmutableNodeContext, NULL, NULL, roc);
roc->js = ctx->js;
roc->node = ctx->node->pn_left;
return node;
}
return Qnil;
}
/*
* call-seq:
* pn_extra
*
* Returns extra informaton about the node as an Integer.
*/
static VALUE data_pn_extra(VALUE self) {
ImmutableNodeContext * ctx;
Data_Get_Struct(self, ImmutableNodeContext, ctx);
return INT2NUM(ctx->node->pn_extra);
}
/*
* call-seq:
* name
*
* Returns the name of the node.
*/
static VALUE name(VALUE self) {
ImmutableNodeContext * ctx;
Data_Get_Struct(self, ImmutableNodeContext, ctx);
return rb_str_new2(JS_GetStringBytes(ATOM_TO_STRING(ctx->node->pn_atom)));
}
/*
* call-seq:
* regexp
*
* Returns the regexp value as a String.
*/
static VALUE regexp(VALUE self) {
ImmutableNodeContext * ctx;
jsval result;
Data_Get_Struct(self, ImmutableNodeContext, ctx);
js_regexp_toString(ctx->js, ctx->node->pn_pob->object, &result);
return rb_str_new2(JS_GetStringBytes(JSVAL_TO_STRING(result)));
}
/*
* call-seq:
* function_name
*
* Returns the function name as a String.
*/
static VALUE function_name(VALUE self) {
ImmutableNodeContext * ctx;
JSFunction * f;
JSObject * object;
Data_Get_Struct(self, ImmutableNodeContext, ctx);
object = ctx->node->pn_funpob->object;
f = (JSFunction *)JS_GetPrivate(ctx->js, ctx->node->pn_funpob->object);
if(f->atom) {
return rb_str_new2(JS_GetStringBytes(ATOM_TO_STRING(f->atom)));
} else {
return Qnil;
}
}
/*
* call-seq:
* function_args
*
* Returns the function argument names as an Array of String.
*/
static VALUE function_args(VALUE self) {
ImmutableNodeContext * ctx;
JSFunction * f;
JSObject * object;
jsuword* names;
VALUE func_args;
int i;
Data_Get_Struct(self, ImmutableNodeContext, ctx);
object = ctx->node->pn_funpob->object;
f = (JSFunction *)JS_GetPrivate(ctx->js, ctx->node->pn_funpob->object);
func_args = rb_ary_new2(f->nargs);
if(f->nargs > 0) {
names = js_GetLocalNameArray(ctx->js, f, &ctx->js->tempPool);
for(i = 0; i < f->nargs; i++) {
rb_ary_push(func_args,
rb_str_new2(JS_GetStringBytes(ATOM_TO_STRING(names[i])))
);
}
}
return func_args;
}
/*
* call-seq:
* function_body
*
* Returns the function body as an ImmutableNode.
*/
static VALUE function_body(VALUE self) {
ImmutableNodeContext * ctx;
JSFunction * f;
JSObject * object;
Data_Get_Struct(self, ImmutableNodeContext, ctx);
object = ctx->node->pn_funpob->object;
f = (JSFunction *)JS_GetPrivate(ctx->js, ctx->node->pn_funpob->object);
if(ctx->node->pn_body) {
ImmutableNodeContext *roc;
VALUE node = Data_Make_Struct(cNode, ImmutableNodeContext, NULL, NULL, roc);
roc->js = ctx->js;
roc->node = ctx->node->pn_body;
return node;
}
return Qnil;
}
/*
* call-seq:
* pn_right
*
* Returns right side as an ImmutableNode.
*/
static VALUE data_pn_right(VALUE self)
{
ImmutableNodeContext * ctx;
Data_Get_Struct(self, ImmutableNodeContext, ctx);
if(ctx->node->pn_right) {
ImmutableNodeContext *roc;
VALUE node = Data_Make_Struct(cNode, ImmutableNodeContext, NULL, NULL, roc);
roc->js = ctx->js;
roc->node = ctx->node->pn_right;
return node;
}
return Qnil;
}
/*
* call-seq:
* children
*
* Returns children as an Array of ImmutableNode.
*/
static VALUE children(VALUE self) {
ImmutableNodeContext * ctx;
JSParseNode * p;
VALUE children;
Data_Get_Struct(self, ImmutableNodeContext, ctx);
children = rb_ary_new();
for(p = ctx->node->pn_head; p != NULL; p = p->pn_next) {
ImmutableNodeContext *roc;
VALUE node = Data_Make_Struct(cNode, ImmutableNodeContext, NULL, NULL, roc);
roc->js = ctx->js;
roc->node = p;
rb_ary_push(children, node);
}
return children;
}
void init_Johnson_SpiderMonkey_Immutable_Node(VALUE spidermonkey)
{
/* HACK: These comments are *only* to make RDoc happy.
VALUE johnson = rb_define_module("Johnson");
VALUE spidermonkey = rb_define_module_under(johnson, "SpiderMonkey");
*/
/* ImmutableNode class. */
cNode = rb_define_class_under(spidermonkey, "ImmutableNode", rb_cObject);
rb_define_alloc_func(cNode, allocate);
rb_define_singleton_method(cNode, "parse_io", parse_io, -1);
rb_define_method(cNode, "line", line, 0);
rb_define_method(cNode, "index", begin_index, 0);
rb_define_method(cNode, "pn_arity", pn_arity, 0);
rb_define_method(cNode, "pn_type", pn_type, 0);
rb_define_method(cNode, "pn_expr", data_pn_expr, 0);
rb_define_method(cNode, "pn_kid", data_pn_kid, 0);
rb_define_method(cNode, "pn_kid1", data_pn_kid1, 0);
rb_define_method(cNode, "pn_kid2", data_pn_kid2, 0);
rb_define_method(cNode, "pn_kid3", data_pn_kid3, 0);
rb_define_method(cNode, "pn_dval", data_pn_dval, 0);
rb_define_method(cNode, "pn_op", data_pn_op, 0);
rb_define_method(cNode, "pn_left", data_pn_left, 0);
rb_define_method(cNode, "pn_extra", data_pn_extra, 0);
rb_define_method(cNode, "name", name, 0);
rb_define_method(cNode, "regexp", regexp, 0);
rb_define_method(cNode, "function_name", function_name, 0);
rb_define_method(cNode, "function_args", function_args, 0);
rb_define_method(cNode, "function_body", function_body, 0);
rb_define_method(cNode, "pn_right", data_pn_right, 0);
rb_define_method(cNode, "children", children, 0);
}