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