ext/rubype/rubype.c in rubype-0.2.6 vs ext/rubype/rubype.c in rubype-0.3.0
- old
+ new
@@ -1,38 +1,100 @@
#include "rubype.h"
-VALUE rb_mRubype, rb_cAny, rb_mBoolean, rb_cTypePair;
+VALUE rb_mRubype, rb_cContract, rb_eRubypeArgumentTypeError, rb_eRubypeReturnTypeError, rb_eInvalidTypesigError;
+static ID id_is_a_p, id_to_s, id_meth, id_owner, id_arg_types, id_rtn_type;
-#define STR2SYM(x) ID2SYM(rb_intern(x))
+#define error_fmt "\nfor %"PRIsVALUE"\nExpected: %"PRIsVALUE"\nActual: %"PRIsVALUE""
+#define unmatch_type_p(obj, type_info) !(match_type_p(obj, type_info))
+int match_type_p(VALUE obj, VALUE type_info)
+{
+ switch (TYPE(type_info)) {
+ case T_SYMBOL: return rb_respond_to(obj, rb_to_id(type_info));
+ break;
+
+ case T_MODULE: return (int)rb_funcall(obj, id_is_a_p, 1, type_info);
+ break;
+
+ case T_CLASS: return (int)rb_funcall(obj, id_is_a_p, 1, type_info);
+ break;
+
+ default: return 0;
+ break;
+ }
+}
+
+VALUE expected_mes(VALUE expected)
+{
+ switch (TYPE(expected)) {
+ case T_SYMBOL: return rb_sprintf("respond to #%"PRIsVALUE, expected);
+ break;
+
+ case T_MODULE: return rb_funcall(expected, id_to_s, 0);
+ break;
+
+ case T_CLASS: return rb_funcall(expected, id_to_s, 0);
+ break;
+
+ default: return rb_str_new2("");
+ break;
+ }
+}
+
static VALUE
-rb_mod_prepend(int argc, VALUE *argv, VALUE module)
+rb_rubype_assert_type(VALUE self, VALUE args, VALUE rtn)
{
int i;
- ID id_prepend_features, id_prepended;
+ VALUE target;
+ VALUE meth_caller, meth, arg_types, arg_type, rtn_type, arg;
- CONST_ID(id_prepend_features, "prepend_features");
- CONST_ID(id_prepended, "prepended");
- for (i = 0; i < argc; i++)
- Check_Type(argv[i], T_MODULE);
- while (argc--) {
- rb_funcall(argv[argc], id_prepend_features, 1, module);
- rb_funcall(argv[argc], id_prepended, 1, module);
+ meth_caller = rb_ivar_get(self, id_owner);
+ meth = rb_ivar_get(self, id_meth);
+ arg_types = rb_ivar_get(self, id_arg_types);
+ rtn_type = rb_ivar_get(self, id_rtn_type);
+
+ for (i=0; i<RARRAY_LEN(args); i++) {
+ arg = rb_ary_entry(args, i);
+ arg_type = rb_ary_entry(arg_types, i);
+
+ if (unmatch_type_p(arg, arg_type)){
+ target = rb_sprintf("%"PRIsVALUE"#%"PRIsVALUE"'s %d argument", meth_caller, meth, i+1);
+ rb_raise(rb_eRubypeArgumentTypeError, error_fmt, target, expected_mes(arg_type), arg);
}
+ }
- return module;
+ if (unmatch_type_p(rtn, rtn_type)){
+ target = rb_sprintf("%"PRIsVALUE"#%"PRIsVALUE"'s return", meth_caller, meth);
+ rb_raise(rb_eRubypeReturnTypeError, error_fmt, target, expected_mes(rtn_type), rtn);
+ }
+ return rtn;
}
+static VALUE
+rb_rubype_initialize(VALUE self, VALUE arg_types, VALUE rtn_type, VALUE owner, VALUE meth) {
+ rb_ivar_set(self, id_owner, owner);
+ rb_ivar_set(self, id_meth, meth);
+ rb_ivar_set(self, id_arg_types, arg_types);
+ rb_ivar_set(self, id_rtn_type, rtn_type);
+ return Qnil;
+}
+
void
Init_rubype(void)
{
- // rb_mRubype = rb_define_module("Rubype");
- // rb_cAny = rb_define_class("Any", rb_cObject);
- // rb_mBoolean = rb_define_module("Boolean");
- // rb_include_module(rb_cTrueClass, rb_mBoolean);
- // rb_include_module(rb_cFalseClass, rb_mBoolean);
- // rb_define_class(
- // "TypePair",
- // rb_funcall(rb_cStruct, rb_intern("new"), 2, STR2SYM("last_arg_type"), STR2SYM("rtn_type"))
- // );
- rb_define_method(rb_cModule, "prepend", rb_mod_prepend, -1);
+ rb_mRubype = rb_define_module("Rubype");
+
+ rb_eRubypeArgumentTypeError = rb_define_class_under(rb_mRubype, "ArgumentTypeError", rb_eTypeError);
+ rb_eRubypeReturnTypeError = rb_define_class_under(rb_mRubype, "ReturnTypeError", rb_eTypeError);
+ rb_eInvalidTypesigError = rb_define_class_under(rb_mRubype, "InvalidTypesigError", rb_eTypeError);
+
+ rb_cContract = rb_define_class_under(rb_mRubype, "Contract", rb_cObject);
+ rb_define_method(rb_cContract, "initialize", rb_rubype_initialize, 4);
+ rb_define_method(rb_cContract, "assert_type", rb_rubype_assert_type, 2);
+
+ id_meth = rb_intern("@meth");
+ id_owner = rb_intern("@owner");
+ id_arg_types = rb_intern("@arg_types");
+ id_rtn_type = rb_intern("@rtn_type");
+ id_is_a_p = rb_intern("is_a?");
+ id_to_s = rb_intern("to_s");
}