/*
StrongTyping - Method parameter checking for Ruby
Copyright (C) 2003 Ryan Pavlik
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "ruby.h"
#include "strongtyping.h"
static int check_args(int argc, VALUE *obj, VALUE *mod);
static VALUE
strongtyping_expect(int argc, VALUE *argv, VALUE self UNUSED) {
int i = 0;
VALUE obj[MAXARGS], mod[MAXARGS];
VALUE typestr;
if(!argc) return Qnil;
if(argc % 2)
rb_raise(rb_eSyntaxError, "expect() requires argument pairs");
#ifndef __GNUC__
if(argc*2 > MAXARGS*2)
rb_raise(rb_eSyntaxError, "too many arguments to expect()");
#endif
for(i = 0; i < argc; i += 2) {
obj[i/2] = argv[i];
mod[(i+1)/2] = argv[i+1];
}
if(rb_funcall(obj[0], id_isa, 1, cQueryParams)) {
rb_funcall(obj[0], rb_intern("<<"), 1, rb_ary_new4(argc/2, mod));
rb_raise(eArgList, "");
}
i = check_args(argc / 2, obj, mod);
if(i < 0) return Qnil;
typestr = rb_funcall(mod[i], id_inspect, 0);
rb_raise(eArgumentTypeError, "Expecting %s as argument %d, got %s",
RSTRING(typestr)->ptr, i + 1,
rb_class2name(rb_funcall(obj[i], id_class, 0)));
}
static VALUE
strongtyping_overload(int argc, VALUE *argv, VALUE self UNUSED) {
struct RArray *q;
if(argc < 1)
rb_raise(rb_eArgError, "At least one parameter required");
Check_Type(argv[0], T_ARRAY);
q = RARRAY(argv[0]);
if(q->len && rb_funcall(q->ptr[0], id_isa, 1, cQueryParams)) {
rb_funcall(q->ptr[0], rb_intern("<<"), 1,
rb_ary_new4(argc - 1, argv + 1));
return Qnil;
}
if(q->len != (argc - 1))
return Qnil;
if(check_args(argc - 1, q->ptr, argv + 1) < 0) {
if(argc == 2) rb_yield(*RARRAY(*argv)->ptr);
else rb_yield(*argv);
}
return Qnil;
}
static VALUE
strongtyping_overload_exception(int argc, VALUE *argv, VALUE self UNUSED) {
struct RArray *q;
if(argc < 1)
rb_raise(rb_eArgError, "At least one parameters required");
Check_Type(argv[0], T_ARRAY);
q = RARRAY(argv[0]);
if(q->len && (argc - 1) == 0)
return Qnil;
if(check_args(argc - 1, q->ptr, argv + 1) < 0)
rb_yield(argv[0]);
return Qnil;
}
static VALUE
strongtyping_overload_error(VALUE self UNUSED, VALUE args) {
struct RArray *q;
VALUE classlist;
char *name = 0;
int i = 0;
Check_Type(args, T_ARRAY);
q = RARRAY(args);
if(q->len && rb_funcall(q->ptr[0], id_isa, 1, cQueryParams))
rb_raise(eArgList, "");
classlist = rb_str_new2("");
for(i = 0; i < q->len; i++) {
if(i > 0) rb_str_cat(classlist, ", ", 2);
name = rb_class2name(rb_funcall(q->ptr[i], id_class, 0));
rb_str_cat(classlist, name, strlen(name));
}
rb_raise(eOverloadError, "No matching template for arguments: [%s]",
RSTRING(classlist)->ptr);
}
static int
check_args(int argc, VALUE *obj, VALUE *mod) {
int i = 0;
VALUE ret;
for(i = 0; i < argc; i++) {
if(TYPE(mod[i]) == T_ARRAY) {
int j = 0, ok = 0;
for(j = 0; j < RARRAY(mod[i])->len; j++)
if(rb_funcall(obj[i], id_isa, 1, RARRAY(mod[i])->ptr[j]) == Qtrue)
ok = 1;
if(ok) continue;
else return i;
} else {
ret = rb_funcall(obj[i], id_isa, 1, mod[i]);
if(ret == Qfalse) return i;
}
}
return -1;
}
static VALUE
call_method(VALUE ary) {
VALUE method = RARRAY(ary)->ptr[0],
query = RARRAY(ary)->ptr[1];
VALUE *argv = NULL;
VALUE ret;
int argc = 0,
i = 0;
argc = FIX2INT(rb_funcall(method, rb_intern("arity"), 0));
if(argc == 0) {
rb_funcall(query, rb_intern("<<"), 1, rb_ary_new());
rb_raise(eArgList, "");
} else if(argc < 0) argc = -argc;
argv = malloc(sizeof(VALUE) * argc);
argv[0] = query;
for(i = 1; i < argc - 1; i++) argv[i] = Qnil;
ret = rb_funcall2(method, rb_intern("call"), argc, argv);
free(argv);
return ret;
}
static VALUE
grab_types(VALUE query) {
return query;
}
static VALUE
strongtyping_get_arg_types(VALUE obj UNUSED, VALUE method) {
VALUE query, ary;
query = rb_funcall(cQueryParams, rb_intern("new"), 0);
ary = rb_ary_new3(2, method, query);
return rb_rescue2(call_method, ary, grab_types, query, eArgList, 0);
}
static VALUE
strongtyping_verify_args_for(VALUE self, VALUE method, VALUE args) {
struct RArray *list = NULL,
*t = NULL,
*a = NULL;
int i = 0;
VALUE template = strongtyping_get_arg_types(self, method);
list = RARRAY(template);
a = RARRAY(args);
for(i = 0; i < list->len; i++) {
t = RARRAY(list->ptr[i]);
if(a->len != t->len) continue;
if(check_args(a->len, a->ptr, t->ptr) < 0) return Qtrue;
}
return Qfalse;
}
void Init_strongtyping() {
mStrongTyping = rb_define_module("StrongTyping");
id_isa = rb_intern("is_a?");
id_class = rb_intern("class");
id_inspect = rb_intern("inspect");
cQueryParams = rb_define_class_under(mStrongTyping,
"%QueryParams",
rb_cArray);
eArgumentTypeError = rb_define_class_under(mStrongTyping,
"ArgumentTypeError",
rb_eArgError);
eOverloadError = rb_define_class_under(mStrongTyping,
"OverloadError",
eArgumentTypeError);
eArgList = rb_define_class_under(mStrongTyping,
"%ArgList",
rb_eException);
rb_define_module_function(mStrongTyping, "expect",
strongtyping_expect, -1);
rb_define_module_function(mStrongTyping, "overload",
strongtyping_overload, -1);
rb_define_module_function(mStrongTyping, "overload_exception",
strongtyping_overload_exception, -1);
rb_define_module_function(mStrongTyping, "overload_default",
strongtyping_overload_error, 1);
rb_define_module_function(mStrongTyping, "overload_error",
strongtyping_overload_error, 1);
rb_define_module_function(mStrongTyping, "get_arg_types",
strongtyping_get_arg_types, 1);
rb_define_module_function(mStrongTyping, "verify_args_for",
strongtyping_verify_args_for, 2);
}
syntax highlighted by Code2HTML, v. 0.9.1