Ported upgrade to expressions too

This commit is contained in:
Matthias Koefferlein 2023-11-26 16:19:09 +01:00
parent 2b93dc2dc7
commit cb1589b2ba
3 changed files with 141 additions and 53 deletions

View File

@ -286,6 +286,25 @@ struct test_arg_func<gsi::ObjectType>
return;
}
if (arg.is_list ()) {
// we may implicitly convert an array into a constructor call of a target object -
// for now we only check whether the number of arguments is compatible with the array given.
int n = arg.size ();
*ret = false;
for (gsi::ClassBase::method_iterator c = atype.cls ()->begin_constructors (); c != atype.cls ()->end_constructors (); ++c) {
if ((*c)->compatible_with_num_args (n)) {
*ret = true;
break;
}
}
return;
}
if (! arg.is_user ()) {
*ret = false;
return;
@ -628,29 +647,77 @@ struct writer<gsi::ObjectType>
{
void operator() (gsi::SerialArgs *aa, tl::Variant *arg, const gsi::ArgType &atype, tl::Heap *heap)
{
if (atype.is_ref () || atype.is_cref () || atype.is_ptr () || atype.is_cptr ()) {
if (arg->is_nil ()) {
if (arg->is_nil ()) {
if (atype.is_ref () || atype.is_cref ()) {
throw tl::Exception (tl::to_string (tr ("Cannot pass nil to reference parameters")));
} else if (! atype.is_cptr () && ! atype.is_ptr ()) {
throw tl::Exception (tl::to_string (tr ("Cannot pass nil to direct parameters")));
}
if (atype.is_ref () || atype.is_cref ()) {
throw tl::Exception (tl::to_string (tr ("Cannot pass nil to reference parameters")));
aa->write<void *> ((void *) 0);
} else if (arg->is_list ()) {
// we may implicitly convert an array into a constructor call of a target object -
// for now we only check whether the number of arguments is compatible with the array given.
int n = arg->size ();
const gsi::MethodBase *meth = 0;
for (gsi::ClassBase::method_iterator c = atype.cls ()->begin_constructors (); c != atype.cls ()->end_constructors (); ++c) {
if ((*c)->compatible_with_num_args (n)) {
meth = *c;
break;
}
}
aa->write<void *> ((void *) 0);
if (!meth) {
throw tl::Exception (tl::to_string (tr ("No constructor of %s available that takes %d arguments (implicit call from tuple)")), atype.cls ()->name (), n);
}
} else {
// implicit call of constructor
gsi::SerialArgs retlist (meth->retsize ());
gsi::SerialArgs arglist (meth->argsize ());
if (! arg->is_user ()) {
throw tl::Exception (tl::sprintf (tl::to_string (tr ("Unexpected object type (expected argument of class %s)")), atype.cls ()->name ()));
int narg = 0;
for (gsi::MethodBase::argument_iterator a = meth->begin_arguments (); a != meth->end_arguments () && narg < n; ++a, ++narg) {
try {
// Note: this const_cast is ugly, but it will basically enable "out" parameters
// TODO: clean this up.
gsi::do_on_type<writer> () (a->type (), &arglist, (arg->get_list ().begin () + narg).operator-> (), *a, heap);
} catch (tl::Exception &ex) {
std::string msg = ex.msg () + tl::sprintf (tl::to_string (tr (" (argument '%s')")), a->spec ()->name ());
throw tl::Exception (msg);
}
}
const tl::VariantUserClassBase *cls = arg->user_cls ();
if (!cls) {
throw tl::Exception (tl::sprintf (tl::to_string (tr ("Unexpected object type (expected argument of class %s)")), atype.cls ()->name ()));
}
if (cls->is_const () && (atype.is_ref () || atype.is_ptr ())) {
throw tl::Exception (tl::sprintf (tl::to_string (tr ("Cannot pass a const reference of class %s to a non-const reference or pointer parameter")), atype.cls ()->name ()));
}
meth->call (0, arglist, retlist);
void *new_obj = retlist.read<void *> (*heap);
if (new_obj && (atype.is_ptr () || atype.is_cptr () || atype.is_ref () || atype.is_cref ())) {
// For pointers or refs, ownership over these objects is not transferred.
// Hence we have to keep them on the heap.
// TODO: what if the called method takes ownership using keep()?
heap->push (new gsi::ObjectHolder (atype.cls (), new_obj));
}
aa->write<void *> (new_obj);
} else {
if (! arg->is_user ()) {
throw tl::Exception (tl::sprintf (tl::to_string (tr ("Unexpected object type (expected argument of class %s)")), atype.cls ()->name ()));
}
const tl::VariantUserClassBase *cls = arg->user_cls ();
if (!cls) {
throw tl::Exception (tl::sprintf (tl::to_string (tr ("Unexpected object type (expected argument of class %s)")), atype.cls ()->name ()));
}
if (cls->is_const () && (atype.is_ref () || atype.is_ptr ())) {
throw tl::Exception (tl::sprintf (tl::to_string (tr ("Cannot pass a const reference of class %s to a non-const reference or pointer parameter")), atype.cls ()->name ()));
}
if (atype.is_ref () || atype.is_cref () || atype.is_ptr () || atype.is_cptr ()) {
if (cls->gsi_cls ()->is_derived_from (atype.cls ())) {
@ -673,36 +740,24 @@ struct writer<gsi::ObjectType>
throw tl::Exception (tl::sprintf (tl::to_string (tr ("Unexpected object type (expected argument of class %s)")), atype.cls ()->name ()));
}
}
} else {
} else {
if (cls->gsi_cls ()->is_derived_from (atype.cls ())) {
if (! arg->is_user ()) {
throw tl::Exception (tl::sprintf (tl::to_string (tr ("Unexpected object type (expected argument of class %s)")), atype.cls ()->name ()));
}
if (cls->gsi_cls ()->adapted_type_info ()) {
aa->write<void *> (cls->gsi_cls ()->create_adapted_from_obj (get_object (*arg)));
} else {
aa->write<void *> ((void *) cls->gsi_cls ()->clone (get_object (*arg)));
}
const tl::VariantUserClassBase *cls = arg->user_cls ();
if (!cls) {
throw tl::Exception (tl::sprintf (tl::to_string (tr ("Unexpected object type (expected argument of class %s)")), atype.cls ()->name ()));
}
if (cls->is_const () && (atype.is_ref () || atype.is_ptr ())) {
throw tl::Exception (tl::sprintf (tl::to_string (tr ("Cannot pass a const reference of class %s to a non-const reference or pointer parameter")), atype.cls ()->name ()));
}
} else if (cls->gsi_cls ()->can_convert_to (atype.cls ())) {
if (cls->gsi_cls ()->is_derived_from (atype.cls ())) {
aa->write<void *> (atype.cls ()->create_obj_from (cls->gsi_cls (), get_object (*arg)));
if (cls->gsi_cls ()->adapted_type_info ()) {
aa->write<void *> (cls->gsi_cls ()->create_adapted_from_obj (get_object (*arg)));
} else {
aa->write<void *> ((void *) cls->gsi_cls ()->clone (get_object (*arg)));
throw tl::Exception (tl::sprintf (tl::to_string (tr ("Unexpected object type (expected argument of class %s)")), atype.cls ()->name ()));
}
} else if (cls->gsi_cls ()->can_convert_to (atype.cls ())) {
aa->write<void *> (atype.cls ()->create_obj_from (cls->gsi_cls (), get_object (*arg)));
} else {
throw tl::Exception (tl::sprintf (tl::to_string (tr ("Unexpected object type (expected argument of class %s)")), atype.cls ()->name ()));
}
}

View File

@ -57,14 +57,14 @@ TEST(1)
v = e.parse ("A.aa").execute ();
EXPECT_EQ (v.to_string (), std::string ("static_a"));
v = e.parse ("A.new.a1").execute ();
v = e.parse ("A.new.get_n").execute ();
EXPECT_EQ (v.to_string (), std::string ("17"));
v = e.parse ("var a=A.new").execute ();
v = e.parse ("a.a5(-5); a.a1").execute ();
v = e.parse ("a.a5(-5); a.get_n").execute ();
EXPECT_EQ (v.to_string (), std::string ("-5"));
// mapping of property assignment to method
v = e.parse ("a.n = -177; a.a1").execute ();
v = e.parse ("a.n = -177; a.get_n").execute ();
EXPECT_EQ (v.to_string (), std::string ("-177"));
bool error = false;
try {
@ -104,9 +104,9 @@ TEST(1)
v = e.parse ("A.instance_count").execute ();
EXPECT_EQ (v.to_int (), base_insts); // remaining instances
v = e.parse ("var a1=A.new; a1.a5(-15); var a2=a1.dup; a2.a5(107); a1.a1").execute ();
v = e.parse ("var a1=A.new; a1.a5(-15); var a2=a1.dup; a2.a5(107); a1.get_n").execute ();
EXPECT_EQ (v.to_string (), std::string ("-15"));
v = e.parse ("var a1=A.new; a1.a5(-15); var a2=a1.dup; a2.a5(107); a2.a1").execute ();
v = e.parse ("var a1=A.new; a1.a5(-15); var a2=a1.dup; a2.a5(107); a2.get_n").execute ();
EXPECT_EQ (v.to_string (), std::string ("107"));
v = e.parse ("var a=A.new; a.get_e.to_s").execute ();
@ -218,12 +218,22 @@ TEST(2)
EXPECT_EQ (v.to_string (), std::string ("5"));
v = e.parse ("var b=B.new; var a1=A.new(-17); var a2=A.new(42); b.av = [ a1, a2 ]; to_s(b.av)").execute ();
EXPECT_EQ (v.to_string (), std::string ("A: -17,A: 42"));
v = e.parse ("var b=B.new; var a1=A.new(-17); var a2=A.new(42); b.av = []; b.push_a(a1); b.push_a(a2); to_s(b.av)").execute ();
EXPECT_EQ (v.to_string (), std::string ("A: -17,A: 42"));
v = e.parse ("var b=B.new; var a1=A.new(-17); var a2=A.new(42); b.av = []; b.push_a_cref(a1); b.push_a_cptr(a2); to_s(b.av)").execute ();
EXPECT_EQ (v.to_string (), std::string ("A: -17,A: 42"));
v = e.parse ("var b=B.new; var a1=A.new(-17); var a2=A.new(42); b.av = []; b.push_a_ref(a1); b.push_a_ptr(a2); to_s(b.av)").execute ();
EXPECT_EQ (v.to_string (), std::string ("A: -17,A: 42"));
v = e.parse ("var b=B.new; var a1=A.new(-17); var a2=A.new(1); b.av_cref = [ a1, a2 ]; to_s(b.av_cref)").execute ();
EXPECT_EQ (v.to_string (), std::string ("A: -17,A: 1"));
v = e.parse ("var b=B.new; b.av = [ A.new(-13) ]; b.av_cptr = nil; to_s(b.av)").execute ();
v = e.parse ("var b=B.new; b.av_cptr = [ A.new(-13) ]; to_s(b.av)").execute ();
EXPECT_EQ (v.to_string (), std::string ("A: -13"));
v = e.parse ("var b=B.new; b.av = [ A.new(13) ]; b.av_ptr = nil; to_s(b.av)").execute ();
v = e.parse ("var b=B.new; b.av_ptr = [ A.new(13) ]; to_s(b.av)").execute ();
EXPECT_EQ (v.to_string (), std::string ("A: 13"));
v = e.parse ("var b=B.new; b.av = [ A.new(-13) ]; b.av_cptr = nil; to_s(b.av)").execute ();
EXPECT_EQ (v.to_string (), std::string (""));
v = e.parse ("var b=B.new; b.av = [ A.new(13) ]; b.av_ptr = nil; to_s(b.av)").execute ();
EXPECT_EQ (v.to_string (), std::string (""));
v = e.parse ("var b=B.new; var a1=A.new(17); b.av_ref = [ a1 ]; to_s(b.av_ref)").execute ();
EXPECT_EQ (v.to_string (), std::string ("A: 17"));
v = e.parse ("var b=B.new; b.arg_is_not_nil(nil)").execute ();
@ -234,6 +244,21 @@ TEST(2)
EXPECT_EQ (v.to_string (), std::string ("17"));
v = e.parse ("var b=B.new; b.bx(-1)").execute ();
EXPECT_EQ (v.to_string (), std::string ("xz"));
// List to constructor call
v = e.parse ("var b=B.new; b.av = [ [5, 6], [4, 6, 0.5], [42] ]; to_s(b.av)").execute ();
EXPECT_EQ (v.to_string (), std::string ("A: 11,A: 5,A: 42"));
v = e.parse ("var b=B.new; b.av = []; b.push_a([ 1, 2 ]); b.push_a([ 17 ]); to_s(b.av)").execute ();
EXPECT_EQ (v.to_string (), std::string ("A: 3,A: 17"));
v = e.parse ("var b=B.new; b.av = []; b.push_a([ 1, 2 ]); b.push_a_cref([ 17 ]); to_s(b.av)").execute ();
EXPECT_EQ (v.to_string (), std::string ("A: 3,A: 17"));
v = e.parse ("var b=B.new; b.av = []; b.push_a([ 1, 2 ]); b.push_a_cptr([ 17 ]); to_s(b.av)").execute ();
EXPECT_EQ (v.to_string (), std::string ("A: 3,A: 17"));
v = e.parse ("var b=B.new; b.av = []; b.push_a([ 1, 2 ]); b.push_a_ref([ 17 ]); to_s(b.av)").execute ();
EXPECT_EQ (v.to_string (), std::string ("A: 3,A: 17"));
v = e.parse ("var b=B.new; b.av = []; b.push_a([ 1, 2 ]); b.push_a_ptr([ 17 ]); to_s(b.av)").execute ();
EXPECT_EQ (v.to_string (), std::string ("A: 3,A: 17"));
/*
TODO: No detailed type analysis for ambiguity resolution so far:
v = e.parse ("var b=B.new; b.bx('hello', 1)").execute ();
@ -316,15 +341,15 @@ TEST(2)
EXPECT_EQ (v.to_string (), std::string ("A: 177"));
v = e.parse ("b.amember_or_nil(false)").execute ();
EXPECT_EQ (v.to_string (), std::string ("nil"));
v = e.parse ("b.amember_ptr.a5(177); b.amember_ref.a1").execute ();
v = e.parse ("b.amember_ptr.a5(177); b.amember_ref.get_n").execute ();
EXPECT_EQ (v.to_string (), std::string ("177"));
v = e.parse ("b.amember_ref.a1c").execute ();
v = e.parse ("b.amember_ref.get_n_const").execute ();
EXPECT_EQ (v.to_string (), std::string ("177"));
v = e.parse ("b.amember_cref.a1c").execute ();
v = e.parse ("b.amember_cref.get_n_const").execute ();
EXPECT_EQ (v.to_string (), std::string ("177"));
error = false;
try {
v = e.parse ("b.amember_cref.a1").execute ();
v = e.parse ("b.amember_cref.get_n").execute ();
} catch (...) {
// can't call non-const method on const ref
error = true;
@ -334,16 +359,16 @@ TEST(2)
// references: storage in variables
v = e.parse ("var aref = b.amember_ptr").execute ();
v = e.parse ("aref.n = 178").execute ();
v = e.parse ("aref.a1").execute ();
v = e.parse ("aref.get_n").execute ();
EXPECT_EQ (v.to_string (), std::string ("178"));
v = e.parse ("aref.a1 == 178").execute ();
v = e.parse ("aref.get_n == 178").execute ();
EXPECT_EQ (v.to_string (), std::string ("true"));
v = e.parse ("b.amember_ref.a1").execute ();
v = e.parse ("b.amember_ref.get_n").execute ();
EXPECT_EQ (v.to_string (), std::string ("178"));
// references: storage in variables
v = e.parse ("var aref = b.amember_cptr").execute ();
v = e.parse ("aref.a1c").execute ();
v = e.parse ("aref.get_n_const").execute ();
EXPECT_EQ (v.to_string (), std::string ("178"));
error = false;
try {

View File

@ -265,7 +265,15 @@ bool TestBase::do_test (bool editable, bool slow)
reset_checkpoint ();
execute (this);
try {
execute (this);
} catch (tl::Exception &ex) {
raise (std::string ("Exception caught: ") + ex.msg ());
} catch (std::runtime_error &ex) {
raise (std::string ("std::runtime_error caught: ") + ex.what ());
} catch (...) {
raise (std::string ("unspecific exception caught: "));
}
m_testtmp.clear ();