From 40aeae949157971e85ce812a19c3bac255e465f3 Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Fri, 25 Aug 2023 14:02:48 -0400 Subject: [PATCH 001/181] Consolidate includes and initialization into geometry.c/.h --- src/builtin/builtin.c | 10 ++------- src/builtin/functiondefs.c | 4 +--- src/geometry/CMakeLists.txt | 14 +++++++----- src/geometry/discretization.c | 41 +++++++++++++++++++++++++++++++++++ src/geometry/discretization.h | 12 ++++++++++ src/geometry/field.c | 1 + src/geometry/field.h | 2 +- src/geometry/functional.c | 1 + src/geometry/geometry.c | 15 +++++++++++++ src/geometry/geometry.h | 21 ++++++++++++++++++ src/geometry/integrate.c | 4 +--- src/geometry/mesh.c | 6 +---- src/geometry/selection.c | 3 +-- 13 files changed, 107 insertions(+), 27 deletions(-) create mode 100644 src/geometry/discretization.c create mode 100644 src/geometry/discretization.h create mode 100644 src/geometry/geometry.c create mode 100644 src/geometry/geometry.h diff --git a/src/builtin/builtin.c b/src/builtin/builtin.c index 120da9f7..ff59347d 100644 --- a/src/builtin/builtin.c +++ b/src/builtin/builtin.c @@ -10,10 +10,7 @@ #include "functiondefs.h" #include "file.h" #include "system.h" -#include "mesh.h" -#include "selection.h" -#include "functional.h" -#include "field.h" +#include "geometry.h" #include "classes.h" /* ********************************************************************** @@ -309,10 +306,7 @@ void builtin_initialize(void) { sparse_initialize(); // Initialize geometry - mesh_initialize(); - selection_initialize(); - field_initialize(); - functional_initialize(); + geometry_initialize(); } void builtin_finalize(void) { diff --git a/src/builtin/functiondefs.c b/src/builtin/functiondefs.c index e7ce0776..b2b721cc 100644 --- a/src/builtin/functiondefs.c +++ b/src/builtin/functiondefs.c @@ -11,9 +11,7 @@ #include "builtin.h" #include "common.h" #include "matrix.h" -#include "mesh.h" -#include "field.h" -#include "selection.h" +#include "geometry.h" #include "cmplx.h" #ifndef M_PI diff --git a/src/geometry/CMakeLists.txt b/src/geometry/CMakeLists.txt index 74d9e689..cad815fb 100644 --- a/src/geometry/CMakeLists.txt +++ b/src/geometry/CMakeLists.txt @@ -1,10 +1,12 @@ target_sources(morpho PRIVATE - field.c field.h - functional.c functional.h - integrate.c integrate.h - mesh.c mesh.h - selection.c selection.h + discretization.c discretization.h + field.c field.h + functional.c functional.h + geometry.c geometry.h + integrate.c integrate.h + mesh.c mesh.h + selection.c selection.h ) target_sources(morpho @@ -12,8 +14,10 @@ target_sources(morpho FILE_SET public_headers TYPE HEADERS FILES + discretization.h field.h functional.h + geometry.h integrate.h mesh.h selection.h diff --git a/src/geometry/discretization.c b/src/geometry/discretization.c new file mode 100644 index 00000000..379d0bde --- /dev/null +++ b/src/geometry/discretization.c @@ -0,0 +1,41 @@ +/** @file discretization.c + * @author T J Atherton + * + * @brief Finite element discretizations + */ + +#include "geometry.h" + +typedef void (*interpolationfn) (double *, double *); + +typedef struct { + grade grade; + int *shape; + int nnodes; + interpolationfn ifn; +} discretization; + +/* ------------------------------------------------------- + * CG1 elements + * ------------------------------------------------------- */ + +void cg1interpolate(double *lambda, double *wts) { + wts[0]=lambda[1]; + wts[1]=lambda[0]; +} + +int cg1shape[] = { 1, 0 }; + +discretization cg1 = { + .grade = 1, + .shape = cg1shape, + .nnodes = 2, + .ifn = cg1interpolate +}; + +/* ********************************************************************** + * Discretization objects + * ********************************************************************** */ + +void discretization_initialize(void) { +} diff --git a/src/geometry/discretization.h b/src/geometry/discretization.h new file mode 100644 index 00000000..3d145472 --- /dev/null +++ b/src/geometry/discretization.h @@ -0,0 +1,12 @@ +/** @file discretization.h + * @author T J Atherton + * + * @brief Finite element discretizations + */ + +#ifndef discretization_h +#define discretization_h + +void discretization_initialize(void); + +#endif diff --git a/src/geometry/field.c b/src/geometry/field.c index 8580f09b..7e470452 100644 --- a/src/geometry/field.c +++ b/src/geometry/field.c @@ -9,6 +9,7 @@ #include "classes.h" #include "common.h" #include "matrix.h" +#include "sparse.h" static value field_gradeoption; diff --git a/src/geometry/field.h b/src/geometry/field.h index 250bab52..800948ba 100644 --- a/src/geometry/field.h +++ b/src/geometry/field.h @@ -8,8 +8,8 @@ #define field_h #include "object.h" -#include "mesh.h" #include "matrix.h" +#include "mesh.h" #include /* ------------------------------------------------------- diff --git a/src/geometry/functional.c b/src/geometry/functional.c index ce03db14..86929d51 100644 --- a/src/geometry/functional.c +++ b/src/geometry/functional.c @@ -14,6 +14,7 @@ #include "matrix.h" #include "sparse.h" #include "integrate.h" +#include "selection.h" #include #ifndef M_PI diff --git a/src/geometry/geometry.c b/src/geometry/geometry.c new file mode 100644 index 00000000..31a4169c --- /dev/null +++ b/src/geometry/geometry.c @@ -0,0 +1,15 @@ +/** @file geometry.c + * @author T J Atherton + * + * @brief Geometry wrapper + */ + +#include "geometry.h" + +void geometry_initialize(void) { + mesh_initialize(); + field_initialize(); + functional_initialize(); + discretization_initialize(); + selection_initialize(); +} diff --git a/src/geometry/geometry.h b/src/geometry/geometry.h new file mode 100644 index 00000000..e187a6b8 --- /dev/null +++ b/src/geometry/geometry.h @@ -0,0 +1,21 @@ +/** @file geometry.h + * @author T J Atherton + * + * @brief Geometry wrapper + */ + +#ifndef geometry_h +#define geometry_h + +#include "mesh.h" +#include "field.h" +#include "selection.h" +#include "functional.h" +#include "discretization.h" +#include "integrate.h" + +void geometry_initialize(void); + + + +#endif diff --git a/src/geometry/integrate.c b/src/geometry/integrate.c index db7605bd..56901134 100644 --- a/src/geometry/integrate.c +++ b/src/geometry/integrate.c @@ -11,9 +11,7 @@ #include "matrix.h" #include "sparse.h" -#include "mesh.h" -#include "selection.h" -#include "functional.h" +#include "geometry.h" bool integrate_recognizequantities(unsigned int nquantity, value *quantity, value *out) { if (nquantity>0) { diff --git a/src/geometry/mesh.c b/src/geometry/mesh.c index 4f9978e8..15db7ac1 100644 --- a/src/geometry/mesh.c +++ b/src/geometry/mesh.c @@ -6,15 +6,11 @@ #include "morpho.h" #include "classes.h" -#include "mesh.h" #include "file.h" #include "parse.h" #include "sparse.h" #include "matrix.h" -#include "selection.h" - -// Temporary include -#include "integrate.h" +#include "geometry.h" #include diff --git a/src/geometry/selection.c b/src/geometry/selection.c index 57c8c219..aa78b4af 100644 --- a/src/geometry/selection.c +++ b/src/geometry/selection.c @@ -10,8 +10,7 @@ #include "classes.h" #include "matrix.h" #include "sparse.h" -#include "mesh.h" -#include "selection.h" +#include "geometry.h" /* ********************************************************************** * Selection object definitions From 305e43eaeec4eef3b0f301f689ee5d4282582be7 Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Fri, 25 Aug 2023 17:52:12 -0400 Subject: [PATCH 002/181] objectdiscretizations and FunctionSpace veneer class --- src/geometry/discretization.c | 215 ++++++++++++++++++++++++++++++--- src/geometry/discretization.h | 49 ++++++++ test/discretization/cg2.morpho | 10 ++ 3 files changed, 260 insertions(+), 14 deletions(-) create mode 100644 test/discretization/cg2.morpho diff --git a/src/geometry/discretization.c b/src/geometry/discretization.c index 379d0bde..1212cecf 100644 --- a/src/geometry/discretization.c +++ b/src/geometry/discretization.c @@ -6,36 +6,223 @@ #include "geometry.h" -typedef void (*interpolationfn) (double *, double *); +/* ********************************************************************** + * Discretization objects + * ********************************************************************** */ + +objecttype objectdiscretizationtype; -typedef struct { - grade grade; - int *shape; - int nnodes; - interpolationfn ifn; -} discretization; +/** Field object definitions */ +void objectdiscretization_printfn(object *obj, void *v) { + objectdiscretization *disc=(objectdiscretization *) obj; + morpho_printf(v, "", disc->discretization->name); +} + +size_t objectdiscretization_sizefn(object *obj) { + return sizeof(objectdiscretization); +} + +objecttypedefn objectdiscretizationdefn = { + .printfn=objectdiscretization_printfn, + .markfn=NULL, + .freefn=NULL, + .sizefn=objectdiscretization_sizefn +}; + +/* ********************************************************************** + * Discretization definitions + * ********************************************************************** */ + +#define LINE_OPCODE 1 +#define AREA_OPCODE 2 +#define QUANTITY_OPCODE 255 + +#define LINE(id, v1, v2) 1, id, v1, v2 // Identify a grade 1 subelement given by two vertex indices +#define AREA(id, v1, v2, v3) 2, id, v1, v2, v3 // Identify a grade 2 subelement given by three vertex indices +#define QUANTITY(grade, id, qno) 128, grade, id, qno // Fetch quantity from subelement of grade with id and quantity number +#define ENDDEFN -1 /* ------------------------------------------------------- - * CG1 elements + * CG1 element in 1D * ------------------------------------------------------- */ -void cg1interpolate(double *lambda, double *wts) { +/* + * 0 - 1 // One degree of freedom per vertex + */ + +void cg1_1dinterpolate(double *lambda, double *wts) { wts[0]=lambda[1]; wts[1]=lambda[0]; } -int cg1shape[] = { 1, 0 }; +int cg1_1dshape[] = { 1, 0 }; + +double cg1_1dnodes[] = { 0.0, 1.0 }; + +eldefninstruction cg1_1ddefn[] = { + QUANTITY(0,0,0), // Fetch quantity on vertex 0 + QUANTITY(0,1,0), // Fetch quantity on vertex 1 + ENDDEFN +}; -discretization cg1 = { +discretization cg1_1d = { + .name = "CG1", .grade = 1, - .shape = cg1shape, + .shape = cg1_1dshape, + .degree = 1, .nnodes = 2, - .ifn = cg1interpolate + .nsubel = 0, + .nodes = cg1_1dnodes, + .ifn = cg1_1dinterpolate, + .eldefn = cg1_1ddefn +}; + +/* ------------------------------------------------------- + * CG2 element in 1D + * ------------------------------------------------------- */ + +/* + * 0 - 2 - 1 // One degree of freedom per vertex; one at the midpoint + */ + +void cg2_1dinterpolate(double *lambda, double *wts) { + double dl = (lambda[1]-lambda[0]); + wts[0]=lambda[1]*dl; + wts[1]=4*lambda[0]*lambda[1]; + wts[2]=-lambda[0]*dl; +} + +int cg2_1dshape[] = { 1, 1 }; + +double cg2_1dnodes[] = { 0.0, 1.0, 0.5 }; + +eldefninstruction cg2_1ddefn[] = { + LINE(0,0,1), // Identify line subelement with vertex indices (0,1) + QUANTITY(0,0,0), // Fetch quantity on vertex 0 + QUANTITY(0,1,0), // Fetch quantity on vertex 1 + QUANTITY(1,0,0), // Fetch quantity from line subelement + ENDDEFN +}; + +discretization cg2_1d = { + .name = "CG2", + .grade = 1, + .shape = cg2_1dshape, + .degree = 2, + .nnodes = 3, + .ifn = cg2_1dinterpolate, + .eldefn = cg2_1ddefn +}; + +/* ------------------------------------------------------- + * CG2 element in 2D + * ------------------------------------------------------- */ + +/* 2 + * |\ + * 5 4 + * | \ + * 0-3-1 // One degree of freedom per vertex; one at the midpoint + */ + +void cg2_2dinterpolate(double *lambda, double *wts) { + wts[0]=lambda[2]*(2*lambda[2]-1); + wts[1]=lambda[0]*(2*lambda[0]-1); + wts[2]=lambda[1]*(2*lambda[1]-1); + wts[3]=4*lambda[0]*lambda[2]; + wts[4]=4*lambda[0]*lambda[1]; + wts[5]=4*lambda[1]*lambda[2]; +} + +int cg2_2dshape[] = { 1, 1, 0 }; + +double cg2_2dnodes[] = { 0.0, 0.0, + 1.0, 0.0, + 0.0, 1.0, + 0.5, 0.0, + 0.5, 0.5, + 0.0, 0.5 }; + +eldefninstruction cg2_2deldefn[] = { + LINE(0,0,1), // Identify line subelement with vertex indices (0,1) + LINE(1,1,2), // Identify line subelement with vertex indices (1,2) + LINE(2,2,0), // Identify line subelement with vertex indices (2,0) + QUANTITY(0,0,0), // Fetch quantity on vertex 0 + QUANTITY(0,1,0), // Fetch quantity on vertex 1 + QUANTITY(0,2,0), // Fetch quantity on vertex 2 + QUANTITY(1,0,0), // Fetch quantity from line 0 + QUANTITY(1,1,0), // Fetch quantity from line 1 + QUANTITY(1,2,0), // Fetch quantity from line 2 + ENDDEFN +}; + +discretization cg2_2d = { + .name = "CG2", + .grade = 2, + .shape = cg2_1dshape, + .degree = 2, + .nnodes = 6, + .nodes = cg2_2dnodes, + .ifn = cg2_2dinterpolate, + .eldefn = cg2_2deldefn +}; + +discretization *discretizations[] = { + &cg1_1d, + &cg2_1d, + &cg2_2d, + NULL }; /* ********************************************************************** - * Discretization objects + * Match DOFs to node numbers + * ********************************************************************** */ + +void discretization_process(objectmesh *mesh, discretization *disc) { + elementid subel[disc->nsubel+1]; + + for (eldefninstruction *instr=disc->eldefn; instr!=NULL && *instr!=ENDDEFN; ) { + switch(*instr) { + case LINE_OPCODE: + instr+=4; + break; + case AREA_OPCODE: + instr+=5; + break; + case QUANTITY_OPCODE: + instr+=4; + break; + default: + UNREACHABLE("Error in finite element definition"); + } + } +} + +/* ********************************************************************** + * FunctionSpace class + * ********************************************************************** */ + +/** Constructs a functionspace object */ +value functionspace_constructor(vm *v, int nargs, value *args) { + +} + +MORPHO_BEGINCLASS(FunctionSpace) +MORPHO_METHOD(MORPHO_GETINDEX_METHOD, NULL, BUILTIN_FLAGSEMPTY) +MORPHO_ENDCLASS + +/* ********************************************************************** + * Initialization * ********************************************************************** */ void discretization_initialize(void) { + objectdiscretizationtype=object_addtype(&objectdiscretizationdefn); + + builtin_addfunction(FUNCTIONSPACE_CLASSNAME, functionspace_constructor, BUILTIN_FLAGSEMPTY); + + objectstring objname = MORPHO_STATICSTRING(OBJECT_CLASSNAME); + value objclass = builtin_findclass(MORPHO_OBJECT(&objname)); + + value functionspaceclass=builtin_addclass(FUNCTIONSPACE_CLASSNAME, MORPHO_GETCLASSDEFINITION(FunctionSpace), objclass); + object_setveneerclass(OBJECT_DISCRETIZATION, functionspaceclass); } diff --git a/src/geometry/discretization.h b/src/geometry/discretization.h index 3d145472..e753348c 100644 --- a/src/geometry/discretization.h +++ b/src/geometry/discretization.h @@ -7,6 +7,55 @@ #ifndef discretization_h #define discretization_h +/* ------------------------------------------------------- + * Discretization type definitions + * ------------------------------------------------------- */ + +/** @brief Interpolation functions are called to assign weights to the nodes given barycentric coordinates */ +typedef void (*interpolationfn) (double *, double *); + +/** @brief Element definitions comprise a sequence of instructions to map field degrees of freedom to local nodes */ +typedef int eldefninstruction; + +/** @brief Discretization definitions */ +typedef struct { + char *name; /** Name of the discretization */ + grade grade; /** Grade of element this discretization is defined on */ + int *shape; /** Number of degrees of freedom on each grade; must have grade+1 entries */ + int degree; /** Highest degree of polynomial represented by this element */ + int nnodes; /** Number of nodes for this element type */ + int nsubel; /** Number of subelements used by */ + double *nodes; /** Node positions */ + interpolationfn ifn; /** Interpolation function; receives barycentric coordinates as input and returns weights per node */ + eldefninstruction *eldefn; /** Element definition */ +} discretization; + +/* ------------------------------------------------------- + * Discretization object type + * ------------------------------------------------------- */ + +extern objecttype objectdiscretizationtype; +#define OBJECT_DISCRETIZATION objectdiscretizationtype + +typedef struct { + object obj; + discretization *discretization; +} objectdiscretization; + +/* ------------------------------------------------------- + * FunctionSpace veneer class + * ------------------------------------------------------- */ + +#define FUNCTIONSPACE_CLASSNAME "FunctionSpace" + +/* ------------------------------------------------------- + * Discretization error messages + * ------------------------------------------------------- */ + +/* ------------------------------------------------------- + * Discretization interface + * ------------------------------------------------------- */ + void discretization_initialize(void); #endif diff --git a/test/discretization/cg2.morpho b/test/discretization/cg2.morpho new file mode 100644 index 00000000..e5ac7694 --- /dev/null +++ b/test/discretization/cg2.morpho @@ -0,0 +1,10 @@ + +import meshtools + +var m = LineMesh(fn (t) [t,0,0], 0..1:1) + +var l = FunctionSpace("CG", 2) + +var f = Field(m, fn (x,y,z) x^2, functionspace=l) + +print LineIntegral(fn (x, u) u, f).total() From 3b9c9e02f22d3b24c468d405a34eb2107613ce30 Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Fri, 25 Aug 2023 21:18:00 -0400 Subject: [PATCH 003/181] Create FunctionSpace objects --- src/geometry/discretization.c | 47 +++++++++++++++++++++++++++++++++- src/geometry/discretization.h | 6 +++++ src/geometry/field.c | 2 +- src/geometry/field.h | 2 ++ test/discretization/cg2.morpho | 8 +++--- 5 files changed, 60 insertions(+), 5 deletions(-) diff --git a/src/geometry/discretization.c b/src/geometry/discretization.c index 1212cecf..359c0bf4 100644 --- a/src/geometry/discretization.c +++ b/src/geometry/discretization.c @@ -29,6 +29,15 @@ objecttypedefn objectdiscretizationdefn = { .sizefn=objectdiscretization_sizefn }; +/** Creates a new discretization object + * @param[in] discretization - discretization definition to use */ +objectdiscretization *objectdiscretization_new(discretization *disc) { + objectdiscretization *new = (objectdiscretization *) object_new(sizeof(objectdiscretization), OBJECT_DISCRETIZATION); + if (new) new->discretization=disc; + + return new; +} + /* ********************************************************************** * Discretization definitions * ********************************************************************** */ @@ -175,9 +184,18 @@ discretization *discretizations[] = { }; /* ********************************************************************** - * Match DOFs to node numbers + * Discretization functions * ********************************************************************** */ +/** Find a discretization definition based on a name and grade */ +discretization *discretization_find(char *name, grade g) { + for (int i=0; discretizations[i]!=NULL; i++) { + if (strcmp(name, discretizations[i]->name)==0 && + g==discretizations[i]->grade) return discretizations[i]; + } + return NULL; +} + void discretization_process(objectmesh *mesh, discretization *disc) { elementid subel[disc->nsubel+1]; @@ -204,7 +222,31 @@ void discretization_process(objectmesh *mesh, discretization *disc) { /** Constructs a functionspace object */ value functionspace_constructor(vm *v, int nargs, value *args) { + value grd=MORPHO_INTEGER(1); + value out=MORPHO_NIL; + int nfixed; + + if (!builtin_options(v, nargs, args, &nfixed, 1, field_gradeoption, &grd)) + morpho_runtimeerror(v, FNSPC_ARGS); + if (nfixed==1 && + MORPHO_ISSTRING(MORPHO_GETARG(args, 0)) && + MORPHO_ISINTEGER(grd)) { + char *label = MORPHO_GETCSTRING(MORPHO_GETARG(args, 0)); + + discretization *d=discretization_find(label, MORPHO_GETINTEGERVALUE(grd)); + + if (d) { + objectdiscretization *obj=objectdiscretization_new(d); + if (obj) { + out = MORPHO_OBJECT(obj); + morpho_bindobjects(v, 1, &out); + } else morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED); + } else morpho_runtimeerror(v, FNSPC_NOTFOUND, label, MORPHO_GETINTEGERVALUE(grd)); + + } else morpho_runtimeerror(v, FNSPC_ARGS); + + return out; } MORPHO_BEGINCLASS(FunctionSpace) @@ -225,4 +267,7 @@ void discretization_initialize(void) { value functionspaceclass=builtin_addclass(FUNCTIONSPACE_CLASSNAME, MORPHO_GETCLASSDEFINITION(FunctionSpace), objclass); object_setveneerclass(OBJECT_DISCRETIZATION, functionspaceclass); + + morpho_defineerror(FNSPC_ARGS, ERROR_HALT, FNSPC_ARGS_MSG); + morpho_defineerror(FNSPC_NOTFOUND, ERROR_HALT, FNSPC_NOTFOUND_MSG); } diff --git a/src/geometry/discretization.h b/src/geometry/discretization.h index e753348c..c9d17c51 100644 --- a/src/geometry/discretization.h +++ b/src/geometry/discretization.h @@ -52,6 +52,12 @@ typedef struct { * Discretization error messages * ------------------------------------------------------- */ +#define FNSPC_ARGS "FnSpcArgs" +#define FNSPC_ARGS_MSG "Function space must be initialized with a label and a grade." + +#define FNSPC_NOTFOUND "FnSpcNtFnd" +#define FNSPC_NOTFOUND_MSG "Function space '%s' on grade %i not found." + /* ------------------------------------------------------- * Discretization interface * ------------------------------------------------------- */ diff --git a/src/geometry/field.c b/src/geometry/field.c index 7e470452..e6874974 100644 --- a/src/geometry/field.c +++ b/src/geometry/field.c @@ -11,7 +11,7 @@ #include "matrix.h" #include "sparse.h" -static value field_gradeoption; +value field_gradeoption; /* ********************************************************************** * Field objects diff --git a/src/geometry/field.h b/src/geometry/field.h index 800948ba..4c692144 100644 --- a/src/geometry/field.h +++ b/src/geometry/field.h @@ -50,6 +50,8 @@ objectfield *object_newfield(objectmesh *mesh, value prototype, unsigned int *do #define FIELD_CLASSNAME "Field" +extern value field_gradeoption; + #define FIELD_GRADEOPTION "grade" #define FIELD_OP_METHOD "op" #define FIELD_SHAPE_METHOD "shape" diff --git a/test/discretization/cg2.morpho b/test/discretization/cg2.morpho index e5ac7694..b110ef05 100644 --- a/test/discretization/cg2.morpho +++ b/test/discretization/cg2.morpho @@ -3,8 +3,10 @@ import meshtools var m = LineMesh(fn (t) [t,0,0], 0..1:1) -var l = FunctionSpace("CG", 2) +var l = FunctionSpace("CG2", grade=1) -var f = Field(m, fn (x,y,z) x^2, functionspace=l) +print l -print LineIntegral(fn (x, u) u, f).total() +//var f = Field(m, fn (x,y,z) x^2, functionspace=l) + +//print LineIntegral(fn (x, u) u, f).total(m) From 7a5bd62c28984ae667625328a9109d8f384ccb76 Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Fri, 25 Aug 2023 21:40:34 -0400 Subject: [PATCH 004/181] Construct a Field that has the correct number of DOFs for a given FunctionSpace --- src/geometry/discretization.h | 6 ++++++ src/geometry/field.c | 14 +++++++++++--- src/geometry/field.h | 2 ++ test/discretization/cg2.morpho | 8 +++++--- 4 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/geometry/discretization.h b/src/geometry/discretization.h index c9d17c51..413c0171 100644 --- a/src/geometry/discretization.h +++ b/src/geometry/discretization.h @@ -42,6 +42,12 @@ typedef struct { discretization *discretization; } objectdiscretization; +/** Tests whether an object is a discretization */ +#define MORPHO_ISDISCRETIZATION(val) object_istype(val, OBJECT_DISCRETIZATION) + +/** Gets the object as a discretization */ +#define MORPHO_GETDISCRETIZATION(val) ((objectdiscretization *) MORPHO_GETOBJECT(val)) + /* ------------------------------------------------------- * FunctionSpace veneer class * ------------------------------------------------------- */ diff --git a/src/geometry/field.c b/src/geometry/field.c index e6874974..dc1721af 100644 --- a/src/geometry/field.c +++ b/src/geometry/field.c @@ -10,8 +10,10 @@ #include "common.h" #include "matrix.h" #include "sparse.h" +#include "geometry.h" value field_gradeoption; +value field_functionspaceoption; /* ********************************************************************** * Field objects @@ -451,14 +453,15 @@ value field_constructor(vm *v, int nargs, value *args) { value prototype=MORPHO_NIL; // Prototype object value grd = MORPHO_NIL; + value fnspc = MORPHO_NIL; int nfixed; - if (!builtin_options(v, nargs, args, &nfixed, 1, field_gradeoption, &grd)) + if (!builtin_options(v, nargs, args, &nfixed, 2, field_gradeoption, &grd, field_functionspaceoption, &fnspc)) morpho_runtimeerror(v, FIELD_ARGS); for (unsigned int i=0; idiscretization->grade; i++) dof[i]=disc->discretization->shape[i]; + grd = MORPHO_INTEGER(disc->discretization->grade); + } else if (MORPHO_ISINTEGER(grd)) { dof[MORPHO_GETINTEGERVALUE(grd)]=1; } else if (MORPHO_ISLIST(grd)) { objectlist *list = MORPHO_GETLIST(grd); @@ -870,6 +877,7 @@ void field_initialize(void) { objectfieldtype=object_addtype(&objectfielddefn); field_gradeoption=builtin_internsymbolascstring(FIELD_GRADEOPTION); + field_functionspaceoption=builtin_internsymbolascstring(FIELD_FUNCTIONSPACEOPTION); builtin_addfunction(FIELD_CLASSNAME, field_constructor, BUILTIN_FLAGSEMPTY); diff --git a/src/geometry/field.h b/src/geometry/field.h index 4c692144..ed55a43d 100644 --- a/src/geometry/field.h +++ b/src/geometry/field.h @@ -53,6 +53,8 @@ objectfield *object_newfield(objectmesh *mesh, value prototype, unsigned int *do extern value field_gradeoption; #define FIELD_GRADEOPTION "grade" +#define FIELD_FUNCTIONSPACEOPTION "functionspace" + #define FIELD_OP_METHOD "op" #define FIELD_SHAPE_METHOD "shape" #define FIELD_MESH_METHOD "mesh" diff --git a/test/discretization/cg2.morpho b/test/discretization/cg2.morpho index b110ef05..f040e00b 100644 --- a/test/discretization/cg2.morpho +++ b/test/discretization/cg2.morpho @@ -1,12 +1,14 @@ import meshtools -var m = LineMesh(fn (t) [t,0,0], 0..1:1) +var m = LineMesh(fn (t) [t,0,0], 0..1:0.5) var l = FunctionSpace("CG2", grade=1) -print l +var f = Field(m, functionspace=l) -//var f = Field(m, fn (x,y,z) x^2, functionspace=l) +print f + +print f.shape() //print LineIntegral(fn (x, u) u, f).total(m) From 8ec8a77bdb2ac96ab95ce50f00c682d1f82fa6e6 Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Sat, 26 Aug 2023 14:50:43 -0400 Subject: [PATCH 005/181] First go at element definitions --- src/geometry/discretization.c | 78 ++++++++++++++++++++++++++++------ src/geometry/discretization.h | 2 + src/geometry/field.c | 25 +++++++++-- src/geometry/field.h | 1 + src/geometry/mesh.c | 2 +- src/geometry/mesh.h | 1 + test/discretization/cg2.morpho | 3 ++ 7 files changed, 95 insertions(+), 17 deletions(-) diff --git a/src/geometry/discretization.c b/src/geometry/discretization.c index 359c0bf4..a4c1a9b8 100644 --- a/src/geometry/discretization.c +++ b/src/geometry/discretization.c @@ -46,9 +46,9 @@ objectdiscretization *objectdiscretization_new(discretization *disc) { #define AREA_OPCODE 2 #define QUANTITY_OPCODE 255 -#define LINE(id, v1, v2) 1, id, v1, v2 // Identify a grade 1 subelement given by two vertex indices -#define AREA(id, v1, v2, v3) 2, id, v1, v2, v3 // Identify a grade 2 subelement given by three vertex indices -#define QUANTITY(grade, id, qno) 128, grade, id, qno // Fetch quantity from subelement of grade with id and quantity number +#define LINE(id, v1, v2) LINE_OPCODE, id, v1, v2 // Identify a grade 1 subelement given by two vertex indices +#define AREA(id, v1, v2, v3) AREA_OPCODE, id, v1, v2, v3 // Identify a grade 2 subelement given by three vertex indices +#define QUANTITY(grade, id, qno) QUANTITY_OPCODE, grade, id, qno // Fetch quantity from subelement of grade with id and quantity number #define ENDDEFN -1 /* ------------------------------------------------------- @@ -196,24 +196,69 @@ discretization *discretization_find(char *name, grade g) { return NULL; } -void discretization_process(objectmesh *mesh, discretization *disc) { - elementid subel[disc->nsubel+1]; +#define FETCH(instr) (*(instr++)) + +bool discretization_processdefn(objectfield *field, discretization *disc, int nv, int *vids, int *dof) { + elementid subel[disc->nsubel+1]; // Element IDs of sub elements + int sid, svids[nv], nmatch, k=0; + + objectsparse *vmatrix[disc->grade+1]; // Vertex->elementid connectivity matrices + for (grade g=0; g<=disc->grade; g++) vmatrix[g]=mesh_addconnectivityelement(field->mesh, g, 0); for (eldefninstruction *instr=disc->eldefn; instr!=NULL && *instr!=ENDDEFN; ) { - switch(*instr) { - case LINE_OPCODE: - instr+=4; + switch(FETCH(instr)) { + case LINE_OPCODE: // Find the line element defined by two vertices + { + sid = FETCH(instr); + svids[0] = vids[FETCH(instr)]; + svids[1] = vids[FETCH(instr)]; + + if (!mesh_matchelements(vmatrix[1], MESH_GRADE_LINE, 2, svids, 1, &nmatch, &subel[sid])) return false; + } break; - case AREA_OPCODE: - instr+=5; + case AREA_OPCODE: // Find the area element defined by three vertices + { + int sid = FETCH(instr), v0 = FETCH(instr), v1 = FETCH(instr), v2 = FETCH(instr); + + sid = FETCH(instr); + svids[0] = vids[FETCH(instr)]; + svids[1] = vids[FETCH(instr)]; + svids[2] = vids[FETCH(instr)]; + + if (!mesh_matchelements(vmatrix[2], MESH_GRADE_AREA, 3, svids, 1, &nmatch, &subel[sid])) return false; + } break; case QUANTITY_OPCODE: - instr+=4; + { + grade g = FETCH(instr); + int sid = FETCH(instr), qno = FETCH(instr); + + if (!field_getindex(field, g, (g==0 ? vids[sid]: subel[sid]), qno, &dof[k])) return false; + printf("%i \n", dof[k]); + k++; + } break; default: UNREACHABLE("Error in finite element definition"); } } + return true; +} + +bool discretization_process(objectfield *field, discretization *disc) { + objectsparse *conn = mesh_getconnectivityelement(field->mesh, 0, disc->grade); + elementid nel=mesh_nelements(conn); + int indx[disc->nnodes]; + + + + for (elementid id=0; iddiscretization); + } +} + MORPHO_BEGINCLASS(FunctionSpace) -MORPHO_METHOD(MORPHO_GETINDEX_METHOD, NULL, BUILTIN_FLAGSEMPTY) +MORPHO_METHOD(FUNCTIONSPACE_LAYOUT_METHOD, FunctionSpace_layout, BUILTIN_FLAGSEMPTY) MORPHO_ENDCLASS /* ********************************************************************** diff --git a/src/geometry/discretization.h b/src/geometry/discretization.h index 413c0171..f5eb0ce9 100644 --- a/src/geometry/discretization.h +++ b/src/geometry/discretization.h @@ -54,6 +54,8 @@ typedef struct { #define FUNCTIONSPACE_CLASSNAME "FunctionSpace" +#define FUNCTIONSPACE_LAYOUT_METHOD "layout" + /* ------------------------------------------------------- * Discretization error messages * ------------------------------------------------------- */ diff --git a/src/geometry/field.c b/src/geometry/field.c index dc1721af..bd9044f6 100644 --- a/src/geometry/field.c +++ b/src/geometry/field.c @@ -291,6 +291,21 @@ bool field_getelementwithindex(objectfield *field, int indx, value *out) { return false; } +/** Constructs a single index, suitable for use with fieldgetelementwithindex from the grade, element id and quantity number + * @param[in] field - field to use + * @param[in] grade - grade to access + * @param[in] el - element id + * @param[in] indx - index within the element + * @param[out] out - the retrieved index + * @return true on success */ +bool field_getindex(objectfield *field, grade grade, elementid el, int indx, int *out) { + int ix=field->offset[grade]+field->dof[grade]*el+indx; + if (!(ixoffset[grade+1] && indxdof[grade])) return false; + + *out=ix; + return true; +} + /** Retrieve the list of doubles that represent an entry in a field * @param[in] field - field to use * @param[in] grade - grade to access @@ -532,10 +547,12 @@ value Field_setindex(vm *v, int nargs, value *args) { elementid el = (nindices>1 ? indx[1] : indx[0]); int elindx = (nindices>2 ? indx[2] : 0); - /* If only one index is specified, increment g to the lowest nonempty grade */ - if (nindices==1 && f->dof) while (f->dof[g]==0 && gngrades) g++; - - if (!field_setelement(f, g, el, elindx, MORPHO_GETARG(args, nargs-1))) { + /* If only one index is specified, treat it as a single index */ + if (nindices==1 && f->dof) { + if (!field_setelementwithindex(f, indx[0], MORPHO_GETARG(args, nargs-1))) { + morpho_runtimeerror(v, FIELD_INCOMPATIBLEVAL); + } + } else if (!field_setelement(f, g, el, elindx, MORPHO_GETARG(args, nargs-1))) { morpho_runtimeerror(v, FIELD_INCOMPATIBLEVAL); } } else morpho_runtimeerror(v, FIELD_INVLDINDICES); diff --git a/src/geometry/field.h b/src/geometry/field.h index ed55a43d..9e6edb42 100644 --- a/src/geometry/field.h +++ b/src/geometry/field.h @@ -97,6 +97,7 @@ unsigned int field_sizeprototype(value prototype); unsigned int field_dofforgrade(objectfield *f, grade g); bool field_getelement(objectfield *field, grade grade, elementid el, int indx, value *out); bool field_getelementwithindex(objectfield *field, int indx, value *out); +bool field_getindex(objectfield *field, grade grade, elementid el, int indx, int *out); bool field_getelementaslist(objectfield *field, grade grade, elementid el, int indx, unsigned int *nentries, double **out); bool field_setelement(objectfield *field, grade grade, elementid el, int indx, value val); diff --git a/src/geometry/mesh.c b/src/geometry/mesh.c index 15db7ac1..bbbf1189 100644 --- a/src/geometry/mesh.c +++ b/src/geometry/mesh.c @@ -527,7 +527,7 @@ static int mesh_compareid(const void *a, const void *b) { * @param[out] nmatches - the number of matches found * @param[out] matches - matched vertex ids * @returns true on success, false otherwise */ -static bool mesh_matchelements(objectsparse *vmatrix, grade g, int nids, int *ids, int maxmatches, int *nmatches, int *matches) { +bool mesh_matchelements(objectsparse *vmatrix, grade g, int nids, int *ids, int maxmatches, int *nmatches, int *matches) { int nentries[nids], *entries[nids], length=0, k=0; /* Obtain connectivity information from the columns of vertex connectivity matrix */ diff --git a/src/geometry/mesh.h b/src/geometry/mesh.h index 3972271f..2e3a0295 100644 --- a/src/geometry/mesh.h +++ b/src/geometry/mesh.h @@ -144,6 +144,7 @@ objectsparse *mesh_addgrade(objectmesh *mesh, grade g); objectsparse *mesh_addconnectivityelement(objectmesh *mesh, unsigned int row, unsigned int col); objectsparse *mesh_getconnectivityelement(objectmesh *mesh, unsigned int row, unsigned int col); +bool mesh_matchelements(objectsparse *vmatrix, grade g, int nids, int *ids, int maxmatches, int *nmatches, int *matches); bool mesh_getconnectivity(objectsparse *conn, elementid id, int *nentries, int **entries); void mesh_freezeconnectivity(objectmesh *mesh); diff --git a/test/discretization/cg2.morpho b/test/discretization/cg2.morpho index f040e00b..90cebd6d 100644 --- a/test/discretization/cg2.morpho +++ b/test/discretization/cg2.morpho @@ -6,9 +6,12 @@ var m = LineMesh(fn (t) [t,0,0], 0..1:0.5) var l = FunctionSpace("CG2", grade=1) var f = Field(m, functionspace=l) +for (i in 0...5) f[i] = i print f +print l.layout(f) + print f.shape() //print LineIntegral(fn (x, u) u, f).total(m) From a664afc4c51ca935a13f58d9161fd0cd58464790 Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Sat, 26 Aug 2023 15:23:36 -0400 Subject: [PATCH 006/181] Layout matrix --- src/geometry/discretization.c | 54 +++++++++++++++++-------------- test/discretization/cg2.morpho | 2 +- test/discretization/cg2_2d.morpho | 20 ++++++++++++ 3 files changed, 50 insertions(+), 26 deletions(-) create mode 100644 test/discretization/cg2_2d.morpho diff --git a/src/geometry/discretization.c b/src/geometry/discretization.c index a4c1a9b8..362bfded 100644 --- a/src/geometry/discretization.c +++ b/src/geometry/discretization.c @@ -206,26 +206,15 @@ bool discretization_processdefn(objectfield *field, discretization *disc, int nv for (grade g=0; g<=disc->grade; g++) vmatrix[g]=mesh_addconnectivityelement(field->mesh, g, 0); for (eldefninstruction *instr=disc->eldefn; instr!=NULL && *instr!=ENDDEFN; ) { - switch(FETCH(instr)) { - case LINE_OPCODE: // Find the line element defined by two vertices + eldefninstruction op=FETCH(instr); + switch(op) { + case LINE_OPCODE: // Find an element defined by n vertices + case AREA_OPCODE: { sid = FETCH(instr); - svids[0] = vids[FETCH(instr)]; - svids[1] = vids[FETCH(instr)]; + for (int i=0; i<=op; i++) svids[i] = vids[FETCH(instr)]; - if (!mesh_matchelements(vmatrix[1], MESH_GRADE_LINE, 2, svids, 1, &nmatch, &subel[sid])) return false; - } - break; - case AREA_OPCODE: // Find the area element defined by three vertices - { - int sid = FETCH(instr), v0 = FETCH(instr), v1 = FETCH(instr), v2 = FETCH(instr); - - sid = FETCH(instr); - svids[0] = vids[FETCH(instr)]; - svids[1] = vids[FETCH(instr)]; - svids[2] = vids[FETCH(instr)]; - - if (!mesh_matchelements(vmatrix[2], MESH_GRADE_AREA, 3, svids, 1, &nmatch, &subel[sid])) return false; + if (!mesh_matchelements(vmatrix[1], op, op+1, svids, 1, &nmatch, &subel[sid])) return false; } break; case QUANTITY_OPCODE: @@ -234,7 +223,6 @@ bool discretization_processdefn(objectfield *field, discretization *disc, int nv int sid = FETCH(instr), qno = FETCH(instr); if (!field_getindex(field, g, (g==0 ? vids[sid]: subel[sid]), qno, &dof[k])) return false; - printf("%i \n", dof[k]); k++; } break; @@ -245,20 +233,30 @@ bool discretization_processdefn(objectfield *field, discretization *disc, int nv return true; } -bool discretization_process(objectfield *field, discretization *disc) { +/** Constructs a layout matrix that maps element ids (columns) to degree of freedom indices in a field */ +bool discretization_layout(objectfield *field, discretization *disc, objectsparse **out) { objectsparse *conn = mesh_getconnectivityelement(field->mesh, 0, disc->grade); elementid nel=mesh_nelements(conn); - int indx[disc->nnodes]; - + objectsparse *new = object_newsparse(NULL, NULL); + if (!new) return false; + sparseccs_resize(&new->ccs, field->nelements, nel, nel*disc->nnodes, NULL); for (elementid id=0; idccs.cptr[id]=id*disc->nnodes; + if (!discretization_processdefn(field, disc, nv, vids, new->ccs.rix+new->ccs.cptr[id])) goto discretization_layout_cleanup; } + new->ccs.cptr[nel]=nel*disc->nnodes; // Last column pointer points to next column + + *out=new; return true; + +discretization_layout_cleanup: + if (new) object_free((object *) new); + return false; } /* ********************************************************************** @@ -295,12 +293,18 @@ value functionspace_constructor(vm *v, int nargs, value *args) { } value FunctionSpace_layout(vm *v, int nargs, value *args) { + value out=MORPHO_NIL; objectdiscretization *slf = MORPHO_GETDISCRETIZATION(MORPHO_SELF(args)); if (nargs==1 && MORPHO_ISFIELD(MORPHO_GETARG(args, 0))) { objectfield *field = MORPHO_GETFIELD(MORPHO_GETARG(args, 0)); + objectsparse *new; - discretization_process(field, slf->discretization); + if (discretization_layout(field, slf->discretization, &new)) { + out=MORPHO_OBJECT(new); + morpho_bindobjects(v, 1, &out); + } } + return out; } MORPHO_BEGINCLASS(FunctionSpace) diff --git a/test/discretization/cg2.morpho b/test/discretization/cg2.morpho index 90cebd6d..8902cf57 100644 --- a/test/discretization/cg2.morpho +++ b/test/discretization/cg2.morpho @@ -8,7 +8,7 @@ var l = FunctionSpace("CG2", grade=1) var f = Field(m, functionspace=l) for (i in 0...5) f[i] = i -print f +//print f print l.layout(f) diff --git a/test/discretization/cg2_2d.morpho b/test/discretization/cg2_2d.morpho new file mode 100644 index 00000000..a0ada794 --- /dev/null +++ b/test/discretization/cg2_2d.morpho @@ -0,0 +1,20 @@ + +import meshtools + +var mb = MeshBuilder() +mb.addvertex([0,0]) +mb.addvertex([1,0]) +mb.addvertex([0,1]) +mb.addvertex([1,1]) +mb.addface([0,1,2]) +mb.addface([1,3,2]) +var m = mb.build() +m.addgrade(1) + +var l = FunctionSpace("CG2", grade=2) + +var f = Field(m, functionspace=l) + +print l.layout(f) + +print f.shape() From 61aa3bce9d529e278b25da81386df4a3c7b033a0 Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Sat, 26 Aug 2023 16:35:22 -0400 Subject: [PATCH 007/181] First go at interpolation --- src/geometry/discretization.c | 14 +-- src/geometry/discretization.h | 5 +- src/geometry/field.c | 100 ++++++++++++++++----- src/geometry/field.h | 1 + test/discretization/cg2_interpolate.morpho | 9 ++ 5 files changed, 102 insertions(+), 27 deletions(-) create mode 100644 test/discretization/cg2_interpolate.morpho diff --git a/src/geometry/discretization.c b/src/geometry/discretization.c index 362bfded..189f7b1a 100644 --- a/src/geometry/discretization.c +++ b/src/geometry/discretization.c @@ -64,7 +64,7 @@ void cg1_1dinterpolate(double *lambda, double *wts) { wts[1]=lambda[0]; } -int cg1_1dshape[] = { 1, 0 }; +unsigned int cg1_1dshape[] = { 1, 0 }; double cg1_1dnodes[] = { 0.0, 1.0 }; @@ -101,7 +101,7 @@ void cg2_1dinterpolate(double *lambda, double *wts) { wts[2]=-lambda[0]*dl; } -int cg2_1dshape[] = { 1, 1 }; +unsigned int cg2_1dshape[] = { 1, 1 }; double cg2_1dnodes[] = { 0.0, 1.0, 0.5 }; @@ -119,6 +119,7 @@ discretization cg2_1d = { .shape = cg2_1dshape, .degree = 2, .nnodes = 3, + .nodes = cg2_1dnodes, .ifn = cg2_1dinterpolate, .eldefn = cg2_1ddefn }; @@ -143,7 +144,7 @@ void cg2_2dinterpolate(double *lambda, double *wts) { wts[5]=4*lambda[1]*lambda[2]; } -int cg2_2dshape[] = { 1, 1, 0 }; +unsigned int cg2_2dshape[] = { 1, 1, 0 }; double cg2_2dnodes[] = { 0.0, 0.0, 1.0, 0.0, @@ -198,7 +199,8 @@ discretization *discretization_find(char *name, grade g) { #define FETCH(instr) (*(instr++)) -bool discretization_processdefn(objectfield *field, discretization *disc, int nv, int *vids, int *dof) { +/** Steps through an element definition, generating subelements and identifying quantities */ +bool discretization_doftofieldindx(objectfield *field, discretization *disc, int nv, int *vids, int *dof) { elementid subel[disc->nsubel+1]; // Element IDs of sub elements int sid, svids[nv], nmatch, k=0; @@ -209,7 +211,7 @@ bool discretization_processdefn(objectfield *field, discretization *disc, int nv eldefninstruction op=FETCH(instr); switch(op) { case LINE_OPCODE: // Find an element defined by n vertices - case AREA_OPCODE: + case AREA_OPCODE: // TODO: Need to cope with (mis) orientation of these subelements { sid = FETCH(instr); for (int i=0; i<=op; i++) svids[i] = vids[FETCH(instr)]; @@ -247,7 +249,7 @@ bool discretization_layout(objectfield *field, discretization *disc, objectspars if (!mesh_getconnectivity(conn, id, &nv, &vids)) goto discretization_layout_cleanup; new->ccs.cptr[id]=id*disc->nnodes; - if (!discretization_processdefn(field, disc, nv, vids, new->ccs.rix+new->ccs.cptr[id])) goto discretization_layout_cleanup; + if (!discretization_doftofieldindx(field, disc, nv, vids, new->ccs.rix+new->ccs.cptr[id])) goto discretization_layout_cleanup; } new->ccs.cptr[nel]=nel*disc->nnodes; // Last column pointer points to next column diff --git a/src/geometry/discretization.h b/src/geometry/discretization.h index f5eb0ce9..8efb9aa4 100644 --- a/src/geometry/discretization.h +++ b/src/geometry/discretization.h @@ -21,7 +21,7 @@ typedef int eldefninstruction; typedef struct { char *name; /** Name of the discretization */ grade grade; /** Grade of element this discretization is defined on */ - int *shape; /** Number of degrees of freedom on each grade; must have grade+1 entries */ + unsigned int *shape; /** Number of degrees of freedom on each grade; must have grade+1 entries */ int degree; /** Highest degree of polynomial represented by this element */ int nnodes; /** Number of nodes for this element type */ int nsubel; /** Number of subelements used by */ @@ -70,6 +70,9 @@ typedef struct { * Discretization interface * ------------------------------------------------------- */ +bool discretization_doftofieldindx(objectfield *field, discretization *disc, int nv, int *vids, int *dof); +bool discretization_layout(objectfield *field, discretization *disc, objectsparse **out); + void discretization_initialize(void); #endif diff --git a/src/geometry/field.c b/src/geometry/field.c index bd9044f6..79dc3cf1 100644 --- a/src/geometry/field.c +++ b/src/geometry/field.c @@ -162,14 +162,83 @@ objectfield *object_newfield(objectmesh *mesh, value prototype, unsigned int *do return new; } +/** Applies an initialization function to every vertex */ +bool field_applyfunctiontovertices(vm *v, objectmesh *mesh, value fn, objectfield *field) { + value coords[mesh->dim]; // Vertex coords + value ret=MORPHO_NIL; // Return value + int nv = mesh_nvertices(mesh); + + for (elementid i=0; idim, coords, &ret)) return false; + + if (!field_setelement(field, MESH_GRADE_VERTEX, i, 0, ret)) { + // if we can't set the field value to the ouptut of the function clean up + morpho_runtimeerror(v, FIELD_OPRETURN); + return false; + } + } + } + return true; +} + +/** Applies an initialization function to every DOF in an element */ +bool field_applyfunctiontoelements(vm *v, objectmesh *mesh, value fn, discretization *disc, objectfield *field) { + objectsparse *conn = mesh_getconnectivityelement(mesh, 0, disc->grade); + if (!conn) return false; + elementid nel = mesh_nelements(conn); + + for (elementid id=0; idnnodes]; // Get indices corresponding to each degree of freedom per element + if (!discretization_doftofieldindx(field, disc, nv, vids, indx)) return false; + + for (int i=0; innodes; i++) { // Loop over nodes + double lambda[nv], ll=0.0; // Convert node positions in reference element to barycentric coordinates + for (int j=0; jnodes[i*disc->grade+j]; ll+=lambda[j]; } + lambda[nv-1]=1-ll; + + double xx[mesh->dim]; // Interpolate position in physical space using barycentric coordinates + for (int j=0; jdim; j++) xx[j]=0.0; + for (int j=0; jdim, xx, lambda[j], x[j], xx); + + printf("<<"); + for (int j=0; jnodes[i*disc->grade+j]); + printf(">> "); + for (int j=0; jdim; j++) printf("%g ", xx[j]); + printf(": "); + + value coords[mesh->dim], ret; + for (int j=0; jdim; j++) coords[j]=MORPHO_FLOAT(xx[j]); + + if (!morpho_call(v, fn, mesh->dim, coords, &ret)) return false; + + morpho_printvalue(v, ret); + printf(" -> %i\n", indx[i]); + + if (!field_setelementwithindex(field, indx[i], ret)) { + // if we can't set the field value to the ouptut of the function clean up + morpho_runtimeerror(v, FIELD_OPRETURN); + return false; + } + } + } + + return true; +} + /** Creates a field by applying a function to the vertices of a mesh * @param[in] v - virtual machine to use for function calls * @param[in] mesh - mesh to use * @param[in] fn - function to call * @returns field object or NULL on failure */ -objectfield *field_newwithfunction(vm *v, objectmesh *mesh, value fn) { - objectmatrix *vert=mesh->vert; - int nv = vert->ncols; +objectfield *field_newwithfunction(vm *v, objectmesh *mesh, value fn, discretization *disc) { value ret=MORPHO_NIL; // Return value value coords[mesh->dim]; // Vertex coords objectfield *new = NULL; @@ -181,23 +250,13 @@ objectfield *field_newwithfunction(vm *v, objectmesh *mesh, value fn) { if (MORPHO_ISOBJECT(ret)) handle=morpho_retainobjects(v, 1, &ret); } - new=object_newfield(mesh, ret, NULL); + new=object_newfield(mesh, ret, (disc ? disc->shape : NULL)); if (new) { - for (elementid i=0; idim, coords, &ret)){ - // if the fn call fails go to clean up this should throw an error from morpho_call - goto field_newwithfunction_cleanup; - } - if (!field_setelement(new, MESH_GRADE_VERTEX, i, 0, ret)) { - // if we can't set the field value to the ouptut of the function clean up - morpho_runtimeerror(v,FIELD_OPRETURN); - goto field_newwithfunction_cleanup; - } - } + if (disc) { + if (!field_applyfunctiontoelements(v, mesh, fn, disc, new)) goto field_newwithfunction_cleanup; + } else { + if (!field_applyfunctiontovertices(v, mesh, fn, new)) goto field_newwithfunction_cleanup; } } @@ -489,9 +548,10 @@ value field_constructor(vm *v, int nargs, value *args) { unsigned int dof[ngrades]; for (unsigned int i=0; idiscretization->grade; i++) dof[i]=disc->discretization->shape[i]; grd = MORPHO_INTEGER(disc->discretization->grade); } else if (MORPHO_ISINTEGER(grd)) { @@ -504,7 +564,7 @@ value field_constructor(vm *v, int nargs, value *args) { if (MORPHO_ISNIL(fn)) { new = object_newfield(mesh, prototype, (MORPHO_ISNIL(grd) ? NULL: dof)); } else { - new = field_newwithfunction(v, mesh, fn); + new = field_newwithfunction(v, mesh, fn, (disc ? disc->discretization : NULL)); } if (new) { diff --git a/src/geometry/field.h b/src/geometry/field.h index 9e6edb42..8c94cb2c 100644 --- a/src/geometry/field.h +++ b/src/geometry/field.h @@ -101,6 +101,7 @@ bool field_getindex(objectfield *field, grade grade, elementid el, int indx, int bool field_getelementaslist(objectfield *field, grade grade, elementid el, int indx, unsigned int *nentries, double **out); bool field_setelement(objectfield *field, grade grade, elementid el, int indx, value val); +bool field_setelementwithindex(objectfield *field, int ix, value val); void field_initialize(void); diff --git a/test/discretization/cg2_interpolate.morpho b/test/discretization/cg2_interpolate.morpho new file mode 100644 index 00000000..98b86ee3 --- /dev/null +++ b/test/discretization/cg2_interpolate.morpho @@ -0,0 +1,9 @@ + +import meshtools + +var m = LineMesh(fn (t) [t,0,0], 0..1:0.5) + +var l = FunctionSpace("CG2", grade=1) + +var f = Field(m, fn (x,y,z) x^2, functionspace=l) +print f From 3d8c2842ebecc3312bb1b5618cc1fa89e974eba2 Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Sat, 26 Aug 2023 17:03:24 -0400 Subject: [PATCH 008/181] Field initializer with discretization --- src/geometry/discretization.c | 12 ++++++------ src/geometry/field.c | 16 +++++++++------- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/geometry/discretization.c b/src/geometry/discretization.c index 189f7b1a..6359e673 100644 --- a/src/geometry/discretization.c +++ b/src/geometry/discretization.c @@ -60,8 +60,8 @@ objectdiscretization *objectdiscretization_new(discretization *disc) { */ void cg1_1dinterpolate(double *lambda, double *wts) { - wts[0]=lambda[1]; - wts[1]=lambda[0]; + wts[0]=lambda[0]; + wts[1]=lambda[1]; } unsigned int cg1_1dshape[] = { 1, 0 }; @@ -95,10 +95,10 @@ discretization cg1_1d = { */ void cg2_1dinterpolate(double *lambda, double *wts) { - double dl = (lambda[1]-lambda[0]); - wts[0]=lambda[1]*dl; + double dl = (lambda[0]-lambda[1]); + wts[0]=lambda[0]*dl; wts[1]=4*lambda[0]*lambda[1]; - wts[2]=-lambda[0]*dl; + wts[2]=-lambda[1]*dl; // TODO: CHECK } unsigned int cg2_1dshape[] = { 1, 1 }; @@ -136,7 +136,7 @@ discretization cg2_1d = { */ void cg2_2dinterpolate(double *lambda, double *wts) { - wts[0]=lambda[2]*(2*lambda[2]-1); + wts[0]=lambda[2]*(2*lambda[2]-1); // TODO: FIX THIS wts[1]=lambda[0]*(2*lambda[0]-1); wts[2]=lambda[1]*(2*lambda[1]-1); wts[3]=4*lambda[0]*lambda[2]; diff --git a/src/geometry/field.c b/src/geometry/field.c index 79dc3cf1..1a13b36d 100644 --- a/src/geometry/field.c +++ b/src/geometry/field.c @@ -201,29 +201,31 @@ bool field_applyfunctiontoelements(vm *v, objectmesh *mesh, value fn, discretiza for (int i=0; innodes; i++) { // Loop over nodes double lambda[nv], ll=0.0; // Convert node positions in reference element to barycentric coordinates - for (int j=0; jnodes[i*disc->grade+j]; ll+=lambda[j]; } - lambda[nv-1]=1-ll; + for (int j=0; jnodes[i*disc->grade+j]; ll+=lambda[j+1]; } + lambda[0]=1-ll; double xx[mesh->dim]; // Interpolate position in physical space using barycentric coordinates for (int j=0; jdim; j++) xx[j]=0.0; for (int j=0; jdim, xx, lambda[j], x[j], xx); - printf("<<"); + /*printf("<<"); for (int j=0; jnodes[i*disc->grade+j]); printf(">> "); + printf("["); + for (int j=0; jdim; j++) printf("%g ", xx[j]); - printf(": "); + printf(": ");*/ value coords[mesh->dim], ret; for (int j=0; jdim; j++) coords[j]=MORPHO_FLOAT(xx[j]); if (!morpho_call(v, fn, mesh->dim, coords, &ret)) return false; - morpho_printvalue(v, ret); - printf(" -> %i\n", indx[i]); + //morpho_printvalue(v, ret); + //printf(" -> %i\n", indx[i]); if (!field_setelementwithindex(field, indx[i], ret)) { - // if we can't set the field value to the ouptut of the function clean up morpho_runtimeerror(v, FIELD_OPRETURN); return false; } From e0c603e4f8be02dfe4eed3021947bff2b2c867f7 Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Sun, 27 Aug 2023 10:15:31 -0400 Subject: [PATCH 009/181] Fix CG2 interpolation function --- src/geometry/discretization.c | 12 ++++++------ test/discretization/cg2_interpolate.morpho | 2 ++ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/geometry/discretization.c b/src/geometry/discretization.c index 6359e673..6fcc2ae6 100644 --- a/src/geometry/discretization.c +++ b/src/geometry/discretization.c @@ -136,12 +136,12 @@ discretization cg2_1d = { */ void cg2_2dinterpolate(double *lambda, double *wts) { - wts[0]=lambda[2]*(2*lambda[2]-1); // TODO: FIX THIS - wts[1]=lambda[0]*(2*lambda[0]-1); - wts[2]=lambda[1]*(2*lambda[1]-1); - wts[3]=4*lambda[0]*lambda[2]; - wts[4]=4*lambda[0]*lambda[1]; - wts[5]=4*lambda[1]*lambda[2]; + wts[0]=lambda[0]*(2*lambda[0]-1); // TODO: FIX THIS + wts[1]=lambda[1]*(2*lambda[1]-1); + wts[2]=lambda[2]*(2*lambda[2]-1); + wts[3]=4*lambda[0]*lambda[1]; + wts[4]=4*lambda[1]*lambda[2]; + wts[5]=4*lambda[2]*lambda[0]; } unsigned int cg2_2dshape[] = { 1, 1, 0 }; diff --git a/test/discretization/cg2_interpolate.morpho b/test/discretization/cg2_interpolate.morpho index 98b86ee3..294e2922 100644 --- a/test/discretization/cg2_interpolate.morpho +++ b/test/discretization/cg2_interpolate.morpho @@ -7,3 +7,5 @@ var l = FunctionSpace("CG2", grade=1) var f = Field(m, fn (x,y,z) x^2, functionspace=l) print f + +print LineIntegral(fn (x, g) g, f).total(m) \ No newline at end of file From 12f0938d90b76392ad724888694dcce5cebef150 Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Sun, 27 Aug 2023 11:24:47 -0400 Subject: [PATCH 010/181] Fields now refer to function spaces --- src/geometry/discretization.h | 3 ++ src/geometry/field.c | 48 ++++++++++++++-------- src/geometry/field.h | 4 +- src/geometry/functional.c | 15 +++++-- src/geometry/integrate.c | 34 ++++++++------- src/geometry/integrate.h | 19 ++++++++- test/discretization/cg2_interpolate.morpho | 2 +- 7 files changed, 84 insertions(+), 41 deletions(-) diff --git a/src/geometry/discretization.h b/src/geometry/discretization.h index 8efb9aa4..5aa629d0 100644 --- a/src/geometry/discretization.h +++ b/src/geometry/discretization.h @@ -7,6 +7,9 @@ #ifndef discretization_h #define discretization_h +#include "mesh.h" +#include "field.h" + /* ------------------------------------------------------- * Discretization type definitions * ------------------------------------------------------- */ diff --git a/src/geometry/field.c b/src/geometry/field.c index 1a13b36d..d8b03b1f 100644 --- a/src/geometry/field.c +++ b/src/geometry/field.c @@ -29,6 +29,7 @@ void objectfield_printfn(object *obj, void *v) { void objectfield_markfn(object *obj, void *v) { objectfield *c = (objectfield *) obj; morpho_markvalue(v, c->prototype); + morpho_markvalue(v, c->fnspc); } void objectfield_freefn(object *obj) { @@ -100,10 +101,20 @@ unsigned int field_size(objectmesh *mesh, value prototype, unsigned int ngrades, /** Creates a new field * @param[in] mesh - Mesh the field is attached to * @param[in] prototype - a prototype object - * @param[in] dof - umber of degrees of freedom per entry in each grade (should be maxgrade entries) */ -objectfield *object_newfield(objectmesh *mesh, value prototype, unsigned int *dof) { + * @param[in] disc - a prototype object + * @param[in] shape - (optional) number of degrees of freedom per entry in each grade (should be maxgrade entries) */ +objectfield *object_newfield(objectmesh *mesh, value prototype, value fnspc, unsigned int *shape) { int ngrades=mesh_maxgrade(mesh)+1; + unsigned int dof[ngrades+1]; // Extract shape from discretization or the provided function space + if (MORPHO_ISDISCRETIZATION(fnspc)) { + discretization *disc = MORPHO_GETDISCRETIZATION(fnspc)->discretization; + for (int i=0; i<=disc->grade; i++) dof[i]=disc->shape[i]; + for (int i=disc->grade+1; i<=ngrades; i++) dof[i]=0; + } else if (shape) { + for (int i=0; i<=ngrades; i++) dof[i]=shape[i]; + } + unsigned int offset[ngrades+1]; unsigned int size=field_size(mesh, prototype, ngrades, dof, offset); objectfield *new=NULL; @@ -120,6 +131,7 @@ objectfield *object_newfield(objectmesh *mesh, value prototype, unsigned int *do new->psize=field_sizeprototype(prototype); new->nelements=size/new->psize; new->ngrades=ngrades; + new->fnspc=(MORPHO_ISDISCRETIZATION(fnspc) ? fnspc : MORPHO_NIL); new->offset=noffset; memcpy(noffset, offset, sizeof(unsigned int)*(ngrades+1)); @@ -184,7 +196,10 @@ bool field_applyfunctiontovertices(vm *v, objectmesh *mesh, value fn, objectfiel } /** Applies an initialization function to every DOF in an element */ -bool field_applyfunctiontoelements(vm *v, objectmesh *mesh, value fn, discretization *disc, objectfield *field) { +bool field_applyfunctiontoelements(vm *v, objectmesh *mesh, value fn, value fnspc, objectfield *field) { + if (!MORPHO_ISDISCRETIZATION(fnspc)) return false; + discretization *disc = MORPHO_GETDISCRETIZATION(fnspc)->discretization; + objectsparse *conn = mesh_getconnectivityelement(mesh, 0, disc->grade); if (!conn) return false; elementid nel = mesh_nelements(conn); @@ -240,7 +255,7 @@ bool field_applyfunctiontoelements(vm *v, objectmesh *mesh, value fn, discretiza * @param[in] mesh - mesh to use * @param[in] fn - function to call * @returns field object or NULL on failure */ -objectfield *field_newwithfunction(vm *v, objectmesh *mesh, value fn, discretization *disc) { +objectfield *field_newwithfunction(vm *v, objectmesh *mesh, value fn, value fnspc) { value ret=MORPHO_NIL; // Return value value coords[mesh->dim]; // Vertex coords objectfield *new = NULL; @@ -252,11 +267,11 @@ objectfield *field_newwithfunction(vm *v, objectmesh *mesh, value fn, discretiza if (MORPHO_ISOBJECT(ret)) handle=morpho_retainobjects(v, 1, &ret); } - new=object_newfield(mesh, ret, (disc ? disc->shape : NULL)); + new=object_newfield(mesh, ret, fnspc, NULL); if (new) { - if (disc) { - if (!field_applyfunctiontoelements(v, mesh, fn, disc, new)) goto field_newwithfunction_cleanup; + if (fnspc) { + if (!field_applyfunctiontoelements(v, mesh, fn, fnspc, new)) goto field_newwithfunction_cleanup; } else { if (!field_applyfunctiontovertices(v, mesh, fn, new)) goto field_newwithfunction_cleanup; } @@ -298,7 +313,7 @@ bool field_addpool(objectfield *f) { /** Clones a field */ objectfield *field_clone(objectfield *f) { - objectfield *new = object_newfield(f->mesh, f->prototype, f->dof); + objectfield *new = object_newfield(f->mesh, f->prototype, f->fnspc, f->dof); if (new) memcpy(new->data.elements, f->data.elements, f->data.nrows*sizeof(double)); return new; } @@ -499,7 +514,7 @@ bool field_op(vm *v, value fn, objectfield *f, int nargs, objectfield **args, va if (!fld) { if (field_checkprototype(ret)) { if (MORPHO_ISOBJECT(ret)) handle=morpho_retainobjects(v, 1, &ret); - fld=object_newfield(f->mesh, ret, f->dof); + fld=object_newfield(f->mesh, ret, f->fnspc, f->dof); if (!fld) { morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED); return false; } } else { morpho_runtimeerror(v, FIELD_OPRETURN); return false; @@ -550,12 +565,9 @@ value field_constructor(vm *v, int nargs, value *args) { unsigned int dof[ngrades]; for (unsigned int i=0; idiscretization->grade; i++) dof[i]=disc->discretization->shape[i]; - grd = MORPHO_INTEGER(disc->discretization->grade); + } else if (MORPHO_ISINTEGER(grd)) { dof[MORPHO_GETINTEGERVALUE(grd)]=1; } else if (MORPHO_ISLIST(grd)) { @@ -564,9 +576,9 @@ value field_constructor(vm *v, int nargs, value *args) { } if (MORPHO_ISNIL(fn)) { - new = object_newfield(mesh, prototype, (MORPHO_ISNIL(grd) ? NULL: dof)); + new = object_newfield(mesh, prototype, fnspc, dof); } else { - new = field_newwithfunction(v, mesh, fn, (disc ? disc->discretization : NULL)); + new = field_newwithfunction(v, mesh, fn, fnspc); } if (new) { @@ -678,7 +690,7 @@ value Field_add(vm *v, int nargs, value *args) { objectfield *b=MORPHO_GETFIELD(MORPHO_GETARG(args, 0)); if (field_compareshape(a, b)) { - objectfield *new = object_newfield(a->mesh, a->prototype, a->dof); + objectfield *new = object_newfield(a->mesh, a->prototype, a->fnspc, a->dof); if (new) { out=MORPHO_OBJECT(new); @@ -718,7 +730,7 @@ value Field_sub(vm *v, int nargs, value *args) { objectfield *b=MORPHO_GETFIELD(MORPHO_GETARG(args, 0)); if (field_compareshape(a, b)) { - objectfield *new = object_newfield(a->mesh, a->prototype, a->dof); + objectfield *new = object_newfield(a->mesh, a->prototype, a->fnspc, a->dof); if (new) { out=MORPHO_OBJECT(new); diff --git a/src/geometry/field.h b/src/geometry/field.h index 8c94cb2c..3cdadb5b 100644 --- a/src/geometry/field.h +++ b/src/geometry/field.h @@ -32,6 +32,8 @@ typedef struct { unsigned int nelements; /** Total number of elements in the fireld */ void *pool; /** Pool of statically allocated objects */ + value fnspc; /** Function space used */ + objectmatrix data; /** Underlying data store */ } objectfield; @@ -42,7 +44,7 @@ typedef struct { #define MORPHO_GETFIELD(val) ((objectfield *) MORPHO_GETOBJECT(val)) /** Creates an empty field object */ -objectfield *object_newfield(objectmesh *mesh, value prototype, unsigned int *dof); +objectfield *object_newfield(objectmesh *mesh, value prototype, value disc, unsigned int *shape); /* ------------------------------------------------------- * Field class diff --git a/src/geometry/functional.c b/src/geometry/functional.c index 86929d51..187e1522 100644 --- a/src/geometry/functional.c +++ b/src/geometry/functional.c @@ -402,7 +402,7 @@ bool functional_mapfieldgradientX(vm *v, functional_mapinfo *info, value *out) { /* Create the output field */ if (n>0) { - grad=object_newfield(mesh, field->prototype, field->dof); + grad=object_newfield(mesh, field->prototype, field->fnspc, field->dof); if (!grad) { morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED); return false; } field_zero(grad); } @@ -719,7 +719,7 @@ bool functional_mapnumericalfieldgradientX(vm *v, functional_mapinfo *info, valu objectsparse *conn=mesh_getconnectivityelement(mesh, 0, grd); // Connectivity for the element /* Create the output field */ - objectfield *grad=object_newfield(mesh, field->prototype, field->dof); + objectfield *grad=object_newfield(mesh, field->prototype, field->fnspc, field->dof); if (!grad) return false; field_zero(grad); @@ -1365,7 +1365,7 @@ bool functional_mapfieldgradient(vm *v, functional_mapinfo *info, value *out) { /* Create output fields */ for (int i=0; imesh, info->field->prototype, info->field->dof); + new[i]=object_newfield(info->mesh, info->field->prototype, info->field->fnspc, info->field->dof); if (!new[i]) { morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED); goto functional_mapfieldgradient_cleanup; } field_zero(new[i]); @@ -1463,7 +1463,7 @@ bool functional_mapnumericalfieldgradient(vm *v, functional_mapinfo *info, value for (int i=0; imesh, info->field->prototype, info->field->dof); + new[i] = object_newfield(info->mesh, info->field->prototype, info->field->fnspc, info->field->dof); if (!new[i]) { morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED); goto functional_mapfieldgradient_cleanup; } field_zero(new[i]); @@ -4211,6 +4211,13 @@ bool lineintegral_integrand(vm *v, objectmesh *mesh, elementid id, int nv, int * field_getelement(MORPHO_GETFIELD(iref.fields[k]), MESH_GRADE_VERTEX, vid[i], 0, &q[i][k]); } } + + quantity quantities[iref.nfields+1]; + for (int k=0; knqdof=ndof; @@ -2548,7 +2551,7 @@ bool integrator_configurewithdictionary(integrator *integrate, grade g, objectdi * @param[in] quantity - List of quantities for each vertex. * @param[in] ref - a pointer to any data required by the function * @returns True on success */ -bool integrator_integrate(integrator *integrate, integrandfunction *integrand, int dim, double **x, unsigned int nquantity, value **quantity, void *ref) { +bool integrator_integrate(integrator *integrate, integrandfunction *integrand, int dim, double **x, unsigned int nquantity, quantity *quantity, void *ref) { integrate->integrand=integrand; integrate->ref=ref; @@ -2563,16 +2566,16 @@ bool integrator_integrate(integrator *integrate, integrandfunction *integrand, i error_clear(&integrate->emsg); // Quantities used for interpolation live at the start of the quantity stack - integrator_countquantitydof(integrate, nquantity, quantity[0]); + integrator_countquantitydof(integrate, nquantity, quantity); integrate->ndof = integrate->dim+integrate->nqdof; // Number of degrees of freedom - integrator_addquantity(integrate, nquantity, quantity[0]); + //integrator_addquantity(integrate, nquantity, quantity[0]); // Create first element int vids[integrate->nbary], qids[integrate->nbary]; for (int i=0; inbary; i++) { vids[i]=integrator_addvertex(integrate, dim, x[i]); - if (nquantity) qids[i]=integrator_addquantity(integrate, nquantity, quantity[i]); + //if (nquantity) qids[i]=integrator_addquantity(integrate, nquantity, quantity[i]); } int elid = integrator_addelement(integrate, vids, qids); @@ -2623,11 +2626,11 @@ bool integrator_integrate(integrator *integrate, integrandfunction *integrand, i * @param[in] grade - Grade to integrate over * @param[in] x - vertices of the triangle x[0] = {x,y,z} etc. * @param[in] nquantity - number of quantities per vertex - * @param[in] quantity - List of quantities for each endpoint. + * @param[in] quantity - List of quantities * @param[in] ref - a pointer to any data required by the function * @param[out] out - value of the integral * @returns true on success. */ -bool integrate(integrandfunction *integrand, objectdictionary *method, unsigned int dim, unsigned int grade, double **x, unsigned int nquantity, value **quantity, void *ref, double *out, double *err) { +bool integrate(integrandfunction *integrand, objectdictionary *method, unsigned int dim, unsigned int grade, double **x, unsigned int nquantity, quantity *quantity, void *ref, double *out, double *err) { bool success=false; integrator integrate; integrator_init(&integrate); @@ -2649,6 +2652,7 @@ bool integrate(integrandfunction *integrand, objectdictionary *method, unsigned * Testing code * -------------------------------- */ +/* int nevals; bool test_integrand(unsigned int dim, double *t, double *x, unsigned int nquantity, value *quantity, void *data, double *fout) { @@ -2672,7 +2676,7 @@ void integrate_test1(double *out, double *err) { double *xx[] = { x0, x1, x2, x3 }; value *quantities[] = { NULL, NULL, NULL, NULL }; - integrate(test_integrand, NULL, 3, 3, xx, 0, quantities, NULL, out, err); + integrate(test_integrand, NULL, 3, 3, xx, 0, NULL, NULL, out, err); return; } @@ -2687,7 +2691,7 @@ void integrate_test2(double *out) { double x3[3] = { 0, 0, 1 }; double *xx[] = { x0, x1, x2, x3 }; value *quantities[] = { NULL, NULL, NULL, NULL }; - integrate_integrate(test_integrand, 3, 3, xx, 0, quantities, NULL, out); + integrate_integrate(test_integrand, 3, 3, xx, 0, NULL, NULL, out); } void integrate_test(void) { @@ -2715,4 +2719,4 @@ void integrate_test(void) { printf("Old: %g (relative error %g) tol: %g\n", fabs(trueval-out), fabs(trueval-out)/trueval, INTEGRATE_ACCURACYGOAL); exit(0); -} +}*/ diff --git a/src/geometry/integrate.h b/src/geometry/integrate.h index 33d16f88..413cce8c 100644 --- a/src/geometry/integrate.h +++ b/src/geometry/integrate.h @@ -10,6 +10,7 @@ #include #include "morpho.h" #include "dict.h" +#include "discretization.h" #define INTEGRATE_RULELABEL "rule" #define INTEGRATE_DEGREELABEL "degree" @@ -98,6 +99,17 @@ typedef struct { DECLARE_VARRAY(quadratureworkitem, quadratureworkitem) +/* ---------------------------------- + * Quantities + * ---------------------------------- */ + +typedef struct { + int nnodes; /** Number of quantity values per element */ + value *vals; /** List of quantity values */ + interpolationfn ifn; /** Interpolation function */ + int ndof; /** Number of degrees of freedom (this will be filled out by the integrator) */ +} quantity; + /* ---------------------------------- * Integrator * ---------------------------------- */ @@ -107,7 +119,9 @@ typedef struct { int dim; /** Dimension of points in embedded space */ int nbary; /** Number of barycentric coordinates */ + int nquantity; /** Number of quantities to interpolate */ + quantity *quantities; /** Quantity list */ int nqdof; /** Number of quantity degrees of freedom */ int ndof; /** Number of degrees of freedom */ @@ -122,7 +136,8 @@ typedef struct { varray_quadratureworkitem worklist; /** Work list */ varray_double vertexstack; /** Stack of vertices */ varray_int elementstack; /** Stack of elements */ - varray_value quantitystack; /** Stack of quantities */ + + varray_value quantitystack; /** Stack of quantity values generated by interpolation */ double ztol; /** Tolerance for zero detection */ double tol; /** Tolerance for relative error */ @@ -153,7 +168,7 @@ typedef struct { bool integrate_integrate(integrandfunction *integrand, unsigned int dim, unsigned int grade, double **x, unsigned int nquantity, value **quantity, void *ref, double *out); -bool integrate(integrandfunction *integrand, objectdictionary *method, unsigned int dim, unsigned int grade, double **x, unsigned int nquantity, value **quantity, void *ref, double *out, double *err); +bool integrate(integrandfunction *integrand, objectdictionary *method, unsigned int dim, unsigned int grade, double **x, unsigned int nquantity, quantity *quantity, void *ref, double *out, double *err); void integrate_test(void); diff --git a/test/discretization/cg2_interpolate.morpho b/test/discretization/cg2_interpolate.morpho index 294e2922..b792a8f7 100644 --- a/test/discretization/cg2_interpolate.morpho +++ b/test/discretization/cg2_interpolate.morpho @@ -8,4 +8,4 @@ var l = FunctionSpace("CG2", grade=1) var f = Field(m, fn (x,y,z) x^2, functionspace=l) print f -print LineIntegral(fn (x, g) g, f).total(m) \ No newline at end of file +//print LineIntegral(fn (x, g) g, f, method={ }).total(m) \ No newline at end of file From 9b05b9d509cdef6d79787252ad65395c9e53c3a5 Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Sun, 27 Aug 2023 22:05:44 -0400 Subject: [PATCH 011/181] Basic quantity interpolation --- src/geometry/discretization.c | 4 +- src/geometry/functional.c | 29 +++++++-- src/geometry/integrate.c | 69 +++++++++++----------- src/geometry/integrate.h | 4 +- test/discretization/cg2_interpolate.morpho | 4 +- 5 files changed, 67 insertions(+), 43 deletions(-) diff --git a/src/geometry/discretization.c b/src/geometry/discretization.c index 6fcc2ae6..a0d2be98 100644 --- a/src/geometry/discretization.c +++ b/src/geometry/discretization.c @@ -97,8 +97,8 @@ discretization cg1_1d = { void cg2_1dinterpolate(double *lambda, double *wts) { double dl = (lambda[0]-lambda[1]); wts[0]=lambda[0]*dl; - wts[1]=4*lambda[0]*lambda[1]; - wts[2]=-lambda[1]*dl; // TODO: CHECK + wts[1]=-lambda[1]*dl; + wts[2]=4*lambda[0]*lambda[1]; } unsigned int cg2_1dshape[] = { 1, 1 }; diff --git a/src/geometry/functional.c b/src/geometry/functional.c index 187e1522..9c95c0c4 100644 --- a/src/geometry/functional.c +++ b/src/geometry/functional.c @@ -4204,26 +4204,45 @@ bool lineintegral_integrand(vm *v, objectmesh *mesh, elementid id, int nv, int * integral_cleartlvars(v); vm_settlvar(v, elementhandle, MORPHO_OBJECT(&elref)); - value q0[iref.nfields+1], q1[iref.nfields+1]; + /*value q0[iref.nfields+1], q1[iref.nfields+1]; value *q[2] = { q0, q1 }; for (unsigned int k=0; kfnspc)) { + discretization *disc=MORPHO_GETDISCRETIZATION(f->fnspc)->discretization; + quantities[k].nnodes=disc->nnodes; + quantities[k].ifn=disc->ifn; + + int dof[disc->nnodes]; + discretization_doftofieldindx(f, disc, nv, vid, dof); + + quantities[k].vals=malloc(sizeof(value)*disc->nnodes); + for (int i=0; innodes; i++) { + field_getelementwithindex(f, dof[i], &quantities[k].vals[i]); + } + } else { + quantities[k].nnodes=nv; + quantities[k].ifn=MORPHO_NIL; + quantities[k].vals=malloc(sizeof(value)*nv); + for (unsigned int i=0; idim, MESH_GRADE_LINE, x, iref.nfields, q, &iref, out, &err); + success=integrate(integral_integrandfn, MORPHO_GETDICTIONARY(iref.method), mesh->dim, MESH_GRADE_LINE, x, iref.nfields, quantities, &iref, out, &err); } else { - success=integrate_integrate(integral_integrandfn, mesh->dim, MESH_GRADE_LINE, x, iref.nfields, q, &iref, out); + //success=integrate_integrate(integral_integrandfn, mesh->dim, MESH_GRADE_LINE, x, iref.nfields, q, &iref, out); } if (success) *out *=elref.elementsize; diff --git a/src/geometry/integrate.c b/src/geometry/integrate.c index fe2e9692..324877e8 100644 --- a/src/geometry/integrate.c +++ b/src/geometry/integrate.c @@ -2055,17 +2055,25 @@ int integrator_addquantity(integrator *integrate, int nq, value *quantity) { return qid; } -/** Count degrees of freedom for each quantity */ -void integrator_countquantitydof(integrator *integrate, int nq, quantity *quantity) { +/** Process the list of quantities given */ +void integrator_initializequantities(integrator *integrate, int nq, quantity *quantity) { + integrate->nquantity=nq; + integrate->quantity=quantity; + int ndof=0; for (int i=0; iqval[i]=q; ndof++; } else if (MORPHO_ISMATRIX(q)) { objectmatrix *m = MORPHO_GETMATRIX(q); quantity[i].ndof=matrix_countdof(m); + + objectmatrix *new = object_clonematrix(m); // Use a copy of the matrix + integrate->qval[i]=MORPHO_OBJECT(new); + ndof+=quantity[i].ndof; } else return; } @@ -2171,18 +2179,14 @@ void integrator_estimate(integrator *integrate) { * Linear interpolation * -------------------------------- */ -/*void xlinearinterpolate(int nbary, double *bary, int nels, double **v, double *x) { - for (int j=0; jdim+integrate->nqdof, integrate->nbary, 1.0, bary, 1, vmat, integrate->nbary, 0.0, x, 1); + cblas_dgemm(CblasColMajor, CblasNoTrans, CblasNoTrans, 1, integrate->dim, integrate->nbary, 1.0, bary, 1, vmat, integrate->nbary, 0.0, x, 1); } /** Also provide a version using BLAS to accelerate multiplication */ @@ -2216,14 +2220,19 @@ void preparequantities(integrator *integrate, value **quantity, double *qmat) { /** Sets up interpolation matrix */ void prepareinterpolation(integrator *integrate, int elementid, double *vmat) { double *vert[integrate->nbary]; // Vertex information - value *quantity[integrate->nbary]; // Quantities integrator_getvertices(integrate, elementid, vert); preparevertices(integrate, vert, vmat); - - if (integrate->nquantity) { - integrator_getquantities(integrate, elementid, quantity); - preparequantities(integrate, quantity, vmat+integrate->nbary*integrate->dim); +} + +void interpolatequantities(integrator *integrate, double *bary) { + for (int i=0; inquantity; i++) { + int nnodes = integrate->quantity[i].nnodes; + double wts[nnodes]; + (integrate->quantity[i].ifn) (bary, wts); + + double val=integrate_sumlistweighted(nnodes, (double *) integrate->quantity[i].vals, wts); + integrate->qval[i]=MORPHO_FLOAT(val); } } @@ -2246,20 +2255,15 @@ void postprocessquantities(integrator *integrate, double *qout) { * Function to perform quadrature * -------------------------------- */ -/** Weighted sum of a list */ -double integrate_sumlistweighted(unsigned int nel, double *list, double *wts) { - return cblas_ddot(nel, list, 1, wts, 1); -} - /** Evaluates the integrand at specified places */ bool integrate_evalfn(integrator *integrate, quadraturerule *rule, int imin, int imax, double *vmat, double *x, double *f) { for (int i=imin; inodes[integrate->nbary*i], vmat, x); - if (integrate->nquantity) postprocessquantities(integrate, x+integrate->dim); + interpolatequantities(integrate, &rule->nodes[integrate->nbary*i]); // Evaluate function - if (!(*integrate->integrand) (rule->grade, &rule->nodes[integrate->nbary*i], x, integrate->nquantity, integrate->quantitystack.data, integrate->ref, &f[i])) return false; + if (!(*integrate->integrand) (rule->grade, &rule->nodes[integrate->nbary*i], x, integrate->nquantity, integrate->qval, integrate->ref, &f[i])) return false; } return true; } @@ -2275,7 +2279,7 @@ bool quadrature(integrator *integrate, quadraturerule *rule, quadratureworkitem np++; } - double vmat[integrate->nbary*integrate->ndof]; // Interpolation matrix + double vmat[integrate->nbary]; // Interpolation matrix prepareinterpolation(integrate, work->elementid, vmat); // Evaluate function at quadrature points @@ -2557,7 +2561,6 @@ bool integrator_integrate(integrator *integrate, integrandfunction *integrand, i integrate->ref=ref; integrate->dim=dim; // Dimensionality of vertices - integrate->nquantity=nquantity; integrate->worklist.count=0; // Reset all these without deallocating integrate->vertexstack.count=0; @@ -2565,19 +2568,19 @@ bool integrator_integrate(integrator *integrate, integrandfunction *integrand, i integrate->quantitystack.count=0; error_clear(&integrate->emsg); - // Quantities used for interpolation live at the start of the quantity stack - integrator_countquantitydof(integrate, nquantity, quantity); - integrate->ndof = integrate->dim+integrate->nqdof; // Number of degrees of freedom + // Quantities + value qval[nquantity+1]; + integrate->qval=qval; - //integrator_addquantity(integrate, nquantity, quantity[0]); + integrator_initializequantities(integrate, nquantity, quantity); + integrate->ndof = integrate->dim+integrate->nqdof; // Number of degrees of freedom // Create first element - int vids[integrate->nbary], qids[integrate->nbary]; + int vids[integrate->nbary]; for (int i=0; inbary; i++) { vids[i]=integrator_addvertex(integrate, dim, x[i]); - //if (nquantity) qids[i]=integrator_addquantity(integrate, nquantity, quantity[i]); } - int elid = integrator_addelement(integrate, vids, qids); + int elid = integrator_addelement(integrate, vids, NULL); // Add it to the work list quadratureworkitem work; @@ -2656,7 +2659,7 @@ bool integrate(integrandfunction *integrand, objectdictionary *method, unsigned int nevals; bool test_integrand(unsigned int dim, double *t, double *x, unsigned int nquantity, value *quantity, void *data, double *fout) { - //double val = pow(sin(x[0]+x[1]),-0.5); //x[0]*x[1]*x[2]; // exp(-x[0]*x[0]); //sqrt(x[0]); //exp(-x[0]*x[0]); //*x[1]*x[2]; + //double val = pow(sin(x[0]+x[1]),-0.5); //x[0]*x[1]*x[2]; // exp(-x[0]*x[0]); //sqrt(x[0]); //exp(-x[0]*x[0]); // *x[1]*x[2]; //if (x[0]-x[1]<0.5) val=1.0; double val = sin(3*x[0]+6*x[1]); diff --git a/src/geometry/integrate.h b/src/geometry/integrate.h index 413cce8c..5e156193 100644 --- a/src/geometry/integrate.h +++ b/src/geometry/integrate.h @@ -121,8 +121,10 @@ typedef struct { int nbary; /** Number of barycentric coordinates */ int nquantity; /** Number of quantities to interpolate */ - quantity *quantities; /** Quantity list */ + quantity *quantity; /** Quantity list */ + value *qval; /** Interpolated quantity values */ int nqdof; /** Number of quantity degrees of freedom */ + int ndof; /** Number of degrees of freedom */ bool adapt; /** Enable adaptive integration */ diff --git a/test/discretization/cg2_interpolate.morpho b/test/discretization/cg2_interpolate.morpho index b792a8f7..9dc8cc7e 100644 --- a/test/discretization/cg2_interpolate.morpho +++ b/test/discretization/cg2_interpolate.morpho @@ -5,7 +5,7 @@ var m = LineMesh(fn (t) [t,0,0], 0..1:0.5) var l = FunctionSpace("CG2", grade=1) -var f = Field(m, fn (x,y,z) x^2, functionspace=l) +var f = Field(m, fn (x,y,z) x*(1-x), functionspace=l) print f -//print LineIntegral(fn (x, g) g, f, method={ }).total(m) \ No newline at end of file +print LineIntegral(fn (x, g) g, f, method={ }).total(m) \ No newline at end of file From 6a3d1cb8e1d5a71e7ec7b5296edf5a43b96dcd33 Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Mon, 28 Aug 2023 06:37:36 -0400 Subject: [PATCH 012/181] Remove quantity stack --- src/geometry/integrate.c | 78 ++++------------------------------------ src/geometry/integrate.h | 2 -- 2 files changed, 7 insertions(+), 73 deletions(-) diff --git a/src/geometry/integrate.c b/src/geometry/integrate.c index 324877e8..97ae61f7 100644 --- a/src/geometry/integrate.c +++ b/src/geometry/integrate.c @@ -1974,7 +1974,6 @@ void integrator_init(integrator *integrate) { varray_quadratureworkiteminit(&integrate->worklist); varray_doubleinit(&integrate->vertexstack); varray_intinit(&integrate->elementstack); - varray_valueinit(&integrate->quantitystack); integrate->ztol = INTEGRATE_ZEROCHECK; integrate->tol = INTEGRATE_ACCURACYGOAL; @@ -1994,11 +1993,7 @@ void integrator_clear(integrator *integrate) { varray_quadratureworkitemclear(&integrate->worklist); varray_intclear(&integrate->elementstack); varray_doubleclear(&integrate->vertexstack); - for (unsigned int i=0; iquantitystack.count; i++) { - value v=integrate->quantitystack.data[i]; - if (MORPHO_ISOBJECT(v)) morpho_freeobject(v); - } - varray_valueclear(&integrate->quantitystack); + for (int i=0; inquantity; i++) morpho_freeobject(integrate->qval[i]); } /** Adds a vertex to the integrators vertex stack, returning the id */ @@ -2018,43 +2013,6 @@ int integrator_addelement(integrator *integrate, int *vids, int *qids) { return elid; } -/** - Quantities are stored as needed on the quantity stack. The first n values are used - to store interpolated values. As new vertices are added, n entries are added to the quantity stack - - | base: | q list 0: | q list 1: | - | q0, q1, ... qn | q0, q1, ... qn | q0, q1, ... | - - As elements are added, they include both vertex ids and references to entries on the quantity stack. Each element is 2*nbary entries long: - - nbary nbary - | vid0, vid1, ... : qid0, qid1, ... | - - For each element, we build an interpolation matrix, - - [ x0 y0 q0,0 q1,0 ... qn,0 ] - [ x1 y1 q0,1 q1,1 ... qn,1 ] - [ x2 y1 q0,2 q1,2 ... qn,2 ] - - which when multiplied by the barycentric coordinates yields the interpolated quantite - - [l0, l1, l2] . Interp -> [ x0, y0, q0, q1, ... qn ] */ - -/** Adds nq quantities to the quantity stack, returning the id of the first element */ -int integrator_addquantity(integrator *integrate, int nq, value *quantity) { - int qid=integrate->quantitystack.count; - for (int i=0; iquantitystack, quantity[i]); - } else if (MORPHO_ISMATRIX(quantity[i])) { - objectmatrix *new = object_clonematrix(MORPHO_GETMATRIX(quantity[i])); - // TODO: Raise error on fail - varray_valuewrite(&integrate->quantitystack, MORPHO_OBJECT(new)); - } else return false; - } - return qid; -} - /** Process the list of quantities given */ void integrator_initializequantities(integrator *integrate, int nq, quantity *quantity) { integrate->nquantity=nq; @@ -2089,15 +2047,6 @@ void integrator_getvertices(integrator *integrate, int elementid, double **vert) } } -/** Retrieves the quantity pointers given an elementid. - @warning: The pointers returned become invalid after a subsequent call to integrator_addvertex */ -void integrator_getquantities(integrator *integrate, int elementid, value **quantities) { - for (int i=0; inbary; i++) { - int qid=integrate->elementstack.data[elementid+integrate->nbary+i]; // Note quantities stored after vertices - quantities[i]=&(integrate->quantitystack.data[qid]); - } -} - /** Retrieves an element with elementid */ void integrator_getelement(integrator *integrate, int elementid, int *vid, int *qid) { for (int i=0; inbary; i++) { @@ -2225,6 +2174,7 @@ void prepareinterpolation(integrator *integrate, int elementid, double *vmat) { preparevertices(integrate, vert, vmat); } +/** Interpolates quantities */ void interpolatequantities(integrator *integrate, double *bary) { for (int i=0; inquantity; i++) { int nnodes = integrate->quantity[i].nnodes; @@ -2236,21 +2186,6 @@ void interpolatequantities(integrator *integrate, double *bary) { } } -/** Processes the results of interpolation */ -void postprocessquantities(integrator *integrate, double *qout) { - int k=0; // DOF counter - for (int i=0; inquantity; i++) { - value q = integrate->quantitystack.data[i]; - if (MORPHO_ISFLOAT(q)) { - integrate->quantitystack.data[i]=MORPHO_FLOAT(qout[k]); - } else if (MORPHO_ISMATRIX(q)) { - objectmatrix *m = MORPHO_GETMATRIX(q); - m->elements=&qout[k]; - k+=m->ncols*m->nrows; - } - } -} - /* -------------------------------- * Function to perform quadrature * -------------------------------- */ @@ -2283,7 +2218,7 @@ bool quadrature(integrator *integrate, quadraturerule *rule, quadratureworkitem prepareinterpolation(integrate, work->elementid, vmat); // Evaluate function at quadrature points - double x[integrate->ndof],f[nmax]; + double x[integrate->ndof], f[nmax]; if (!integrate_evalfn(integrate, rule, 0, rule->nnodes, vmat, x, f)) return false; double r[np+1]; @@ -2332,6 +2267,8 @@ bool quadrature(integrator *integrate, quadraturerule *rule, quadratureworkitem bool subdivide(integrator *integrate, quadratureworkitem *work, int *nels, quadratureworkitem *newitems) { subdivisionrule *rule = integrate->subdivide; + UNREACHABLE("Subdivision temporarily disabled."); + // Fetch the element data int npts = integrate->nbary+rule->npts; int vid[npts], qid[npts]; @@ -2347,10 +2284,10 @@ bool subdivide(integrator *integrate, quadratureworkitem *work, int *nels, quadr linearinterpolate(integrate, &rule->pts[j*integrate->nbary], vmat, x); vid[integrate->nbary+j]=integrator_addvertex(integrate, integrate->dim, x); - if (integrate->nquantity) { + /*if (integrate->nquantity) { postprocessquantities(integrate, x+integrate->dim); qid[integrate->nbary+j]=integrator_addquantity(integrate, integrate->nquantity, integrate->quantitystack.data); - } + }*/ } // Create elements @@ -2565,7 +2502,6 @@ bool integrator_integrate(integrator *integrate, integrandfunction *integrand, i integrate->worklist.count=0; // Reset all these without deallocating integrate->vertexstack.count=0; integrate->elementstack.count=0; - integrate->quantitystack.count=0; error_clear(&integrate->emsg); // Quantities diff --git a/src/geometry/integrate.h b/src/geometry/integrate.h index 5e156193..41331be6 100644 --- a/src/geometry/integrate.h +++ b/src/geometry/integrate.h @@ -139,8 +139,6 @@ typedef struct { varray_double vertexstack; /** Stack of vertices */ varray_int elementstack; /** Stack of elements */ - varray_value quantitystack; /** Stack of quantity values generated by interpolation */ - double ztol; /** Tolerance for zero detection */ double tol; /** Tolerance for relative error */ int maxiterations; /** Maximum number of subdivisions to perform */ From ffde0a73fe9bfaa4cbe727eae1faf67afc15e30e Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Mon, 28 Aug 2023 17:54:54 -0400 Subject: [PATCH 013/181] Local -> Ref -> Physical pipeline and working subdivision --- src/geometry/integrate.c | 138 ++++++++++++--------- src/geometry/integrate.h | 7 +- test/discretization/cg2_interpolate.morpho | 8 +- 3 files changed, 85 insertions(+), 68 deletions(-) diff --git a/src/geometry/integrate.c b/src/geometry/integrate.c index 97ae61f7..772f302f 100644 --- a/src/geometry/integrate.c +++ b/src/geometry/integrate.c @@ -2003,13 +2003,10 @@ int integrator_addvertex(integrator *integrate, int ndof, double *v) { return vid; } -/** Adds an element to the element stack, returning the id. Elements consist of : - - vertex ids - - a number of quantity ids. */ -int integrator_addelement(integrator *integrate, int *vids, int *qids) { +/** Adds an element to the element stack, returning the id. Elements are specified by their coordinates in the reference element */ +int integrator_addelement(integrator *integrate, int *vids) { int elid=integrate->elementstack.count; varray_intadd(&integrate->elementstack, vids, integrate->nbary); - if (integrate->nquantity && qids) varray_intadd(&integrate->elementstack, qids, integrate->nbary); return elid; } @@ -2048,10 +2045,9 @@ void integrator_getvertices(integrator *integrate, int elementid, double **vert) } /** Retrieves an element with elementid */ -void integrator_getelement(integrator *integrate, int elementid, int *vid, int *qid) { +void integrator_getelement(integrator *integrate, int elementid, int *vid) { for (int i=0; inbary; i++) { vid[i]=integrate->elementstack.data[elementid+i]; - if (integrate->nquantity && qid) qid[i]=integrate->elementstack.data[elementid+integrate->nbary+i]; } } @@ -2128,23 +2124,25 @@ void integrator_estimate(integrator *integrate) { * Linear interpolation * -------------------------------- */ -/** Weighted sum of a list */ -double integrate_sumlistweighted(unsigned int nel, double *list, double *wts) { - return cblas_ddot(nel, list, 1, wts, 1); -} - -void linearinterpolate(integrator *integrate, double *bary, double *vmat, double *x) { - // Multiply 1 x nbary (lambda) with nbary x dim (vmat) - cblas_dgemm(CblasColMajor, CblasNoTrans, CblasNoTrans, 1, integrate->dim, integrate->nbary, 1.0, bary, 1, vmat, integrate->nbary, 0.0, x, 1); -} - -/** Also provide a version using BLAS to accelerate multiplication */ -void preparevertices(integrator *integrate, double **v, double *vv) { - int k=0; - for (int j=0; jdim; j++) { - for (int i=0; inbary; i++) { - vv[k]=v[i][j]; - k++; +/** Construct vertex transformation matrices + @param[in] integrate - the integrator + @param[in] vref - vertices specified in reference element (length integrate->nbary) + @param[out] r - matrix mapping local node coordinates to ref. el coordinates [r has nbary rows and nbary columns] + @param[out] v - matrix mapping ref. el coordinates to physical coordinates [v has dim rows and nbary columns] */ +void preparevertices(integrator *integrate, double **vref, double *r, double *v) { + int l=0; + if (r) for (int i=0; inbary; i++) { // Loop over vertices [defined rel. to ref. element] + for (int k=0; knbary; k++) { // Sum over barycentric coordinates + r[l]=vref[i][k]; + l++; + } + } + + l=0; + if (v) for (int i=0; inbary; i++) { // Loop over vertices [defined rel. to ref. element] + for (int j=0; jdim; j++) { // Loop over dimensions + v[l]=integrate->x[i][j]; + l++; } } } @@ -2167,11 +2165,29 @@ void preparequantities(integrator *integrate, value **quantity, double *qmat) { } /** Sets up interpolation matrix */ -void prepareinterpolation(integrator *integrate, int elementid, double *vmat) { +void prepareinterpolation(integrator *integrate, int elementid, double *rmat, double *vmat) { double *vert[integrate->nbary]; // Vertex information - integrator_getvertices(integrate, elementid, vert); - preparevertices(integrate, vert, vmat); + preparevertices(integrate, vert, rmat, vmat); +} + +/** Weighted sum of a list */ +double integrate_sumlistweighted(unsigned int nel, double *list, double *wts) { + return cblas_ddot(nel, list, 1, wts, 1); +} + +/** Transforms local element coordinates to reference element coordinates */ +void transformtorefelement(integrator *integrate, double *rmat, double *local, double *bary) { + // Multiply nbary x nbary (rmat) with nbary x 1 (local) to get nbary x 1 (bary) + cblas_dgemm(CblasColMajor, CblasNoTrans, CblasNoTrans, integrate->nbary, 1, integrate->nbary, 1.0, rmat, integrate->nbary, local, integrate->nbary, 0.0, bary, integrate->nbary); + +} + + +/** Transform from reference element barycentric coordinates to physical coordinates */ +void interpolatecoordinates(integrator *integrate, double *lambda, double *vmat, double *x) { + // Multiply dim x nbary (vmat) with nbary x 1 (lambda) to get dim x 1 (x) + cblas_dgemm(CblasColMajor, CblasNoTrans, CblasNoTrans, integrate->dim, 1, integrate->nbary, 1.0, vmat, integrate->dim, lambda, integrate->nbary, 0.0, x, integrate->dim); } /** Interpolates quantities */ @@ -2191,14 +2207,16 @@ void interpolatequantities(integrator *integrate, double *bary) { * -------------------------------- */ /** Evaluates the integrand at specified places */ -bool integrate_evalfn(integrator *integrate, quadraturerule *rule, int imin, int imax, double *vmat, double *x, double *f) { +bool integrate_evalfn(integrator *integrate, quadraturerule *rule, int imin, int imax, double *rmat, double *vmat, double *x, double *f) { + double node[integrate->nbary]; + for (int i=imin; inodes[integrate->nbary*i], vmat, x); - interpolatequantities(integrate, &rule->nodes[integrate->nbary*i]); + transformtorefelement(integrate, rmat, &rule->nodes[integrate->nbary*i], node); + interpolatecoordinates(integrate, node, vmat, x); + interpolatequantities(integrate, node); // Evaluate function - if (!(*integrate->integrand) (rule->grade, &rule->nodes[integrate->nbary*i], x, integrate->nquantity, integrate->qval, integrate->ref, &f[i])) return false; + if (!(*integrate->integrand) (rule->grade, node, x, integrate->nquantity, integrate->qval, integrate->ref, &f[i])) return false; } return true; } @@ -2214,12 +2232,13 @@ bool quadrature(integrator *integrate, quadraturerule *rule, quadratureworkitem np++; } - double vmat[integrate->nbary]; // Interpolation matrix - prepareinterpolation(integrate, work->elementid, vmat); + double rmat[integrate->nbary*integrate->nbary]; // Transform local element coordinates to ref. el. + double vmat[integrate->nbary*integrate->dim]; // Transform barycentric coordinates in ref. el. to physical coordinates + prepareinterpolation(integrate, work->elementid, rmat, vmat); // Evaluate function at quadrature points double x[integrate->ndof], f[nmax]; - if (!integrate_evalfn(integrate, rule, 0, rule->nnodes, vmat, x, f)) return false; + if (!integrate_evalfn(integrate, rule, 0, rule->nnodes, rmat, vmat, x, f)) return false; double r[np+1]; double eps[np+1]; eps[0]=0.0; @@ -2235,7 +2254,7 @@ bool quadrature(integrator *integrate, quadraturerule *rule, quadratureworkitem // Attempt p-refinement if available for (quadraturerule *q=rule->ext; q!=NULL; q=q->ext) { ip++; - if (!integrate_evalfn(integrate, q, nmin, q->nnodes, vmat, x, f)) return false; + if (!integrate_evalfn(integrate, q, nmin, q->nnodes, rmat, vmat, x, f)) return false; r[ip]=integrate_sumlistweighted(q->nnodes, f, q->weights); eps[ip]=fabs(r[ip]-r[ip-1]); @@ -2267,27 +2286,19 @@ bool quadrature(integrator *integrate, quadraturerule *rule, quadratureworkitem bool subdivide(integrator *integrate, quadratureworkitem *work, int *nels, quadratureworkitem *newitems) { subdivisionrule *rule = integrate->subdivide; - UNREACHABLE("Subdivision temporarily disabled."); - // Fetch the element data - int npts = integrate->nbary+rule->npts; - int vid[npts], qid[npts]; - integrator_getelement(integrate, work->elementid, vid, qid); + int vid[integrate->nbary+rule->npts]; + integrator_getelement(integrate, work->elementid, vid); // Get ready for interpolation - double vmat[integrate->nbary*integrate->ndof]; // Vertex information - prepareinterpolation(integrate, work->elementid, vmat); + double rmat[integrate->nbary*integrate->nbary]; // Vertex information + prepareinterpolation(integrate, work->elementid, rmat, NULL); - // Interpolate vertices and quantities, and add these new vertices and quantities - double x[integrate->ndof]; + // Interpolate vertices + double lambda[integrate->nbary]; for (int j=0; jnpts; j++) { - linearinterpolate(integrate, &rule->pts[j*integrate->nbary], vmat, x); - vid[integrate->nbary+j]=integrator_addvertex(integrate, integrate->dim, x); - - /*if (integrate->nquantity) { - postprocessquantities(integrate, x+integrate->dim); - qid[integrate->nbary+j]=integrator_addquantity(integrate, integrate->nquantity, integrate->quantitystack.data); - }*/ + transformtorefelement(integrate, rmat, &rule->pts[j*integrate->nbary], lambda); + vid[integrate->nbary+j]=integrator_addvertex(integrate, integrate->nbary, lambda); } // Create elements @@ -2296,15 +2307,14 @@ bool subdivide(integrator *integrate, quadratureworkitem *work, int *nels, quadr newitems[i].err=0.0; newitems[i].weight=work->weight*rule->weights[i]; - // Construct new element from the vertex ids and quantity ids - int vids[integrate->nbary], qids[integrate->nbary]; + // Construct new element from the vertex ids + int vids[integrate->nbary]; for (int k=0; knbary; k++) { vids[k]=vid[rule->newels[integrate->nbary*i+k]]; - if (integrate->nquantity) qids[k]=qid[rule->newels[integrate->nbary*i+k]]; } // Define the new element - newitems[i].elementid=integrator_addelement(integrate, vids, qids); + newitems[i].elementid=integrator_addelement(integrate, vids); } *nels = rule->nels; @@ -2494,12 +2504,13 @@ bool integrator_configurewithdictionary(integrator *integrate, grade g, objectdi * @returns True on success */ bool integrator_integrate(integrator *integrate, integrandfunction *integrand, int dim, double **x, unsigned int nquantity, quantity *quantity, void *ref) { - integrate->integrand=integrand; + integrate->integrand=integrand; // Integrand function integrate->ref=ref; - integrate->dim=dim; // Dimensionality of vertices + integrate->x=x; // Vertices + integrate->dim=dim; - integrate->worklist.count=0; // Reset all these without deallocating + integrate->worklist.count=0; // Reset these integrate->vertexstack.count=0; integrate->elementstack.count=0; error_clear(&integrate->emsg); @@ -2511,12 +2522,15 @@ bool integrator_integrate(integrator *integrate, integrandfunction *integrand, i integrator_initializequantities(integrate, nquantity, quantity); integrate->ndof = integrate->dim+integrate->nqdof; // Number of degrees of freedom - // Create first element + // Create first element, which corresponds to the reference element int vids[integrate->nbary]; for (int i=0; inbary; i++) { - vids[i]=integrator_addvertex(integrate, dim, x[i]); + double xref[integrate->nbary]; + for (int j=0; jnbary; j++) xref[j]=0.0; + xref[i]=1.0; + vids[i]=integrator_addvertex(integrate, integrate->nbary, xref); } - int elid = integrator_addelement(integrate, vids, NULL); + int elid = integrator_addelement(integrate, vids); // Add it to the work list quadratureworkitem work; diff --git a/src/geometry/integrate.h b/src/geometry/integrate.h index 41331be6..52d3fdc3 100644 --- a/src/geometry/integrate.h +++ b/src/geometry/integrate.h @@ -116,8 +116,11 @@ typedef struct { typedef struct { integrandfunction *integrand; /** Function to integrate */ + void *ref; /** Reference to pass to integrand function */ int dim; /** Dimension of points in embedded space */ + double **x; /** Vertices defining the element */ + int nbary; /** Number of barycentric coordinates */ int nquantity; /** Number of quantities to interpolate */ @@ -127,10 +130,10 @@ typedef struct { int ndof; /** Number of degrees of freedom */ - bool adapt; /** Enable adaptive integration */ quadraturerule *rule; /** Quadrature rule to use */ quadraturerule *errrule; /** Additional rule for error estimation */ + bool adapt; /** Enable adaptive integration */ subdivisionrule *subdivide; /** Subdivision rule to use */ int workp; /** Index of largest item in the work list */ @@ -148,8 +151,6 @@ typedef struct { double err; /** Estimated error of the integral */ error emsg; /** Store error messages from the integrator */ - - void *ref; /** Reference to pass to integrand */ } integrator; /* ------------------------------------------------------- diff --git a/test/discretization/cg2_interpolate.morpho b/test/discretization/cg2_interpolate.morpho index 9dc8cc7e..8506a281 100644 --- a/test/discretization/cg2_interpolate.morpho +++ b/test/discretization/cg2_interpolate.morpho @@ -1,11 +1,13 @@ import meshtools -var m = LineMesh(fn (t) [t,0,0], 0..1:0.5) +var m = LineMesh(fn (t) [t,0,0], 0..1) var l = FunctionSpace("CG2", grade=1) -var f = Field(m, fn (x,y,z) x*(1-x), functionspace=l) +var f = Field(m, fn (x,y,z) x^2, functionspace=l) print f -print LineIntegral(fn (x, g) g, f, method={ }).total(m) \ No newline at end of file +print LineIntegral(fn (x, g) 1-x[0]^20, f, method={ }).total(m) + +//print LineIntegral(fn (x, g) g, f, method={ }).total(m) \ No newline at end of file From 4342fb113d95b4c2218ecd778eb5d9653d8249c9 Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Mon, 28 Aug 2023 19:16:30 -0400 Subject: [PATCH 014/181] Two dimensional quadrature works with quadratic elements --- src/geometry/discretization.c | 4 +++- test/discretization/cg2_2d.morpho | 2 ++ test/discretization/cg2_interpolate.morpho | 4 +++- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/geometry/discretization.c b/src/geometry/discretization.c index a0d2be98..44d405e3 100644 --- a/src/geometry/discretization.c +++ b/src/geometry/discretization.c @@ -119,6 +119,7 @@ discretization cg2_1d = { .shape = cg2_1dshape, .degree = 2, .nnodes = 3, + .nsubel = 1, .nodes = cg2_1dnodes, .ifn = cg2_1dinterpolate, .eldefn = cg2_1ddefn @@ -169,9 +170,10 @@ eldefninstruction cg2_2deldefn[] = { discretization cg2_2d = { .name = "CG2", .grade = 2, - .shape = cg2_1dshape, + .shape = cg2_2dshape, .degree = 2, .nnodes = 6, + .nsubel = 3, .nodes = cg2_2dnodes, .ifn = cg2_2dinterpolate, .eldefn = cg2_2deldefn diff --git a/test/discretization/cg2_2d.morpho b/test/discretization/cg2_2d.morpho index a0ada794..a5578ac6 100644 --- a/test/discretization/cg2_2d.morpho +++ b/test/discretization/cg2_2d.morpho @@ -18,3 +18,5 @@ var f = Field(m, functionspace=l) print l.layout(f) print f.shape() + +print AreaIntegral(fn (x) sqrt(x[0]*x[1]), method={ }).total(m)-4/9 \ No newline at end of file diff --git a/test/discretization/cg2_interpolate.morpho b/test/discretization/cg2_interpolate.morpho index 8506a281..f47a8fd2 100644 --- a/test/discretization/cg2_interpolate.morpho +++ b/test/discretization/cg2_interpolate.morpho @@ -8,6 +8,8 @@ var l = FunctionSpace("CG2", grade=1) var f = Field(m, fn (x,y,z) x^2, functionspace=l) print f +print LineIntegral(fn (x) 1-x[0]^20, method={ }).total(m) + print LineIntegral(fn (x, g) 1-x[0]^20, f, method={ }).total(m) -//print LineIntegral(fn (x, g) g, f, method={ }).total(m) \ No newline at end of file +print LineIntegral(fn (x, g) 1-g^10, f, method={ }).total(m) From 915c2e8da73e83a37afbbc75223cedd88be08ea0 Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Mon, 28 Aug 2023 21:13:18 -0400 Subject: [PATCH 015/181] Fix quantity interpolation for matrices --- src/geometry/functional.c | 109 ++++++++++++++++++------------ src/geometry/integrate.c | 15 +++- src/linalg/matrix.c | 13 ++-- src/linalg/matrix.h | 1 + test/discretization/cg2_2d.morpho | 8 ++- 5 files changed, 93 insertions(+), 53 deletions(-) diff --git a/src/geometry/functional.c b/src/geometry/functional.c index 9c95c0c4..6a4cff98 100644 --- a/src/geometry/functional.c +++ b/src/geometry/functional.c @@ -4161,6 +4161,41 @@ void integral_freeref(void *ref) { MORPHO_FREE(ref); } +/** Prepares quantity list */ +void integral_preparequantities(integralref *iref, int nv, int *vid, quantity *quantities) { + for (int k=0; knfields; k++) { + objectfield *f=MORPHO_GETFIELD(iref->fields[k]); + + if (MORPHO_ISDISCRETIZATION(f->fnspc)) { + discretization *disc=MORPHO_GETDISCRETIZATION(f->fnspc)->discretization; + quantities[k].nnodes=disc->nnodes; + quantities[k].ifn=disc->ifn; + + int dof[disc->nnodes]; + discretization_doftofieldindx(f, disc, nv, vid, dof); + + quantities[k].vals=MORPHO_MALLOC(sizeof(value)*disc->nnodes); + for (int i=0; innodes; i++) { + field_getelementwithindex(f, dof[i], &quantities[k].vals[i]); + } + } else { + quantities[k].nnodes=nv; + quantities[k].ifn=MORPHO_NIL; + quantities[k].vals=MORPHO_MALLOC(sizeof(value)*nv); + for (unsigned int i=0; ifnspc)) { - discretization *disc=MORPHO_GETDISCRETIZATION(f->fnspc)->discretization; - quantities[k].nnodes=disc->nnodes; - quantities[k].ifn=disc->ifn; - - int dof[disc->nnodes]; - discretization_doftofieldindx(f, disc, nv, vid, dof); - - quantities[k].vals=malloc(sizeof(value)*disc->nnodes); - for (int i=0; innodes; i++) { - field_getelementwithindex(f, dof[i], &quantities[k].vals[i]); - } - } else { - quantities[k].nnodes=nv; - quantities[k].ifn=MORPHO_NIL; - quantities[k].vals=malloc(sizeof(value)*nv); + success=integrate(integral_integrandfn, MORPHO_GETDICTIONARY(iref.method), mesh->dim, MESH_GRADE_LINE, x, iref.nfields, quantities, &iref, out, &err); + + integral_clearquantities(iref.nfields, quantities); + } else { // Old integrator + value q0[iref.nfields+1], q1[iref.nfields+1]; + value *q[2] = { q0, q1 }; + for (unsigned int k=0; kdim, MESH_GRADE_LINE, x, iref.nfields, quantities, &iref, out, &err); - } else { - //success=integrate_integrate(integral_integrandfn, mesh->dim, MESH_GRADE_LINE, x, iref.nfields, q, &iref, out); + + success=integrate_integrate(integral_integrandfn, mesh->dim, MESH_GRADE_LINE, x, iref.nfields, q, &iref, out); } if (success) *out *=elref.elementsize; @@ -4367,19 +4381,24 @@ bool areaintegral_integrand(vm *v, objectmesh *mesh, elementid id, int nv, int * /* Set up quantities */ integral_cleartlvars(v); vm_settlvar(v, elementhandle, MORPHO_OBJECT(&elref)); - - value q0[iref.nfields+1], q1[iref.nfields+1], q2[iref.nfields+1]; - value *q[3] = { q0, q1, q2 }; - for (unsigned int k=0; kdim, MESH_GRADE_AREA, x, iref.nfields, q, &iref, out, &err); + quantity quantities[iref.nfields+1]; + integral_preparequantities(&iref, nv, vid, quantities); + + success=integrate(integral_integrandfn, MORPHO_GETDICTIONARY(iref.method), mesh->dim, MESH_GRADE_AREA, x, iref.nfields, quantities, &iref, out, &err); + + integral_clearquantities(iref.nfields, quantities); } else { + value q0[iref.nfields+1], q1[iref.nfields+1], q2[iref.nfields+1]; + value *q[3] = { q0, q1, q2 }; + for (unsigned int k=0; kdim, MESH_GRADE_AREA, x, iref.nfields, q, &iref, out); } diff --git a/src/geometry/integrate.c b/src/geometry/integrate.c index 772f302f..a8adad0b 100644 --- a/src/geometry/integrate.c +++ b/src/geometry/integrate.c @@ -2197,8 +2197,19 @@ void interpolatequantities(integrator *integrate, double *bary) { double wts[nnodes]; (integrate->quantity[i].ifn) (bary, wts); - double val=integrate_sumlistweighted(nnodes, (double *) integrate->quantity[i].vals, wts); - integrate->qval[i]=MORPHO_FLOAT(val); + if (MORPHO_ISFLOAT(integrate->qval[i])) { + double qval[nnodes]; + for (int j=0; jquantity[i].vals[j]); + double val=integrate_sumlistweighted(nnodes, qval, wts); + integrate->qval[i]=MORPHO_FLOAT(val); + } else if (MORPHO_ISMATRIX(integrate->qval[i])) { + objectmatrix *out = MORPHO_GETMATRIX(integrate->qval[i]); + matrix_zero(out); + for (int j=0; jquantity[i].vals[j])); + } + } + } } diff --git a/src/linalg/matrix.c b/src/linalg/matrix.c index e7097b81..15cbbd63 100644 --- a/src/linalg/matrix.c +++ b/src/linalg/matrix.c @@ -44,9 +44,7 @@ objectmatrix *object_newmatrix(unsigned int nrows, unsigned int ncols, bool zero new->ncols=ncols; new->nrows=nrows; new->elements=new->matrixdata; - if (zero) { - memset(new->elements, 0, sizeof(double)*nel); - } + if (zero) matrix_zero(new); } return new; @@ -599,6 +597,13 @@ objectmatrixerror matrix_trace(objectmatrix *a, double *out) { /** Scale a matrix */ objectmatrixerror matrix_scale(objectmatrix *a, double scale) { cblas_dscal(a->ncols*a->nrows, scale, a->elements, 1); + + return MATRIX_OK; +} + +/** Sets a matrix to zero */ +objectmatrixerror matrix_zero(objectmatrix *a) { + memset(a->elements, 0, sizeof(double)*a->nrows*a->ncols); return MATRIX_OK; } @@ -606,7 +611,7 @@ objectmatrixerror matrix_scale(objectmatrix *a, double scale) { /** Load the indentity matrix*/ objectmatrixerror matrix_identity(objectmatrix *a) { if (a->ncols!=a->nrows) return MATRIX_NSQ; - memset(a->elements, 0, sizeof(double)*a->nrows*a->ncols); + matrix_zero(a); for (int i=0; inrows; i++) a->elements[i+a->nrows*i]=1.0; return MATRIX_OK; } diff --git a/src/linalg/matrix.h b/src/linalg/matrix.h index 0573c5af..04ece1a1 100644 --- a/src/linalg/matrix.h +++ b/src/linalg/matrix.h @@ -183,6 +183,7 @@ objectmatrixerror matrix_transpose(objectmatrix *a, objectmatrix *out); objectmatrixerror matrix_trace(objectmatrix *a, double *out); objectmatrixerror matrix_scale(objectmatrix *a, double scale); objectmatrixerror matrix_identity(objectmatrix *a); +objectmatrixerror matrix_zero(objectmatrix *a); double matrix_sum(objectmatrix *a); objectmatrixerror matrix_eigensystem(objectmatrix *a, double *wr, double *wi, objectmatrix *vec); bool matrix_eigen(vm *v, objectmatrix *a, value *evals, value *evecs); diff --git a/test/discretization/cg2_2d.morpho b/test/discretization/cg2_2d.morpho index a5578ac6..15f174bb 100644 --- a/test/discretization/cg2_2d.morpho +++ b/test/discretization/cg2_2d.morpho @@ -13,10 +13,14 @@ m.addgrade(1) var l = FunctionSpace("CG2", grade=2) -var f = Field(m, functionspace=l) +var f = Field(m, fn (x,y) Matrix([[x,y],[-y, x]]), functionspace=l) print l.layout(f) print f.shape() -print AreaIntegral(fn (x) sqrt(x[0]*x[1]), method={ }).total(m)-4/9 \ No newline at end of file +print f + +print AreaIntegral(fn (x) sqrt(x[0]*x[1]), method={ }).total(m)-4/9 + +print AreaIntegral(fn (x, q) q.trace()^2, f, method={ }).total(m)-4/3 \ No newline at end of file From d6fabea858e7418b4fff630a25c8e0cce910189c Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Mon, 28 Aug 2023 21:28:09 -0400 Subject: [PATCH 016/181] Code clean of integrator Remove redundant features of the data structure Improve function name consistency --- src/geometry/integrate.c | 89 ++++++++++++++-------------------------- src/geometry/integrate.h | 5 --- 2 files changed, 30 insertions(+), 64 deletions(-) diff --git a/src/geometry/integrate.c b/src/geometry/integrate.c index a8adad0b..06b3e291 100644 --- a/src/geometry/integrate.c +++ b/src/geometry/integrate.c @@ -1960,8 +1960,6 @@ void integrator_init(integrator *integrate) { integrate->dim=0; integrate->nbary=0; integrate->nquantity=0; - integrate->nqdof=0; - integrate->ndof=0; integrate->adapt=true; integrate->rule = NULL; @@ -1969,8 +1967,6 @@ void integrator_init(integrator *integrate) { integrate->subdivide = NULL; - integrate->workp = -1; - integrate->freep = -1; varray_quadratureworkiteminit(&integrate->worklist); varray_doubleinit(&integrate->vertexstack); varray_intinit(&integrate->elementstack); @@ -2015,24 +2011,19 @@ void integrator_initializequantities(integrator *integrate, int nq, quantity *qu integrate->nquantity=nq; integrate->quantity=quantity; - int ndof=0; for (int i=0; iqval[i]=q; - ndof++; } else if (MORPHO_ISMATRIX(q)) { objectmatrix *m = MORPHO_GETMATRIX(q); quantity[i].ndof=matrix_countdof(m); objectmatrix *new = object_clonematrix(m); // Use a copy of the matrix integrate->qval[i]=MORPHO_OBJECT(new); - - ndof+=quantity[i].ndof; } else return; } - integrate->nqdof=ndof; } /** Retrieves the vertex pointers given an elementid. @@ -2129,7 +2120,7 @@ void integrator_estimate(integrator *integrate) { @param[in] vref - vertices specified in reference element (length integrate->nbary) @param[out] r - matrix mapping local node coordinates to ref. el coordinates [r has nbary rows and nbary columns] @param[out] v - matrix mapping ref. el coordinates to physical coordinates [v has dim rows and nbary columns] */ -void preparevertices(integrator *integrate, double **vref, double *r, double *v) { +void integrator_preparevertices(integrator *integrate, double **vref, double *r, double *v) { int l=0; if (r) for (int i=0; inbary; i++) { // Loop over vertices [defined rel. to ref. element] for (int k=0; knbary; k++) { // Sum over barycentric coordinates @@ -2147,51 +2138,32 @@ void preparevertices(integrator *integrate, double **vref, double *r, double *v) } } -/** Prepares quantities for interpolation */ -void preparequantities(integrator *integrate, value **quantity, double *qmat) { - int k=0; // DOF counter - for (int i=0; inquantity; i++) { - if (MORPHO_ISFLOAT(quantity[0][i])) { - for (int j=0; jnbary; j++) morpho_valuetofloat(quantity[j][i], &qmat[k*integrate->nbary+j]); - k++; - } else if (MORPHO_ISMATRIX(quantity[0][i])) { - for (int j=0; jnbary; j++) { - objectmatrix *m = MORPHO_GETMATRIX(quantity[j][i]); - int mdof=m->ncols*m->nrows; - for (int l=0; lnbary+j]=m->elements[l]; - } - } else return; - } -} - /** Sets up interpolation matrix */ -void prepareinterpolation(integrator *integrate, int elementid, double *rmat, double *vmat) { +void integrator_prepareinterpolation(integrator *integrate, int elementid, double *rmat, double *vmat) { double *vert[integrate->nbary]; // Vertex information integrator_getvertices(integrate, elementid, vert); - preparevertices(integrate, vert, rmat, vmat); + integrator_preparevertices(integrate, vert, rmat, vmat); } /** Weighted sum of a list */ -double integrate_sumlistweighted(unsigned int nel, double *list, double *wts) { +double integrator_sumlistweighted(unsigned int nel, double *list, double *wts) { return cblas_ddot(nel, list, 1, wts, 1); } /** Transforms local element coordinates to reference element coordinates */ -void transformtorefelement(integrator *integrate, double *rmat, double *local, double *bary) { +void integrator_transformtorefelement(integrator *integrate, double *rmat, double *local, double *bary) { // Multiply nbary x nbary (rmat) with nbary x 1 (local) to get nbary x 1 (bary) cblas_dgemm(CblasColMajor, CblasNoTrans, CblasNoTrans, integrate->nbary, 1, integrate->nbary, 1.0, rmat, integrate->nbary, local, integrate->nbary, 0.0, bary, integrate->nbary); - } - /** Transform from reference element barycentric coordinates to physical coordinates */ -void interpolatecoordinates(integrator *integrate, double *lambda, double *vmat, double *x) { +void integrator_interpolatecoordinates(integrator *integrate, double *lambda, double *vmat, double *x) { // Multiply dim x nbary (vmat) with nbary x 1 (lambda) to get dim x 1 (x) cblas_dgemm(CblasColMajor, CblasNoTrans, CblasNoTrans, integrate->dim, 1, integrate->nbary, 1.0, vmat, integrate->dim, lambda, integrate->nbary, 0.0, x, integrate->dim); } /** Interpolates quantities */ -void interpolatequantities(integrator *integrate, double *bary) { +void integrator_interpolatequantities(integrator *integrate, double *bary) { for (int i=0; inquantity; i++) { int nnodes = integrate->quantity[i].nnodes; double wts[nnodes]; @@ -2200,7 +2172,7 @@ void interpolatequantities(integrator *integrate, double *bary) { if (MORPHO_ISFLOAT(integrate->qval[i])) { double qval[nnodes]; for (int j=0; jquantity[i].vals[j]); - double val=integrate_sumlistweighted(nnodes, qval, wts); + double val=integrator_sumlistweighted(nnodes, qval, wts); integrate->qval[i]=MORPHO_FLOAT(val); } else if (MORPHO_ISMATRIX(integrate->qval[i])) { objectmatrix *out = MORPHO_GETMATRIX(integrate->qval[i]); @@ -2218,13 +2190,13 @@ void interpolatequantities(integrator *integrate, double *bary) { * -------------------------------- */ /** Evaluates the integrand at specified places */ -bool integrate_evalfn(integrator *integrate, quadraturerule *rule, int imin, int imax, double *rmat, double *vmat, double *x, double *f) { +bool integrator_evalfn(integrator *integrate, quadraturerule *rule, int imin, int imax, double *rmat, double *vmat, double *x, double *f) { double node[integrate->nbary]; for (int i=imin; inodes[integrate->nbary*i], node); - interpolatecoordinates(integrate, node, vmat, x); - interpolatequantities(integrate, node); + integrator_transformtorefelement(integrate, rmat, &rule->nodes[integrate->nbary*i], node); + integrator_interpolatecoordinates(integrate, node, vmat, x); + integrator_interpolatequantities(integrate, node); // Evaluate function if (!(*integrate->integrand) (rule->grade, node, x, integrate->nquantity, integrate->qval, integrate->ref, &f[i])) return false; @@ -2233,7 +2205,7 @@ bool integrate_evalfn(integrator *integrate, quadraturerule *rule, int imin, int } /** Integrates a function over an element specified in work, filling out the integral and error estimate if provided */ -bool quadrature(integrator *integrate, quadraturerule *rule, quadratureworkitem *work) { +bool integrator_quadrature(integrator *integrate, quadraturerule *rule, quadratureworkitem *work) { int n = rule->nnodes; int nmax = rule->nnodes; @@ -2245,17 +2217,17 @@ bool quadrature(integrator *integrate, quadraturerule *rule, quadratureworkitem double rmat[integrate->nbary*integrate->nbary]; // Transform local element coordinates to ref. el. double vmat[integrate->nbary*integrate->dim]; // Transform barycentric coordinates in ref. el. to physical coordinates - prepareinterpolation(integrate, work->elementid, rmat, vmat); + integrator_prepareinterpolation(integrate, work->elementid, rmat, vmat); // Evaluate function at quadrature points - double x[integrate->ndof], f[nmax]; - if (!integrate_evalfn(integrate, rule, 0, rule->nnodes, rmat, vmat, x, f)) return false; + double x[integrate->nbary], f[nmax]; + if (!integrator_evalfn(integrate, rule, 0, rule->nnodes, rmat, vmat, x, f)) return false; double r[np+1]; double eps[np+1]; eps[0]=0.0; // Obtain estimate - r[0]=integrate_sumlistweighted(rule->nnodes, f, rule->weights); + r[0]=integrator_sumlistweighted(rule->nnodes, f, rule->weights); work->lval = work->val = work->weight*r[0]; // Estimate error @@ -2265,9 +2237,9 @@ bool quadrature(integrator *integrate, quadraturerule *rule, quadratureworkitem // Attempt p-refinement if available for (quadraturerule *q=rule->ext; q!=NULL; q=q->ext) { ip++; - if (!integrate_evalfn(integrate, q, nmin, q->nnodes, rmat, vmat, x, f)) return false; + if (!integrator_evalfn(integrate, q, nmin, q->nnodes, rmat, vmat, x, f)) return false; - r[ip]=integrate_sumlistweighted(q->nnodes, f, q->weights); + r[ip]=integrator_sumlistweighted(q->nnodes, f, q->weights); eps[ip]=fabs(r[ip]-r[ip-1]); nmin = q->nnodes; @@ -2280,7 +2252,7 @@ bool quadrature(integrator *integrate, quadraturerule *rule, quadratureworkitem } else if (integrate->errrule) { // Otherwise, use the error rule to obtain the estimate if (rule==integrate->errrule) return true; // We already are using the error rule double temp = work->val; // Retain the lower order estimate - if (!quadrature(integrate, integrate->errrule, work)) return false; + if (!integrator_quadrature(integrate, integrate->errrule, work)) return false; work->lval=temp; work->err=fabs(work->val-temp); // Estimate error from difference of rules } else { @@ -2294,7 +2266,7 @@ bool quadrature(integrator *integrate, quadraturerule *rule, quadratureworkitem * Subdivision * -------------------------------- */ -bool subdivide(integrator *integrate, quadratureworkitem *work, int *nels, quadratureworkitem *newitems) { +bool integrator_subdivide(integrator *integrate, quadratureworkitem *work, int *nels, quadratureworkitem *newitems) { subdivisionrule *rule = integrate->subdivide; // Fetch the element data @@ -2303,12 +2275,12 @@ bool subdivide(integrator *integrate, quadratureworkitem *work, int *nels, quadr // Get ready for interpolation double rmat[integrate->nbary*integrate->nbary]; // Vertex information - prepareinterpolation(integrate, work->elementid, rmat, NULL); + integrator_prepareinterpolation(integrate, work->elementid, rmat, NULL); // Interpolate vertices double lambda[integrate->nbary]; for (int j=0; jnpts; j++) { - transformtorefelement(integrate, rmat, &rule->pts[j*integrate->nbary], lambda); + integrator_transformtorefelement(integrate, rmat, &rule->pts[j*integrate->nbary], lambda); vid[integrate->nbary+j]=integrator_addvertex(integrate, integrate->nbary, lambda); } @@ -2340,7 +2312,7 @@ bool subdivide(integrator *integrate, quadratureworkitem *work, int *nels, quadr /** Laurie's sharper error estimator: BIT 23 (1983), 258-261 The norm of the difference between two rules |A-B| is usually too pessimistic; this attempts to extrapolate a sharper estimate if convergence looks good */ -void sharpenerrorestimate(integrator *integrate, quadratureworkitem *work, int nels, quadratureworkitem *newitems) { +void integrator_sharpenerrorestimate(integrator *integrate, quadratureworkitem *work, int nels, quadratureworkitem *newitems) { double a1=work->val, b1=work->lval, a2=0, b2=0; for (int k=0; kval-=work->val; integrate->err-=work->err; @@ -2531,7 +2503,6 @@ bool integrator_integrate(integrator *integrate, integrandfunction *integrand, i integrate->qval=qval; integrator_initializequantities(integrate, nquantity, quantity); - integrate->ndof = integrate->dim+integrate->nqdof; // Number of degrees of freedom // Create first element, which corresponds to the reference element int vids[integrate->nbary]; @@ -2547,7 +2518,7 @@ bool integrator_integrate(integrator *integrate, integrandfunction *integrand, i quadratureworkitem work; work.weight = 1.0; work.elementid = elid; - quadrature(integrate, integrate->rule, &work); // Perform initial quadrature + integrator_quadrature(integrate, integrate->rule, &work); // Perform initial quadrature integrator_pushworkitem(integrate, &work); integrator_estimate(integrate); // Initial estimate @@ -2563,14 +2534,14 @@ bool integrator_integrate(integrator *integrate, integrandfunction *integrand, i int nels; // Number of elements created quadratureworkitem newitems[integrate->subdivide->nels]; - subdivide(integrate, &work, &nels, newitems); - for (int k=0; krule, &newitems[k]); + integrator_subdivide(integrate, &work, &nels, newitems); + for (int k=0; krule, &newitems[k]); // Error estimate - sharpenerrorestimate(integrate, &work, nels, newitems); + integrator_sharpenerrorestimate(integrate, &work, nels, newitems); // Add new items to heap and update error estimates - update(integrate, &work, nels, newitems); + integrator_update(integrate, &work, nels, newitems); } // Final estimate by Kahan summing heap diff --git a/src/geometry/integrate.h b/src/geometry/integrate.h index 52d3fdc3..e4ccd385 100644 --- a/src/geometry/integrate.h +++ b/src/geometry/integrate.h @@ -126,9 +126,6 @@ typedef struct { int nquantity; /** Number of quantities to interpolate */ quantity *quantity; /** Quantity list */ value *qval; /** Interpolated quantity values */ - int nqdof; /** Number of quantity degrees of freedom */ - - int ndof; /** Number of degrees of freedom */ quadraturerule *rule; /** Quadrature rule to use */ quadraturerule *errrule; /** Additional rule for error estimation */ @@ -136,8 +133,6 @@ typedef struct { bool adapt; /** Enable adaptive integration */ subdivisionrule *subdivide; /** Subdivision rule to use */ - int workp; /** Index of largest item in the work list */ - int freep; /** Index of a free item in the work list */ varray_quadratureworkitem worklist; /** Work list */ varray_double vertexstack; /** Stack of vertices */ varray_int elementstack; /** Stack of elements */ From 1a278326e9a3406639e56d7e0a6a2a7746fa39c1 Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Mon, 28 Aug 2023 21:37:28 -0400 Subject: [PATCH 017/181] CG1 elements --- src/geometry/discretization.c | 43 ++++++++++++++++++++++++++++++- test/discretization/cg2_2d.morpho | 2 -- 2 files changed, 42 insertions(+), 3 deletions(-) diff --git a/src/geometry/discretization.c b/src/geometry/discretization.c index 44d405e3..697e9713 100644 --- a/src/geometry/discretization.c +++ b/src/geometry/discretization.c @@ -125,6 +125,46 @@ discretization cg2_1d = { .eldefn = cg2_1ddefn }; +/* ------------------------------------------------------- + * CG1 element in 2D + * ------------------------------------------------------- */ + +/* 2 + * |\ + * 0-1 // One degree of freedom per vertex + */ + +void cg1_2dinterpolate(double *lambda, double *wts) { + wts[0]=lambda[0]; + wts[1]=lambda[1]; + wts[2]=lambda[2]; +} + +unsigned int cg1_2dshape[] = { 1, 0, 0 }; + +double cg1_2dnodes[] = { 0.0, 0.0, + 1.0, 0.0, + 0.0, 1.0 }; + +eldefninstruction cg1_2deldefn[] = { + QUANTITY(0,0,0), // Fetch quantity on vertex 0 + QUANTITY(0,1,0), // Fetch quantity on vertex 1 + QUANTITY(0,2,0), // Fetch quantity on vertex 2 + ENDDEFN +}; + +discretization cg1_2d = { + .name = "CG1", + .grade = 2, + .shape = cg1_2dshape, + .degree = 1, + .nnodes = 3, + .nsubel = 0, + .nodes = cg1_2dnodes, + .ifn = cg1_2dinterpolate, + .eldefn = cg1_2deldefn +}; + /* ------------------------------------------------------- * CG2 element in 2D * ------------------------------------------------------- */ @@ -137,7 +177,7 @@ discretization cg2_1d = { */ void cg2_2dinterpolate(double *lambda, double *wts) { - wts[0]=lambda[0]*(2*lambda[0]-1); // TODO: FIX THIS + wts[0]=lambda[0]*(2*lambda[0]-1); wts[1]=lambda[1]*(2*lambda[1]-1); wts[2]=lambda[2]*(2*lambda[2]-1); wts[3]=4*lambda[0]*lambda[1]; @@ -182,6 +222,7 @@ discretization cg2_2d = { discretization *discretizations[] = { &cg1_1d, &cg2_1d, + &cg1_2d, &cg2_2d, NULL }; diff --git a/test/discretization/cg2_2d.morpho b/test/discretization/cg2_2d.morpho index 15f174bb..804f229d 100644 --- a/test/discretization/cg2_2d.morpho +++ b/test/discretization/cg2_2d.morpho @@ -19,8 +19,6 @@ print l.layout(f) print f.shape() -print f - print AreaIntegral(fn (x) sqrt(x[0]*x[1]), method={ }).total(m)-4/9 print AreaIntegral(fn (x, q) q.trace()^2, f, method={ }).total(m)-4/3 \ No newline at end of file From 59323cc10a81a751a9822d76650e0fd44a203186 Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Mon, 28 Aug 2023 21:49:19 -0400 Subject: [PATCH 018/181] Locate linear interpolant --- src/geometry/discretization.c | 8 ++++++++ src/geometry/discretization.h | 3 +++ 2 files changed, 11 insertions(+) diff --git a/src/geometry/discretization.c b/src/geometry/discretization.c index 697e9713..811f36e8 100644 --- a/src/geometry/discretization.c +++ b/src/geometry/discretization.c @@ -240,6 +240,14 @@ discretization *discretization_find(char *name, grade g) { return NULL; } +/** Finds a linear discretization for a given grade */ +discretization *discretization_findlinear(grade g) { + for (int i=0; discretizations[i]!=NULL; i++) { + if (discretizations[i]->grade && discretizations[i]->degree==1) return discretizations[i]; + } + return NULL; +} + #define FETCH(instr) (*(instr++)) /** Steps through an element definition, generating subelements and identifying quantities */ diff --git a/src/geometry/discretization.h b/src/geometry/discretization.h index 5aa629d0..87bfe5e6 100644 --- a/src/geometry/discretization.h +++ b/src/geometry/discretization.h @@ -73,6 +73,9 @@ typedef struct { * Discretization interface * ------------------------------------------------------- */ +discretization *discretization_find(char *name, grade g); +discretization *discretization_findlinear(grade g); + bool discretization_doftofieldindx(objectfield *field, discretization *disc, int nv, int *vids, int *dof); bool discretization_layout(objectfield *field, discretization *disc, objectsparse **out); From a720e45d1f69fe7ff326633b1ea5711ff6f6da2f Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Mon, 28 Aug 2023 21:57:56 -0400 Subject: [PATCH 019/181] Minor fixes to quadrature definitions --- src/geometry/integrate.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/geometry/integrate.c b/src/geometry/integrate.c index 06b3e291..3f450cf4 100644 --- a/src/geometry/integrate.c +++ b/src/geometry/integrate.c @@ -1213,6 +1213,7 @@ quadraturerule cubtri19 = { .nnodes = 19, .nodes = cubtripts, .weights = cubtri19wts, + .ext = NULL }; quadraturerule cubtri7 = { @@ -1222,6 +1223,7 @@ quadraturerule cubtri7 = { .nnodes = 7, .nodes = cubtripts, .weights = cubtri7wts, + .ext = NULL }; /* -------------------------------- @@ -1766,7 +1768,7 @@ quadraturerule *quadrules[] = { &keast4, &keast5, &tet5, &tet6, - &grundmann1, &grundmann2, &grundmann3, &grundmann4, + &grundmann1, &grundmann2, &grundmann3, &grundmann4, &grundmann5, NULL }; From c209b6a4f16f6209d05a4b87a5c28b1d68ca7732 Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Tue, 29 Aug 2023 07:32:53 -0400 Subject: [PATCH 020/181] CG3 element definition --- src/geometry/discretization.c | 41 +++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/src/geometry/discretization.c b/src/geometry/discretization.c index 811f36e8..ef8a56d5 100644 --- a/src/geometry/discretization.c +++ b/src/geometry/discretization.c @@ -125,6 +125,47 @@ discretization cg2_1d = { .eldefn = cg2_1ddefn }; +/* ------------------------------------------------------- + * CG3 element in 1D + * ------------------------------------------------------- */ + +/* + * 0 - 2 - 3 - 1 // One degree of freedom per vertex; two on the line + */ + +void cg3_1dinterpolate(double *lambda, double *wts) { + double a = (9.0/2.0)*lambda[0]*lambda[1]; + wts[0]=lambda[0]*(1-a); + wts[1]=lambda[1]*(1-a); + wts[2]=a*(2*lambda[0]-lambda[1]); + wts[3]=a*(2*lambda[1]-lambda[0]); +} + +unsigned int cg3_1dshape[] = { 1, 2 }; + +double cg3_1dnodes[] = { 0.0, 1.0, 1.0/3.0, 2.0/3.0 }; + +eldefninstruction cg3_1ddefn[] = { + LINE(0,0,1), // Identify line subelement with vertex indices (0,1) + QUANTITY(0,0,0), // Fetch quantity on vertex 0 + QUANTITY(0,1,0), // Fetch quantity on vertex 1 + QUANTITY(1,0,0), // Fetch quantity from line subelement + QUANTITY(1,0,1), // Fetch quantity from line subelement + ENDDEFN +}; + +discretization cg3_1d = { + .name = "CG3", + .grade = 1, + .shape = cg3_1dshape, + .degree = 3, + .nnodes = 4, + .nsubel = 1, + .nodes = cg3_1dnodes, + .ifn = cg3_1dinterpolate, + .eldefn = cg3_1ddefn +}; + /* ------------------------------------------------------- * CG1 element in 2D * ------------------------------------------------------- */ From 0208ababeca72b0108df9d53e3a23f9e10abdc8a Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Mon, 4 Sep 2023 09:16:22 -0400 Subject: [PATCH 021/181] Grad operator --- src/geometry/discretization.c | 17 +++++++++++++++++ src/geometry/discretization.h | 1 + test/discretization/cg2_grad.morpho | 23 +++++++++++++++++++++++ 3 files changed, 41 insertions(+) create mode 100644 test/discretization/cg2_grad.morpho diff --git a/src/geometry/discretization.c b/src/geometry/discretization.c index ef8a56d5..7bfd1cd7 100644 --- a/src/geometry/discretization.c +++ b/src/geometry/discretization.c @@ -226,6 +226,17 @@ void cg2_2dinterpolate(double *lambda, double *wts) { wts[5]=4*lambda[2]*lambda[0]; } +void cg2_2dgrad(double *lambda, double *grad) { + double g[] = + { 4*lambda[0]-1, 0, 0, + 0, 4*lambda[1]-1, 0, + 0, 0, 4*lambda[2]-1, + 4*lambda[1], 4*lambda[0], 0, + 0, 4*lambda[2], 4*lambda[1], + 4*lambda[2], 0, 4*lambda[0] }; + memcpy(grad, g, sizeof(g)); +} + unsigned int cg2_2dshape[] = { 1, 1, 0 }; double cg2_2dnodes[] = { 0.0, 0.0, @@ -257,6 +268,7 @@ discretization cg2_2d = { .nsubel = 3, .nodes = cg2_2dnodes, .ifn = cg2_2dinterpolate, + .gfn = cg2_2dgrad, .eldefn = cg2_2deldefn }; @@ -353,6 +365,11 @@ bool discretization_layout(objectfield *field, discretization *disc, objectspars return false; } +void discretization_gradient(discretization *disc, double *lambda) { + + +} + /* ********************************************************************** * FunctionSpace class * ********************************************************************** */ diff --git a/src/geometry/discretization.h b/src/geometry/discretization.h index 87bfe5e6..b12bc07e 100644 --- a/src/geometry/discretization.h +++ b/src/geometry/discretization.h @@ -30,6 +30,7 @@ typedef struct { int nsubel; /** Number of subelements used by */ double *nodes; /** Node positions */ interpolationfn ifn; /** Interpolation function; receives barycentric coordinates as input and returns weights per node */ + interpolationfn gfn; /** Gradient interpolation function */ eldefninstruction *eldefn; /** Element definition */ } discretization; diff --git a/test/discretization/cg2_grad.morpho b/test/discretization/cg2_grad.morpho new file mode 100644 index 00000000..15c88c7d --- /dev/null +++ b/test/discretization/cg2_grad.morpho @@ -0,0 +1,23 @@ + +import meshtools + +var mb = MeshBuilder() +mb.addvertex([0,0]) +mb.addvertex([1,0]) +mb.addvertex([0,1]) +mb.addvertex([1,1]) +mb.addface([0,1,2]) +mb.addface([1,3,2]) +var m = mb.build() +m.addgrade(1) + +var l = FunctionSpace("CG2", grade=2) + +var f = Field(m, fn (x,y) x*y, functionspace=l) + +fn integrand(x, q) { + var g = grad(q) + return g.norm()^2 +} + +print AreaIntegral(integrand, f, method={ }).total(m)-4/3 \ No newline at end of file From bb446104b01df703a7ae3f0cc7d674f7c877e3fd Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Tue, 5 Sep 2023 06:44:48 -0400 Subject: [PATCH 022/181] Update discretization.c --- src/geometry/discretization.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/geometry/discretization.c b/src/geometry/discretization.c index 7bfd1cd7..bd0f7935 100644 --- a/src/geometry/discretization.c +++ b/src/geometry/discretization.c @@ -366,6 +366,25 @@ bool discretization_layout(objectfield *field, discretization *disc, objectspars } void discretization_gradient(discretization *disc, double *lambda) { + int nbary = disc->grade+1; + + // Compute Jacobian for element + double jdata[disc->grade*disc->grade]; + objectmatrix jacobian = MORPHO_STATICMATRIX(jdata, disc->grade, disc->grade); + + // How the barycentric coordinates depend on the local coordinates + double ldata[disc->grade*nbary]; + memset(ldata, 0, sizeof(double)*disc->grade*nbary); + for (int i=0; igrade; i++) { + ldata[i]=-1; + ldata[disc->grade*(i+1)+i]=1; + } + objectmatrix lmat = MORPHO_STATICMATRIX(ldata, disc->grade, nbary); + + // Compute gradients of the basis functions + double gdata[disc->nnodes*nbary]; + (disc->gfn) (lambda, gdata); + objectmatrix gmat = MORPHO_STATICMATRIX(g, nbary, disc->nnodes); } From c99cf9beec78122c902c8190ecc4378f9a059722 Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Thu, 9 Nov 2023 13:57:08 -0500 Subject: [PATCH 023/181] Fix label --- .DS_Store | Bin 6148 -> 6148 bytes src/geometry/discretization.c | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/.DS_Store b/.DS_Store index 43e29107f277fa8ca714c24b860a06851e042203..0c97312a0f2175a8d7f788ec39cbb428b92ac7c7 100644 GIT binary patch delta 48 zcmZoMXffEJ%EGv7@(i{@9;xbTT@zzt9R(vZlgSHNWEguU8?ehW_HMq%qRzaTo#QV* E0An={PXGV_ delta 46 zcmZoMXffEJ$})K-Td9bdnT~>yfn}|ZLbaihg{h8$siom$C3fS@k6BchH?wm5=LY~V CBMi*| diff --git a/src/geometry/discretization.c b/src/geometry/discretization.c index bd0f7935..2cc0bc9d 100644 --- a/src/geometry/discretization.c +++ b/src/geometry/discretization.c @@ -384,7 +384,7 @@ void discretization_gradient(discretization *disc, double *lambda) { // Compute gradients of the basis functions double gdata[disc->nnodes*nbary]; (disc->gfn) (lambda, gdata); - objectmatrix gmat = MORPHO_STATICMATRIX(g, nbary, disc->nnodes); + objectmatrix gmat = MORPHO_STATICMATRIX(gdata, nbary, disc->nnodes); } From 56e31ad6c5eaa7f655db93cbe78c9e6953266c9b Mon Sep 17 00:00:00 2001 From: Tim Atherton <54725421+softmattertheory@users.noreply.github.com> Date: Fri, 26 Jan 2024 20:40:07 -0500 Subject: [PATCH 024/181] Separate parse_expressionlist and parse_arglist --- src/builtin/builtin.h | 7 +++- src/support/parse.c | 41 +++++++++++++++++--- test/types/function_signature.morpho | 11 ++++++ test/types/multiple_dispatch.morpho | 24 ++++++++++++ test/types/type_in_function.morpho | 10 +++++ test/types/type_in_global.morpho | 7 ++++ test/types/type_violation_in_function.morpho | 10 +++++ 7 files changed, 104 insertions(+), 6 deletions(-) create mode 100644 test/types/function_signature.morpho create mode 100644 test/types/multiple_dispatch.morpho create mode 100644 test/types/type_in_function.morpho create mode 100644 test/types/type_in_global.morpho create mode 100644 test/types/type_violation_in_function.morpho diff --git a/src/builtin/builtin.h b/src/builtin/builtin.h index 38cfd458..bc8e14a9 100644 --- a/src/builtin/builtin.h +++ b/src/builtin/builtin.h @@ -21,7 +21,12 @@ /** Flags that describe properties of the built in function */ typedef unsigned int builtinfunctionflags; -#define BUILTIN_FLAGSEMPTY 0 +#define BUILTIN_FLAGSEMPTY 0 + +#define MORPHO_FN_FLAGSEMPTY (0) +#define MORPHO_FN_PUREFN (1<<1) +#define MORPHO_FN_CONSTRUCTOR (1<<2) +#define MORPHO_FN_CONSTRUCTOR (1<<2) /** Type of C function that implements a built in Morpho function */ typedef value (*builtinfunction) (vm *v, int nargs, value *args); diff --git a/src/support/parse.c b/src/support/parse.c index e0d69ea2..c3faa98f 100644 --- a/src/support/parse.c +++ b/src/support/parse.c @@ -358,11 +358,40 @@ bool parse_synchronize(parser *p); * Utility functions for this parser * ------------------------------------------- */ +/** @brief Parses a list of expressions + * @param[in] p the parser + * @param[in] rightdelimiter token type that denotes the end of the arguments list + * @param[out] nel the number of elements + * @details Note that the arguments are output in reverse order, i.e. the + * first argument is deepest in the tree. */ +bool parse_expressionlist(parser *p, tokentype rightdelimiter, unsigned int *nel, void *out) { + syntaxtreeindx prev=SYNTAXTREE_UNCONNECTED, current=SYNTAXTREE_UNCONNECTED; + token start = p->current; + unsigned int n=0; + + if (!parse_checktoken(p, rightdelimiter)) { + do { + PARSE_CHECK(parse_pseudoexpression(p, ¤t)); + PARSE_CHECK(parse_addnode(p, NODE_ARGLIST, MORPHO_NIL, &start, prev, current, ¤t)); + prev = current; + n++; + } while (parse_checktokenadvance(p, TOKEN_COMMA)); + } + + /* Output the number of args */ + if (nel) *nel=n; + + *((syntaxtreeindx *) out)=current; + + return true; +} + /** @brief Parses an argument list * @param[in] p the parser * @param[in] rightdelimiter token type that denotes the end of the arguments list * @param[out] nargs the number of arguments - * @returns indx of the arguments list + * @param[out] out the syntaxtreeindex, updated + * @returns true on success * @details Note that the arguments are output in reverse order, i.e. the * first argument is deepest in the tree. */ bool parse_arglist(parser *p, tokentype rightdelimiter, unsigned int *nargs, void *out) { @@ -817,7 +846,7 @@ bool parse_call(parser *p, void *out) { syntaxtreeindx right; unsigned int nargs; - PARSE_CHECK(parse_arglist(p, TOKEN_RIGHTPAREN, &nargs, &right)); + PARSE_CHECK(parse_expressionlist(p, TOKEN_RIGHTPAREN, &nargs, &right)); PARSE_CHECK(parse_checkrequiredtoken(p, TOKEN_RIGHTPAREN, PARSE_CALLRGHTPARENMISSING)); return parse_addnode(p, NODE_CALL, MORPHO_NIL, &start, left, right, out); @@ -834,7 +863,7 @@ bool parse_index(parser *p, void *out) { syntaxtreeindx right; unsigned int nindx; - PARSE_CHECK(parse_arglist(p, TOKEN_RIGHTSQBRACKET, &nindx, &right)); + PARSE_CHECK(parse_expressionlist(p, TOKEN_RIGHTSQBRACKET, &nindx, &right)); PARSE_CHECK(parse_checkrequiredtoken(p, TOKEN_RIGHTSQBRACKET, PARSE_CALLRGHTPARENMISSING)); return parse_addnode(p, NODE_INDEX, MORPHO_NIL, &start, left, right, (syntaxtreeindx *) out); @@ -844,7 +873,7 @@ bool parse_index(parser *p, void *out) { bool parse_list(parser *p, void *out) { token start = p->previous; syntaxtreeindx right; - PARSE_CHECK(parse_arglist(p, TOKEN_RIGHTSQBRACKET, NULL, &right)); + PARSE_CHECK(parse_expressionlist(p, TOKEN_RIGHTSQBRACKET, NULL, &right)); PARSE_CHECK(parse_checkrequiredtoken(p, TOKEN_RIGHTSQBRACKET, PARSE_MSSNGSQBRC)); return parse_addnode(p, NODE_LIST, MORPHO_NIL, &start, SYNTAXTREE_UNCONNECTED, right, out); @@ -1405,7 +1434,9 @@ bool parse_declaration(parser *p, void *out) { success=parse_classdeclaration(p, out); } else if (parse_checktokenadvance(p, TOKEN_IMPORT)) { success=parse_importdeclaration(p, out); - } else { + } /*else if (parse_checktoken(p, TOKEN_SYMBOL)) { + // Detect Typed variable declarations here + } */else { success=parse_statement(p, out); } diff --git a/test/types/function_signature.morpho b/test/types/function_signature.morpho new file mode 100644 index 00000000..a61999d1 --- /dev/null +++ b/test/types/function_signature.morpho @@ -0,0 +1,11 @@ +// Typed parameters in function signature + +fn f(String x, List y) { + print x +} + +f("Hello", []) +// expect: Hello + +f([], []) +// expect error '' diff --git a/test/types/multiple_dispatch.morpho b/test/types/multiple_dispatch.morpho new file mode 100644 index 00000000..3969d344 --- /dev/null +++ b/test/types/multiple_dispatch.morpho @@ -0,0 +1,24 @@ +// Dipatch on multiple types + +fn f(String x) { + print x +} + +fn f(List x) { + print x.append("Ho") +} + +fn f(Float x) { + print x+1 +} + +f("Hi") +// expect: [ "Hi ] + +f(1.5) +// expect: 2.5 + +var a = [] +f(a) +print a +// expect: [ "Ho" ] \ No newline at end of file diff --git a/test/types/type_in_function.morpho b/test/types/type_in_function.morpho new file mode 100644 index 00000000..07fbc469 --- /dev/null +++ b/test/types/type_in_function.morpho @@ -0,0 +1,10 @@ +// Type definition in function + +fn f() { + String u = "Boo" + + print u +} + +f() +// expect: Boo \ No newline at end of file diff --git a/test/types/type_in_global.morpho b/test/types/type_in_global.morpho new file mode 100644 index 00000000..66c33222 --- /dev/null +++ b/test/types/type_in_global.morpho @@ -0,0 +1,7 @@ +// Check type + +String a = "Foo" + +print a +// expect: Foo + diff --git a/test/types/type_violation_in_function.morpho b/test/types/type_violation_in_function.morpho new file mode 100644 index 00000000..fcf13a30 --- /dev/null +++ b/test/types/type_violation_in_function.morpho @@ -0,0 +1,10 @@ +// Type definition in function + +fn f() { + String u + + u = Matrix(1) +} + +f() +// expect error 'TypeErr' From 270c6bfe0da9268aa4bda43e6c42187897d2bc9c Mon Sep 17 00:00:00 2001 From: Tim Atherton <54725421+softmattertheory@users.noreply.github.com> Date: Fri, 26 Jan 2024 21:08:08 -0500 Subject: [PATCH 025/181] Parse type qualifiers in function parameters --- src/datastructures/syntaxtree.c | 1 + src/datastructures/syntaxtree.h | 1 + src/support/parse.c | 12 +++++++++++- 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/datastructures/syntaxtree.c b/src/datastructures/syntaxtree.c index 63b97e2b..76b6689b 100644 --- a/src/datastructures/syntaxtree.c +++ b/src/datastructures/syntaxtree.c @@ -88,6 +88,7 @@ static char * nodedisplay[] = { "print", // NODE_PRINT ":=", // NODE_DECLARATION + "type", // NODE_TYPE "fn", // NODE_FUNCTION "", // NODE_METHOD "class", // NODE_CLASS diff --git a/src/datastructures/syntaxtree.h b/src/datastructures/syntaxtree.h index 413194e9..fc9df63c 100644 --- a/src/datastructures/syntaxtree.h +++ b/src/datastructures/syntaxtree.h @@ -104,6 +104,7 @@ enum { NODE_PRINT, NODE_DECLARATION, + NODE_TYPE, NODE_FUNCTION, NODE_METHOD, NODE_CLASS, diff --git a/src/support/parse.c b/src/support/parse.c index c3faa98f..f863be5b 100644 --- a/src/support/parse.c +++ b/src/support/parse.c @@ -415,7 +415,16 @@ bool parse_arglist(parser *p, tokentype rightdelimiter, unsigned int *nargs, voi varg = true; vargthis = true; } - PARSE_CHECK(parse_pseudoexpression(p, ¤t)); + if (parse_checktokenadvance(p, TOKEN_SYMBOL)) { + PARSE_CHECK(parse_symbol(p, ¤t)); + + if (parse_checktokenadvance(p, TOKEN_SYMBOL)) { + syntaxtreeindx label; + PARSE_CHECK(parse_symbol(p, &label)); + + PARSE_CHECK(parse_addnode(p, NODE_TYPE, MORPHO_NIL, &start, current, label, ¤t)); + } + } else PARSE_CHECK(parse_pseudoexpression(p, ¤t)); if (vargthis) PARSE_CHECK(parse_addnode(p, NODE_RANGE, MORPHO_NIL, &start, SYNTAXTREE_UNCONNECTED, current, ¤t)); @@ -680,6 +689,7 @@ bool parse_tuple(parser *p, token *start, syntaxtreeindx first, void *out) { prev = current; } while (parse_checktokenadvance(p, TOKEN_COMMA)); } + PARSE_CHECK(parse_checkrequiredtoken(p, TOKEN_RIGHTPAREN, PARSE_MSSNGSQBRC)); return parse_addnode(p, NODE_TUPLE, MORPHO_NIL, start, SYNTAXTREE_UNCONNECTED, current, out); From 7f46ac921a96f2b6752977903463d28ed8787b94 Mon Sep 17 00:00:00 2001 From: Tim Atherton <54725421+softmattertheory@users.noreply.github.com> Date: Sat, 27 Jan 2024 11:42:21 -0500 Subject: [PATCH 026/181] Add reentrant function flag --- src/builtin/builtin.h | 2 +- src/core/compile.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/builtin/builtin.h b/src/builtin/builtin.h index bc8e14a9..add9b066 100644 --- a/src/builtin/builtin.h +++ b/src/builtin/builtin.h @@ -26,7 +26,7 @@ typedef unsigned int builtinfunctionflags; #define MORPHO_FN_FLAGSEMPTY (0) #define MORPHO_FN_PUREFN (1<<1) #define MORPHO_FN_CONSTRUCTOR (1<<2) -#define MORPHO_FN_CONSTRUCTOR (1<<2) +#define MORPHO_FN_REENTRANT (1<<3) /** Type of C function that implements a built in Morpho function */ typedef value (*builtinfunction) (vm *v, int nargs, value *args); diff --git a/src/core/compile.h b/src/core/compile.h index f4f2ed73..29cc1fbc 100644 --- a/src/core/compile.h +++ b/src/core/compile.h @@ -133,6 +133,7 @@ typedef struct { bool iscaptured; /** Whether the register becomes an upvalue */ unsigned int scopedepth; /** Scope depth at which the register was allocated */ value symbol; /** Symbol associated with the register */ + value type; /** Type associated with the register */ } registeralloc; DECLARE_VARRAY(registeralloc, registeralloc) From 60cdd475ac10a4d11382174b0546abd3f395933d Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Wed, 10 Apr 2024 20:49:00 -0400 Subject: [PATCH 027/181] Resurrect discretizations --- src/geometry/functional.c | 9 +++++++-- src/geometry/geometry.h | 2 -- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/geometry/functional.c b/src/geometry/functional.c index e0f08b04..2808f920 100644 --- a/src/geometry/functional.c +++ b/src/geometry/functional.c @@ -4192,7 +4192,7 @@ void integral_preparequantities(integralref *iref, int nv, int *vid, quantity *q } } else { quantities[k].nnodes=nv; - quantities[k].ifn=MORPHO_NIL; + quantities[k].ifn=NULL; quantities[k].vals=MORPHO_MALLOC(sizeof(value)*nv); for (unsigned int i=0; idim, MESH_GRADE_VOLUME, x, iref.nfields, q, &iref, out, &err); + quantity quantities[iref.nfields+1]; + integral_preparequantities(&iref, nv, vid, quantities); + + success=integrate(integral_integrandfn, MORPHO_GETDICTIONARY(iref.method), mesh->dim, MESH_GRADE_VOLUME, x, iref.nfields, quantities, &iref, out, &err); + + integral_clearquantities(iref.nfields, quantities); } else { success=integrate_integrate(integral_integrandfn, mesh->dim, MESH_GRADE_VOLUME, x, iref.nfields, q, &iref, out); } diff --git a/src/geometry/geometry.h b/src/geometry/geometry.h index e187a6b8..076fe0e4 100644 --- a/src/geometry/geometry.h +++ b/src/geometry/geometry.h @@ -16,6 +16,4 @@ void geometry_initialize(void); - - #endif From dd43647a42fbb0dcf9594a74932bbb446ca2a865 Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Wed, 10 Apr 2024 21:29:59 -0400 Subject: [PATCH 028/181] Fix compiler definition table --- src/core/compile.c | 1 + src/support/parse.c | 2 +- test/types/multiple_dispatch.morpho | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/core/compile.c b/src/core/compile.c index dbc51067..539b619b 100644 --- a/src/core/compile.c +++ b/src/core/compile.c @@ -1332,6 +1332,7 @@ compilenoderule noderules[] = { { compiler_print }, // NODE_PRINT { compiler_declaration }, // NODE_DECLARATION + { NODE_NORULE }, // NODE_TYPE { compiler_function }, // NODE_FUNCTION { NODE_NORULE }, // NODE_METHOD { compiler_class }, // NODE_CLASS diff --git a/src/support/parse.c b/src/support/parse.c index f1722486..31c5ee13 100644 --- a/src/support/parse.c +++ b/src/support/parse.c @@ -416,7 +416,7 @@ bool parse_arglist(parser *p, tokentype rightdelimiter, unsigned int *nargs, voi if (parse_checktokenadvance(p, TOKEN_SYMBOL)) { PARSE_CHECK(parse_symbol(p, ¤t)); - if (parse_checktokenadvance(p, TOKEN_SYMBOL)) { + if (parse_checktokenadvance(p, TOKEN_SYMBOL)) { // If two symbols in a row, then the first is the type syntaxtreeindx label; PARSE_CHECK(parse_symbol(p, &label)); diff --git a/test/types/multiple_dispatch.morpho b/test/types/multiple_dispatch.morpho index 3969d344..39b481f0 100644 --- a/test/types/multiple_dispatch.morpho +++ b/test/types/multiple_dispatch.morpho @@ -13,7 +13,7 @@ fn f(Float x) { } f("Hi") -// expect: [ "Hi ] +// expect: Hi f(1.5) // expect: 2.5 From e2fb4bda912ddc0480ce415c7c42c12f8f992865 Mon Sep 17 00:00:00 2001 From: Tim Atherton <54725421+softmattertheory@users.noreply.github.com> Date: Thu, 11 Apr 2024 10:34:56 -0400 Subject: [PATCH 029/181] Lineintegral_hessian --- src/geometry/functional.c | 5 ++++- .../lineintegral/lineintegral_hessian.morpho | 13 +++++++++++++ .../scalarpotential_hessian.morpho | 15 +++++++++++++++ 3 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 test/functionals/lineintegral/lineintegral_hessian.morpho create mode 100644 test/functionals/scalarpotential/scalarpotential_hessian.morpho diff --git a/src/geometry/functional.c b/src/geometry/functional.c index 74bd30ab..9eddda18 100644 --- a/src/geometry/functional.c +++ b/src/geometry/functional.c @@ -4243,6 +4243,8 @@ FUNCTIONAL_METHOD(LineIntegral, total, MESH_GRADE_LINE, integralref, integral_pr FUNCTIONAL_METHOD(LineIntegral, gradient, MESH_GRADE_LINE, integralref, integral_prepareref, functional_mapnumericalgradient, lineintegral_integrand, NULL, GRADSQ_ARGS, SYMMETRY_NONE); +FUNCTIONAL_METHOD(LineIntegral, hessian, MESH_GRADE_LINE, integralref, integral_prepareref, functional_mapnumericalhessian, lineintegral_integrand, NULL, GRADSQ_ARGS, SYMMETRY_NONE) + /** Initialize a LineIntegral object */ value LineIntegral_init(vm *v, int nargs, value *args) { objectinstance *self = MORPHO_GETINSTANCE(MORPHO_SELF(args)); @@ -4321,7 +4323,8 @@ MORPHO_METHOD(MORPHO_INITIALIZER_METHOD, LineIntegral_init, BUILTIN_FLAGSEMPTY), MORPHO_METHOD(FUNCTIONAL_INTEGRAND_METHOD, LineIntegral_integrand, BUILTIN_FLAGSEMPTY), MORPHO_METHOD(FUNCTIONAL_TOTAL_METHOD, LineIntegral_total, BUILTIN_FLAGSEMPTY), MORPHO_METHOD(FUNCTIONAL_GRADIENT_METHOD, LineIntegral_gradient, BUILTIN_FLAGSEMPTY), -MORPHO_METHOD(FUNCTIONAL_FIELDGRADIENT_METHOD, LineIntegral_fieldgradient, BUILTIN_FLAGSEMPTY) +MORPHO_METHOD(FUNCTIONAL_FIELDGRADIENT_METHOD, LineIntegral_fieldgradient, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD(FUNCTIONAL_HESSIAN_METHOD, LineIntegral_hessian, BUILTIN_FLAGSEMPTY) MORPHO_ENDCLASS /* ---------------------------------------------- diff --git a/test/functionals/lineintegral/lineintegral_hessian.morpho b/test/functionals/lineintegral/lineintegral_hessian.morpho new file mode 100644 index 00000000..e07b4a5e --- /dev/null +++ b/test/functionals/lineintegral/lineintegral_hessian.morpho @@ -0,0 +1,13 @@ + +import meshtools +import "../numericalderivatives.morpho" + +var m = LineMesh(fn (t) [t,0,0], 0..1:0.5) + +// A line integral with only spatial dependence +var lc = LineIntegral(fn (x) (x[0]*(1-x[0]))^2) + +var h = lc.hessian(m) +var h2 = numericalhessian(lc, m) + +print (Matrix(h) - h2).norm() < 1e-5 // expect: true diff --git a/test/functionals/scalarpotential/scalarpotential_hessian.morpho b/test/functionals/scalarpotential/scalarpotential_hessian.morpho new file mode 100644 index 00000000..c411bb43 --- /dev/null +++ b/test/functionals/scalarpotential/scalarpotential_hessian.morpho @@ -0,0 +1,15 @@ + +import "../numericalderivatives.morpho" + +var m = Mesh("tetrahedron.mesh") + +fn phi(x,y,z) { + return x^2+y^2+z^2 +} + +var a = ScalarPotential(phi) + +var h = a.hessian() +var h2 = numericalhessian(a, m) + +//print (Matrix(h) - h2).norm() < 1e-5 // expect: true From dc4f2ccc1a03a2ba256132ded19c9e1e8dd949a5 Mon Sep 17 00:00:00 2001 From: Tim Atherton <54725421+softmattertheory@users.noreply.github.com> Date: Thu, 11 Apr 2024 12:43:57 -0400 Subject: [PATCH 030/181] Modernize scalar potential and add hessian --- src/geometry/functional.c | 57 ++++++------------- .../scalarpotential_hessian.morpho | 6 +- 2 files changed, 20 insertions(+), 43 deletions(-) diff --git a/src/geometry/functional.c b/src/geometry/functional.c index 9eddda18..8717bf3b 100644 --- a/src/geometry/functional.c +++ b/src/geometry/functional.c @@ -1935,10 +1935,20 @@ MORPHO_ENDCLASS static value scalarpotential_functionproperty; static value scalarpotential_gradfunctionproperty; +typedef struct { + value fn; +} scalarpotentialref; + +bool scalarpotential_prepareref(objectinstance *self, objectmesh *mesh, grade g, objectselection *sel, scalarpotentialref *ref) { + ref->fn=MORPHO_NIL; + return (objectinstance_getpropertyinterned(self, scalarpotential_functionproperty, &ref->fn) && + MORPHO_ISCALLABLE(ref->fn)); +} + /** Evaluate the scalar potential */ bool scalarpotential_integrand(vm *v, objectmesh *mesh, elementid id, int nv, int *vid, void *ref, double *out) { double *x; - value fn = *(value *) ref; + value fn = ((scalarpotentialref *) ref)->fn; value args[mesh->dim]; value ret; @@ -1975,7 +1985,6 @@ bool scalarpotential_gradient(vm *v, objectmesh *mesh, elementid id, int nv, int return false; } - /** Initialize a scalar potential */ value ScalarPotential_init(vm *v, int nargs, value *args) { objectinstance_setproperty(MORPHO_GETINSTANCE(MORPHO_SELF(args)), functional_gradeproperty, MORPHO_INTEGER(MESH_GRADE_VERTEX)); @@ -1996,25 +2005,11 @@ value ScalarPotential_init(vm *v, int nargs, value *args) { return MORPHO_NIL; } -/** Integrand function */ -value ScalarPotential_integrand(vm *v, int nargs, value *args) { - functional_mapinfo info; - value out=MORPHO_NIL; +FUNCTIONAL_METHOD(ScalarPotential, integrand, MESH_GRADE_VERTEX, scalarpotentialref, scalarpotential_prepareref, functional_mapintegrand, scalarpotential_integrand, NULL, SCALARPOTENTIAL_FNCLLBL, SYMMETRY_NONE) - if (functional_validateargs(v, nargs, args, &info)) { - value fn; - if (objectinstance_getpropertyinterned(MORPHO_GETINSTANCE(MORPHO_SELF(args)), scalarpotential_functionproperty, &fn)) { - info.g = MESH_GRADE_VERTEX; - info.integrand = scalarpotential_integrand; - info.ref = &fn; - if (MORPHO_ISCALLABLE(fn)) { - functional_mapintegrand(v, &info, &out); - } else morpho_runtimeerror(v, SCALARPOTENTIAL_FNCLLBL); - } else morpho_runtimeerror(v, VM_OBJECTLACKSPROPERTY, SCALARPOTENTIAL_FUNCTION_PROPERTY); - } - if (!MORPHO_ISNIL(out)) morpho_bindobjects(v, 1, &out); - return out; -} +FUNCTIONAL_METHOD(ScalarPotential, total, MESH_GRADE_VERTEX, scalarpotentialref, scalarpotential_prepareref, functional_sumintegrand, scalarpotential_integrand, NULL, SCALARPOTENTIAL_FNCLLBL, SYMMETRY_NONE) + +FUNCTIONAL_METHOD(ScalarPotential, hessian, MESH_GRADE_VERTEX, scalarpotentialref, scalarpotential_prepareref, functional_mapnumericalhessian, scalarpotential_integrand, NULL, SCALARPOTENTIAL_FNCLLBL, SYMMETRY_NONE) /** Evaluate a gradient */ value ScalarPotential_gradient(vm *v, int nargs, value *args) { @@ -2051,30 +2046,12 @@ value ScalarPotential_gradient(vm *v, int nargs, value *args) { return out; } -/** Total function */ -value ScalarPotential_total(vm *v, int nargs, value *args) { - functional_mapinfo info; - value out=MORPHO_NIL; - - if (functional_validateargs(v, nargs, args, &info)) { - value fn; - if (objectinstance_getpropertyinterned(MORPHO_GETINSTANCE(MORPHO_SELF(args)), scalarpotential_functionproperty, &fn)) { - info.g = MESH_GRADE_VERTEX; - info.integrand = scalarpotential_integrand; - info.ref = &fn; - if (MORPHO_ISCALLABLE(fn)) { - functional_sumintegrand(v, &info, &out); - } else morpho_runtimeerror(v, SCALARPOTENTIAL_FNCLLBL); - } else morpho_runtimeerror(v, VM_OBJECTLACKSPROPERTY, SCALARPOTENTIAL_FUNCTION_PROPERTY); - } - return out; -} - MORPHO_BEGINCLASS(ScalarPotential) MORPHO_METHOD(MORPHO_INITIALIZER_METHOD, ScalarPotential_init, BUILTIN_FLAGSEMPTY), MORPHO_METHOD(FUNCTIONAL_INTEGRAND_METHOD, ScalarPotential_integrand, BUILTIN_FLAGSEMPTY), MORPHO_METHOD(FUNCTIONAL_GRADIENT_METHOD, ScalarPotential_gradient, BUILTIN_FLAGSEMPTY), -MORPHO_METHOD(FUNCTIONAL_TOTAL_METHOD, ScalarPotential_total, BUILTIN_FLAGSEMPTY) +MORPHO_METHOD(FUNCTIONAL_TOTAL_METHOD, ScalarPotential_total, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD(FUNCTIONAL_HESSIAN_METHOD, ScalarPotential_hessian, BUILTIN_FLAGSEMPTY) MORPHO_ENDCLASS /* ---------------------------------------------- diff --git a/test/functionals/scalarpotential/scalarpotential_hessian.morpho b/test/functionals/scalarpotential/scalarpotential_hessian.morpho index c411bb43..8899dc62 100644 --- a/test/functionals/scalarpotential/scalarpotential_hessian.morpho +++ b/test/functionals/scalarpotential/scalarpotential_hessian.morpho @@ -4,12 +4,12 @@ import "../numericalderivatives.morpho" var m = Mesh("tetrahedron.mesh") fn phi(x,y,z) { - return x^2+y^2+z^2 + return x^2+y^2+0.5*z^2 + x*y } var a = ScalarPotential(phi) -var h = a.hessian() +var h = a.hessian(m) var h2 = numericalhessian(a, m) -//print (Matrix(h) - h2).norm() < 1e-5 // expect: true +print (Matrix(h) - h2).norm() < 1e-6 // expect: true From 572174c6b7a5b48b4ff79dc7f60932d3c5966407 Mon Sep 17 00:00:00 2001 From: Tim Atherton <54725421+softmattertheory@users.noreply.github.com> Date: Thu, 11 Apr 2024 17:10:01 -0400 Subject: [PATCH 031/181] Parallelized hessian calculations --- src/geometry/functional.c | 227 ++++++++++++++---- .../linecurvaturesq/hessian.morpho | 21 ++ 2 files changed, 205 insertions(+), 43 deletions(-) create mode 100644 test/functionals/linecurvaturesq/hessian.morpho diff --git a/src/geometry/functional.c b/src/geometry/functional.c index 8717bf3b..bfdc0634 100644 --- a/src/geometry/functional.c +++ b/src/geometry/functional.c @@ -28,9 +28,13 @@ value functional_fieldproperty; * Utility functions * ********************************************************************** */ +double fddelta1, // = pow(MORPHO_EPS, 1.0/3.0), + fddelta2; // = pow(MORPHO_EPS, 1.0/4.0); + // Estimates the correct stepsize for cell centered finite differences -double functional_fdstepsize(double x) { - double h = pow(MORPHO_EPS, 1.0/3.0); +double functional_fdstepsize(double x, int order) { + double h = fddelta1; + if (order==2) h = fddelta2; // h should be multiplied by an estimate of the lengthscale over which f changes, // | f / f''' | ^ (1/3) @@ -473,7 +477,7 @@ bool functional_numericalgradient(vm *v, objectmesh *mesh, elementid i, int nv, matrix_getelement(mesh->vert, k, vid[j], &x0); - eps=functional_fdstepsize(x0); + eps=functional_fdstepsize(x0, 1); matrix_setelement(mesh->vert, k, vid[j], x0+eps); if (!(*integrand) (v, mesh, i, nv, vid, ref, &fp)) return false; @@ -487,36 +491,6 @@ bool functional_numericalgradient(vm *v, objectmesh *mesh, elementid i, int nv, return true; } -/* Calculates a numerical gradient for a remote vertex */ -/*static bool functional_numericalremotegradientold(vm *v, functional_mapinfo *info, objectsparse *conn, elementid remoteid, elementid i, int nv, int *vid, objectmatrix *frc) { - objectmesh *mesh = info->mesh; - double f0,fp,fm,x0,eps=1e-10; // Should use sqrt(machineeps)*(1+|x|) here - - int *rvid=(info->g==0 ? &remoteid : NULL), - rnv=(info->g==0 ? 1 : 0); // The vertex indices - - if (conn) sparseccs_getrowindices(&conn->ccs, remoteid, &rnv, &rvid); - - // Loop over vertices in element - for (unsigned int j=0; jdim; k++) { - matrix_getelement(frc, k, vid[j], &f0); - - matrix_getelement(mesh->vert, k, vid[j], &x0); - matrix_setelement(mesh->vert, k, vid[j], x0+eps); - if (!(*info->integrand) (v, mesh, remoteid, rnv, rvid, info->ref, &fp)) return false; - matrix_setelement(mesh->vert, k, vid[j], x0-eps); - if (!(*info->integrand) (v, mesh, remoteid, rnv, rvid, info->ref, &fm)) return false; - matrix_setelement(mesh->vert, k, vid[j], x0); - - matrix_setelement(frc, k, vid[j], f0+(fp-fm)/(2*eps)); - } - } - - return true; -}*/ - static bool functional_numericalremotegradient(vm *v, functional_mapinfo *info, objectsparse *conn, elementid remoteid, elementid i, int nv, int *vid, objectmatrix *frc) { objectmesh *mesh = info->mesh; double f0,fp,fm,x0,eps=1e-6; @@ -526,7 +500,7 @@ static bool functional_numericalremotegradient(vm *v, functional_mapinfo *info, matrix_getelement(frc, k, remoteid, &f0); matrix_getelement(mesh->vert, k, remoteid, &x0); - eps=functional_fdstepsize(x0); + eps=functional_fdstepsize(x0, 1); matrix_setelement(mesh->vert, k, remoteid, x0+eps); if (!(*info->integrand) (v, mesh, i, nv, vid, info->ref, &fp)) return false; @@ -768,7 +742,7 @@ bool functional_mapnumericalfieldgradientX(vm *v, functional_mapinfo *info, valu int k=field->offset[g]+id*field->psize*field->dof[g]+j; double fld=field->data.elements[k]; - eps=functional_fdstepsize(fld); + eps=functional_fdstepsize(fld, 1); field->data.elements[k]+=eps; @@ -801,7 +775,7 @@ bool functional_mapnumericalfieldgradientX(vm *v, functional_mapinfo *info, valu * @param[in] info - map info * @param[out] out - a matrix of integrand values * @returns true on success, false otherwise. Error reporting through VM. */ -bool functional_mapnumericalhessian(vm *v, functional_mapinfo *info, value *out) { +bool functional_mapnumericalhessianX(vm *v, functional_mapinfo *info, value *out) { objectmesh *mesh = info->mesh; objectselection *sel = info->sel; if (sel) UNREACHABLE("Selections not implemented in hessian"); @@ -980,8 +954,11 @@ bool functional_mapfn_elements(void *arg) { /** Dispatches tasks to threadpool */ bool functional_parallelmap(int ntasks, functional_task *tasks) { + int nthreads = morpho_threadnumber(); + if (!nthreads) nthreads=1; + if (!functional_poolinitialized) { - functional_poolinitialized=threadpool_init(&functional_pool, morpho_threadnumber()); + functional_poolinitialized=threadpool_init(&functional_pool, nthreads); if (!functional_poolinitialized) return false; } @@ -1274,7 +1251,7 @@ bool functional_numericalgrad(vm *v, objectmesh *mesh, elementid eid, elementid matrix_getelement(mesh->vert, k, i, &x0); - eps=functional_fdstepsize(x0); + eps=functional_fdstepsize(x0, 1); matrix_setelement(mesh->vert, k, i, x0+eps); if (!(*integrand) (v, mesh, eid, nv, vid, ref, &fp)) return false; matrix_setelement(mesh->vert, k, i, x0-eps); @@ -1436,14 +1413,12 @@ bool functional_numericalfieldgrad(vm *v, objectmesh *mesh, elementid eid, objec int k=field->offset[g]+i*field->psize*field->dof[g]+j; double f0=field->data.elements[k]; - eps=functional_fdstepsize(f0); + eps=functional_fdstepsize(f0, 1); field->data.elements[k]+=eps; - if (!(*integrand) (v, mesh, eid, nv, vid, ref, &fr)) return false; field->data.elements[k]=f0-eps; - if (!(*integrand) (v, mesh, eid, nv, vid, ref, &fl)) return false; field->data.elements[k]=f0; @@ -1542,6 +1517,167 @@ bool functional_mapnumericalfieldgradient(vm *v, functional_mapinfo *info, value return success; } +/* ---------------------------- + * Map numerical hessians + * ---------------------------- */ + +/** Adds a value to an element of a sparse matrix */ +bool functional_sparseaccumulate(objectsparse *A, int i, int j, double val) { + double f0 = 0.0; + value h0; + if (sparsedok_get(&A->dok, i, j, &h0)) { + if (!morpho_valuetofloat(h0, &f0)) return false; + } + + sparsedok_insert(&A->dok, i, j, MORPHO_FLOAT(f0+val)); +} + +/** Computes the contribution to the hessian of element eid with respect to vertices i and j */ +bool functional_numericalhess(vm *v, objectmesh *mesh, elementid eid, elementid i, elementid j, int nv, int *vid, functional_integrand *integrand, void *ref, objectsparse *hess) { + double x0,y0,epsx=1e-4,epsy=1e-4; + + for (unsigned int k=0; kdim; k++) { // Loop over coordinates in vertex i + matrix_getelement(mesh->vert, k, i, &x0); + epsx=functional_fdstepsize(x0, 2); + + if (i==j) { // Use a special formula for diagonal elements + double fc, fr, fl; + if (!(*integrand) (v, mesh, eid, nv, vid, ref, &fc)) return false; + + matrix_setelement(mesh->vert, k, i, x0+epsx); + if (!(*integrand) (v, mesh, eid, nv, vid, ref, &fr)) return false; + + matrix_setelement(mesh->vert, k, i, x0-epsx); + if (!(*integrand) (v, mesh, eid, nv, vid, ref, &fl)) return false; + + matrix_setelement(mesh->vert, k, i, x0); // Restore vertex to original position + + functional_sparseaccumulate(hess, i*mesh->dim+k, i*mesh->dim+k, (fr + fl - 2*fc)/(epsx*epsx)); + } + + // Loop over coordinates in vertex j + for (unsigned int l=(i==j? k+1 : k); // Detect whether we're in an off diagonal block + ldim; l++) { + double fll,frr,flr,frl; + + matrix_getelement(mesh->vert, l, j, &y0); + epsy=functional_fdstepsize(y0, 2); + + matrix_setelement(mesh->vert, k, i, x0+epsx); + matrix_setelement(mesh->vert, l, j, y0+epsy); + if (!(*integrand) (v, mesh, eid, nv, vid, ref, &frr)) return false; + + matrix_setelement(mesh->vert, l, j, y0-epsy); + if (!(*integrand) (v, mesh, eid, nv, vid, ref, &frl)) return false; + + matrix_setelement(mesh->vert, k, i, x0-epsx); + if (!(*integrand) (v, mesh, eid, nv, vid, ref, &fll)) return false; + + matrix_setelement(mesh->vert, l, j, y0+epsy); + if (!(*integrand) (v, mesh, eid, nv, vid, ref, &flr)) return false; + + matrix_setelement(mesh->vert, k, i, x0); // Restore vertices to original position + matrix_setelement(mesh->vert, l, j, y0); + + functional_sparseaccumulate(hess, i*mesh->dim+k, j*mesh->dim+l, (frr + fll - flr - frl)/(4*epsx*epsy)); + functional_sparseaccumulate(hess, j*mesh->dim+l, i*mesh->dim+k, (frr + fll - flr - frl)/(4*epsx*epsy)); + } + } + + return true; +} + +/** Computes the gradient of element id with respect to its constituent vertices and any dependencies */ +bool functional_numericalhessianmapfn(vm *v, objectmesh *mesh, elementid id, int nv, int *vid, void *ref, void *out) { + bool success=true; + functional_mapinfo *info=(functional_mapinfo *) ref; + + for (int i=0; iintegrand, info->ref, out)) return false; + } + } + + // Now handle dependencies + if (info->dependencies) { + varray_elementid dependencies; + varray_elementidinit(&dependencies); + + // Get list of vertices this element depends on + if ((info->dependencies) (info, id, &dependencies)) { + for (int j=0; jintegrand, info->ref, out)) success=false; + } + } + + varray_elementidclear(&dependencies); + } + + return success; +} + +/** Compute the hessian numerically */ +bool functional_mapnumericalhessian(vm *v, functional_mapinfo *info, value *out) { + int success=false; + int ntask=morpho_threadnumber(); + if (ntask==0) ntask = 1; + functional_task task[ntask]; + + varray_elementid imageids; + varray_elementidinit(&imageids); + + objectsparse *new[ntask]; // Create an output matrix for each thread + for (int i=0; imesh->dim*mesh_nvertices(info->mesh); + + // Create one output matrix per thread + new[i]=object_newsparse(&N, &N); + if (!new[i]) { morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED); goto functional_maphessian_cleanup; } + + // Clone the vertex matrix for each thread + meshclones[i]=*info->mesh; + meshclones[i].vert=object_clonematrix(info->mesh->vert); + task[i].mesh=&meshclones[i]; + + task[i].ref=(void *) info; // Use this to pass the info structure + task[i].mapfn=functional_numericalhessianmapfn; + task[i].result=(void *) new[i]; + } + + functional_parallelmap(ntask, task); + + /* Then add up all the matrices */ + sparse_checkformat(new[0], SPARSE_CCS, true, true); + for (int i=1; isym==SYMMETRY_ADD) functional_symmetrysumforces(info->mesh, new[0]); + + // ...and return the result + *out = MORPHO_OBJECT(new[0]); + +functional_maphessian_cleanup: + // Free the temporary copies of the vertex matrices + for (int i=0; i Date: Thu, 11 Apr 2024 20:27:30 -0400 Subject: [PATCH 032/181] Correct bounds for hessian --- src/geometry/functional.c | 6 ++++-- test/functionals/length/length_hessian.morpho | 4 ++++ test/functionals/lineintegral/lineintegral_hessian.morpho | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/geometry/functional.c b/src/geometry/functional.c index bfdc0634..381e16ea 100644 --- a/src/geometry/functional.c +++ b/src/geometry/functional.c @@ -1530,6 +1530,7 @@ bool functional_sparseaccumulate(objectsparse *A, int i, int j, double val) { } sparsedok_insert(&A->dok, i, j, MORPHO_FLOAT(f0+val)); + return true; } /** Computes the contribution to the hessian of element eid with respect to vertices i and j */ @@ -1556,8 +1557,9 @@ bool functional_numericalhess(vm *v, objectmesh *mesh, elementid eid, elementid } // Loop over coordinates in vertex j - for (unsigned int l=(i==j? k+1 : k); // Detect whether we're in an off diagonal block + for (unsigned int l=0; //(i==j? k+1 : k); // Detect whether we're in an off diagonal block ldim; l++) { + if (i==j && k==l) continue; double fll,frr,flr,frl; matrix_getelement(mesh->vert, l, j, &y0); @@ -1580,7 +1582,7 @@ bool functional_numericalhess(vm *v, objectmesh *mesh, elementid eid, elementid matrix_setelement(mesh->vert, l, j, y0); functional_sparseaccumulate(hess, i*mesh->dim+k, j*mesh->dim+l, (frr + fll - flr - frl)/(4*epsx*epsy)); - functional_sparseaccumulate(hess, j*mesh->dim+l, i*mesh->dim+k, (frr + fll - flr - frl)/(4*epsx*epsy)); + //functional_sparseaccumulate(hess, j*mesh->dim+l, i*mesh->dim+k, (frr + fll - flr - frl)/(4*epsx*epsy)); } } diff --git a/test/functionals/length/length_hessian.morpho b/test/functionals/length/length_hessian.morpho index c78a89bf..b9fbe887 100644 --- a/test/functionals/length/length_hessian.morpho +++ b/test/functionals/length/length_hessian.morpho @@ -11,5 +11,9 @@ print abs(a.total(m) - 4*sqrt(2)) < 1e-10 print (a.gradient(m)-numericalgradient(a, m)).norm()<1e-6 // expect: true +//print Matrix(a.hessian(m)) +//print "" +//print numericalhessian(a, m, eps=1e-4) + print (Matrix(a.hessian(m))-numericalhessian(a, m, eps=1e-4)).norm() < 1e-2 // expect: true diff --git a/test/functionals/lineintegral/lineintegral_hessian.morpho b/test/functionals/lineintegral/lineintegral_hessian.morpho index e07b4a5e..407ec6e4 100644 --- a/test/functionals/lineintegral/lineintegral_hessian.morpho +++ b/test/functionals/lineintegral/lineintegral_hessian.morpho @@ -2,7 +2,7 @@ import meshtools import "../numericalderivatives.morpho" -var m = LineMesh(fn (t) [t,0,0], 0..1:0.5) +var m = LineMesh(fn (t) [t,0,0], 0..1:1) // A line integral with only spatial dependence var lc = LineIntegral(fn (x) (x[0]*(1-x[0]))^2) From 3e3e12b38114539bcfd768e1735e60ae99b82dd7 Mon Sep 17 00:00:00 2001 From: Tim Atherton <54725421+softmattertheory@users.noreply.github.com> Date: Fri, 12 Apr 2024 09:20:25 -0400 Subject: [PATCH 033/181] Correct LineCurvatureSq hessian --- src/geometry/functional.c | 12 ++++++++--- .../linecurvaturesq/gradient.morpho | 21 +++---------------- .../linecurvaturesq/hessian.morpho | 11 ++++++---- 3 files changed, 19 insertions(+), 25 deletions(-) diff --git a/src/geometry/functional.c b/src/geometry/functional.c index 381e16ea..8eab3909 100644 --- a/src/geometry/functional.c +++ b/src/geometry/functional.c @@ -1607,9 +1607,11 @@ bool functional_numericalhessianmapfn(vm *v, objectmesh *mesh, elementid id, int // Get list of vertices this element depends on if ((info->dependencies) (info, id, &dependencies)) { - for (int j=0; jintegrand, info->ref, out)) success=false; + for (int i=0; iintegrand, info->ref, out)) success=false; + } } } @@ -2638,6 +2640,8 @@ bool equielement_dependencies(functional_mapinfo *info, elementid id, varray_ele varray_elementid nbrs; varray_elementidinit(&nbrs); + // varray_elementidwrite(out, id); // EquiElement is a vertex element, and hence depends on itself + if (mesh_findneighbors(mesh, MESH_GRADE_VERTEX, id, eref->grade, &nbrs)>0) { for (unsigned int i=0; i0) { for (unsigned int i=0; i Date: Fri, 12 Apr 2024 10:30:05 -0400 Subject: [PATCH 034/181] Update functional.c --- src/geometry/functional.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/geometry/functional.c b/src/geometry/functional.c index 8eab3909..2209628f 100644 --- a/src/geometry/functional.c +++ b/src/geometry/functional.c @@ -1594,6 +1594,8 @@ bool functional_numericalhessianmapfn(vm *v, objectmesh *mesh, elementid id, int bool success=true; functional_mapinfo *info=(functional_mapinfo *) ref; + // TODO: Exploit symmetry of hessian to reduce work + for (int i=0; iintegrand, info->ref, out)) return false; From 7f7f9a57503e6d80cadebc97b8cac2bf5cfdc9b4 Mon Sep 17 00:00:00 2001 From: Tim Atherton <54725421+softmattertheory@users.noreply.github.com> Date: Fri, 12 Apr 2024 10:44:27 -0400 Subject: [PATCH 035/181] LineTorsionSq hessian --- src/geometry/functional.c | 4 +++- .../functionals/linetorsionsq/gradient.morpho | 23 ++++--------------- test/functionals/linetorsionsq/hessian.morpho | 18 +++++++++++++++ 3 files changed, 25 insertions(+), 20 deletions(-) create mode 100644 test/functionals/linetorsionsq/hessian.morpho diff --git a/src/geometry/functional.c b/src/geometry/functional.c index 2209628f..b782b16f 100644 --- a/src/geometry/functional.c +++ b/src/geometry/functional.c @@ -3009,12 +3009,14 @@ FUNCTIONAL_INIT(LineTorsionSq, MESH_GRADE_LINE) FUNCTIONAL_METHOD(LineTorsionSq, integrand, MESH_GRADE_LINE, curvatureref, curvature_prepareref, functional_mapintegrand, linetorsionsq_integrand, NULL, FUNCTIONAL_ARGS, SYMMETRY_NONE) FUNCTIONAL_METHOD(LineTorsionSq, total, MESH_GRADE_LINE, curvatureref, curvature_prepareref, functional_sumintegrand, linetorsionsq_integrand, NULL, FUNCTIONAL_ARGS, SYMMETRY_NONE) FUNCTIONAL_METHOD(LineTorsionSq, gradient, MESH_GRADE_LINE, curvatureref, curvature_prepareref, functional_mapnumericalgradient, linetorsionsq_integrand, linetorsionsq_dependencies, FUNCTIONAL_ARGS, SYMMETRY_ADD) +FUNCTIONAL_METHOD(LineTorsionSq, hessian, MESH_GRADE_LINE, curvatureref, curvature_prepareref, functional_mapnumericalhessian, linetorsionsq_integrand, linetorsionsq_dependencies, FUNCTIONAL_ARGS, SYMMETRY_ADD) MORPHO_BEGINCLASS(LineTorsionSq) MORPHO_METHOD(MORPHO_INITIALIZER_METHOD, LineTorsionSq_init, BUILTIN_FLAGSEMPTY), MORPHO_METHOD(FUNCTIONAL_INTEGRAND_METHOD, LineTorsionSq_integrand, BUILTIN_FLAGSEMPTY), MORPHO_METHOD(FUNCTIONAL_GRADIENT_METHOD, LineTorsionSq_gradient, BUILTIN_FLAGSEMPTY), -MORPHO_METHOD(FUNCTIONAL_TOTAL_METHOD, LineTorsionSq_total, BUILTIN_FLAGSEMPTY) +MORPHO_METHOD(FUNCTIONAL_TOTAL_METHOD, LineTorsionSq_total, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD(FUNCTIONAL_HESSIAN_METHOD, LineTorsionSq_hessian, BUILTIN_FLAGSEMPTY) MORPHO_ENDCLASS /* ---------------------------------------------- diff --git a/test/functionals/linetorsionsq/gradient.morpho b/test/functionals/linetorsionsq/gradient.morpho index 043506da..bac61b99 100644 --- a/test/functionals/linetorsionsq/gradient.morpho +++ b/test/functionals/linetorsionsq/gradient.morpho @@ -2,6 +2,8 @@ import constants import meshtools +import "../numericalderivatives.morpho" + var np=20 var R=1 @@ -11,23 +13,6 @@ var m = LineMesh(fn (t) [R*cos(t), R*sin(t), t], 0..2*Pi:2*Pi/np, closed=false) var lc = LineTorsionSq() var grad = lc.gradient(m) +var ngrad = numericalgradient(lc, m) -var dim = grad.dimensions() -var ngrad = Matrix(dim[0], dim[1]) - -// Manually calculate the gradient -var vert = m.vertexmatrix() -var eps = 1e-8 - -for (i in 0...dim[0]) { - for (j in 0...dim[1]) { - var v = vert[i, j] - vert[i, j] = v + eps - var fp = lc.total(m) - vert[i, j] = v - eps - var fm = lc.total(m) - ngrad[i,j] = (fp-fm)/(2*eps) - } -} - -print (grad-ngrad).norm()/grad.count() < 1e-4 // expect: true +print (grad-ngrad).norm()/grad.count() < 1e-6 // expect: true diff --git a/test/functionals/linetorsionsq/hessian.morpho b/test/functionals/linetorsionsq/hessian.morpho new file mode 100644 index 00000000..c420afd4 --- /dev/null +++ b/test/functionals/linetorsionsq/hessian.morpho @@ -0,0 +1,18 @@ +// Line torsion Sq +import constants +import meshtools + +import "../numericalderivatives.morpho" + +var np=4 +var R=1 + +var m = LineMesh(fn (t) [R*cos(t), R*sin(t), t], 0..2*Pi:2*Pi/np, closed=false) + +// Create the manifold +var lc = LineTorsionSq() + +var h = lc.hessian(m) +var h2 = numericalhessian(lc, m) + +print (Matrix(h) - h2).norm()/h2.count() < 1e-5 // expect: true \ No newline at end of file From 92be96f14296476769f097c60c78808f350e73da Mon Sep 17 00:00:00 2001 From: Tim Atherton <54725421+softmattertheory@users.noreply.github.com> Date: Fri, 12 Apr 2024 10:46:36 -0400 Subject: [PATCH 036/181] Remove old hessian code --- src/geometry/functional.c | 129 -------------------------------------- 1 file changed, 129 deletions(-) diff --git a/src/geometry/functional.c b/src/geometry/functional.c index b782b16f..972f44ca 100644 --- a/src/geometry/functional.c +++ b/src/geometry/functional.c @@ -527,71 +527,6 @@ double functional_sumlist(double *list, unsigned int nel) { return sum; } -/* Calculates a numerical hessian */ -static bool functional_numericalhessian(vm *v, objectmesh *mesh, elementid i, int nv, int *vid, functional_integrand *integrand, void *ref, objectsparse *hess) { - double eps=1e-4; // ~ (eps)^(1/4) - value f0; - - // Finite difference rules from Abramowitz and Stegun 1972, p. 884 - double d2xy[] = { 1.0, eps, eps, // Data for second derivative formula - 1.0,-eps,-eps, - -1.0,-eps, eps, - -1.0, eps,-eps}; - - double d2xx[] = { -1.0, 0, 2*eps, // Data for second derivative formula - -1.0, 0, -2*eps, - +16.0, 0, +eps, - +16.0, 0, -eps, - -30.0, 0, 0 }; - - double *d2,scale=1.0; - int neval, nevalxx=5, nevalxy=4; - - // Loop over vertices in element - for (unsigned int j=0; jdim; l++) { - double x0,y0; // x and y are the two variables currently being differentiated wrt; x=Xj[l], y=Xk[m] - for (unsigned int m=0; mdim; m++) { - double ff=0; - matrix_getelement(mesh->vert, l, vid[j], &x0); // Get the initial values - matrix_getelement(mesh->vert, m, vid[k], &y0); - - if (sparsedok_get(&hess->dok, vid[j]*mesh->dim+l, vid[k]*mesh->dim+m, &f0)) ff=MORPHO_GETFLOATVALUE(f0); - - if ((j==k) && (l==m)) { // Diagonal element - d2=d2xx; neval=nevalxx; - scale=1.0/(12.0*eps*eps); - } else { - d2=d2xy; neval=nevalxy; - scale=1.0/(4.0*eps*eps); - } - - double f[neval]; - for (int n=0; nvert, l, vid[j], x0+d2[3*n+1]); - matrix_setelement(mesh->vert, m, vid[k], y0+d2[3*n+2]); // For j==l and l==m, this just overwrites the x value - if (!(*integrand) (v, mesh, i, nv, vid, ref, &f[n])) return false; - f[n]*=d2[3*n+0]; - } - - double d2f = functional_sumlist(f, neval); - - matrix_setelement(mesh->vert, l, vid[j], x0); // Reset element - matrix_setelement(mesh->vert, m, vid[k], y0); - - f0=MORPHO_FLOAT(ff+d2f*scale); - - sparsedok_insert(&hess->dok, vid[j]*mesh->dim+l, vid[k]*mesh->dim+m, f0); - } - } - } - } - return true; -} - /* ************************* * Map functions * ************************* */ @@ -770,70 +705,6 @@ bool functional_mapnumericalfieldgradientX(vm *v, functional_mapinfo *info, valu return ret; } -/** Map numerical hessian over the elements - * @param[in] v - virtual machine in use - * @param[in] info - map info - * @param[out] out - a matrix of integrand values - * @returns true on success, false otherwise. Error reporting through VM. */ -bool functional_mapnumericalhessianX(vm *v, functional_mapinfo *info, value *out) { - objectmesh *mesh = info->mesh; - objectselection *sel = info->sel; - if (sel) UNREACHABLE("Selections not implemented in hessian"); - grade g = info->g; - functional_integrand *integrand = info->integrand; - void *ref = info->ref; - - objectsparse *conn=NULL; - objectsparse *hess=NULL; - - bool ret=false; - int n=0; - - varray_elementid dependencies; - if (info->dependencies) varray_elementidinit(&dependencies); - - /* How many elements? */ - if (!functional_countelements(v, mesh, g, &n, &conn)) return false; - - /* Create the output matrix */ - if (n>0) { - int ndof = mesh->vert->nrows*mesh->vert->ncols; - hess=object_newsparse(&ndof, &ndof); - if (!hess) { morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED); return false; } - } - - int vertexid; // Use this if looping over grade 0 - int *vid=(g==0 ? &vertexid : NULL), - nv=(g==0 ? 1 : 0); // The vertex indice - - for (elementid i=0; iccs, i, &nv, &vid); - else vertexid=i; - - if (vid && nv>0) { - if (!functional_numericalhessian(v, mesh, i, nv, vid, integrand, ref, hess)) goto functional_mapnumericalhessian_cleanup; - - if (info->dependencies && // Loop over dependencies if there are any - (info->dependencies) (info, i, &dependencies)) { - for (int j=0; jdependencies) varray_elementidclear(&dependencies); - if (!ret) object_free((object *) hess); - - return ret; -} - /* ********************************************************************** * Multithreaded map functions * ********************************************************************** */ From dc5bcf54d444d8d65126a3ff99935f8c18aad392 Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Fri, 12 Apr 2024 12:47:48 -0400 Subject: [PATCH 037/181] Parse typed variable declarations --- src/core/compile.c | 34 +++++++++++++-------- src/support/parse.c | 49 ++++++++++++++++++++++++++++-- test/types/type_in_function.morpho | 2 +- test/types/type_in_global.morpho | 3 +- 4 files changed, 70 insertions(+), 18 deletions(-) diff --git a/src/core/compile.c b/src/core/compile.c index 539b619b..ad1de997 100644 --- a/src/core/compile.c +++ b/src/core/compile.c @@ -1332,7 +1332,7 @@ compilenoderule noderules[] = { { compiler_print }, // NODE_PRINT { compiler_declaration }, // NODE_DECLARATION - { NODE_NORULE }, // NODE_TYPE + { compiler_declaration }, // NODE_TYPE { compiler_function }, // NODE_FUNCTION { NODE_NORULE }, // NODE_METHOD { compiler_class }, // NODE_CLASS @@ -2330,13 +2330,23 @@ static codeinfo compiler_logical(compiler *c, syntaxtreenode *node, registerindx /** Compile declarations */ static codeinfo compiler_declaration(compiler *c, syntaxtreenode *node, registerindx reqout) { - syntaxtreenode *varnode = compiler_getnode(c, node->left); + syntaxtreenode *decnode = node; + syntaxtreenode *typenode = NULL; + + if (node->type==NODE_TYPE) { + typenode=compiler_getnode(c, node->left); + decnode=compiler_getnode(c, node->right); + } + + syntaxtreenode *varnode = NULL; syntaxtreenode *lftnode = NULL, *indxnode = NULL; codeinfo right; value var=MORPHO_NIL; registerindx reg; unsigned int ninstructions = 0; - + + varnode=compiler_getnode(c, decnode->left); + /* Find the symbol */ if (varnode) { if (varnode->type==NODE_SYMBOL) { @@ -2368,7 +2378,7 @@ static codeinfo compiler_declaration(compiler *c, syntaxtreenode *node, register /* If this is an array, we must create it */ if (indxnode) { /* Set up a call to the Array() function */ - array=compiler_findbuiltin(c, node, ARRAY_CLASSNAME, reqout); + array=compiler_findbuiltin(c, decnode, ARRAY_CLASSNAME, reqout); ninstructions+=array.ninstructions; // Dimensions @@ -2377,13 +2387,13 @@ static codeinfo compiler_declaration(compiler *c, syntaxtreenode *node, register ninstructions+=indxinfo.ninstructions; // Initializer - if (node->right!=SYNTAXTREE_UNCONNECTED) { + if (decnode->right!=SYNTAXTREE_UNCONNECTED) { iend=compiler_regalloctop(c); - right = compiler_nodetobytecode(c, node->right, iend); + right = compiler_nodetobytecode(c, decnode->right, iend); ninstructions+=right.ninstructions; - right=compiler_movetoregister(c, node, right, iend); // Ensure in register + right=compiler_movetoregister(c, decnode, right, iend); // Ensure in register ninstructions+=right.ninstructions; } @@ -2394,27 +2404,27 @@ static codeinfo compiler_declaration(compiler *c, syntaxtreenode *node, register compiler_regfreetoend(c, istart); if (vloc.returntype==REGISTER && array.dest!=vloc.dest) { // Move to correct register - codeinfo move=compiler_movetoregister(c, node, array, vloc.dest); + codeinfo move=compiler_movetoregister(c, decnode, array, vloc.dest); ninstructions+=move.ninstructions; } else reg=array.dest; } else if (node->right!=SYNTAXTREE_UNCONNECTED) { /* Not an array, but has an initializer */ - right = compiler_nodetobytecode(c, node->right, reg); + right = compiler_nodetobytecode(c, decnode->right, reg); ninstructions+=right.ninstructions; /* Ensure operand is in the desired register */ - right=compiler_movetoregister(c, node, right, reg); + right=compiler_movetoregister(c, decnode, right, reg); ninstructions+=right.ninstructions; compiler_releaseoperand(c, right); } else { /* Otherwise, we should zero out the register */ - registerindx cnil = compiler_addconstant(c, node, MORPHO_NIL, false, false); + registerindx cnil = compiler_addconstant(c, decnode, MORPHO_NIL, false, false); compiler_addinstruction(c, ENCODE_LONG(OP_LCT, reg, cnil), node); ninstructions++; } if (vloc.returntype!=REGISTER) { - codeinfo mv=compiler_movefromregister(c, node, vloc, reg); + codeinfo mv=compiler_movefromregister(c, decnode, vloc, reg); ninstructions+=mv.ninstructions; compiler_regfreetemp(c, reg); diff --git a/src/support/parse.c b/src/support/parse.c index 31c5ee13..a3cb7c8d 100644 --- a/src/support/parse.c +++ b/src/support/parse.c @@ -64,6 +64,18 @@ bool parse_advance(parser *p) { return ERROR_SUCCEEDED(*p->err); } +/** Saves the state of the parser and attached lexer */ +bool parse_savestate(parser *p, parser *op, lexer *ol) { + *ol = *p->lex; // Save the state of the parser and lexer + *op = *p; +} + +/** Restores the parser from a saved position */ +bool parse_restorestate(parser *op, lexer *ol, parser *out) { + *out = *op; + *out->lex = *ol; +} + /** @brief Continues parsing while tokens have a lower or equal precendece than a specified value. * @param p the parser in use * @param precendence precedence value to keep below or equal to @@ -421,6 +433,11 @@ bool parse_arglist(parser *p, tokentype rightdelimiter, unsigned int *nargs, voi PARSE_CHECK(parse_symbol(p, &label)); PARSE_CHECK(parse_addnode(p, NODE_TYPE, MORPHO_NIL, &start, current, label, ¤t)); + } else if (parse_checktokenadvance(p, TOKEN_EQUAL)) { // Symbol followed by equals is an optional argument + syntaxtreeindx val; + PARSE_CHECK(parse_pseudoexpression(p, &val)); + + PARSE_CHECK(parse_addnode(p, NODE_ASSIGN, MORPHO_NIL, &start, current, val, ¤t)); } } else PARSE_CHECK(parse_pseudoexpression(p, ¤t)); @@ -975,6 +992,32 @@ bool parse_vardeclaration(parser *p, void *out) { return true; } +/** Parses a typed var declaration */ +bool parse_typedvardeclaration(parser *p, void *out) { + syntaxtreeindx new=SYNTAXTREE_UNCONNECTED; + token start = p->previous; + + lexer ol; + parser op; + parse_savestate(p, &op, &ol); + + parse_advance(p); + + if (parse_checktoken(p, TOKEN_SYMBOL)) { // It is a typed variable declaration + syntaxtreeindx type=SYNTAXTREE_UNCONNECTED, var=SYNTAXTREE_UNCONNECTED; + PARSE_CHECK(parse_symbol(p, &type)); + PARSE_CHECK(parse_vardeclaration(p, &var)); + PARSE_CHECK(parse_addnode(p, NODE_TYPE, MORPHO_NIL, &start, type, var, &new)); + } else { // It was really an expression statement + parse_restorestate(&op, &ol, p); + PARSE_CHECK(parse_statement(p, &new)); + } + + *((syntaxtreeindx *) out) = new; + + return true; +} + /** Parses a function declaration */ bool parse_functiondeclaration(parser *p, void *out) { value name=MORPHO_NIL; @@ -1444,9 +1487,9 @@ bool parse_declaration(parser *p, void *out) { success=parse_classdeclaration(p, out); } else if (parse_checktokenadvance(p, TOKEN_IMPORT)) { success=parse_importdeclaration(p, out); - } /*else if (parse_checktoken(p, TOKEN_SYMBOL)) { - // Detect Typed variable declarations here - } */else { + } else if (parse_checktoken(p, TOKEN_SYMBOL)) { // Typed var declaration ? + success=parse_typedvardeclaration(p, out); + } else { success=parse_statement(p, out); } diff --git a/test/types/type_in_function.morpho b/test/types/type_in_function.morpho index 07fbc469..73459358 100644 --- a/test/types/type_in_function.morpho +++ b/test/types/type_in_function.morpho @@ -7,4 +7,4 @@ fn f() { } f() -// expect: Boo \ No newline at end of file +// expect: Boo \ No newline at end of file diff --git a/test/types/type_in_global.morpho b/test/types/type_in_global.morpho index 66c33222..fb285995 100644 --- a/test/types/type_in_global.morpho +++ b/test/types/type_in_global.morpho @@ -3,5 +3,4 @@ String a = "Foo" print a -// expect: Foo - +// expect: Foo From 587e032275cc9f2afd0d479ad0691ce2d5ff56cf Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Fri, 12 Apr 2024 19:14:06 -0400 Subject: [PATCH 038/181] Setting the type of a variable --- src/core/compile.c | 43 ++++++++++++++++++++++++++++++++++++++++--- src/core/compile.h | 1 + src/support/parse.c | 6 +++--- 3 files changed, 44 insertions(+), 6 deletions(-) diff --git a/src/core/compile.c b/src/core/compile.c index ad1de997..76b619b4 100644 --- a/src/core/compile.c +++ b/src/core/compile.c @@ -355,13 +355,31 @@ static bool compiler_inloop(compiler *c) { return (f->loopdepth>0); } +/* ------------------------------------------ + * Types + * ------------------------------------------- */ + +/** Identifies a type from a label */ +bool compiler_findtype(compiler *c, value label, value *out) { + value type=MORPHO_NIL; + + objectclass *clss=compiler_findclass(c, label); // A class we defined + if (clss) { + type = MORPHO_OBJECT(clss); + } else type = builtin_findclass(label); // Or a built in one + + if (!MORPHO_ISNIL(type)) *out = type; + + return (!MORPHO_ISNIL(type)); +} + /* ------------------------------------------ * Register allocation and deallocation * ------------------------------------------- */ /** Finds a free register in the current function state and claims it */ static registerindx compiler_regallocwithstate(compiler *c, functionstate *f, value symbol) { - registeralloc r = (registeralloc) {.isallocated=true, .iscaptured=false, .scopedepth=f->scopedepth, .symbol=symbol}; + registeralloc r = (registeralloc) {.isallocated=true, .iscaptured=false, .scopedepth=f->scopedepth, .symbol=symbol, .type=MORPHO_NIL, .currenttype=MORPHO_NIL }; registerindx i = REGISTER_UNALLOCATED; if (compiler_inargs(c)) { @@ -415,7 +433,7 @@ static void compiler_regsetsymbol(compiler *c, registerindx reg, value symbol) { static registerindx compiler_regalloctop(compiler *c) { functionstate *f = compiler_currentfunctionstate(c); registerindx i = REGISTER_UNALLOCATED; - registeralloc r = (registeralloc) {.isallocated=true, .iscaptured=false, .scopedepth=f->scopedepth, .symbol=MORPHO_NIL}; + registeralloc r = (registeralloc) {.isallocated=true, .iscaptured=false, .scopedepth=f->scopedepth, .symbol=MORPHO_NIL, .type=MORPHO_NIL, .currenttype=MORPHO_NIL}; /* Search backwards from the end to find the register AFTER the last allocated register */ @@ -467,6 +485,8 @@ static void compiler_regfree(compiler *c, functionstate *f, registerindx reg) { debugannotation_setreg(&c->out->annotations, reg, MORPHO_NIL); } f->registers.data[reg].symbol=MORPHO_NIL; + f->registers.data[reg].type=MORPHO_NIL; + f->registers.data[reg].currenttype=MORPHO_NIL; } } @@ -508,6 +528,13 @@ static void compiler_regfreeatscope(compiler *c, unsigned int scopedepth) { } } +/** Sets the type associated with a register */ +void compiler_regsettype(compiler *c, registerindx reg, value type) { + functionstate *f = compiler_currentfunctionstate(c); + if (reg>=f->registers.count) return; + f->registers.data[reg].type=type; +} + /** @brief Finds the register that contains symbol in a given functionstate * @details Searches backwards so that the innermost scope has priority */ static registerindx compiler_findsymbol(functionstate *f, value symbol) { @@ -612,6 +639,11 @@ static void compiler_regshow(compiler *c) { } else { printf("unallocated"); } + if (!MORPHO_ISNIL(r->type)) { + printf(" ["); + morpho_printvalue(NULL, r->type); + printf("]"); + } printf("\n"); } printf("--End registers\n"); @@ -2341,7 +2373,7 @@ static codeinfo compiler_declaration(compiler *c, syntaxtreenode *node, register syntaxtreenode *varnode = NULL; syntaxtreenode *lftnode = NULL, *indxnode = NULL; codeinfo right; - value var=MORPHO_NIL; + value var=MORPHO_NIL, type=MORPHO_NIL; registerindx reg; unsigned int ninstructions = 0; @@ -2375,6 +2407,11 @@ static codeinfo compiler_declaration(compiler *c, syntaxtreenode *node, register reg=compiler_regtemp(c, REGISTER_UNALLOCATED); } + if (typenode && + compiler_findtype(c, typenode->content, &type)) { + compiler_regsettype(c, reg, type); + } + /* If this is an array, we must create it */ if (indxnode) { /* Set up a call to the Array() function */ diff --git a/src/core/compile.h b/src/core/compile.h index a4fcdcb6..f4bd0f22 100644 --- a/src/core/compile.h +++ b/src/core/compile.h @@ -137,6 +137,7 @@ typedef struct { unsigned int scopedepth; /** Scope depth at which the register was allocated */ value symbol; /** Symbol associated with the register */ value type; /** Type associated with the register */ + value currenttype; /** Current type held by the register */ } registeralloc; DECLARE_VARRAY(registeralloc, registeralloc) diff --git a/src/support/parse.c b/src/support/parse.c index a3cb7c8d..8b5daae3 100644 --- a/src/support/parse.c +++ b/src/support/parse.c @@ -992,12 +992,12 @@ bool parse_vardeclaration(parser *p, void *out) { return true; } -/** Parses a typed var declaration */ +/** Parses a possible typed var declaration */ bool parse_typedvardeclaration(parser *p, void *out) { syntaxtreeindx new=SYNTAXTREE_UNCONNECTED; token start = p->previous; - lexer ol; + lexer ol; // Store the state of the parser parser op; parse_savestate(p, &op, &ol); @@ -1008,7 +1008,7 @@ bool parse_typedvardeclaration(parser *p, void *out) { PARSE_CHECK(parse_symbol(p, &type)); PARSE_CHECK(parse_vardeclaration(p, &var)); PARSE_CHECK(parse_addnode(p, NODE_TYPE, MORPHO_NIL, &start, type, var, &new)); - } else { // It was really an expression statement + } else { // It was really an expression statement parse_restorestate(&op, &ol, p); PARSE_CHECK(parse_statement(p, &new)); } From 2692dae5f2f61c71cdc487bb4b769324ff548962 Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Fri, 12 Apr 2024 23:26:23 -0400 Subject: [PATCH 039/181] Type violation on constants --- src/core/compile.c | 87 ++++++++++++++++++----- src/core/compile.h | 3 + test/types/type_violation_constant.morpho | 8 +++ 3 files changed, 79 insertions(+), 19 deletions(-) create mode 100644 test/types/type_violation_constant.morpho diff --git a/src/core/compile.c b/src/core/compile.c index 76b619b4..4d96962f 100644 --- a/src/core/compile.c +++ b/src/core/compile.c @@ -290,6 +290,56 @@ objectclass *compiler_findclass(compiler *c, value name) { return NULL; } +/* ------------------------------------------ + * Types + * ------------------------------------------- */ + +/** Identifies a type from a label */ +bool compiler_findtype(compiler *c, value label, value *out) { + value type=MORPHO_NIL; + + objectclass *clss=compiler_findclass(c, label); // A class we defined + if (clss) { + type = MORPHO_OBJECT(clss); + } else type = builtin_findclass(label); // Or a built in one + + if (!MORPHO_ISNIL(type)) *out = type; + + return (!MORPHO_ISNIL(type)); +} + +/** Identifies a type from a value */ +bool compiler_typefromvalue(compiler *c, value v, value *out) { + objectclass *clss = NULL; + value type = MORPHO_NIL; + + if (MORPHO_ISINSTANCE(v)) { + clss=MORPHO_GETINSTANCE(v)->klass; + } else if (MORPHO_ISOBJECT(v)) { + clss = object_getveneerclass(MORPHO_GETOBJECT(v)->type); + } else type = value_getveneerclass(v); + + if (clss) type = MORPHO_OBJECT(clss); + + if (!MORPHO_ISNIL(type)) *out = type; + + return (!MORPHO_ISNIL(type)); +} + +/** Checks if type "match" matches a given type "type" */ +bool compiler_checktype(compiler *c, value type, value match) { + if (MORPHO_ISNIL(type) || // If type is unset, we always match + MORPHO_ISEQUAL(type, match)) return true; // Or if the types are the same + + return false; +} + +/** Determines the type associated with a constant */ +bool compiler_getconstanttype(compiler *c, unsigned int i, value *type) { + value val = compiler_getconstant(c, i); + return compiler_typefromvalue(c, val, type); +} + /* ------------------------------------------ * Modules * ------------------------------------------- */ @@ -355,24 +405,6 @@ static bool compiler_inloop(compiler *c) { return (f->loopdepth>0); } -/* ------------------------------------------ - * Types - * ------------------------------------------- */ - -/** Identifies a type from a label */ -bool compiler_findtype(compiler *c, value label, value *out) { - value type=MORPHO_NIL; - - objectclass *clss=compiler_findclass(c, label); // A class we defined - if (clss) { - type = MORPHO_OBJECT(clss); - } else type = builtin_findclass(label); // Or a built in one - - if (!MORPHO_ISNIL(type)) *out = type; - - return (!MORPHO_ISNIL(type)); -} - /* ------------------------------------------ * Register allocation and deallocation * ------------------------------------------- */ @@ -535,6 +567,17 @@ void compiler_regsettype(compiler *c, registerindx reg, value type) { f->registers.data[reg].type=type; } +/** Sets the current type of a register. Raises a type violation error if this is not compatible with the required type */ +bool compiler_regsetcurrenttype(compiler *c, syntaxtreenode *node, registerindx reg, value type) { + functionstate *f = compiler_currentfunctionstate(c); + if (reg>=f->registers.count) return false; + + if (compiler_checktype(c, f->registers.data[reg].type, type)) return true; + + compiler_error(c, node, COMPILE_TYPEVIOLATION); + return false; +} + /** @brief Finds the register that contains symbol in a given functionstate * @details Searches backwards so that the innermost scope has priority */ static registerindx compiler_findsymbol(functionstate *f, value symbol) { @@ -829,14 +872,19 @@ static registerindx compiler_getlocal(compiler *c, value symbol) { * @param reg destination register, or REGISTER_UNALLOCATED to allocate a new one * @returns Number of instructions generated */ static codeinfo compiler_movetoregister(compiler *c, syntaxtreenode *node, codeinfo info, registerindx reg) { + value type = MORPHO_NIL; codeinfo out = info; out.ninstructions=0; if (CODEINFO_ISCONSTANT(info)) { out.returntype=REGISTER; out.dest=compiler_regtemp(c, reg); + + if (compiler_getconstanttype(c, info.dest, &type)) { + compiler_regsetcurrenttype(c, node, out.dest, type); + } + compiler_addinstruction(c, ENCODE_LONG(OP_LCT, out.dest, info.dest), node); - out.ninstructions++; } else if (CODEINFO_ISUPVALUE(info)) { /* Move upvalues */ @@ -3782,6 +3830,7 @@ void compile_initialize(void) { morpho_defineerror(COMPILE_VARPRMLST, ERROR_COMPILE, COMPILE_VARPRMLST_MSG); morpho_defineerror(COMPILE_INVLDLBL, ERROR_COMPILE, COMPILE_INVLDLBL_MSG); morpho_defineerror(COMPILE_MSSNGINDX, ERROR_COMPILE, COMPILE_MSSNGINDX_MSG); + morpho_defineerror(COMPILE_TYPEVIOLATION, ERROR_COMPILE, COMPILE_TYPEVIOLATION_MSG); morpho_addfinalizefn(compile_finalize); } diff --git a/src/core/compile.h b/src/core/compile.h index f4bd0f22..ac38c374 100644 --- a/src/core/compile.h +++ b/src/core/compile.h @@ -105,6 +105,9 @@ #define COMPILE_MSSNGINDX "MssngIndx" #define COMPILE_MSSNGINDX_MSG "Missing index or indices." +#define COMPILE_TYPEVIOLATION "TypErr" +#define COMPILE_TYPEVIOLATION_MSG "Type violation." + /* ********************************************************************** * Compiler typedefs * ********************************************************************** */ diff --git a/test/types/type_violation_constant.morpho b/test/types/type_violation_constant.morpho new file mode 100644 index 00000000..cfe9ce0c --- /dev/null +++ b/test/types/type_violation_constant.morpho @@ -0,0 +1,8 @@ +// Type definition in function + +fn f() { + String u = 1 +} + +f() +// expect error 'TypeErr' From b0b495c19120f703b221e68e54dc42f427a8bd43 Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Sat, 13 Apr 2024 07:39:42 -0400 Subject: [PATCH 040/181] Check for type violations --- src/classes/list.c | 2 +- src/core/compile.c | 39 ++++++++++++++++++-- src/core/compile.h | 2 +- test/types/type_violation_in_function.morpho | 4 +- 4 files changed, 39 insertions(+), 8 deletions(-) diff --git a/src/classes/list.c b/src/classes/list.c index 3204a149..2ae0a27f 100644 --- a/src/classes/list.c +++ b/src/classes/list.c @@ -696,7 +696,7 @@ void list_initialize(void) { value objclass = builtin_findclass(MORPHO_OBJECT(&objname)); // List constructor function - builtin_addfunction(LIST_CLASSNAME, list_constructor, BUILTIN_FLAGSEMPTY); + builtin_addfunction(LIST_CLASSNAME, list_constructor, MORPHO_FN_CONSTRUCTOR); // List constructor function value listclass=builtin_addclass(LIST_CLASSNAME, MORPHO_GETCLASSDEFINITION(List), objclass); diff --git a/src/core/compile.c b/src/core/compile.c index 4d96962f..d7e0ec2d 100644 --- a/src/core/compile.c +++ b/src/core/compile.c @@ -308,6 +308,12 @@ bool compiler_findtype(compiler *c, value label, value *out) { return (!MORPHO_ISNIL(type)); } +/** Identify a type from a label */ +bool compiler_findtypefromcstring(compiler *c, char *label, value *out) { + objectstring str = MORPHO_STATICSTRING(label); + return compiler_findtype(c, MORPHO_OBJECT(&str), out); +} + /** Identifies a type from a value */ bool compiler_typefromvalue(compiler *c, value v, value *out) { objectclass *clss = NULL; @@ -317,7 +323,7 @@ bool compiler_typefromvalue(compiler *c, value v, value *out) { clss=MORPHO_GETINSTANCE(v)->klass; } else if (MORPHO_ISOBJECT(v)) { clss = object_getveneerclass(MORPHO_GETOBJECT(v)->type); - } else type = value_getveneerclass(v); + } else clss = value_getveneerclass(v); if (clss) type = MORPHO_OBJECT(clss); @@ -567,14 +573,30 @@ void compiler_regsettype(compiler *c, registerindx reg, value type) { f->registers.data[reg].type=type; } +/** Raises a type violation error */ +void compiler_typeviolation(compiler *c, syntaxtreenode *node, value type, value badtype, value symbol) { + char *tname="(unknown)", *bname="(unknown)"; + char *sym=""; + + if (MORPHO_ISCLASS(type)) tname=MORPHO_GETCSTRING(MORPHO_GETCLASS(type)->name); + if (MORPHO_ISCLASS(badtype)) bname=MORPHO_GETCSTRING(MORPHO_GETCLASS(badtype)->name); + if (MORPHO_ISSTRING(symbol)) sym = MORPHO_GETCSTRING(symbol); + + compiler_error(c, node, COMPILE_TYPEVIOLATION, bname, tname, sym); +} + /** Sets the current type of a register. Raises a type violation error if this is not compatible with the required type */ bool compiler_regsetcurrenttype(compiler *c, syntaxtreenode *node, registerindx reg, value type) { functionstate *f = compiler_currentfunctionstate(c); if (reg>=f->registers.count) return false; - if (compiler_checktype(c, f->registers.data[reg].type, type)) return true; + if (compiler_checktype(c, f->registers.data[reg].type, type)) { + f->registers.data[reg].currenttype=type; + return true; + } + + compiler_typeviolation(c, node, f->registers.data[reg].type, type, f->registers.data[reg].symbol); - compiler_error(c, node, COMPILE_TYPEVIOLATION); return false; } @@ -687,6 +709,10 @@ static void compiler_regshow(compiler *c) { morpho_printvalue(NULL, r->type); printf("]"); } + if (!MORPHO_ISNIL(r->currenttype)) { + printf(" contains: "); + morpho_printvalue(NULL, r->currenttype); + } printf("\n"); } printf("--End registers\n"); @@ -1465,6 +1491,11 @@ static codeinfo compiler_list(compiler *c, syntaxtreenode *node, registerindx re char *classname = LIST_CLASSNAME; if (node->type==NODE_TUPLE) classname = TUPLE_CLASSNAME; codeinfo out = compiler_findbuiltin(c, node, classname, reqout); + + value listtype=MORPHO_NIL; /* Set the type associated with the register */ + if (compiler_findtypefromcstring(c, classname, &listtype)) { + if (!compiler_regsetcurrenttype(c, node, out.dest, listtype)) return CODEINFO_EMPTY; + } varray_syntaxtreeindxinit(&entries); if (node->right!=SYNTAXTREE_UNCONNECTED) syntaxtree_flatten(compiler_getsyntaxtree(c), node->right, 1, dictentrytype, &entries); @@ -2493,7 +2524,7 @@ static codeinfo compiler_declaration(compiler *c, syntaxtreenode *node, register ninstructions+=move.ninstructions; } else reg=array.dest; - } else if (node->right!=SYNTAXTREE_UNCONNECTED) { /* Not an array, but has an initializer */ + } else if (decnode->right!=SYNTAXTREE_UNCONNECTED) { /* Not an array, but has an initializer */ right = compiler_nodetobytecode(c, decnode->right, reg); ninstructions+=right.ninstructions; diff --git a/src/core/compile.h b/src/core/compile.h index ac38c374..96e60f71 100644 --- a/src/core/compile.h +++ b/src/core/compile.h @@ -106,7 +106,7 @@ #define COMPILE_MSSNGINDX_MSG "Missing index or indices." #define COMPILE_TYPEVIOLATION "TypErr" -#define COMPILE_TYPEVIOLATION_MSG "Type violation." +#define COMPILE_TYPEVIOLATION_MSG "Type violation: Attempting to assign type %s to %s variable %s." /* ********************************************************************** * Compiler typedefs diff --git a/test/types/type_violation_in_function.morpho b/test/types/type_violation_in_function.morpho index fcf13a30..f1ed0974 100644 --- a/test/types/type_violation_in_function.morpho +++ b/test/types/type_violation_in_function.morpho @@ -1,9 +1,9 @@ -// Type definition in function +// Type violation in function fn f() { String u - u = Matrix(1) + u = [] } f() From 35ea3561641c0742ff32e76def69f4f0910af3ba Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Sat, 13 Apr 2024 16:47:55 -0400 Subject: [PATCH 041/181] Type analysis for Range and Dictionary --- src/core/compile.c | 16 +++++++++++++--- src/core/compile.h | 4 +++- test/types/type_violation_dictionary.morpho | 10 ++++++++++ test/types/type_violation_range.morpho | 10 ++++++++++ 4 files changed, 36 insertions(+), 4 deletions(-) create mode 100644 test/types/type_violation_dictionary.morpho create mode 100644 test/types/type_violation_range.morpho diff --git a/src/core/compile.c b/src/core/compile.c index d7e0ec2d..41dce301 100644 --- a/src/core/compile.c +++ b/src/core/compile.c @@ -417,7 +417,7 @@ static bool compiler_inloop(compiler *c) { /** Finds a free register in the current function state and claims it */ static registerindx compiler_regallocwithstate(compiler *c, functionstate *f, value symbol) { - registeralloc r = (registeralloc) {.isallocated=true, .iscaptured=false, .scopedepth=f->scopedepth, .symbol=symbol, .type=MORPHO_NIL, .currenttype=MORPHO_NIL }; + registeralloc r = REGISTERALLOC_EMPTY(f->scopedepth, symbol); registerindx i = REGISTER_UNALLOCATED; if (compiler_inargs(c)) { @@ -471,7 +471,7 @@ static void compiler_regsetsymbol(compiler *c, registerindx reg, value symbol) { static registerindx compiler_regalloctop(compiler *c) { functionstate *f = compiler_currentfunctionstate(c); registerindx i = REGISTER_UNALLOCATED; - registeralloc r = (registeralloc) {.isallocated=true, .iscaptured=false, .scopedepth=f->scopedepth, .symbol=MORPHO_NIL, .type=MORPHO_NIL, .currenttype=MORPHO_NIL}; + registeralloc r = REGISTERALLOC_EMPTY(f->scopedepth, MORPHO_NIL); /* Search backwards from the end to find the register AFTER the last allocated register */ @@ -500,7 +500,7 @@ static registerindx compiler_regtemp(compiler *c, registerindx reqreg) { static registerindx compiler_regtempwithindx(compiler *c, registerindx reg) { functionstate *f = compiler_currentfunctionstate(c); if (reg>=f->registers.count) { - registeralloc empty = (registeralloc) {.isallocated=false, .iscaptured=false, .scopedepth=f->scopedepth, .symbol=MORPHO_NIL}; + registeralloc empty = REGISTERALLOC_EMPTY(f->scopedepth, MORPHO_NIL); while (reg>=f->registers.count) { if (!varray_registerallocadd(&f->registers, &empty, 1)) break; if (f->registers.count>f->nreg) f->nreg=f->registers.count; @@ -1535,6 +1535,11 @@ static codeinfo compiler_dictionary(compiler *c, syntaxtreenode *node, registeri /* Set up a call to the Dictionary() function */ codeinfo out = compiler_findbuiltin(c, node, DICTIONARY_CLASSNAME, reqout); + value dicttype=MORPHO_NIL; /* Set the type associated with the register */ + if (compiler_findtypefromcstring(c, DICTIONARY_CLASSNAME, &dicttype)) { + if (!compiler_regsetcurrenttype(c, node, out.dest, dicttype)) return CODEINFO_EMPTY; + } + varray_syntaxtreeindxinit(&entries); /* Flatten all the child nodes; these end up as a sequence: key, val, key, val, ... */ if (node->left!=SYNTAXTREE_UNCONNECTED) syntaxtree_flatten(compiler_getsyntaxtree(c), node->left, 2, dictentrytype, &entries); @@ -1582,6 +1587,11 @@ static codeinfo compiler_range(compiler *c, syntaxtreenode *node, registerindx r /* Set up a call to the Range() function */ codeinfo rng = compiler_findbuiltin(c, node, RANGE_CLASSNAME, reqout); + + value rngtype=MORPHO_NIL; /* Set the type associated with the register */ + if (compiler_findtypefromcstring(c, RANGE_CLASSNAME, &rngtype)) { + if (!compiler_regsetcurrenttype(c, node, rng.dest, rngtype)) return CODEINFO_EMPTY; + } /* Construct the arguments */ unsigned int n; diff --git a/src/core/compile.h b/src/core/compile.h index 96e60f71..d1274a20 100644 --- a/src/core/compile.h +++ b/src/core/compile.h @@ -105,7 +105,7 @@ #define COMPILE_MSSNGINDX "MssngIndx" #define COMPILE_MSSNGINDX_MSG "Missing index or indices." -#define COMPILE_TYPEVIOLATION "TypErr" +#define COMPILE_TYPEVIOLATION "TypeErr" #define COMPILE_TYPEVIOLATION_MSG "Type violation: Attempting to assign type %s to %s variable %s." /* ********************************************************************** @@ -143,6 +143,8 @@ typedef struct { value currenttype; /** Current type held by the register */ } registeralloc; +#define REGISTERALLOC_EMPTY(sdepth, symb) ((registeralloc) {.isallocated=true, .iscaptured=false, .scopedepth=sdepth, .symbol=symb, .type=MORPHO_NIL, .currenttype=MORPHO_NIL}) + DECLARE_VARRAY(registeralloc, registeralloc) /* ------------------------------------------------------- diff --git a/test/types/type_violation_dictionary.morpho b/test/types/type_violation_dictionary.morpho new file mode 100644 index 00000000..dc29b667 --- /dev/null +++ b/test/types/type_violation_dictionary.morpho @@ -0,0 +1,10 @@ +// Type violation in function from a Dictionary + +fn f() { + String u + + u = { "a" : "b" } +} + +f() +// expect error 'TypeErr' diff --git a/test/types/type_violation_range.morpho b/test/types/type_violation_range.morpho new file mode 100644 index 00000000..16cfb4fa --- /dev/null +++ b/test/types/type_violation_range.morpho @@ -0,0 +1,10 @@ +// Type violation in function from a Range + +fn f() { + String u + + u = 1..3 +} + +f() +// expect error 'TypeErr' From 63a19b6495978c16a5a8adf4ba0a5e94f64b3d1f Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Sun, 14 Apr 2024 10:01:33 -0400 Subject: [PATCH 042/181] Type propagation from move --- src/core/compile.c | 12 +++++++++++- test/types/type_instance.morpho | 7 +++++++ test/types/type_violation_propagation.morpho | 12 ++++++++++++ 3 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 test/types/type_instance.morpho create mode 100644 test/types/type_violation_propagation.morpho diff --git a/src/core/compile.c b/src/core/compile.c index 41dce301..4418f34c 100644 --- a/src/core/compile.c +++ b/src/core/compile.c @@ -600,6 +600,14 @@ bool compiler_regsetcurrenttype(compiler *c, syntaxtreenode *node, registerindx return false; } +/** Gets the current type of a register */ +bool compiler_regcurrenttype(compiler *c, registerindx reg, value *type) { + functionstate *f = compiler_currentfunctionstate(c); + if (reg>=f->registers.count) return false; + *type = f->registers.data[reg].currenttype; + return true; +} + /** @brief Finds the register that contains symbol in a given functionstate * @details Searches backwards so that the innermost scope has priority */ static registerindx compiler_findsymbol(functionstate *f, value symbol) { @@ -933,6 +941,8 @@ static codeinfo compiler_movetoregister(compiler *c, syntaxtreenode *node, codei } if (out.dest!=info.dest) { + if (compiler_regcurrenttype(c, info.dest, &type)) compiler_regsetcurrenttype(c, node, out.dest, type); + compiler_addinstruction(c, ENCODE_DOUBLE(OP_MOV, out.dest, info.dest), node); out.ninstructions++; } @@ -2823,7 +2833,7 @@ static codeinfo compiler_call(compiler *c, syntaxtreenode *node, registerindx re registerindx top=compiler_regtop(c); compiler_beginargs(c); - + /* Compile the function selector */ syntaxtreenode *selnode=compiler_getnode(c, node->left); codeinfo func = compiler_nodetobytecode(c, node->left, (reqout Date: Sun, 14 Apr 2024 16:53:46 -0400 Subject: [PATCH 043/181] Type violations from instances --- src/core/compile.c | 36 ++++++++++++++++++----- src/support/parse.c | 4 +-- test/types/type_instance.morpho | 2 +- test/types/type_violation_instance.morpho | 11 +++++++ 4 files changed, 43 insertions(+), 10 deletions(-) create mode 100644 test/types/type_violation_instance.morpho diff --git a/src/core/compile.c b/src/core/compile.c index 4418f34c..a2a246b6 100644 --- a/src/core/compile.c +++ b/src/core/compile.c @@ -569,7 +569,7 @@ static void compiler_regfreeatscope(compiler *c, unsigned int scopedepth) { /** Sets the type associated with a register */ void compiler_regsettype(compiler *c, registerindx reg, value type) { functionstate *f = compiler_currentfunctionstate(c); - if (reg>=f->registers.count) return; + if (reg<0 || reg>=f->registers.count) return; f->registers.data[reg].type=type; } @@ -2571,18 +2571,29 @@ static codeinfo compiler_declaration(compiler *c, syntaxtreenode *node, register } /** Compiles an parameter declaration */ -static void compiler_functionparameters(compiler *c, syntaxtreeindx indx) { +static registerindx compiler_functionparameters(compiler *c, syntaxtreeindx indx) { syntaxtreenode *node = compiler_getnode(c, indx); - if (!node) return; + if (!node) return REGISTER_UNALLOCATED; switch(node->type) { case NODE_SYMBOL: { if (!compiler_hasvariadicarg(c)) { - compiler_addlocal(c, node, node->content); + return compiler_addlocal(c, node, node->content); } else compiler_error(c, node, COMPILE_VARPRMLST); } break; + case NODE_TYPE: + { + value type=MORPHO_NIL; + syntaxtreenode *typenode = compiler_getnode(c, node->left); + compiler_findtype(c, typenode->content, &type); + + registerindx reg = compiler_functionparameters(c, node->right); + compiler_regsettype(c, reg, type); + compiler_regsetcurrenttype(c, node, reg, type); + } + break; case NODE_ASSIGN: { syntaxtreenode *name=compiler_getnode(c, node->left); @@ -2610,6 +2621,8 @@ static void compiler_functionparameters(compiler *c, syntaxtreeindx indx) { compiler_error(c, node, COMPILE_ARGSNOTSYMBOLS); break; } + + return REGISTER_UNALLOCATED;; } value _selfsymbol; @@ -2834,10 +2847,16 @@ static codeinfo compiler_call(compiler *c, syntaxtreenode *node, registerindx re compiler_beginargs(c); - /* Compile the function selector */ + // Compile the function selector syntaxtreenode *selnode=compiler_getnode(c, node->left); codeinfo func = compiler_nodetobytecode(c, node->left, (reqouttype==NODE_SYMBOL) { + compiler_findtype(c, selnode->content, &rtype); + } + // Detect possible forward reference if (selnode->type==NODE_SYMBOL && compiler_catch(c, COMPILE_SYMBOLNOTDEFINED)) { syntaxtreenode *symbol=compiler_getnode(c, node->left); @@ -2875,11 +2894,14 @@ static codeinfo compiler_call(compiler *c, syntaxtreenode *node, registerindx re /* Free all the registers used for the call */ compiler_regfreetoend(c, func.dest+1); + + /* Set the current type of the register */ + compiler_regsetcurrenttype(c, selnode, func.dest, rtype); /* Move the result to the requested register */ if (reqout!=REGISTER_UNALLOCATED && func.dest!=reqout) { - compiler_addinstruction(c, ENCODE_DOUBLE(OP_MOV, reqout, func.dest), node); - ninstructions++; + codeinfo mv = compiler_movetoregister(c, node, func, reqout); + ninstructions+=mv.ninstructions; compiler_regfreetemp(c, func.dest); func.dest=reqout; } diff --git a/src/support/parse.c b/src/support/parse.c index 8b5daae3..a1fb18b2 100644 --- a/src/support/parse.c +++ b/src/support/parse.c @@ -65,13 +65,13 @@ bool parse_advance(parser *p) { } /** Saves the state of the parser and attached lexer */ -bool parse_savestate(parser *p, parser *op, lexer *ol) { +void parse_savestate(parser *p, parser *op, lexer *ol) { *ol = *p->lex; // Save the state of the parser and lexer *op = *p; } /** Restores the parser from a saved position */ -bool parse_restorestate(parser *op, lexer *ol, parser *out) { +void parse_restorestate(parser *op, lexer *ol, parser *out) { *out = *op; *out->lex = *ol; } diff --git a/test/types/type_instance.morpho b/test/types/type_instance.morpho index 95601ca0..f368695e 100644 --- a/test/types/type_instance.morpho +++ b/test/types/type_instance.morpho @@ -4,4 +4,4 @@ class A { } A a = A() -print a \ No newline at end of file +print a // expect: diff --git a/test/types/type_violation_instance.morpho b/test/types/type_violation_instance.morpho new file mode 100644 index 00000000..d48389fc --- /dev/null +++ b/test/types/type_violation_instance.morpho @@ -0,0 +1,11 @@ +// Type violation by assigning incorrect instance + +class A { } + +class B { } + +fn f() { + A a = B() // expect error 'TypeErr' +} + +f() From 19f2c257821463b46a524146724277c0e0ac867d Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Tue, 16 Apr 2024 09:19:28 -0400 Subject: [PATCH 044/181] Metafunctions --- src/builtin/builtin.c | 1 + src/classes/CMakeLists.txt | 40 +++++++++++---------- src/classes/classes.h | 1 + src/classes/metafunction.c | 72 ++++++++++++++++++++++++++++++++++++++ src/classes/metafunction.h | 46 ++++++++++++++++++++++++ 5 files changed, 141 insertions(+), 19 deletions(-) create mode 100644 src/classes/metafunction.c create mode 100644 src/classes/metafunction.h diff --git a/src/builtin/builtin.c b/src/builtin/builtin.c index dadc69e7..c2bec790 100644 --- a/src/builtin/builtin.c +++ b/src/builtin/builtin.c @@ -323,6 +323,7 @@ void builtin_initialize(void) { string_initialize(); // Classes function_initialize(); + metafunction_initialize(); class_initialize(); upvalue_initialize(); invocation_initialize(); diff --git a/src/classes/CMakeLists.txt b/src/classes/CMakeLists.txt index 8e2a27dc..9decb74e 100644 --- a/src/classes/CMakeLists.txt +++ b/src/classes/CMakeLists.txt @@ -1,25 +1,26 @@ target_sources(morpho PRIVATE classes.h - array.c array.h - closure.c closure.h - clss.c clss.h - cmplx.c cmplx.h - dict.c dict.h - err.c err.h - file.c file.h - flt.c flt.h - function.c function.h - instance.c instance.h - int.c int.h - invocation.c invocation.h - json.c json.h - list.c list.h - range.c range.h - strng.c strng.h - system.c system.h - tuple.c tuple.h - upvalue.c upvalue.h + array.c array.h + closure.c closure.h + clss.c clss.h + cmplx.c cmplx.h + dict.c dict.h + err.c err.h + file.c file.h + flt.c flt.h + function.c function.h + instance.c instance.h + int.c int.h + invocation.c invocation.h + json.c json.h + list.c list.h + metafunction.c metafunction.h + range.c range.h + strng.c strng.h + system.c system.h + tuple.c tuple.h + upvalue.c upvalue.h ) target_sources(morpho @@ -41,6 +42,7 @@ target_sources(morpho invocation.h json.h list.h + metafunction.h range.h strng.h system.h diff --git a/src/classes/classes.h b/src/classes/classes.h index 875511c0..a3947dc6 100644 --- a/src/classes/classes.h +++ b/src/classes/classes.h @@ -11,6 +11,7 @@ #include "upvalue.h" #include "function.h" +#include "metafunction.h" #include "clss.h" #include "cmplx.h" #include "closure.h" diff --git a/src/classes/metafunction.c b/src/classes/metafunction.c new file mode 100644 index 00000000..3e5511d1 --- /dev/null +++ b/src/classes/metafunction.c @@ -0,0 +1,72 @@ +/** @file metafunction.c + * @author T J Atherton + * + * @brief Implement objectmetafunctions and the Metafunction veneer class + */ + +#include "morpho.h" +#include "classes.h" +#include "common.h" + +/* ********************************************************************** + * objectmetafunction definitions + * ********************************************************************** */ + +void objectmetafunction_freefn(object *obj) { + objectmetafunction *func = (objectmetafunction *) obj; +} + +void objectmetafunction_markfn(object *obj, void *v) { + objectmetafunction *f = (objectmetafunction *) obj; +} + +size_t objectmetafunction_sizefn(object *obj) { + return sizeof(objectmetafunction); +} + +void objectmetafunction_printfn(object *obj, void *v) { + objectmetafunction *f = (objectmetafunction *) obj; + //if (f) morpho_printf(v, "", (MORPHO_ISNIL(f->name) ? "" : MORPHO_GETCSTRING(f->name))); +} + +objecttypedefn objectmetafunctiondefn = { + .printfn=objectmetafunction_printfn, + .markfn=objectmetafunction_markfn, + .freefn=objectmetafunction_freefn, + .sizefn=objectmetafunction_sizefn, + .hashfn=NULL, + .cmpfn=NULL +}; + +/* ********************************************************************** + * objectmetafunction utility functions + * ********************************************************************** */ + + +/* ********************************************************************** + * Metafunction veneer class + * ********************************************************************** */ + + +/* ********************************************************************** + * Initialization and finalization + * ********************************************************************** */ + +objecttype objectmetafunctiontype; + +void metafunction_initialize(void) { + // Create function object type + objectmetafunctiontype=object_addtype(&objectmetafunctiondefn); + + // Locate the Object class to use as the parent class of Metafunction + objectstring objname = MORPHO_STATICSTRING(OBJECT_CLASSNAME); + value objclass = builtin_findclass(MORPHO_OBJECT(&objname)); + + // Create function veneer class + //value metafunctionclass=builtin_addclass(METAFUNCTION_CLASSNAME, MORPHO_GETCLASSDEFINITION(Metafunction), objclass); + //object_setveneerclass(OBJECT_FUNCTION, functionclass); + + // No constructor as objectmetafunctions are generated by the compiler + + // Metafunction error messages +} diff --git a/src/classes/metafunction.h b/src/classes/metafunction.h new file mode 100644 index 00000000..ffbb1491 --- /dev/null +++ b/src/classes/metafunction.h @@ -0,0 +1,46 @@ +/** @file metafunction.h + * @author T J Atherton + * + * @brief Defines metafunction object type and Metafunction veneer class + */ + +#ifndef metafunction_h +#define metafunction_h + +#include "object.h" + +/* ------------------------------------------------------- + * Metafunction objects + * ------------------------------------------------------- */ + +extern objecttype objectmetafunctiontype; +#define OBJECT_METAFUNCTION objectmetafunctiontype + +/** A metafunction object */ +typedef struct sobjectmetafunction { + object obj; +} objectmetafunction; + +/** Gets an objectmetafunction from a value */ +#define MORPHO_GETMETAFUNCTION(val) ((objectmetafunction *) MORPHO_GETOBJECT(val)) + +/** Tests whether an object is a metafunction */ +#define MORPHO_ISMETAFUNCTION(val) object_istype(val, OBJECT_METAFUNCTION) + +/* ------------------------------------------------------- + * Metafunction veneer class + * ------------------------------------------------------- */ + +#define METAFUNCTION_CLASSNAME "Metafunction" + +/* ------------------------------------------------------- + * Metafunction error messages + * ------------------------------------------------------- */ + +/* ------------------------------------------------------- + * Metafunction interface + * ------------------------------------------------------- */ + +void metafunction_initialize(void); + +#endif From c9a4389339c97171e80b6b6d9a3891f4f92930b8 Mon Sep 17 00:00:00 2001 From: Tim Atherton <54725421+softmattertheory@users.noreply.github.com> Date: Tue, 16 Apr 2024 21:08:21 -0400 Subject: [PATCH 045/181] More metafunction --- src/classes/metafunction.c | 30 +++++++++++++++++++++++++++--- src/classes/metafunction.h | 7 +++++++ src/support/common.h | 1 + 3 files changed, 35 insertions(+), 3 deletions(-) diff --git a/src/classes/metafunction.c b/src/classes/metafunction.c index 3e5511d1..056d4461 100644 --- a/src/classes/metafunction.c +++ b/src/classes/metafunction.c @@ -13,7 +13,8 @@ * ********************************************************************** */ void objectmetafunction_freefn(object *obj) { - objectmetafunction *func = (objectmetafunction *) obj; + objectmetafunction *f = (objectmetafunction *) obj; + varray_valueclear(f->fns); } void objectmetafunction_markfn(object *obj, void *v) { @@ -21,12 +22,13 @@ void objectmetafunction_markfn(object *obj, void *v) { } size_t objectmetafunction_sizefn(object *obj) { - return sizeof(objectmetafunction); + objectmetafunction *f = (objectmetafunction *) obj; + return sizeof(objectmetafunction)+sizeof(value)*f->fns.count; } void objectmetafunction_printfn(object *obj, void *v) { objectmetafunction *f = (objectmetafunction *) obj; - //if (f) morpho_printf(v, "", (MORPHO_ISNIL(f->name) ? "" : MORPHO_GETCSTRING(f->name))); + if (f) morpho_printf(v, "", (MORPHO_ISNIL(f->name) ? "" : MORPHO_GETCSTRING(f->name))); } objecttypedefn objectmetafunctiondefn = { @@ -42,6 +44,28 @@ objecttypedefn objectmetafunctiondefn = { * objectmetafunction utility functions * ********************************************************************** */ +/** Creates a new metafunction */ +objectmetafunction *object_newmetafunction(value name) { + objectmetafunction *new = (objectmetafunction *) object_new(sizeof(objectmetafunction), OBJECT_LIST); + + if (new) { + new->name=MORPHO_NIL; + if (MORPHO_ISSTRING(name)) new->name=object_clonestring(name); + varray_valueinit(new->fns); + } + + return new; +} + +/** Adds a function to a metafunction */ +bool metafunction_add(objectmetafunction *f, value fn) { + return varray_valuewrite(&f->fns, fn); +} + +/** Resolves a metafunction given calling arguments */ +bool metafunction_resolve(objectmetafunction *f, int nargs, value *args, value *fn) { + return false; +} /* ********************************************************************** * Metafunction veneer class diff --git a/src/classes/metafunction.h b/src/classes/metafunction.h index ffbb1491..987745f4 100644 --- a/src/classes/metafunction.h +++ b/src/classes/metafunction.h @@ -19,6 +19,8 @@ extern objecttype objectmetafunctiontype; /** A metafunction object */ typedef struct sobjectmetafunction { object obj; + value name; + varray_value fns; } objectmetafunction; /** Gets an objectmetafunction from a value */ @@ -41,6 +43,11 @@ typedef struct sobjectmetafunction { * Metafunction interface * ------------------------------------------------------- */ +objectmetafunction *object_newmetafunction(value name); + +bool metafunction_add(objectmetafunction *f, value fn); +bool metafunction_resolve(objectmetafunction *f, int nargs, value *args, value *fn); + void metafunction_initialize(void); #endif diff --git a/src/support/common.h b/src/support/common.h index 3083cb8a..4b356d54 100644 --- a/src/support/common.h +++ b/src/support/common.h @@ -55,6 +55,7 @@ void morpho_setdebuggerfn(vm *v, morphodebuggerfn debuggerfn, void *ref); static inline bool morpho_iscallable(value a) { return (MORPHO_ISFUNCTION(a) || MORPHO_ISBUILTINFUNCTION(a) || + MORPHO_ISMETAFUNCTION(a) || MORPHO_ISINVOCATION(a) || MORPHO_ISCLOSURE(a) || MORPHO_ISCLASS(a)); From 8a44b6d691a9871b4b71afb5e004122380f33b11 Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Wed, 17 Apr 2024 21:43:10 -0400 Subject: [PATCH 046/181] First multiple dispatch --- src/classes/function.c | 11 +++++ src/classes/function.h | 2 + src/classes/metafunction.c | 57 +++++++++++++++++++++-- src/classes/metafunction.h | 2 + src/core/compile.c | 43 ++++++++++------- src/core/vm.c | 5 +- test/types/class_multiple_dispatch.morpho | 23 +++++++++ test/types/multiple_dispatch.morpho | 8 ++-- 8 files changed, 127 insertions(+), 24 deletions(-) create mode 100644 test/types/class_multiple_dispatch.morpho diff --git a/src/classes/function.c b/src/classes/function.c index 2f0d7e02..4b93d6ad 100644 --- a/src/classes/function.c +++ b/src/classes/function.c @@ -17,6 +17,7 @@ void objectfunction_freefn(object *obj) { morpho_freeobject(func->name); varray_optionalparamclear(&func->opt); object_functionclear(func); + varray_valueclear(&func->signature); } void objectfunction_markfn(object *obj, void *v) { @@ -57,6 +58,7 @@ void object_functioninit(objectfunction *func) { func->nregs=0; varray_valueinit(&func->konst); varray_varray_upvalueinit(&func->prototype); + varray_valueinit(&func->signature); } /** @brief Clears a function */ @@ -67,6 +69,7 @@ void object_functionclear(objectfunction *func) { varray_upvalueclear(&func->prototype.data[i]); } varray_varray_upvalueclear(&func->prototype); + varray_valueclear(&func->signature); } /** @brief Creates a new function */ @@ -131,6 +134,14 @@ bool object_functionaddprototype(objectfunction *func, varray_upvalue *v, indx * return success; } +/** Sets the signature of a function + * @param[in] func function object + * @param[in] signature list of types for each parameter (length from func->nargs) */ +bool function_setsignature(objectfunction *func, value *signature) { + func->signature.count=0; // Reset signature; + return varray_valueadd(&func->signature, signature, func->nargs); +} + /* ********************************************************************** * Function veneer class * ********************************************************************** */ diff --git a/src/classes/function.h b/src/classes/function.h index 8b6eda30..4e605326 100644 --- a/src/classes/function.h +++ b/src/classes/function.h @@ -38,6 +38,7 @@ typedef struct sobjectfunction { varray_value konst; varray_varray_upvalue prototype; varray_optionalparam opt; + varray_value signature; } objectfunction; /** Gets an objectfunction from a value */ @@ -69,6 +70,7 @@ varray_value *object_functiongetconstanttable(objectfunction *func); objectfunction *object_newfunction(indx entry, value name, objectfunction *parent, unsigned int nargs); bool object_functionhasvargs(objectfunction *func); void object_functionsetvarg(objectfunction *func, unsigned int varg); +bool function_setsignature(objectfunction *func, value *signature); void objectfunction_printfn(object *obj, void *v); diff --git a/src/classes/metafunction.c b/src/classes/metafunction.c index 056d4461..69bcb3fe 100644 --- a/src/classes/metafunction.c +++ b/src/classes/metafunction.c @@ -14,7 +14,7 @@ void objectmetafunction_freefn(object *obj) { objectmetafunction *f = (objectmetafunction *) obj; - varray_valueclear(f->fns); + varray_valueclear(&f->fns); } void objectmetafunction_markfn(object *obj, void *v) { @@ -46,24 +46,73 @@ objecttypedefn objectmetafunctiondefn = { /** Creates a new metafunction */ objectmetafunction *object_newmetafunction(value name) { - objectmetafunction *new = (objectmetafunction *) object_new(sizeof(objectmetafunction), OBJECT_LIST); + objectmetafunction *new = (objectmetafunction *) object_new(sizeof(objectmetafunction), OBJECT_METAFUNCTION); if (new) { new->name=MORPHO_NIL; if (MORPHO_ISSTRING(name)) new->name=object_clonestring(name); - varray_valueinit(new->fns); + varray_valueinit(&new->fns); } return new; } +/** Wraps a function in a metafunction */ +bool metafunction_wrap(value name, value fn, value *out) { + if (!MORPHO_ISCALLABLE(fn)) return false; + + objectmetafunction *mf = object_newmetafunction(name); + if (!mf) return false; + + metafunction_add(mf, fn); + *out = MORPHO_OBJECT(mf); + + return true; +} + /** Adds a function to a metafunction */ bool metafunction_add(objectmetafunction *f, value fn) { return varray_valuewrite(&f->fns, fn); } +/** Extracts a type from a value */ +bool metafunction_typefromvalue(value v, value *out) { + objectclass *clss = NULL; + value type = MORPHO_NIL; + + if (MORPHO_ISINSTANCE(v)) { + clss=MORPHO_GETINSTANCE(v)->klass; + } else if (MORPHO_ISOBJECT(v)) { + clss = object_getveneerclass(MORPHO_GETOBJECT(v)->type); + } else clss = value_getveneerclass(v); + + if (clss) *out = MORPHO_OBJECT(clss); + return clss; +} + +/** Checks if val matches a given type */ +bool metafunction_matchtype(value type, value val) { + value match; + if (!metafunction_typefromvalue(val, &match)) return false; + + if (MORPHO_ISNIL(type) || // If type is unset, we always match + MORPHO_ISEQUAL(type, match)) return true; // Or if the types are the same + + return false; +} + /** Resolves a metafunction given calling arguments */ -bool metafunction_resolve(objectmetafunction *f, int nargs, value *args, value *fn) { +bool metafunction_resolve(objectmetafunction *f, int nargs, value *args, value *out) { + for (int i=0; ifns.count; i++) { + objectfunction *fn = MORPHO_GETFUNCTION(f->fns.data[i]); + if (nargs!=fn->nargs) continue; + bool match=true; + for (int j=0; jsignature.count && match; j++) { + if (!metafunction_matchtype(fn->signature.data[j], args[j])) match=false; + } + if (match) { *out=f->fns.data[i]; return true; } + } + return false; } diff --git a/src/classes/metafunction.h b/src/classes/metafunction.h index 987745f4..9cf52853 100644 --- a/src/classes/metafunction.h +++ b/src/classes/metafunction.h @@ -44,8 +44,10 @@ typedef struct sobjectmetafunction { * ------------------------------------------------------- */ objectmetafunction *object_newmetafunction(value name); +bool metafunction_wrap(value name, value fn, value *out); bool metafunction_add(objectmetafunction *f, value fn); +bool metafunction_typefromvalue(value v, value *out); bool metafunction_resolve(objectmetafunction *f, int nargs, value *args, value *fn); void metafunction_initialize(void); diff --git a/src/core/compile.c b/src/core/compile.c index a2a246b6..ada5f243 100644 --- a/src/core/compile.c +++ b/src/core/compile.c @@ -316,20 +316,7 @@ bool compiler_findtypefromcstring(compiler *c, char *label, value *out) { /** Identifies a type from a value */ bool compiler_typefromvalue(compiler *c, value v, value *out) { - objectclass *clss = NULL; - value type = MORPHO_NIL; - - if (MORPHO_ISINSTANCE(v)) { - clss=MORPHO_GETINSTANCE(v)->klass; - } else if (MORPHO_ISOBJECT(v)) { - clss = object_getveneerclass(MORPHO_GETOBJECT(v)->type); - } else clss = value_getveneerclass(v); - - if (clss) type = MORPHO_OBJECT(clss); - - if (!MORPHO_ISNIL(type)) *out = type; - - return (!MORPHO_ISNIL(type)); + return metafunction_typefromvalue(v, out); } /** Checks if type "match" matches a given type "type" */ @@ -573,6 +560,14 @@ void compiler_regsettype(compiler *c, registerindx reg, value type) { f->registers.data[reg].type=type; } +/** Gets the current type of a register */ +bool compiler_regtype(compiler *c, registerindx reg, value *type) { + functionstate *f = compiler_currentfunctionstate(c); + if (reg>=f->registers.count) return false; + *type = f->registers.data[reg].type; + return true; +} + /** Raises a type violation error */ void compiler_typeviolation(compiler *c, syntaxtreenode *node, value type, value badtype, value symbol) { char *tname="(unknown)", *bname="(unknown)"; @@ -2673,6 +2668,10 @@ static codeinfo compiler_function(compiler *c, syntaxtreenode *node, registerind compiler_functionparameters(c, node->left); func->nargs=compiler_regtop(c); + + value signature[func->nargs+1]; + for (int i=0; inargs; i++) compiler_regtype(c, i+1, &signature[i]); + function_setsignature(func, signature); /* Check we don't have too many arguments */ if (func->nargs>MORPHO_MAXARGS) { @@ -3031,8 +3030,20 @@ static codeinfo compiler_method(compiler *c, syntaxtreenode *node, registerindx /* Insert the compiled function into the method dictionary, making sure the method name is interned */ objectfunction *method = compiler_getpreviousfunction(c); if (method) { - value symbol = program_internsymbol(c->out, node->content); - dictionary_insert(&klass->methods, symbol, MORPHO_OBJECT(method)); + value omethod = MORPHO_OBJECT(method); + value symbol = program_internsymbol(c->out, node->content), + prev=MORPHO_NIL; + + if (dictionary_get(&klass->methods, symbol, &prev)) { + if (MORPHO_ISMETAFUNCTION(prev)) { + metafunction_add(MORPHO_GETMETAFUNCTION(prev), omethod); + break; + } else { + metafunction_wrap(symbol, prev, &prev); + metafunction_add(MORPHO_GETMETAFUNCTION(prev), omethod); + dictionary_insert(&klass->methods, symbol, prev); + } + } else dictionary_insert(&klass->methods, symbol, omethod); } } break; diff --git a/src/core/vm.c b/src/core/vm.c index 0e881b34..483ca06a 100644 --- a/src/core/vm.c +++ b/src/core/vm.c @@ -1051,6 +1051,7 @@ bool morpho_interpret(vm *v, value *rstart, instructionindx istart) { /* If we're not in the global context, invoke the method on self which is in r0 */ if (v->fp>v->frame) reg[a]=reg[0]; /* Copy self into r[a] and call */ + invoke_on_class: if (MORPHO_ISFUNCTION(ifunc)) { if (!vm_call(v, ifunc, a, c, NULL, &pc, ®)) goto vm_error; } else if (MORPHO_ISBUILTINFUNCTION(ifunc)) { @@ -1064,6 +1065,8 @@ bool morpho_interpret(vm *v, value *rstart, instructionindx istart) { v->fp->inbuiltinfunction=NULL; #endif ERRORCHK(); + } else if (MORPHO_ISMETAFUNCTION(ifunc)) { + if (metafunction_resolve(MORPHO_GETMETAFUNCTION(ifunc), c, reg+a+1, &ifunc)) goto invoke_on_class; } } else { /* Otherwise, raise an error */ @@ -1692,7 +1695,7 @@ bool morpho_call(vm *v, value f, int nargs, value *args, value *ret) { fn=inv->method; r0=inv->receiver; } - + if (MORPHO_ISBUILTINFUNCTION(fn)) { objectbuiltinfunction *f = MORPHO_GETBUILTINFUNCTION(fn); diff --git a/test/types/class_multiple_dispatch.morpho b/test/types/class_multiple_dispatch.morpho new file mode 100644 index 00000000..85c78f8a --- /dev/null +++ b/test/types/class_multiple_dispatch.morpho @@ -0,0 +1,23 @@ +// Dipatch methods on multiple types + +class A { + f(String x) { + print x + } + + f(Float x) { + print x+1 + } + + f(List x) { + print x[-1] + } +} + +A.f("Hi") +// expect: Hi + +A.f(1.5) +// expect: 2.5 + +A.f([1,2]) \ No newline at end of file diff --git a/test/types/multiple_dispatch.morpho b/test/types/multiple_dispatch.morpho index 39b481f0..420fd4a7 100644 --- a/test/types/multiple_dispatch.morpho +++ b/test/types/multiple_dispatch.morpho @@ -4,9 +4,9 @@ fn f(String x) { print x } -fn f(List x) { +/*fn f(List x) { print x.append("Ho") -} +}*/ fn f(Float x) { print x+1 @@ -18,7 +18,9 @@ f("Hi") f(1.5) // expect: 2.5 +/* var a = [] f(a) print a -// expect: [ "Ho" ] \ No newline at end of file +// expect: [ "Ho" ] +*/ \ No newline at end of file From 8d4f0c91262a117812993a265b1ac3a96fea4918 Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Wed, 17 Apr 2024 21:56:20 -0400 Subject: [PATCH 047/181] Invocations can be metafunctions too --- src/core/vm.c | 12 +++++++++--- test/types/class_multiple_dispatch.morpho | 11 +++++++++-- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/core/vm.c b/src/core/vm.c index 483ca06a..0857f265 100644 --- a/src/core/vm.c +++ b/src/core/vm.c @@ -1014,6 +1014,11 @@ bool morpho_interpret(vm *v, value *rstart, instructionindx istart) { /* Check if we have this method */ if (dictionary_getintern(&instance->klass->methods, right, &ifunc)) { + + if (MORPHO_ISMETAFUNCTION(ifunc)) { + metafunction_resolve(MORPHO_GETMETAFUNCTION(ifunc), c, reg+a+1, &ifunc); + } + /* If so, call it */ if (MORPHO_ISFUNCTION(ifunc)) { if (!vm_call(v, ifunc, a, c, NULL, &pc, ®)) goto vm_error; @@ -1051,7 +1056,10 @@ bool morpho_interpret(vm *v, value *rstart, instructionindx istart) { /* If we're not in the global context, invoke the method on self which is in r0 */ if (v->fp>v->frame) reg[a]=reg[0]; /* Copy self into r[a] and call */ - invoke_on_class: + if (MORPHO_ISMETAFUNCTION(ifunc)) { + metafunction_resolve(MORPHO_GETMETAFUNCTION(ifunc), c, reg+a+1, &ifunc); + } + if (MORPHO_ISFUNCTION(ifunc)) { if (!vm_call(v, ifunc, a, c, NULL, &pc, ®)) goto vm_error; } else if (MORPHO_ISBUILTINFUNCTION(ifunc)) { @@ -1065,8 +1073,6 @@ bool morpho_interpret(vm *v, value *rstart, instructionindx istart) { v->fp->inbuiltinfunction=NULL; #endif ERRORCHK(); - } else if (MORPHO_ISMETAFUNCTION(ifunc)) { - if (metafunction_resolve(MORPHO_GETMETAFUNCTION(ifunc), c, reg+a+1, &ifunc)) goto invoke_on_class; } } else { /* Otherwise, raise an error */ diff --git a/test/types/class_multiple_dispatch.morpho b/test/types/class_multiple_dispatch.morpho index 85c78f8a..542a2724 100644 --- a/test/types/class_multiple_dispatch.morpho +++ b/test/types/class_multiple_dispatch.morpho @@ -14,10 +14,17 @@ class A { } } -A.f("Hi") +var a = A() + +a.f("Hi") // expect: Hi A.f(1.5) // expect: 2.5 -A.f([1,2]) \ No newline at end of file +A.f([1,2]) + +var q = a.f + +a.f("Souper") +// expect: Souper From b26abc761384771d8d2cf2a30fe3bcc9311ac1ff Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Wed, 17 Apr 2024 22:35:41 -0400 Subject: [PATCH 048/181] MltplDsptchFld error --- src/core/vm.c | 5 ++-- src/datastructures/error.h | 3 +++ .../types/class_multiple_dispatch_args.morpho | 23 +++++++++++++++++++ 3 files changed, 29 insertions(+), 2 deletions(-) create mode 100644 test/types/class_multiple_dispatch_args.morpho diff --git a/src/core/vm.c b/src/core/vm.c index 0857f265..fd382162 100644 --- a/src/core/vm.c +++ b/src/core/vm.c @@ -1016,7 +1016,7 @@ bool morpho_interpret(vm *v, value *rstart, instructionindx istart) { if (dictionary_getintern(&instance->klass->methods, right, &ifunc)) { if (MORPHO_ISMETAFUNCTION(ifunc)) { - metafunction_resolve(MORPHO_GETMETAFUNCTION(ifunc), c, reg+a+1, &ifunc); + if (!metafunction_resolve(MORPHO_GETMETAFUNCTION(ifunc), c, reg+a+1, &ifunc)) ERROR(VM_MLTPLDSPTCHFLD); } /* If so, call it */ @@ -1057,7 +1057,7 @@ bool morpho_interpret(vm *v, value *rstart, instructionindx istart) { if (v->fp>v->frame) reg[a]=reg[0]; /* Copy self into r[a] and call */ if (MORPHO_ISMETAFUNCTION(ifunc)) { - metafunction_resolve(MORPHO_GETMETAFUNCTION(ifunc), c, reg+a+1, &ifunc); + if (!metafunction_resolve(MORPHO_GETMETAFUNCTION(ifunc), c, reg+a+1, &ifunc)) ERROR(VM_MLTPLDSPTCHFLD); } if (MORPHO_ISFUNCTION(ifunc)) { @@ -2021,6 +2021,7 @@ void morpho_initialize(void) { morpho_defineerror(VM_ARRAYWRONGDIM, ERROR_HALT, VM_ARRAYWRONGDIM_MSG); morpho_defineerror(VM_DVZR, ERROR_HALT, VM_DVZR_MSG); morpho_defineerror(VM_GETINDEXARGS, ERROR_HALT, VM_GETINDEXARGS_MSG); + morpho_defineerror(VM_MLTPLDSPTCHFLD, ERROR_HALT, VM_MLTPLDSPTCHFLD_MSG); morpho_defineerror(VM_DBGQUIT, ERROR_HALT, VM_DBGQUIT_MSG); diff --git a/src/datastructures/error.h b/src/datastructures/error.h index 0db334e3..c6e55e96 100644 --- a/src/datastructures/error.h +++ b/src/datastructures/error.h @@ -188,6 +188,9 @@ void morpho_unreachable(const char *explanation); #define VM_GETINDEXARGS "NonintIndex" #define VM_GETINDEXARGS_MSG "Noninteger array index." +#define VM_MLTPLDSPTCHFLD "MltplDsptchFld" +#define VM_MLTPLDSPTCHFLD_MSG "Mutiple dispatch could not find an implementation that matches these arguments." + /* ------------------------------------------------------- * Error interface * ------------------------------------------------------- */ diff --git a/test/types/class_multiple_dispatch_args.morpho b/test/types/class_multiple_dispatch_args.morpho new file mode 100644 index 00000000..8dcf75d9 --- /dev/null +++ b/test/types/class_multiple_dispatch_args.morpho @@ -0,0 +1,23 @@ +// Dipatch methods on multiple types + +class A { + f(Float x, Int y) { + print x + y + } + + f(Float x, String y) { + print y + } + + f(List x) { + print x[-1] + } +} + +A.f([1]) // expect: 1 + +A.f(1.2, 1) // expect: 2.2 + +A.f(1.2, "Hello") // expect: Hello + +A.f(4) // expect error 'MltplDsptchFld' \ No newline at end of file From 9f719fde3b9027696ff47c82fc0cce10e6ea6c26 Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Thu, 18 Apr 2024 06:31:34 -0400 Subject: [PATCH 049/181] Fix metaconstruction with inheritance --- src/core/compile.c | 19 ++++++++++++------- test/inheritance/inherit_methods.morpho | 2 +- test/super/bound_method.morpho | 2 +- test/super/call_other_method.morpho | 2 +- test/super/call_same_method.morpho | 2 +- test/super/closure.morpho | 2 +- test/super/constructor.morpho | 2 +- test/super/extra_arguments.morpho | 2 +- test/super/indirectly_inherited.morpho | 4 ++-- test/super/missing_arguments.morpho | 2 +- test/super/no_superclass_method.morpho | 2 +- test/types/class_multiple_dispatch.morpho | 1 + 12 files changed, 24 insertions(+), 18 deletions(-) diff --git a/src/core/compile.c b/src/core/compile.c index ada5f243..3a52514d 100644 --- a/src/core/compile.c +++ b/src/core/compile.c @@ -3035,15 +3035,20 @@ static codeinfo compiler_method(compiler *c, syntaxtreenode *node, registerindx prev=MORPHO_NIL; if (dictionary_get(&klass->methods, symbol, &prev)) { - if (MORPHO_ISMETAFUNCTION(prev)) { + if (MORPHO_ISMETAFUNCTION(prev)) { // Add the method to the mf. metafunction_add(MORPHO_GETMETAFUNCTION(prev), omethod); - break; - } else { - metafunction_wrap(symbol, prev, &prev); - metafunction_add(MORPHO_GETMETAFUNCTION(prev), omethod); - dictionary_insert(&klass->methods, symbol, prev); + } else { // Check if we're replacing an inherited method + if ((MORPHO_ISFUNCTION(prev) && + MORPHO_GETFUNCTION(prev)->klass!=klass) || + (MORPHO_ISBUILTINFUNCTION(prev))) { // Just overwrite it + dictionary_insert(&klass->methods, symbol, omethod); + } else { // We're not, so wrap in a mf. + metafunction_wrap(symbol, prev, &prev); + metafunction_add(MORPHO_GETMETAFUNCTION(prev), omethod); + dictionary_insert(&klass->methods, symbol, prev); + } } - } else dictionary_insert(&klass->methods, symbol, omethod); + } else dictionary_insert(&klass->methods, symbol, omethod); // Just insert } } break; diff --git a/test/inheritance/inherit_methods.morpho b/test/inheritance/inherit_methods.morpho index 03376e60..a6cc4144 100644 --- a/test/inheritance/inherit_methods.morpho +++ b/test/inheritance/inherit_methods.morpho @@ -3,7 +3,7 @@ class Foo { override() { print "foo" } } -class Bar < Foo { +class Bar is Foo { methodOnBar() { print "bar" } override() { print "bar" } } diff --git a/test/super/bound_method.morpho b/test/super/bound_method.morpho index 6db8af51..cf1e573e 100644 --- a/test/super/bound_method.morpho +++ b/test/super/bound_method.morpho @@ -4,7 +4,7 @@ class A { } } -class B < A { +class B is A { getClosure() { return super.method } diff --git a/test/super/call_other_method.morpho b/test/super/call_other_method.morpho index 66eb8e25..1a1e8844 100644 --- a/test/super/call_other_method.morpho +++ b/test/super/call_other_method.morpho @@ -4,7 +4,7 @@ class Base { } } -class Derived < Base { +class Derived is Base { bar() { print "Derived.bar()" super.foo() diff --git a/test/super/call_same_method.morpho b/test/super/call_same_method.morpho index c41f18cd..bf6018b9 100644 --- a/test/super/call_same_method.morpho +++ b/test/super/call_same_method.morpho @@ -4,7 +4,7 @@ class Base { } } -class Derived < Base { +class Derived is Base { foo() { print "Derived.foo()" super.foo() diff --git a/test/super/closure.morpho b/test/super/closure.morpho index 42e01f2d..ca262dec 100644 --- a/test/super/closure.morpho +++ b/test/super/closure.morpho @@ -2,7 +2,7 @@ class Base { toString() { return "Base" } } -class Derived < Base { +class Derived is Base { getClosure() { fn closure() { return super.toString() diff --git a/test/super/constructor.morpho b/test/super/constructor.morpho index 96340ef6..f95b6eea 100644 --- a/test/super/constructor.morpho +++ b/test/super/constructor.morpho @@ -4,7 +4,7 @@ class Base { } } -class Derived < Base { +class Derived is Base { init() { print "Derived.init()" super.init("a", "b") diff --git a/test/super/extra_arguments.morpho b/test/super/extra_arguments.morpho index 88bf9cf4..3cc0ea67 100644 --- a/test/super/extra_arguments.morpho +++ b/test/super/extra_arguments.morpho @@ -4,7 +4,7 @@ class Base { } } -class Derived < Base { +class Derived is Base { foo() { print "Derived.foo()" // expect: Derived.foo() super.foo("a", "b", "c", "d") // expect error 'InvldArgs' diff --git a/test/super/indirectly_inherited.morpho b/test/super/indirectly_inherited.morpho index e5b32adf..4d5f42e8 100644 --- a/test/super/indirectly_inherited.morpho +++ b/test/super/indirectly_inherited.morpho @@ -4,9 +4,9 @@ class A { } } -class B < A {} +class B is A {} -class C < B { +class C is B { foo() { print "C.foo()" super.foo() diff --git a/test/super/missing_arguments.morpho b/test/super/missing_arguments.morpho index 1751107e..245bf287 100644 --- a/test/super/missing_arguments.morpho +++ b/test/super/missing_arguments.morpho @@ -4,7 +4,7 @@ class Base { } } -class Derived < Base { +class Derived is Base { foo() { super.foo(1) // expect error 'InvldArgs' } diff --git a/test/super/no_superclass_method.morpho b/test/super/no_superclass_method.morpho index 8d8b786f..2e25d194 100644 --- a/test/super/no_superclass_method.morpho +++ b/test/super/no_superclass_method.morpho @@ -1,6 +1,6 @@ class Base {} -class Derived < Base { +class Derived is Base { foo() { super.doesNotExist(1) // expect error 'ClssLcksMthd' } diff --git a/test/types/class_multiple_dispatch.morpho b/test/types/class_multiple_dispatch.morpho index 542a2724..a2329d43 100644 --- a/test/types/class_multiple_dispatch.morpho +++ b/test/types/class_multiple_dispatch.morpho @@ -23,6 +23,7 @@ A.f(1.5) // expect: 2.5 A.f([1,2]) +// expect: 2 var q = a.f From 36d3fbbb3d5bec2eaf80640f012cebb372d82311 Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Sat, 20 Apr 2024 11:35:32 -0400 Subject: [PATCH 050/181] Reorganization of globals --- src/core/compile.c | 22 +++++++++++----------- src/core/compile.h | 3 --- src/core/vm.c | 7 ++++--- src/datastructures/program.c | 28 +++++++++++++++++++++------- src/datastructures/program.h | 20 +++++++++++++++++++- 5 files changed, 55 insertions(+), 25 deletions(-) diff --git a/src/core/compile.c b/src/core/compile.c index 3a52514d..9e77b8f8 100644 --- a/src/core/compile.c +++ b/src/core/compile.c @@ -1008,12 +1008,12 @@ bool compiler_hasvariadicarg(compiler *c) { * ------------------------------------------- */ /** Should we use global variables or registers? */ -static bool compiler_checkglobal(compiler *c) { +bool compiler_checkglobal(compiler *c) { return ((c->fstackp==0) && (c->fstack[0].scopedepth==0)); } /** Finds a global symbol, optionally searching successively through parent compilers */ -static globalindx compiler_getglobal(compiler *c, value symbol, bool recurse) { +globalindx compiler_findglobal(compiler *c, value symbol, bool recurse) { for (compiler *cc=c; cc!=NULL; cc=cc->parent) { value indx; if (dictionary_get(&cc->globals, symbol, &indx)) { @@ -1027,13 +1027,13 @@ static globalindx compiler_getglobal(compiler *c, value symbol, bool recurse) { } /** Adds a global variable to */ -static globalindx compiler_addglobal(compiler *c, syntaxtreenode *node, value symbol) { - globalindx indx=compiler_getglobal(c, symbol, false); +globalindx compiler_addglobal(compiler *c, syntaxtreenode *node, value symbol) { + globalindx indx=compiler_findglobal(c, symbol, false); if (indx==GLOBAL_UNALLOCATED) { - if (dictionary_insert(&c->globals, object_clonestring(symbol), MORPHO_INTEGER(c->out->nglobals))) { - indx=c->out->nglobals; - c->out->nglobals++; + indx = program_addglobal(c->out, symbol); + + if (dictionary_insert(&c->globals, object_clonestring(symbol), MORPHO_INTEGER(indx))) { debugannotation_setglobal(&c->out->annotations, indx, symbol); } } @@ -1042,7 +1042,7 @@ static globalindx compiler_addglobal(compiler *c, syntaxtreenode *node, value sy } /* Moves the result of a calculation to an global variable */ -static codeinfo compiler_movetoglobal(compiler *c, syntaxtreenode *node, codeinfo in, globalindx slot) { +codeinfo compiler_movetoglobal(compiler *c, syntaxtreenode *node, codeinfo in, globalindx slot) { codeinfo use = in; codeinfo out = CODEINFO_EMPTY; bool tmp=false; @@ -1064,7 +1064,7 @@ static codeinfo compiler_movetoglobal(compiler *c, syntaxtreenode *node, codeinf } -static codeinfo compiler_addvariable(compiler *c, syntaxtreenode *node, value symbol) { +codeinfo compiler_addvariable(compiler *c, syntaxtreenode *node, value symbol) { codeinfo out=CODEINFO_EMPTY; if (compiler_checkglobal(c)) { @@ -3242,7 +3242,7 @@ static codeinfo compiler_symbol(compiler *c, syntaxtreenode *node, registerindx } /* Is it a global variable */ - ret.dest=compiler_getglobal(c, node->content, true); + ret.dest=compiler_findglobal(c, node->content, true); if (ret.dest!=REGISTER_UNALLOCATED) { ret.returntype=GLOBAL; return ret; @@ -3319,7 +3319,7 @@ static codeinfo compiler_assign(compiler *c, syntaxtreenode *node, registerindx /* .. or a global? */ if (reg==REGISTER_UNALLOCATED) { - reg=compiler_getglobal(c, var, true); + reg=compiler_findglobal(c, var, true); if (reg!=REGISTER_UNALLOCATED) { if (indxnode) { /* If an indexed global, move the global into a register */ diff --git a/src/core/compile.h b/src/core/compile.h index d1274a20..5d59794a 100644 --- a/src/core/compile.h +++ b/src/core/compile.h @@ -116,9 +116,6 @@ * Track globals * ------------------------------------------------------- */ -/** @brief Index of a global */ -typedef int globalindx; - /** @brief Value to indicate a global has not been allocated */ #define GLOBAL_UNALLOCATED -1 diff --git a/src/core/vm.c b/src/core/vm.c index fd382162..a71d647a 100644 --- a/src/core/vm.c +++ b/src/core/vm.c @@ -1662,9 +1662,10 @@ bool morpho_run(vm *v, program *p) { /* Initialize global variables */ int oldsize = v->globals.count; - varray_valueresize(&v->globals, p->nglobals); - v->globals.count=p->nglobals; - for (int i=oldsize; inglobals; i++) v->globals.data[i]=MORPHO_NIL; /* Zero out globals */ + int nglobals = program_countglobals(p); + varray_valueresize(&v->globals, nglobals); + v->globals.count=nglobals; + for (int i=oldsize; iglobals.data[i]=MORPHO_NIL; /* Zero out globals */ /* and initially set the register pointer to the bottom of the stack */ value *reg = v->stack.data; diff --git a/src/datastructures/program.c b/src/datastructures/program.c index ad967fe1..ef3a7bc8 100644 --- a/src/datastructures/program.c +++ b/src/datastructures/program.c @@ -16,20 +16,20 @@ * ********************************************************************** */ DEFINE_VARRAY(instruction, instruction); +DEFINE_VARRAY(globalinfo, globalinfo); /** @brief Initializes a program */ -static void vm_programinit(program *p) { +void program_init(program *p) { varray_instructioninit(&p->code); varray_debugannotationinit(&p->annotations); p->global=object_newfunction(MORPHO_PROGRAMSTART, MORPHO_NIL, NULL, 0); p->boundlist=NULL; dictionary_init(&p->symboltable); - //builtin_copysymboltable(&p->symboltable); - p->nglobals=0; + varray_globalinfoinit(&p->globals); } /** @brief Clears a program, freeing associated data structures */ -static void vm_programclear(program *p) { +void program_clear(program *p) { if (p->global) object_free((object *) p->global); varray_instructionclear(&p->code); debugannotation_clear(&p->annotations); @@ -46,21 +46,23 @@ static void vm_programclear(program *p) { #ifdef MORPHO_DEBUG_LOGGARBAGECOLLECTOR fprintf(stderr, "------\n"); #endif - dictionary_clear(&p->symboltable); /* Note we don't free the contents as they should be bound to the program */ + /* Note we don't free the contents as they are already interned */ + varray_globalinfoclear(&p->globals); + dictionary_clear(&p->symboltable); } /** @brief Creates and initializes a new program */ program *morpho_newprogram(void) { program *new = MORPHO_MALLOC(sizeof(program)); - if (new) vm_programinit(new); + if (new) program_init(new); return new; } /** @brief Frees a program */ void morpho_freeprogram(program *p) { - vm_programclear(p); + program_clear(p); MORPHO_FREE(p); } @@ -115,3 +117,15 @@ value program_internsymbol(program *p, value symbol) { program_bindobject(p, MORPHO_GETOBJECT(out)); return out; } + +/** @brief Adds a global to the program */ +globalindx program_addglobal(program *p, value symbol) { + globalinfo info = { .symbol = program_internsymbol(p, symbol), .type=MORPHO_NIL }; + + return (globalindx) varray_globalinfowrite(&p->globals, info); +} + +/** @brief Returns the number of globals allocated in the program */ +int program_countglobals(program *p) { + return p->globals.count; +} diff --git a/src/datastructures/program.h b/src/datastructures/program.h index d58dc73f..fb54d739 100644 --- a/src/datastructures/program.h +++ b/src/datastructures/program.h @@ -24,6 +24,21 @@ DECLARE_VARRAY(instruction, instruction); /** @brief Index into instructions */ typedef indx instructionindx; +/* ------------------------------------------------------- + * Global variables + * ------------------------------------------------------- */ + +/** @brief Index of a global */ +typedef int globalindx; + +/** @brief Record information about each global variable */ +typedef struct { + value symbol; + value type; +} globalinfo; + +DECLARE_VARRAY(globalinfo, globalinfo) + /* ------------------------------------------------------- * Programs comprise instructions and debugging information * ------------------------------------------------------- */ @@ -33,7 +48,7 @@ typedef struct { varray_instruction code; /** Compiled instructions */ varray_debugannotation annotations; /** Information about how the code connects to the source */ objectfunction *global; /** Pseudofunction containing global data */ - unsigned int nglobals; + varray_globalinfo globals; /** Global variables */ object *boundlist; /** Linked list of static objects bound to this program */ dictionary symboltable; /** The symbol table */ } program; @@ -46,6 +61,9 @@ void program_bindobject(program *p, object *obj); value program_internsymbol(program *p, value symbol); +globalindx program_addglobal(program *p, value symbol); +int program_countglobals(program *p); + #endif /* MORPHO_CORE */ #endif /* error_h */ From bdaece8ec06ee536744ed0fffda6d2e5e05f783f Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Sat, 20 Apr 2024 13:27:04 -0400 Subject: [PATCH 051/181] Type tracking for globals --- src/core/compile.c | 61 +++++++++++++++++++++---- src/datastructures/program.c | 21 +++++++++ src/datastructures/program.h | 3 ++ test/types/type_violation_global.morpho | 5 ++ 4 files changed, 81 insertions(+), 9 deletions(-) create mode 100644 test/types/type_violation_global.morpho diff --git a/src/core/compile.c b/src/core/compile.c index 9e77b8f8..42e16fce 100644 --- a/src/core/compile.c +++ b/src/core/compile.c @@ -922,7 +922,7 @@ static codeinfo compiler_movetoregister(compiler *c, syntaxtreenode *node, codei compiler_addinstruction(c, ENCODE_DOUBLE(OP_LUP, out.dest, info.dest), node); out.ninstructions++; } else if (CODEINFO_ISGLOBAL(info)) { - /* Move upvalues */ + /* Move globals */ out.dest=compiler_regtemp(c, reg); out.returntype=REGISTER; compiler_addinstruction(c, ENCODE_LONG(OP_LGL, out.dest, info.dest), node); @@ -1041,6 +1041,45 @@ globalindx compiler_addglobal(compiler *c, syntaxtreenode *node, value symbol) { return indx; } +/** Sets the type of a global variable */ +void compiler_setglobaltype(compiler *c, globalindx indx, value type) { + program_globalsettype(c->out, indx, type); +} + +/** Checks if the type match satisfies the type of the global variable indx */ +bool compiler_checkglobaltype(compiler *c, syntaxtreenode *node, globalindx indx, value match) { + value type=MORPHO_NIL; + if (!program_globaltype(c->out, indx, &type)) return false; + + bool success=compiler_checktype(c, type, match); + + if (!success) { + value symbol=MORPHO_NIL; + program_globalsymbol(c->out, indx, &symbol); + compiler_typeviolation(c, node, type, match, symbol); + } + + return success; +} + +/** Shows all currently allocated globals */ +void compiler_globalshow(compiler *c) { + int nglobals = program_countglobals(c->out); + printf("--Globals (%u in use)\n", nglobals); + for (unsigned int i=0; iout->globals.data[i]; + printf("g%u ",i); + if (!MORPHO_ISNIL(r->symbol)) morpho_printvalue(NULL, r->symbol); + if (!MORPHO_ISNIL(r->type)) { + printf(" ["); + morpho_printvalue(NULL, r->type); + printf("]"); + } + printf("\n"); + } + printf("--End globals\n"); +} + /* Moves the result of a calculation to an global variable */ codeinfo compiler_movetoglobal(compiler *c, syntaxtreenode *node, codeinfo in, globalindx slot) { codeinfo use = in; @@ -1053,17 +1092,20 @@ codeinfo compiler_movetoglobal(compiler *c, syntaxtreenode *node, codeinfo in, g tmp=true; } + value type=MORPHO_NIL; + if (compiler_regcurrenttype(c, in.dest, &type)) { + if (!compiler_checkglobaltype(c, node, slot, type)) goto compiler_movetoglobal_cleanup; + } + compiler_addinstruction(c, ENCODE_LONG(OP_SGL, use.dest, slot) , node); out.ninstructions++; - if (tmp) { - compiler_releaseoperand(c, use); - } - +compiler_movetoglobal_cleanup: + + if (tmp) compiler_releaseoperand(c, use); return out; } - codeinfo compiler_addvariable(compiler *c, syntaxtreenode *node, value symbol) { codeinfo out=CODEINFO_EMPTY; @@ -2466,7 +2508,7 @@ static codeinfo compiler_declaration(compiler *c, syntaxtreenode *node, register syntaxtreenode *varnode = NULL; syntaxtreenode *lftnode = NULL, *indxnode = NULL; - codeinfo right; + codeinfo right=CODEINFO_EMPTY; value var=MORPHO_NIL, type=MORPHO_NIL; registerindx reg; unsigned int ninstructions = 0; @@ -2504,6 +2546,7 @@ static codeinfo compiler_declaration(compiler *c, syntaxtreenode *node, register if (typenode && compiler_findtype(c, typenode->content, &type)) { compiler_regsettype(c, reg, type); + if (vloc.returntype==GLOBAL) compiler_setglobaltype(c, vloc.dest, type); } /* If this is an array, we must create it */ @@ -2546,8 +2589,6 @@ static codeinfo compiler_declaration(compiler *c, syntaxtreenode *node, register /* Ensure operand is in the desired register */ right=compiler_movetoregister(c, decnode, right, reg); ninstructions+=right.ninstructions; - - compiler_releaseoperand(c, right); } else { /* Otherwise, we should zero out the register */ registerindx cnil = compiler_addconstant(c, decnode, MORPHO_NIL, false, false); compiler_addinstruction(c, ENCODE_LONG(OP_LCT, reg, cnil), node); @@ -2560,6 +2601,8 @@ static codeinfo compiler_declaration(compiler *c, syntaxtreenode *node, register compiler_regfreetemp(c, reg); } + + compiler_releaseoperand(c, right); } return CODEINFO(REGISTER, REGISTER_UNALLOCATED, ninstructions); diff --git a/src/datastructures/program.c b/src/datastructures/program.c index ef3a7bc8..df88898f 100644 --- a/src/datastructures/program.c +++ b/src/datastructures/program.c @@ -125,6 +125,27 @@ globalindx program_addglobal(program *p, value symbol) { return (globalindx) varray_globalinfowrite(&p->globals, info); } +/** @brief Sets the type associated with a global variable */ +void program_globalsettype(program *p, globalindx indx, value type) { + if (indx<0 || indx>p->globals.count) return; + + p->globals.data[indx].type=type; +} + +/** @brief Gets the type associated with a global variable */ +bool program_globaltype(program *p, globalindx indx, value *type) { + if (indx<0 || indx>p->globals.count) return false; + *type = p->globals.data[indx].type; + return true; +} + +/** @brief Gets the symbol associated with a global variable */ +bool program_globalsymbol(program *p, globalindx indx, value *symbol) { + if (indx<0 || indx>p->globals.count) return false; + *symbol = p->globals.data[indx].symbol; + return true; +} + /** @brief Returns the number of globals allocated in the program */ int program_countglobals(program *p) { return p->globals.count; diff --git a/src/datastructures/program.h b/src/datastructures/program.h index fb54d739..7450db73 100644 --- a/src/datastructures/program.h +++ b/src/datastructures/program.h @@ -62,6 +62,9 @@ void program_bindobject(program *p, object *obj); value program_internsymbol(program *p, value symbol); globalindx program_addglobal(program *p, value symbol); +void program_globalsettype(program *p, globalindx indx, value type); +bool program_globaltype(program *p, globalindx indx, value *type); +bool program_globalsymbol(program *p, globalindx indx, value *symbol); int program_countglobals(program *p); #endif /* MORPHO_CORE */ diff --git a/test/types/type_violation_global.morpho b/test/types/type_violation_global.morpho new file mode 100644 index 00000000..f964ad40 --- /dev/null +++ b/test/types/type_violation_global.morpho @@ -0,0 +1,5 @@ +// Type definition in global context + +String u = 1 + +// expect error 'TypeErr' From aaf47fd0eb4cb249adedb904bbfd67ac0068f6a2 Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Sat, 20 Apr 2024 15:38:17 -0400 Subject: [PATCH 052/181] Multiple dispatch on functions --- src/core/compile.c | 83 +++++++++++++++++-- src/core/compile.h | 13 +++ src/core/vm.c | 5 ++ test/types/multiple_dispatch.morpho | 13 ++- .../multiple_dispatch_in_function.morpho | 20 +++++ 5 files changed, 119 insertions(+), 15 deletions(-) create mode 100644 test/types/multiple_dispatch_in_function.morpho diff --git a/src/core/compile.c b/src/core/compile.c index 42e16fce..a7514fd9 100644 --- a/src/core/compile.c +++ b/src/core/compile.c @@ -123,6 +123,7 @@ static void compiler_functionstateinit(functionstate *state) { varray_registerallocinit(&state->registers); varray_forwardreferenceinit(&state->forwardref); varray_upvalueinit(&state->upvalues); + varray_functionrefinit(&state->functionref); } /** Clears a functionstate structure */ @@ -134,6 +135,7 @@ static void compiler_functionstateclear(functionstate *state) { varray_registerallocclear(&state->registers); varray_forwardreferenceclear(&state->forwardref); varray_upvalueclear(&state->upvalues); + varray_functionrefclear(&state->functionref); } /** Initializes the function stack */ @@ -170,12 +172,62 @@ static inline bool compiler_ininitializer(compiler *c) { return FUNCTIONTYPE_ISINITIALIZER(f->type); } +/* ------------------------------------------ + * Manage the functionref stack + * ------------------------------------------- */ + +DEFINE_VARRAY(functionref, functionref) + +/** Adds a reference to a function in the current functionstate */ +void compiler_addfunctionref(compiler *c, objectfunction *func) { + functionstate *f=compiler_currentfunctionstate(c); + functionref ref = { .function = MORPHO_OBJECT(func), .symbol = func->name, .scopedepth = f->scopedepth}; + varray_functionrefwrite(&f->functionref, ref); +} + +/** Removes functions only visible within a */ +void compiler_functionreffreeatscope(compiler *c, unsigned int scope) { + functionstate *f=compiler_currentfunctionstate(c); + + /*for (int i=f->functionref.count-1; i>=0; i--) { + + }*/ +} + +void _addmatchingfunctionref(compiler *c, value symbol, value fn, value *out) { + value in = *out; + if (MORPHO_ISNIL(in)) { + *out=fn; return; + } else if (MORPHO_ISFUNCTION(in)) { + if (metafunction_wrap(symbol, in, out)) metafunction_add(MORPHO_GETMETAFUNCTION(*out), fn); + } else if (MORPHO_ISMETAFUNCTION(in)) { + metafunction_add(MORPHO_GETMETAFUNCTION(in), fn); + } +} + +bool compiler_resolvefunctionref(compiler *c, value symbol, value *out) { + functionstate *f=compiler_currentfunctionstate(c); + value fnd=MORPHO_NIL; + int match=0; + + for (int i=0; ifunctionref.count; i++) { + functionref *ref=&f->functionref.data[i]; + if (MORPHO_ISEQUAL(ref->symbol, symbol)) { + _addmatchingfunctionref(c, ref->symbol, ref->function, &fnd); + match++; + } + } + + if (match) *out=fnd; + return match; +} + /* ------------------------------------------ * Increment and decrement the fstack * ------------------------------------------- */ /** Begins a new function, advancing the fstack pointer */ -static void compiler_beginfunction(compiler *c, objectfunction *func, functiontype type) { +void compiler_beginfunction(compiler *c, objectfunction *func, functiontype type) { c->fstackp++; compiler_functionstateinit(&c->fstack[c->fstackp]); c->fstack[c->fstackp].func=func; @@ -184,13 +236,13 @@ static void compiler_beginfunction(compiler *c, objectfunction *func, functionty } /** Sets the function register count */ -static void compiler_setfunctionregistercount(compiler *c) { +void compiler_setfunctionregistercount(compiler *c) { functionstate *f=&c->fstack[c->fstackp]; if (f->nreg>f->func->nregs) f->func->nregs=f->nreg; } /** Ends a function, decrementing the fstack pointer */ -static void compiler_endfunction(compiler *c) { +void compiler_endfunction(compiler *c) { functionstate *f=&c->fstack[c->fstackp]; c->prevfunction=f->func; /* Retain the function in case it needs to be bound as a method */ compiler_setfunctionregistercount(c); @@ -200,12 +252,12 @@ static void compiler_endfunction(compiler *c) { } /** Gets the current function */ -static objectfunction *compiler_getcurrentfunction(compiler *c) { +objectfunction *compiler_getcurrentfunction(compiler *c) { return c->fstack[c->fstackp].func; } /** Gets the current constant table */ -static varray_value *compiler_getcurrentconstanttable(compiler *c) { +varray_value *compiler_getcurrentconstanttable(compiler *c) { objectfunction *f = compiler_getcurrentfunction(c); if (!f) { UNREACHABLE("find current constant table [No current function defined]."); @@ -215,7 +267,7 @@ static varray_value *compiler_getcurrentconstanttable(compiler *c) { } /** Gets constant i from the current constant table */ -static value compiler_getconstant(compiler *c, unsigned int i) { +value compiler_getconstant(compiler *c, unsigned int i) { value ret = MORPHO_NIL; objectfunction *f = compiler_getcurrentfunction(c); if (f && ikonst.count) ret = f->konst.data[i]; @@ -223,10 +275,12 @@ static value compiler_getconstant(compiler *c, unsigned int i) { } /** Gets the most recently compiled function */ -static objectfunction *compiler_getpreviousfunction(compiler *c) { +objectfunction *compiler_getpreviousfunction(compiler *c) { return c->prevfunction; } + + /* ------------------------------------------ * Argument declarations * ------------------------------------------- */ @@ -735,6 +789,7 @@ void compiler_beginscope(compiler *c) { void compiler_endscope(compiler *c) { functionstate *f=compiler_currentfunctionstate(c); compiler_regfreeatscope(c, f->scopedepth); + compiler_functionreffreeatscope(c, f->scopedepth); f->scopedepth--; } @@ -2693,6 +2748,9 @@ static codeinfo compiler_function(compiler *c, syntaxtreenode *node, registerind /* Add the function as a constant */ kindx=compiler_addconstant(c, node, MORPHO_OBJECT(func), false, false); + + /* Keep a reference to the function */ + if (!ismethod) compiler_addfunctionref(c, func); /* Begin the new function definition, finding whether the current function is a regular function or a method declaration by looking at currentmethod */ @@ -3272,11 +3330,20 @@ static codeinfo compiler_super(compiler *c, syntaxtreenode *node, registerindx r /** Lookup a symbol */ static codeinfo compiler_symbol(compiler *c, syntaxtreenode *node, registerindx reqout) { codeinfo ret=CODEINFO_EMPTY; - + /* Is it a local variable? */ ret.dest=compiler_getlocal(c, node->content); if (ret.dest!=REGISTER_UNALLOCATED) return ret; + /* Is it a reference to a function? */ + value fn=MORPHO_NIL; + if (compiler_resolvefunctionref(c, node->content, &fn)) { + /* It is; so add it to the constant table */ + ret.returntype=CONSTANT; + ret.dest=compiler_addconstant(c, node, fn, false, false); + return ret; + } + /* Is it an upvalue? */ ret.dest = compiler_resolveupvalue(c, node->content); if (ret.dest!=REGISTER_UNALLOCATED) { diff --git a/src/core/compile.h b/src/core/compile.h index 5d59794a..241a164a 100644 --- a/src/core/compile.h +++ b/src/core/compile.h @@ -186,6 +186,18 @@ typedef struct { DECLARE_VARRAY(forwardreference, forwardreference) +/* ------------------------------------------------------- + * Visible functions + * ------------------------------------------------------- */ + +typedef struct { + value symbol; /** Symbol associated with the reference */ + value function; /** The function itself */ + unsigned int scopedepth; /** Scope depth at which the function was seen */ +} functionref; + +DECLARE_VARRAY(functionref, functionref) + /* ------------------------------------------------------- * Function types * ------------------------------------------------------- */ @@ -208,6 +220,7 @@ typedef struct { varray_registeralloc registers; varray_upvalue upvalues; varray_forwardreference forwardref; + varray_functionref functionref; /* Functions visible within this state */ registerindx varg; unsigned int nreg; /* Largest number of registers used */ unsigned int scopedepth; diff --git a/src/core/vm.c b/src/core/vm.c index a71d647a..11160c34 100644 --- a/src/core/vm.c +++ b/src/core/vm.c @@ -936,6 +936,11 @@ bool morpho_interpret(vm *v, value *rstart, instructionindx istart) { left=reg[a]; c=DECODE_B(bc); // We use c for consistency between call and invoke... + if (MORPHO_ISMETAFUNCTION(left) && + !metafunction_resolve(MORPHO_GETMETAFUNCTION(left), c, reg+a+1, &left)) { + ERROR(VM_MLTPLDSPTCHFLD); + } + callfunction: // Jump here if an instruction becomes a call if (MORPHO_ISINVOCATION(left)) { /* An method invocation */ diff --git a/test/types/multiple_dispatch.morpho b/test/types/multiple_dispatch.morpho index 420fd4a7..39aa86d1 100644 --- a/test/types/multiple_dispatch.morpho +++ b/test/types/multiple_dispatch.morpho @@ -1,12 +1,12 @@ -// Dipatch on multiple types +// Dipatch a function on multiple types fn f(String x) { print x } -/*fn f(List x) { - print x.append("Ho") -}*/ +fn f(List x) { + x.append("Ho") +} fn f(Float x) { print x+1 @@ -15,12 +15,11 @@ fn f(Float x) { f("Hi") // expect: Hi + f(1.5) // expect: 2.5 -/* var a = [] f(a) print a -// expect: [ "Ho" ] -*/ \ No newline at end of file +// expect: [ Ho ] diff --git a/test/types/multiple_dispatch_in_function.morpho b/test/types/multiple_dispatch_in_function.morpho new file mode 100644 index 00000000..565d5681 --- /dev/null +++ b/test/types/multiple_dispatch_in_function.morpho @@ -0,0 +1,20 @@ +// Multiple dispatch on locally defined functions + +fn g(x) { + fn f(String x) { + print x + } + + fn f(Float x) { + print x+1 + } + + f(x) +} + +g("Hi") +// expect: Hi + + +g(1.5) +// expect: 2.5 From 088241917bc61395c06b62edaee7fab06c1914e8 Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Sat, 20 Apr 2024 16:23:30 -0400 Subject: [PATCH 053/181] Avoid allocating variables for function definitions --- src/core/compile.c | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/src/core/compile.c b/src/core/compile.c index a7514fd9..2f9772ce 100644 --- a/src/core/compile.c +++ b/src/core/compile.c @@ -2811,14 +2811,16 @@ static codeinfo compiler_function(compiler *c, syntaxtreenode *node, registerind /* Generate a closure prototype if necessary */ closure=compiler_closure(c, node, REGISTER_UNALLOCATED); - /* Allocate a variable to refer to the function definition */ + /* Allocate a variable to refer to the function definition, but only in global + context */ codeinfo fvar=CODEINFO_EMPTY; - if (isanonymous) { - fvar.dest=compiler_regtemp(c, reqout); - fvar.returntype=REGISTER; - } else { - /* Check if this resolves a forward reference */ - if (!compiler_resolveforwardreference(c, func->name, &fvar)) { + fvar.dest=compiler_regtemp(c, reqout); + fvar.returntype=REGISTER; + + if (!isanonymous) { + if (!compiler_resolveforwardreference(c, func->name, &fvar) && + compiler_checkglobal(c)) { + compiler_regfreetemp(c, fvar.dest); fvar=compiler_addvariable(c, node, node->content); } } @@ -2833,6 +2835,7 @@ static codeinfo compiler_function(compiler *c, syntaxtreenode *node, registerind /* Wrap in a closure if necessary */ if (closure!=REGISTER_UNALLOCATED) { + compiler_regsetsymbol(c, reg, node->content); compiler_addinstruction(c, ENCODE_DOUBLE(OP_CLOSURE, reg, (registerindx) closure), node); ninstructions++; } @@ -3335,6 +3338,13 @@ static codeinfo compiler_symbol(compiler *c, syntaxtreenode *node, registerindx ret.dest=compiler_getlocal(c, node->content); if (ret.dest!=REGISTER_UNALLOCATED) return ret; + /* Is it an upvalue? */ + ret.dest = compiler_resolveupvalue(c, node->content); + if (ret.dest!=REGISTER_UNALLOCATED) { + ret.returntype=UPVALUE; + return ret; + } + /* Is it a reference to a function? */ value fn=MORPHO_NIL; if (compiler_resolvefunctionref(c, node->content, &fn)) { @@ -3343,13 +3353,6 @@ static codeinfo compiler_symbol(compiler *c, syntaxtreenode *node, registerindx ret.dest=compiler_addconstant(c, node, fn, false, false); return ret; } - - /* Is it an upvalue? */ - ret.dest = compiler_resolveupvalue(c, node->content); - if (ret.dest!=REGISTER_UNALLOCATED) { - ret.returntype=UPVALUE; - return ret; - } /* Is it a global variable */ ret.dest=compiler_findglobal(c, node->content, true); From 3e3c7300d6d0d1fc36f10e04c88c1e64b63e30fe Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Sat, 20 Apr 2024 17:24:39 -0400 Subject: [PATCH 054/181] Update compile.c --- src/core/compile.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/compile.c b/src/core/compile.c index 2f9772ce..a674abc4 100644 --- a/src/core/compile.c +++ b/src/core/compile.c @@ -205,6 +205,7 @@ void _addmatchingfunctionref(compiler *c, value symbol, value fn, value *out) { } } +/** Determines whether a symbol refers to one (or more) functions. If so, returns either a single function or a metafunction as appropriate. */ bool compiler_resolvefunctionref(compiler *c, value symbol, value *out) { functionstate *f=compiler_currentfunctionstate(c); value fnd=MORPHO_NIL; From 9478cffa89f2a4c136141109433060a90b014a80 Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Sat, 20 Apr 2024 17:44:08 -0400 Subject: [PATCH 055/181] Wrap typed function in metafunction --- src/classes/function.c | 6 ++++++ src/classes/function.h | 1 + src/core/compile.c | 5 ++++- test/types/apply_multiple_dispatch.morpho | 15 +++++++++++++++ test/types/function_signature.morpho | 2 +- 5 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 test/types/apply_multiple_dispatch.morpho diff --git a/src/classes/function.c b/src/classes/function.c index 4b93d6ad..9a990168 100644 --- a/src/classes/function.c +++ b/src/classes/function.c @@ -142,6 +142,12 @@ bool function_setsignature(objectfunction *func, value *signature) { return varray_valueadd(&func->signature, signature, func->nargs); } +/** Returns true if any of the parameters are typed */ +bool function_hastypedparameters(objectfunction *func) { + for (int i=0; isignature.count; i++) if (!MORPHO_ISNIL(func->signature.data[i])) return true; + return false; +} + /* ********************************************************************** * Function veneer class * ********************************************************************** */ diff --git a/src/classes/function.h b/src/classes/function.h index 4e605326..c470dd52 100644 --- a/src/classes/function.h +++ b/src/classes/function.h @@ -71,6 +71,7 @@ objectfunction *object_newfunction(indx entry, value name, objectfunction *paren bool object_functionhasvargs(objectfunction *func); void object_functionsetvarg(objectfunction *func, unsigned int varg); bool function_setsignature(objectfunction *func, value *signature); +bool function_hastypedparameters(objectfunction *func); void objectfunction_printfn(object *obj, void *v); diff --git a/src/core/compile.c b/src/core/compile.c index a674abc4..f6d5e70b 100644 --- a/src/core/compile.c +++ b/src/core/compile.c @@ -197,7 +197,10 @@ void compiler_functionreffreeatscope(compiler *c, unsigned int scope) { void _addmatchingfunctionref(compiler *c, value symbol, value fn, value *out) { value in = *out; if (MORPHO_ISNIL(in)) { - *out=fn; return; + // If the function has a signature, will need to wrap in a metafunction + if (MORPHO_ISFUNCTION(fn) && function_hastypedparameters(MORPHO_GETFUNCTION(fn))) { + metafunction_wrap(symbol, fn, out); + } else *out=fn; } else if (MORPHO_ISFUNCTION(in)) { if (metafunction_wrap(symbol, in, out)) metafunction_add(MORPHO_GETMETAFUNCTION(*out), fn); } else if (MORPHO_ISMETAFUNCTION(in)) { diff --git a/test/types/apply_multiple_dispatch.morpho b/test/types/apply_multiple_dispatch.morpho new file mode 100644 index 00000000..ccfbe0a3 --- /dev/null +++ b/test/types/apply_multiple_dispatch.morpho @@ -0,0 +1,15 @@ +// Dispatch with apply + +fn f(String x) { + print x +} + +fn f(Float x) { + print x+1 +} + +apply(f, "Hi") +// expect: Hi + +apply(f, 1.5) +// expect: 2.5 diff --git a/test/types/function_signature.morpho b/test/types/function_signature.morpho index a61999d1..9490d7e2 100644 --- a/test/types/function_signature.morpho +++ b/test/types/function_signature.morpho @@ -8,4 +8,4 @@ f("Hello", []) // expect: Hello f([], []) -// expect error '' +// expect error 'MltplDsptchFld' From 874817b338d3392c5981230b7190628bf278635b Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Sat, 20 Apr 2024 18:12:22 -0400 Subject: [PATCH 056/181] morpho_call now resolves metafunctions --- src/core/vm.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/core/vm.c b/src/core/vm.c index 11160c34..08f177f7 100644 --- a/src/core/vm.c +++ b/src/core/vm.c @@ -1701,6 +1701,12 @@ bool morpho_call(vm *v, value f, int nargs, value *args, value *ret) { value fn=f; value r0=f; + if (MORPHO_ISMETAFUNCTION(fn) && + !metafunction_resolve(MORPHO_GETMETAFUNCTION(fn), nargs, args, &fn)) { + morpho_runtimeerror(v, VM_MLTPLDSPTCHFLD); + return false; + } + if (MORPHO_ISINVOCATION(fn)) { /* An method invocation */ objectinvocation *inv = MORPHO_GETINVOCATION(f); From 12eff2befcbe6de118aa3ced918791d55d3c41cf Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Sat, 20 Apr 2024 19:32:57 -0400 Subject: [PATCH 057/181] Set MORPHO_FN_CONSTRUCTOR in constructors --- src/builtin/builtin.c | 10 ++++++++++ src/classes/array.c | 2 +- src/classes/cmplx.c | 2 +- src/classes/dict.c | 2 +- src/classes/file.c | 3 ++- src/classes/range.c | 2 +- src/classes/strng.c | 3 ++- src/classes/tuple.c | 2 +- 8 files changed, 19 insertions(+), 7 deletions(-) diff --git a/src/builtin/builtin.c b/src/builtin/builtin.c index c2bec790..1dfadd7c 100644 --- a/src/builtin/builtin.c +++ b/src/builtin/builtin.c @@ -207,6 +207,16 @@ value builtin_findfunction(value name) { return out; } +/** Sets the signature of a function */ +bool builtin_setsignature(objectbuiltinfunction *fn, value *signature) { + +} + +/** Parses a function signature */ +bool builtin_parsesignature(char *signature, varray_value *out) { + +} + /* ********************************************************************** * Create and find builtin classes * ********************************************************************** */ diff --git a/src/classes/array.c b/src/classes/array.c index dcbf46c7..8e00a396 100644 --- a/src/classes/array.c +++ b/src/classes/array.c @@ -604,7 +604,7 @@ void array_initialize(void) { value objclass = builtin_findclass(MORPHO_OBJECT(&objname)); // Array constructor function - builtin_addfunction(ARRAY_CLASSNAME, array_constructor, BUILTIN_FLAGSEMPTY); + builtin_addfunction(ARRAY_CLASSNAME, array_constructor, MORPHO_FN_CONSTRUCTOR); // Create Array veneer class value arrayclass=builtin_addclass(ARRAY_CLASSNAME, MORPHO_GETCLASSDEFINITION(Array), objclass); diff --git a/src/classes/cmplx.c b/src/classes/cmplx.c index db41d1ee..1bf7695c 100644 --- a/src/classes/cmplx.c +++ b/src/classes/cmplx.c @@ -691,7 +691,7 @@ void complex_initialize(void) { objectcomplextype=object_addtype(&objectcomplexdefn); // Complex constructor function - builtin_addfunction(COMPLEX_CLASSNAME, complex_constructor, BUILTIN_FLAGSEMPTY); + builtin_addfunction(COMPLEX_CLASSNAME, complex_constructor, MORPHO_FN_CONSTRUCTOR); objectstring objname = MORPHO_STATICSTRING(OBJECT_CLASSNAME); value objclass = builtin_findclass(MORPHO_OBJECT(&objname)); diff --git a/src/classes/dict.c b/src/classes/dict.c index ed893ea1..deb61720 100644 --- a/src/classes/dict.c +++ b/src/classes/dict.c @@ -286,7 +286,7 @@ void dict_initialize(void) { value objclass = builtin_findclass(MORPHO_OBJECT(&objname)); // Dictionary constructor function - builtin_addfunction(DICTIONARY_CLASSNAME, dictionary_constructor, BUILTIN_FLAGSEMPTY); + builtin_addfunction(DICTIONARY_CLASSNAME, dictionary_constructor, MORPHO_FN_CONSTRUCTOR); // Create dictionary veneer class value dictionaryclass=builtin_addclass(DICTIONARY_CLASSNAME, MORPHO_GETCLASSDEFINITION(Dictionary), objclass); diff --git a/src/classes/file.c b/src/classes/file.c index ea527ced..7106cfd3 100644 --- a/src/classes/file.c +++ b/src/classes/file.c @@ -459,7 +459,8 @@ void file_initialize(void) { objectstring objname = MORPHO_STATICSTRING(OBJECT_CLASSNAME); value objclass = builtin_findclass(MORPHO_OBJECT(&objname)); - builtin_addfunction(FILE_CLASSNAME, file_constructor, BUILTIN_FLAGSEMPTY); + builtin_addfunction(FILE_CLASSNAME, file_constructor, MORPHO_FN_CONSTRUCTOR); + value fileclass=builtin_addclass(FILE_CLASSNAME, MORPHO_GETCLASSDEFINITION(File), objclass); object_setveneerclass(OBJECT_FILE, fileclass); diff --git a/src/classes/range.c b/src/classes/range.c index 5724ea2b..d09c52c6 100644 --- a/src/classes/range.c +++ b/src/classes/range.c @@ -191,7 +191,7 @@ void range_initialize(void) { value objclass = builtin_findclass(MORPHO_OBJECT(&objname)); // Create range veneer class - builtin_addfunction(RANGE_CLASSNAME, range_constructor, BUILTIN_FLAGSEMPTY); + builtin_addfunction(RANGE_CLASSNAME, range_constructor, MORPHO_FN_CONSTRUCTOR); value rangeclass=builtin_addclass(RANGE_CLASSNAME, MORPHO_GETCLASSDEFINITION(Range), objclass); object_setveneerclass(OBJECT_RANGE, rangeclass); diff --git a/src/classes/strng.c b/src/classes/strng.c index c210f5b0..2ef2f895 100644 --- a/src/classes/strng.c +++ b/src/classes/strng.c @@ -315,7 +315,8 @@ void string_initialize(void) { value objclass = builtin_findclass(MORPHO_OBJECT(&objname)); // Create string veneer class - builtin_addfunction(STRING_CLASSNAME, string_constructor, BUILTIN_FLAGSEMPTY); + builtin_addfunction(STRING_CLASSNAME, string_constructor, MORPHO_FN_CONSTRUCTOR); + value stringclass=builtin_addclass(STRING_CLASSNAME, MORPHO_GETCLASSDEFINITION(String), objclass); object_setveneerclass(OBJECT_STRING, stringclass); } diff --git a/src/classes/tuple.c b/src/classes/tuple.c index f289c729..aa023e91 100644 --- a/src/classes/tuple.c +++ b/src/classes/tuple.c @@ -287,7 +287,7 @@ void tuple_initialize(void) { value objclass = builtin_findclass(MORPHO_OBJECT(&objname)); // Create tuple veneer class - builtin_addfunction(TUPLE_CLASSNAME, tuple_constructor, BUILTIN_FLAGSEMPTY); + builtin_addfunction(TUPLE_CLASSNAME, tuple_constructor, MORPHO_FN_CONSTRUCTOR); value tupleclass=builtin_addclass(TUPLE_CLASSNAME, MORPHO_GETCLASSDEFINITION(Tuple), objclass); object_setveneerclass(OBJECT_TUPLE, tupleclass); From c80887f81cecb16a964759c76222478d3714d3c4 Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Sun, 21 Apr 2024 16:15:52 -0400 Subject: [PATCH 058/181] First go at signature parsing --- src/builtin/builtin.c | 92 ++++++++++++++++++++++++++++++++++++++++--- src/builtin/builtin.h | 2 + src/classes/array.c | 3 +- 3 files changed, 91 insertions(+), 6 deletions(-) diff --git a/src/builtin/builtin.c b/src/builtin/builtin.c index 1dfadd7c..8af94e67 100644 --- a/src/builtin/builtin.c +++ b/src/builtin/builtin.c @@ -11,6 +11,8 @@ #include "file.h" #include "system.h" #include "classes.h" +#include "lex.h" +#include "parse.h" #include "mesh.h" #include "selection.h" @@ -42,11 +44,12 @@ dictionary *_currentclasstable; * ********************************************************************** */ /** Initialize an objectbuiltinfunction */ -static void builtin_init(objectbuiltinfunction *func) { +void builtin_init(objectbuiltinfunction *func) { func->flags=BUILTIN_FLAGSEMPTY; func->function=NULL; func->name=MORPHO_NIL; func->klass=NULL; + varray_valueinit(&func->signature); } /** @brief An enumerate loop. @@ -131,6 +134,7 @@ void objectbuiltinfunction_printfn(object *obj, void *v) { void objectbuiltinfunction_freefn(object *obj) { objectbuiltinfunction *func = (objectbuiltinfunction *) obj; + varray_valueclear(&func->signature); morpho_freeobject(func->name); } @@ -207,14 +211,92 @@ value builtin_findfunction(value name) { return out; } -/** Sets the signature of a function */ -bool builtin_setsignature(objectbuiltinfunction *fn, value *signature) { +/* ********************************************************************** + * Parse and set signatures + * ********************************************************************** */ + +enum { + SIGNATURE_LEFTBRACE, + SIGNATURE_RIGHTBRACE, + SIGNATURE_COMMA, + SIGNATURE_DOTDOTDOT, + SIGNATURE_SYMBOL, + SIGNATURE_EOF +}; + +tokendefn sigtokens[] = { + { "(", SIGNATURE_LEFTBRACE , NULL }, + { ")", SIGNATURE_RIGHTBRACE , NULL }, + { ",", SIGNATURE_COMMA , NULL }, + { "...", SIGNATURE_DOTDOTDOT , NULL }, + { "", SIGNATURE_EOF , NULL } +}; + +void builtin_initializelexer(lexer *l, char *signature) { + lex_init(l, signature, 0); + lex_settokendefns(l, sigtokens); + lex_seteof(l, SIGNATURE_EOF); + lex_setsymboltype(l, SIGNATURE_SYMBOL); +} + +bool builtin_parsesymbol(parser *p, void *out) { + objectbuiltinfunction *func = (objectbuiltinfunction *) out; + bool success=false; + if (p->previous.length==1 && *p->previous.start=='_') { + value blank = MORPHO_NIL; + success=varray_valueadd(&func->signature, &blank, 1); + } else { + value symbol; + if (!parse_stringfromtoken(p, 0, p->previous.length, &symbol)) return false; + value klass = builtin_findclass(symbol); + morpho_freeobject(symbol); + + if (MORPHO_ISCLASS(klass)) success=varray_valueadd(&func->signature, &klass, 1); + } + return success; } -/** Parses a function signature */ -bool builtin_parsesignature(char *signature, varray_value *out) { +bool builtin_parsesignature(parser *p, void *out) { + if (parse_checktokenadvance(p, SIGNATURE_SYMBOL)) { + // Return type + } + + if (!parse_checktokenadvance(p, SIGNATURE_LEFTBRACE)) return false; + + while (!parse_checktoken(p, SIGNATURE_RIGHTBRACE) && + !parse_checktoken(p, SIGNATURE_EOF)) { + if (parse_checktokenadvance(p, SIGNATURE_SYMBOL)) { + if (!builtin_parsesymbol(p, out)) return false; + } else if (parse_checktokenadvance(p, SIGNATURE_DOTDOTDOT)) { + + } else return false; + + parse_checktokenadvance(p, SIGNATURE_COMMA); + } + + if (!parse_checktokenadvance(p, SIGNATURE_RIGHTBRACE)) return false; + + return true; +} + +/** Sets the signature of a builtin function */ +bool builtin_setsignature(value fn, char *signature) { + error err; + error_init(&err); + + lexer l; + builtin_initializelexer(&l, signature); + + parser p; + parse_init(&p, &l, &err, MORPHO_GETBUILTINFUNCTION(fn)); + parse_setbaseparsefn(&p, builtin_parsesignature); + parse_setskipnewline(&p, false, TOKEN_NONE); + + bool success=parse(&p); + parse_clear(&p); + return success; } /* ********************************************************************** diff --git a/src/builtin/builtin.h b/src/builtin/builtin.h index add9b066..3df56162 100644 --- a/src/builtin/builtin.h +++ b/src/builtin/builtin.h @@ -42,6 +42,7 @@ typedef struct { builtinfunctionflags flags; builtinfunction function; objectclass *klass; + varray_value signature; } objectbuiltinfunction; /** Gets an objectfunction from a value */ @@ -115,6 +116,7 @@ void builtin_setclasstable(dictionary *dict); value builtin_addfunction(char *name, builtinfunction func, builtinfunctionflags flags); value builtin_findfunction(value name); +bool builtin_setsignature(value, char *signature); value builtin_addclass(char *name, builtinclassentry desc[], value superclass); value builtin_findclass(value name); diff --git a/src/classes/array.c b/src/classes/array.c index 8e00a396..81829922 100644 --- a/src/classes/array.c +++ b/src/classes/array.c @@ -604,7 +604,8 @@ void array_initialize(void) { value objclass = builtin_findclass(MORPHO_OBJECT(&objname)); // Array constructor function - builtin_addfunction(ARRAY_CLASSNAME, array_constructor, MORPHO_FN_CONSTRUCTOR); + value cons = builtin_addfunction(ARRAY_CLASSNAME, array_constructor, MORPHO_FN_CONSTRUCTOR); + //builtin_setsignature(cons, "Array (List,String,_,...)"); // Create Array veneer class value arrayclass=builtin_addclass(ARRAY_CLASSNAME, MORPHO_GETCLASSDEFINITION(Array), objclass); From 1e1a650e6c854684a071128ab759374bbc16af81 Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Sun, 21 Apr 2024 17:06:01 -0400 Subject: [PATCH 059/181] Isolate signatures in a new library --- src/builtin/builtin.c | 90 -------------------- src/builtin/builtin.h | 7 +- src/classes/function.c | 14 ++- src/classes/function.h | 5 +- src/classes/metafunction.c | 7 +- src/datastructures/CMakeLists.txt | 2 + src/datastructures/signature.c | 136 ++++++++++++++++++++++++++++++ src/datastructures/signature.h | 26 ++++++ 8 files changed, 183 insertions(+), 104 deletions(-) create mode 100644 src/datastructures/signature.c create mode 100644 src/datastructures/signature.h diff --git a/src/builtin/builtin.c b/src/builtin/builtin.c index 8af94e67..bad68999 100644 --- a/src/builtin/builtin.c +++ b/src/builtin/builtin.c @@ -11,8 +11,6 @@ #include "file.h" #include "system.h" #include "classes.h" -#include "lex.h" -#include "parse.h" #include "mesh.h" #include "selection.h" @@ -211,94 +209,6 @@ value builtin_findfunction(value name) { return out; } -/* ********************************************************************** - * Parse and set signatures - * ********************************************************************** */ - -enum { - SIGNATURE_LEFTBRACE, - SIGNATURE_RIGHTBRACE, - SIGNATURE_COMMA, - SIGNATURE_DOTDOTDOT, - SIGNATURE_SYMBOL, - SIGNATURE_EOF -}; - -tokendefn sigtokens[] = { - { "(", SIGNATURE_LEFTBRACE , NULL }, - { ")", SIGNATURE_RIGHTBRACE , NULL }, - { ",", SIGNATURE_COMMA , NULL }, - { "...", SIGNATURE_DOTDOTDOT , NULL }, - { "", SIGNATURE_EOF , NULL } -}; - -void builtin_initializelexer(lexer *l, char *signature) { - lex_init(l, signature, 0); - lex_settokendefns(l, sigtokens); - lex_seteof(l, SIGNATURE_EOF); - lex_setsymboltype(l, SIGNATURE_SYMBOL); -} - -bool builtin_parsesymbol(parser *p, void *out) { - objectbuiltinfunction *func = (objectbuiltinfunction *) out; - bool success=false; - - if (p->previous.length==1 && *p->previous.start=='_') { - value blank = MORPHO_NIL; - success=varray_valueadd(&func->signature, &blank, 1); - } else { - value symbol; - if (!parse_stringfromtoken(p, 0, p->previous.length, &symbol)) return false; - value klass = builtin_findclass(symbol); - morpho_freeobject(symbol); - - if (MORPHO_ISCLASS(klass)) success=varray_valueadd(&func->signature, &klass, 1); - } - return success; -} - -bool builtin_parsesignature(parser *p, void *out) { - if (parse_checktokenadvance(p, SIGNATURE_SYMBOL)) { - // Return type - } - - if (!parse_checktokenadvance(p, SIGNATURE_LEFTBRACE)) return false; - - while (!parse_checktoken(p, SIGNATURE_RIGHTBRACE) && - !parse_checktoken(p, SIGNATURE_EOF)) { - if (parse_checktokenadvance(p, SIGNATURE_SYMBOL)) { - if (!builtin_parsesymbol(p, out)) return false; - } else if (parse_checktokenadvance(p, SIGNATURE_DOTDOTDOT)) { - - } else return false; - - parse_checktokenadvance(p, SIGNATURE_COMMA); - } - - if (!parse_checktokenadvance(p, SIGNATURE_RIGHTBRACE)) return false; - - return true; -} - -/** Sets the signature of a builtin function */ -bool builtin_setsignature(value fn, char *signature) { - error err; - error_init(&err); - - lexer l; - builtin_initializelexer(&l, signature); - - parser p; - parse_init(&p, &l, &err, MORPHO_GETBUILTINFUNCTION(fn)); - parse_setbaseparsefn(&p, builtin_parsesignature); - parse_setskipnewline(&p, false, TOKEN_NONE); - - bool success=parse(&p); - - parse_clear(&p); - return success; -} - /* ********************************************************************** * Create and find builtin classes * ********************************************************************** */ diff --git a/src/builtin/builtin.h b/src/builtin/builtin.h index 3df56162..dbd4c014 100644 --- a/src/builtin/builtin.h +++ b/src/builtin/builtin.h @@ -59,6 +59,7 @@ typedef struct { typedef struct { enum { BUILTIN_METHOD, BUILTIN_PROPERTY } type; char *name; + char *signature; builtinfunctionflags flags; builtinfunction function; } builtinclassentry; @@ -74,7 +75,9 @@ typedef struct { #define MORPHO_PROPERTY(label) ((builtinclassentry) { .type=(BUILTIN_PROPERTY), .name=(label), .flags=BUILTIN_FLAGSEMPTY, .function=NULL}) -#define MORPHO_METHOD(label, func, flg) ((builtinclassentry) { .type=(BUILTIN_METHOD), .name=(label), .flags=flg, .function=func}) +#define MORPHO_METHOD(label, func, flg) ((builtinclassentry) { .type=(BUILTIN_METHOD), .name=(label), .signature=NULL, .flags=flg, .function=func}) + +#define MORPHO_METHODSIG(label, sig, func, flg) ((builtinclassentry) { .type=(BUILTIN_METHOD), .name=(label), .signature=sig, .flags=flg, .function=func}) #define MORPHO_ENDCLASS , MORPHO_PROPERTY(NULL) \ }; @@ -116,7 +119,7 @@ void builtin_setclasstable(dictionary *dict); value builtin_addfunction(char *name, builtinfunction func, builtinfunctionflags flags); value builtin_findfunction(value name); -bool builtin_setsignature(value, char *signature); +bool builtin_setsignature(value fn, char *signature); value builtin_addclass(char *name, builtinclassentry desc[], value superclass); value builtin_findclass(value name); diff --git a/src/classes/function.c b/src/classes/function.c index 9a990168..deaad6e5 100644 --- a/src/classes/function.c +++ b/src/classes/function.c @@ -17,7 +17,7 @@ void objectfunction_freefn(object *obj) { morpho_freeobject(func->name); varray_optionalparamclear(&func->opt); object_functionclear(func); - varray_valueclear(&func->signature); + signature_clear(&func->sig); } void objectfunction_markfn(object *obj, void *v) { @@ -58,7 +58,7 @@ void object_functioninit(objectfunction *func) { func->nregs=0; varray_valueinit(&func->konst); varray_varray_upvalueinit(&func->prototype); - varray_valueinit(&func->signature); + signature_init(&func->sig); } /** @brief Clears a function */ @@ -69,7 +69,7 @@ void object_functionclear(objectfunction *func) { varray_upvalueclear(&func->prototype.data[i]); } varray_varray_upvalueclear(&func->prototype); - varray_valueclear(&func->signature); + signature_clear(&func->sig); } /** @brief Creates a new function */ @@ -137,15 +137,13 @@ bool object_functionaddprototype(objectfunction *func, varray_upvalue *v, indx * /** Sets the signature of a function * @param[in] func function object * @param[in] signature list of types for each parameter (length from func->nargs) */ -bool function_setsignature(objectfunction *func, value *signature) { - func->signature.count=0; // Reset signature; - return varray_valueadd(&func->signature, signature, func->nargs); +void function_setsignature(objectfunction *func, value *signature) { + signature_set(&func->sig, func->nargs, signature); } /** Returns true if any of the parameters are typed */ bool function_hastypedparameters(objectfunction *func) { - for (int i=0; isignature.count; i++) if (!MORPHO_ISNIL(func->signature.data[i])) return true; - return false; + return signature_istyped(&func->sig); } /* ********************************************************************** diff --git a/src/classes/function.h b/src/classes/function.h index c470dd52..3f259746 100644 --- a/src/classes/function.h +++ b/src/classes/function.h @@ -8,6 +8,7 @@ #define function_h #include "object.h" +#include "signature.h" /* ------------------------------------------------------- * Function objects @@ -38,7 +39,7 @@ typedef struct sobjectfunction { varray_value konst; varray_varray_upvalue prototype; varray_optionalparam opt; - varray_value signature; + signature sig; } objectfunction; /** Gets an objectfunction from a value */ @@ -70,7 +71,7 @@ varray_value *object_functiongetconstanttable(objectfunction *func); objectfunction *object_newfunction(indx entry, value name, objectfunction *parent, unsigned int nargs); bool object_functionhasvargs(objectfunction *func); void object_functionsetvarg(objectfunction *func, unsigned int varg); -bool function_setsignature(objectfunction *func, value *signature); +void function_setsignature(objectfunction *func, value *signature); bool function_hastypedparameters(objectfunction *func); void objectfunction_printfn(object *obj, void *v); diff --git a/src/classes/metafunction.c b/src/classes/metafunction.c index 69bcb3fe..456bb42c 100644 --- a/src/classes/metafunction.c +++ b/src/classes/metafunction.c @@ -107,8 +107,11 @@ bool metafunction_resolve(objectmetafunction *f, int nargs, value *args, value * objectfunction *fn = MORPHO_GETFUNCTION(f->fns.data[i]); if (nargs!=fn->nargs) continue; bool match=true; - for (int j=0; jsignature.count && match; j++) { - if (!metafunction_matchtype(fn->signature.data[j], args[j])) match=false; + int nparams; value *ptypes; + signature_paramlist(&fn->sig, &nparams, &ptypes); + + for (int j=0; jfns.data[i]; return true; } } diff --git a/src/datastructures/CMakeLists.txt b/src/datastructures/CMakeLists.txt index 07d7397c..56ed18c2 100644 --- a/src/datastructures/CMakeLists.txt +++ b/src/datastructures/CMakeLists.txt @@ -5,6 +5,7 @@ target_sources(morpho error.c error.h object.c object.h program.c program.h + signature.c signature.h syntaxtree.c syntaxtree.h value.c value.h varray.c varray.h @@ -21,6 +22,7 @@ target_sources(morpho error.h object.h program.h + signature.h syntaxtree.h value.h varray.h diff --git a/src/datastructures/signature.c b/src/datastructures/signature.c new file mode 100644 index 00000000..e51306ec --- /dev/null +++ b/src/datastructures/signature.c @@ -0,0 +1,136 @@ +/** @file signature.c + * @author T J Atherton + * + * @brief Function signatures and their declarations +*/ + +#include "morpho.h" +#include "classes.h" + +#include "signature.h" +#include "parse.h" + +/* ********************************************************************** + * Manage signature structures + * ********************************************************************** */ + +void signature_init(signature *s) { + varray_valueinit(&s->types); +} + +void signature_clear(signature *s) { + varray_valueclear(&s->types); +} + +/** @brief Sets the contents of a signature + * @param[in] s - the signature structure + * @param[in] nparam - number of fixed parameters + * @param[in] types - list of types of each parameter */ +void signature_set(signature *s, int nparam, value *types) { + s->types.count=0; // Reset + varray_valueadd(&s->types, types, nparam); +} + +/** @brief Returns true if any entries in the signature are typed*/ +bool signature_istyped(signature *s) { + for (int i=0; itypes.count; i++) if (!MORPHO_ISNIL(s->types.data[i])) return true; + return false; +} + +/** @brief Return list of types */ +bool signature_paramlist(signature *s, int *nparams, value **ptypes) { + if (nparams) *nparams = s->types.count; + if (ptypes) *ptypes = s->types.data; + return s->types.data; +} + +/* ********************************************************************** + * Parse signatures + * ********************************************************************** */ + +enum { + SIGNATURE_LEFTBRACE, + SIGNATURE_RIGHTBRACE, + SIGNATURE_COMMA, + SIGNATURE_DOTDOTDOT, + SIGNATURE_SYMBOL, + SIGNATURE_EOF +}; + +tokendefn sigtokens[] = { + { "(", SIGNATURE_LEFTBRACE , NULL }, + { ")", SIGNATURE_RIGHTBRACE , NULL }, + { ",", SIGNATURE_COMMA , NULL }, + { "...", SIGNATURE_DOTDOTDOT , NULL }, + { "", SIGNATURE_EOF , NULL } +}; + +/** @brief Initializes a lexer for parsing signatures */ +void signature_initializelexer(lexer *l, char *signature) { + lex_init(l, signature, 0); + lex_settokendefns(l, sigtokens); + lex_seteof(l, SIGNATURE_EOF); + lex_setsymboltype(l, SIGNATURE_SYMBOL); +} + +/** @brief Parser function to process a symbol held in p->previous */ +bool signature_parsesymbol(parser *p, void *out) { + signature *sig = (signature *) out; + bool success=false; + + if (p->previous.length==1 && *p->previous.start=='_') { + value blank = MORPHO_NIL; + success=varray_valueadd(&sig->types, &blank, 1); + } else { + value symbol; + if (!parse_stringfromtoken(p, 0, p->previous.length, &symbol)) return false; + value klass = builtin_findclass(symbol); + morpho_freeobject(symbol); + + if (MORPHO_ISCLASS(klass)) success=varray_valueadd(&sig->types, &klass, 1); + } + return success; +} + +/** @brief Main parser function for signatures */ +bool signature_parsesignature(parser *p, void *out) { + if (parse_checktokenadvance(p, SIGNATURE_SYMBOL)) { + // Return type + } + + if (!parse_checktokenadvance(p, SIGNATURE_LEFTBRACE)) return false; + + while (!parse_checktoken(p, SIGNATURE_RIGHTBRACE) && + !parse_checktoken(p, SIGNATURE_EOF)) { + if (parse_checktokenadvance(p, SIGNATURE_SYMBOL)) { + if (!signature_parsesymbol(p, out)) return false; + } else if (parse_checktokenadvance(p, SIGNATURE_DOTDOTDOT)) { + + } else return false; + + parse_checktokenadvance(p, SIGNATURE_COMMA); + } + + if (!parse_checktokenadvance(p, SIGNATURE_RIGHTBRACE)) return false; + + return true; +} + +/** Parses a signature */ +bool signature_parse(char *sig, signature *out) { + error err; + error_init(&err); + + lexer l; + signature_initializelexer(&l, sig); + + parser p; + parse_init(&p, &l, &err, out); + parse_setbaseparsefn(&p, signature_parsesignature); + parse_setskipnewline(&p, false, TOKEN_NONE); + + bool success=parse(&p); + + parse_clear(&p); + return success; +} diff --git a/src/datastructures/signature.h b/src/datastructures/signature.h new file mode 100644 index 00000000..547186e6 --- /dev/null +++ b/src/datastructures/signature.h @@ -0,0 +1,26 @@ +/** @file signature.h + * @author T J Atherton + * + * @brief Function signatures and their declarations +*/ + +#ifndef signature_h +#define signature_h + +#include +#include "varray.h" + +typedef struct { + varray_value types; +} signature; + +void signature_init(signature *s); +void signature_clear(signature *s); + +bool signature_istyped(signature *s); +bool signature_paramlist(signature *s, int *nparams, value **ptypes); + +void signature_set(signature *s, int nparam, value *types); +bool signature_parse(char *sig, signature *out); + +#endif From 43412e61fa17a9942079f935ce45f59f151f89a2 Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Sun, 21 Apr 2024 19:12:10 -0400 Subject: [PATCH 060/181] Metafunctions for builtin classes --- src/builtin/builtin.c | 45 ++++++++++++++--------- src/builtin/builtin.h | 6 ++- src/classes/list.c | 19 +++++++++- src/classes/metafunction.c | 25 +++++++++---- src/core/vm.c | 4 ++ test/types/builtin_class_signature.morpho | 10 +++++ 6 files changed, 81 insertions(+), 28 deletions(-) create mode 100644 test/types/builtin_class_signature.morpho diff --git a/src/builtin/builtin.c b/src/builtin/builtin.c index bad68999..f430f7e1 100644 --- a/src/builtin/builtin.c +++ b/src/builtin/builtin.c @@ -47,7 +47,7 @@ void builtin_init(objectbuiltinfunction *func) { func->function=NULL; func->name=MORPHO_NIL; func->klass=NULL; - varray_valueinit(&func->signature); + signature_init(&func->sig); } /** @brief An enumerate loop. @@ -132,7 +132,7 @@ void objectbuiltinfunction_printfn(object *obj, void *v) { void objectbuiltinfunction_freefn(object *obj) { objectbuiltinfunction *func = (objectbuiltinfunction *) obj; - varray_valueclear(&func->signature); + signature_clear(&func->sig); morpho_freeobject(func->name); } @@ -236,24 +236,35 @@ value builtin_addclass(char *name, builtinclassentry desc[], value superclass) { for (unsigned int i=0; desc[i].name!=NULL; i++) { if (desc[i].type==BUILTIN_METHOD) { - objectbuiltinfunction *method = (objectbuiltinfunction *) object_new(sizeof(objectbuiltinfunction), OBJECT_BUILTINFUNCTION); - builtin_init(method); - method->function=desc[i].function; - method->klass=new; - method->name=object_stringfromcstring(desc[i].name, strlen(desc[i].name)); - method->flags=desc[i].flags; - - value selector = dictionary_intern(&builtin_symboltable, method->name); + objectbuiltinfunction *newmethod = (objectbuiltinfunction *) object_new(sizeof(objectbuiltinfunction), OBJECT_BUILTINFUNCTION); + builtin_init(newmethod); + newmethod->function=desc[i].function; + newmethod->klass=new; + newmethod->name=object_stringfromcstring(desc[i].name, strlen(desc[i].name)); + newmethod->flags=desc[i].flags; + if (desc[i].signature) { + signature_parse(desc[i].signature, &newmethod->sig); + } - varray_valuewrite(&builtin_objects, MORPHO_OBJECT(method)); + value selector = dictionary_intern(&builtin_symboltable, newmethod->name); + value method = MORPHO_OBJECT(newmethod); - if (dictionary_get(&new->methods, method->name, NULL) && - ( !superklass || // Ok to redefine methods in the superclass - !dictionary_get(&superklass->methods, method->name, NULL)) ) { - UNREACHABLE("redefinition of method in builtin class (check builtin.c)"); - } + varray_valuewrite(&builtin_objects, method); - dictionary_insert(&new->methods, selector, MORPHO_OBJECT(method)); + value prev=MORPHO_NIL; + if (dictionary_get(&new->methods, newmethod->name, &prev)) { + if (MORPHO_ISBUILTINFUNCTION(prev)) { + objectbuiltinfunction *func = MORPHO_GETBUILTINFUNCTION(prev); + if (func->klass!=new) { // Override superclass methods for now + dictionary_insert(&new->methods, selector, method); + } else if (metafunction_wrap(newmethod->name, prev, &prev)) { + metafunction_add(MORPHO_GETMETAFUNCTION(prev), method); + dictionary_insert(&new->methods, selector, prev); + } + } else if (MORPHO_ISMETAFUNCTION(prev)) { + metafunction_add(MORPHO_GETMETAFUNCTION(prev), method); + } + } else dictionary_insert(&new->methods, selector, method); } } diff --git a/src/builtin/builtin.h b/src/builtin/builtin.h index dbd4c014..326ec3a8 100644 --- a/src/builtin/builtin.h +++ b/src/builtin/builtin.h @@ -14,6 +14,8 @@ #include "morpho.h" #endif +#include "signature.h" + /* ------------------------------------------------------- * Built in function objects * ------------------------------------------------------- */ @@ -42,7 +44,7 @@ typedef struct { builtinfunctionflags flags; builtinfunction function; objectclass *klass; - varray_value signature; + signature sig; } objectbuiltinfunction; /** Gets an objectfunction from a value */ @@ -77,7 +79,7 @@ typedef struct { #define MORPHO_METHOD(label, func, flg) ((builtinclassentry) { .type=(BUILTIN_METHOD), .name=(label), .signature=NULL, .flags=flg, .function=func}) -#define MORPHO_METHODSIG(label, sig, func, flg) ((builtinclassentry) { .type=(BUILTIN_METHOD), .name=(label), .signature=sig, .flags=flg, .function=func}) +#define MORPHO_METHOD_SIGNATURE(label, sig, func, flg) ((builtinclassentry) { .type=(BUILTIN_METHOD), .name=(label), .signature=sig, .flags=flg, .function=func}) #define MORPHO_ENDCLASS , MORPHO_PROPERTY(NULL) \ }; diff --git a/src/classes/list.c b/src/classes/list.c index 2ae0a27f..e7fd7103 100644 --- a/src/classes/list.c +++ b/src/classes/list.c @@ -609,7 +609,7 @@ value List_roll(vm *v, int nargs, value *args) { } /** Sorts a list */ -value List_sort(vm *v, int nargs, value *args) { +value XList_sort(vm *v, int nargs, value *args) { objectlist *slf = MORPHO_GETLIST(MORPHO_SELF(args)); if (nargs==0) { @@ -623,6 +623,20 @@ value List_sort(vm *v, int nargs, value *args) { return MORPHO_NIL; } +/** Sorts a list */ +value List_sort(vm *v, int nargs, value *args) { + list_sort(MORPHO_GETLIST(MORPHO_SELF(args))); + return MORPHO_NIL; +} + +value List_sort_fn(vm *v, int nargs, value *args) { + objectlist *slf = MORPHO_GETLIST(MORPHO_SELF(args)); + if (!list_sortwithfn(v, MORPHO_GETARG(args, 0), slf)) { + morpho_runtimeerror(v, LIST_SRTFN); + } + return MORPHO_NIL; +} + /** Returns a list of indices that would sort the list self */ value List_order(vm *v, int nargs, value *args) { objectlist *slf = MORPHO_GETLIST(MORPHO_SELF(args)); @@ -674,7 +688,8 @@ MORPHO_METHOD(MORPHO_CLONE_METHOD, List_clone, BUILTIN_FLAGSEMPTY), //MORPHO_METHOD(MORPHO_ADD_METHOD, List_add, BUILTIN_FLAGSEMPTY), MORPHO_METHOD(MORPHO_JOIN_METHOD, List_join, BUILTIN_FLAGSEMPTY), MORPHO_METHOD(MORPHO_ROLL_METHOD, List_roll, BUILTIN_FLAGSEMPTY), -MORPHO_METHOD(LIST_SORT_METHOD, List_sort, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(LIST_SORT_METHOD, "()", List_sort, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD_SIGNATURE(LIST_SORT_METHOD, "(_)", List_sort_fn, BUILTIN_FLAGSEMPTY), MORPHO_METHOD(LIST_ORDER_METHOD, List_order, BUILTIN_FLAGSEMPTY), MORPHO_METHOD(LIST_REVERSE_METHOD, List_reverse, BUILTIN_FLAGSEMPTY), MORPHO_METHOD(LIST_ISMEMBER_METHOD, List_ismember, BUILTIN_FLAGSEMPTY), diff --git a/src/classes/metafunction.c b/src/classes/metafunction.c index 456bb42c..f3998ac4 100644 --- a/src/classes/metafunction.c +++ b/src/classes/metafunction.c @@ -101,19 +101,30 @@ bool metafunction_matchtype(value type, value val) { return false; } +signature *_getsignature(value fn) { + if (MORPHO_ISFUNCTION(fn)) { + return &MORPHO_GETFUNCTION(fn)->sig; + } else if (MORPHO_ISBUILTINFUNCTION(fn)) { + return &MORPHO_GETBUILTINFUNCTION(fn)->sig; + } + return NULL; +} + /** Resolves a metafunction given calling arguments */ bool metafunction_resolve(objectmetafunction *f, int nargs, value *args, value *out) { for (int i=0; ifns.count; i++) { - objectfunction *fn = MORPHO_GETFUNCTION(f->fns.data[i]); - if (nargs!=fn->nargs) continue; - bool match=true; + signature *s = _getsignature(f->fns.data[i]); + if (!s) continue; + int nparams; value *ptypes; - signature_paramlist(&fn->sig, &nparams, &ptypes); + signature_paramlist(s, &nparams, &ptypes); + if (nargs!=nparams) continue; - for (int j=0; jfns.data[i]; return true; } + if (j==nparams) { *out=f->fns.data[i]; return true; } } return false; diff --git a/src/core/vm.c b/src/core/vm.c index 08f177f7..aa8adfe6 100644 --- a/src/core/vm.c +++ b/src/core/vm.c @@ -1093,6 +1093,10 @@ bool morpho_interpret(vm *v, value *rstart, instructionindx istart) { if (klass) { value ifunc; if (dictionary_getintern(&klass->methods, right, &ifunc)) { + if (MORPHO_ISMETAFUNCTION(ifunc)) { + if (!metafunction_resolve(MORPHO_GETMETAFUNCTION(ifunc), c, reg+a+1, &ifunc)) ERROR(VM_MLTPLDSPTCHFLD); + } + if (MORPHO_ISBUILTINFUNCTION(ifunc)) { #ifdef MORPHO_PROFILER v->fp->inbuiltinfunction=MORPHO_GETBUILTINFUNCTION(ifunc); diff --git a/test/types/builtin_class_signature.morpho b/test/types/builtin_class_signature.morpho new file mode 100644 index 00000000..857f63b4 --- /dev/null +++ b/test/types/builtin_class_signature.morpho @@ -0,0 +1,10 @@ +// Test multiple dispatch on builtin class + +var lst = [4,3,2,1] + +lst.sort() +print lst // expect: [ 1, 2, 3, 4 ] + +fn f (a, b) { return b-a } +lst.sort(f) +print lst // expect: [ 4, 3, 2, 1 ] From 1b44034a991d3591c73553f7a2466d40c6edbedd Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Sun, 21 Apr 2024 19:40:40 -0400 Subject: [PATCH 061/181] Set constructor flag for Matrix and Sparse --- src/linalg/matrix.c | 2 +- src/linalg/sparse.c | 2 +- test/types/type_violation_matrix.morpho | 10 ++++++++++ 3 files changed, 12 insertions(+), 2 deletions(-) create mode 100644 test/types/type_violation_matrix.morpho diff --git a/src/linalg/matrix.c b/src/linalg/matrix.c index 50a8e1f4..749d2951 100644 --- a/src/linalg/matrix.c +++ b/src/linalg/matrix.c @@ -1539,7 +1539,7 @@ MORPHO_ENDCLASS void matrix_initialize(void) { objectmatrixtype=object_addtype(&objectmatrixdefn); - builtin_addfunction(MATRIX_CLASSNAME, matrix_constructor, BUILTIN_FLAGSEMPTY); + builtin_addfunction(MATRIX_CLASSNAME, matrix_constructor, MORPHO_FN_CONSTRUCTOR); builtin_addfunction(MATRIX_IDENTITYCONSTRUCTOR, matrix_identityconstructor, BUILTIN_FLAGSEMPTY); objectstring objname = MORPHO_STATICSTRING(OBJECT_CLASSNAME); diff --git a/src/linalg/sparse.c b/src/linalg/sparse.c index 364b299e..981394a1 100644 --- a/src/linalg/sparse.c +++ b/src/linalg/sparse.c @@ -1669,7 +1669,7 @@ void sparse_initialize(void) { objectdokkeytype=object_addtype(&objectdokkeydefn); objectsparsetype=object_addtype(&objectsparsedefn); - builtin_addfunction(SPARSE_CLASSNAME, sparse_constructor, BUILTIN_FLAGSEMPTY); + builtin_addfunction(SPARSE_CLASSNAME, sparse_constructor, MORPHO_FN_CONSTRUCTOR); objectstring objname = MORPHO_STATICSTRING(OBJECT_CLASSNAME); value objclass = builtin_findclass(MORPHO_OBJECT(&objname)); diff --git a/test/types/type_violation_matrix.morpho b/test/types/type_violation_matrix.morpho new file mode 100644 index 00000000..bdf0d7f9 --- /dev/null +++ b/test/types/type_violation_matrix.morpho @@ -0,0 +1,10 @@ +// Type violation in function from a Matrix + +fn f() { + String u + + u = Matrix(2,2) +} + +f() +// expect error 'TypeErr' From da2d1fab3dccbc0896b61414cd22b03da2aab1b6 Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Tue, 23 Apr 2024 06:14:31 -0400 Subject: [PATCH 062/181] First go at metafunction resolver --- src/classes/metafunction.c | 47 +++++++++++++++++++++++++++++++++- src/classes/metafunction.h | 19 ++++++++++++++ src/core/compile.c | 5 +++- src/datastructures/signature.c | 13 ++++++++++ src/datastructures/signature.h | 2 ++ 5 files changed, 84 insertions(+), 2 deletions(-) diff --git a/src/classes/metafunction.c b/src/classes/metafunction.c index f3998ac4..0ba171d4 100644 --- a/src/classes/metafunction.c +++ b/src/classes/metafunction.c @@ -111,7 +111,7 @@ signature *_getsignature(value fn) { } /** Resolves a metafunction given calling arguments */ -bool metafunction_resolve(objectmetafunction *f, int nargs, value *args, value *out) { +bool metafunction_slowresolve(objectmetafunction *f, int nargs, value *args, value *out) { for (int i=0; ifns.count; i++) { signature *s = _getsignature(f->fns.data[i]); if (!s) continue; @@ -130,6 +130,51 @@ bool metafunction_resolve(objectmetafunction *f, int nargs, value *args, value * return false; } +/* ********************************************************************** + * Fast metafunction resolver + * ********************************************************************** */ + +enum { + MF_BRANCH_ON_NARG, + MF_MATCH, + MF_RESOLVE, + MF_FAIL +}; + +DEFINE_VARRAY(mfinstruction, mfinstruction); + +/** Compiles the metafunction resolver */ +void metafunction_compile(objectmetafunction *fn) { + int nfn = fn->fns.count; + if (!nfn) return; + + signature *sig[nfn]; + for (int i=0; ifns.data[i]); + + + +} + +/** Execute the metafunction's resolver */ +bool metafunction_resolve(objectmetafunction *fn, int nargs, value *args, value *out) { + mfinstruction *pc = fn->resolver.data; + if (!pc) return metafunction_slowresolve(fn, nargs, args, out); + + do { + switch(pc->opcode) { + case MF_MATCH: + + break; + case MF_RESOLVE: + *out = pc->data.resolve.fn; + return true; + case MF_FAIL: + return false; + } + } while(true); +} + + /* ********************************************************************** * Metafunction veneer class * ********************************************************************** */ diff --git a/src/classes/metafunction.h b/src/classes/metafunction.h index 9cf52853..411e0257 100644 --- a/src/classes/metafunction.h +++ b/src/classes/metafunction.h @@ -16,11 +16,29 @@ extern objecttype objectmetafunctiontype; #define OBJECT_METAFUNCTION objectmetafunctiontype +/** Compiled metafunction instruction set */ +typedef struct { + int opcode; + union { + struct { + value type; + int bsuccess; + int bfail; + } match; + struct { + value fn; + } resolve; + } data; +} mfinstruction; + +DECLARE_VARRAY(mfinstruction, mfinstruction); + /** A metafunction object */ typedef struct sobjectmetafunction { object obj; value name; varray_value fns; + varray_mfinstruction resolver; } objectmetafunction; /** Gets an objectmetafunction from a value */ @@ -45,6 +63,7 @@ typedef struct sobjectmetafunction { objectmetafunction *object_newmetafunction(value name); bool metafunction_wrap(value name, value fn, value *out); +void metafunction_compile(objectmetafunction *fn); bool metafunction_add(objectmetafunction *f, value fn); bool metafunction_typefromvalue(value v, value *out); diff --git a/src/core/compile.c b/src/core/compile.c index f6d5e70b..ae4847b8 100644 --- a/src/core/compile.c +++ b/src/core/compile.c @@ -221,7 +221,10 @@ bool compiler_resolvefunctionref(compiler *c, value symbol, value *out) { match++; } } - + + // Compile the metafunction ready for use + if (MORPHO_ISMETAFUNCTION(fnd)) metafunction_compile(MORPHO_GETMETAFUNCTION(fnd)); + if (match) *out=fnd; return match; } diff --git a/src/datastructures/signature.c b/src/datastructures/signature.c index e51306ec..71f83467 100644 --- a/src/datastructures/signature.c +++ b/src/datastructures/signature.c @@ -134,3 +134,16 @@ bool signature_parse(char *sig, signature *out) { parse_clear(&p); return success; } + +/** Print a signature for debugging purposes */ +void signature_print(signature *s) { + printf("("); + for (int i=0; itypes.count; i++) { + value type=s->types.data[i]; + if (MORPHO_ISNIL(type)) printf("_"); + else if (MORPHO_ISCLASS(type)) morpho_printvalue(NULL, MORPHO_GETCLASS(type)->name); + + if (itypes.count-1) printf(","); + } + printf(")\n"); +} diff --git a/src/datastructures/signature.h b/src/datastructures/signature.h index 547186e6..93ae76ce 100644 --- a/src/datastructures/signature.h +++ b/src/datastructures/signature.h @@ -23,4 +23,6 @@ bool signature_paramlist(signature *s, int *nparams, value **ptypes); void signature_set(signature *s, int nparam, value *types); bool signature_parse(char *sig, signature *out); +void signature_print(signature *s); + #endif From 5d96e89ecf57b2414ccf04c03c2bef4479bca30d Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Tue, 23 Apr 2024 06:46:27 -0400 Subject: [PATCH 063/181] Metafunction compiler structure --- src/classes/metafunction.c | 59 ++++++++++++++++++++++++++++------ src/classes/metafunction.h | 9 +----- src/datastructures/signature.c | 7 ++++ src/datastructures/signature.h | 1 + 4 files changed, 59 insertions(+), 17 deletions(-) diff --git a/src/classes/metafunction.c b/src/classes/metafunction.c index 0ba171d4..25c5d52f 100644 --- a/src/classes/metafunction.c +++ b/src/classes/metafunction.c @@ -135,24 +135,69 @@ bool metafunction_slowresolve(objectmetafunction *f, int nargs, value *args, val * ********************************************************************** */ enum { - MF_BRANCH_ON_NARG, - MF_MATCH, MF_RESOLVE, MF_FAIL }; DEFINE_VARRAY(mfinstruction, mfinstruction); +typedef int mfindx; + +typedef struct { + objectmetafunction *fn; + varray_int checkedargs; /** List of arguments already checked */ +} mfcompiler; + +typedef struct { + signature *sig; /** Signature of the target */ + value fn; /** The target */ +} mfresult; + +/** Initialize the metafunction compiler */ +void mfcompiler_init(mfcompiler *c, objectmetafunction *fn) { + c->fn=fn; + varray_intinit(&c->checkedargs); +} + +/** Clear the metafunction compiler */ +void mfcompiler_clear(mfcompiler *c, objectmetafunction *fn) { + varray_intclear(&c->checkedargs); +} + +mfindx mfcompile_insertinstruction(mfcompiler *c, mfinstruction instr) { + return varray_mfinstructionwrite(&c->fn->resolver, instr); +} + +/** Compiles a single result */ +void mfcompile_singleresult(mfcompiler *c, mfresult *result) { + /** Should check remaining args */ + + mfinstruction instr = { .opcode=MF_RESOLVE, .data.resolvefn=result->fn }; + mfcompile_insertinstruction(c, instr); +} + +/** Attempts to discriminate between a list of possible signatures */ +void mfcompile_set(mfcompiler *fn, int nres, mfresult *rlist) { + if (nres==1) mfcompile_singleresult(fn, rlist); +} + /** Compiles the metafunction resolver */ void metafunction_compile(objectmetafunction *fn) { int nfn = fn->fns.count; if (!nfn) return; - signature *sig[nfn]; - for (int i=0; ifns.data[i]); + mfresult rlist[nfn]; + for (int i=0; ifns.data[i]); + rlist[i].fn=fn->fns.data[i]; + } + mfcompiler compiler; + mfcompiler_init(&compiler, fn); + mfcompile_set(&compiler, nfn, rlist); + mfcompiler_clear(&compiler, fn); } /** Execute the metafunction's resolver */ @@ -162,11 +207,8 @@ bool metafunction_resolve(objectmetafunction *fn, int nargs, value *args, value do { switch(pc->opcode) { - case MF_MATCH: - - break; case MF_RESOLVE: - *out = pc->data.resolve.fn; + *out = pc->data.resolvefn; return true; case MF_FAIL: return false; @@ -174,7 +216,6 @@ bool metafunction_resolve(objectmetafunction *fn, int nargs, value *args, value } while(true); } - /* ********************************************************************** * Metafunction veneer class * ********************************************************************** */ diff --git a/src/classes/metafunction.h b/src/classes/metafunction.h index 411e0257..ceb7a91d 100644 --- a/src/classes/metafunction.h +++ b/src/classes/metafunction.h @@ -20,14 +20,7 @@ extern objecttype objectmetafunctiontype; typedef struct { int opcode; union { - struct { - value type; - int bsuccess; - int bfail; - } match; - struct { - value fn; - } resolve; + value resolvefn; } data; } mfinstruction; diff --git a/src/datastructures/signature.c b/src/datastructures/signature.c index 71f83467..b21c8330 100644 --- a/src/datastructures/signature.c +++ b/src/datastructures/signature.c @@ -37,6 +37,13 @@ bool signature_istyped(signature *s) { return false; } +/** @brief Check if two signatures are equal */ +bool signature_isequal(signature *a, signature *b) { + if (a->types.count!=b->types.count) return false; + for (int i=0; itypes.count; i++) if (!MORPHO_ISEQUAL(a->types.data[i], a->types.data[i])) return false; + return false; +} + /** @brief Return list of types */ bool signature_paramlist(signature *s, int *nparams, value **ptypes) { if (nparams) *nparams = s->types.count; diff --git a/src/datastructures/signature.h b/src/datastructures/signature.h index 93ae76ce..0f796a70 100644 --- a/src/datastructures/signature.h +++ b/src/datastructures/signature.h @@ -18,6 +18,7 @@ void signature_init(signature *s); void signature_clear(signature *s); bool signature_istyped(signature *s); +bool signature_isequal(signature *a, signature *b); bool signature_paramlist(signature *s, int *nparams, value **ptypes); void signature_set(signature *s, int nparam, value *types); From 27e1b2af32813563d7e68e117d52449105d7992d Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Fri, 26 Apr 2024 08:08:27 -0400 Subject: [PATCH 064/181] Reorganization of mfcompiler interface --- src/classes/metafunction.c | 135 +++++++++++++++++--- src/classes/metafunction.h | 1 + test/types/multiple_dispatch_nparams.morpho | 29 +++++ test/types/multiple_dispatch_two.morpho | 15 +++ 4 files changed, 162 insertions(+), 18 deletions(-) create mode 100644 test/types/multiple_dispatch_nparams.morpho create mode 100644 test/types/multiple_dispatch_two.morpho diff --git a/src/classes/metafunction.c b/src/classes/metafunction.c index 25c5d52f..2b8e8f5e 100644 --- a/src/classes/metafunction.c +++ b/src/classes/metafunction.c @@ -143,25 +143,96 @@ DEFINE_VARRAY(mfinstruction, mfinstruction); typedef int mfindx; -typedef struct { - objectmetafunction *fn; - varray_int checkedargs; /** List of arguments already checked */ -} mfcompiler; - typedef struct { signature *sig; /** Signature of the target */ value fn; /** The target */ } mfresult; +typedef struct { + int count; + mfresult *rlist; +} mfset; + +DECLARE_VARRAY(mfset, mfset) +DEFINE_VARRAY(mfset, mfset) + +typedef struct { + objectmetafunction *fn; + dictionary pcount; + varray_mfset set; /** A stack of possible sets */ +} mfcompiler; + /** Initialize the metafunction compiler */ void mfcompiler_init(mfcompiler *c, objectmetafunction *fn) { c->fn=fn; - varray_intinit(&c->checkedargs); + dictionary_init(&c->pcount); + varray_mfsetinit(&c->set); } /** Clear the metafunction compiler */ void mfcompiler_clear(mfcompiler *c, objectmetafunction *fn) { - varray_intclear(&c->checkedargs); + dictionary_clear(&c->pcount); + varray_mfsetclear(&c->set); +} + +/** Pushes a set onto the stack */ +void mfcompiler_pushset(mfcompiler *c, mfset *set) { + varray_mfsetadd(&c->set, set, 1); +} + +/** Pops a set off the stack, optionally returning it */ +bool mfcompiler_popset(mfcompiler *c, mfset *set) { + if (c->set.count<=0) return false; + if (set) *set = c->set.data[c->set.count-1]; + c->set.count--; +} + +/** Counts the range of parameters for the function call */ +void mfcompiler_countparams(mfcompiler *c, mfset *set, int *min, int *max) { + int imin=INT_MAX, imax=INT_MIN; + for (int i=0; icount; i++) { + int nparams; + signature_paramlist(set->rlist[i].sig, &nparams, NULL); + if (nparamsimax) imax=nparams; + } + if (min) *min = imin; + if (max) *max = imax; +} + +/** Places the various outcomes for a parameter into a dictionary */ +bool mfcompile_outcomes(mfcompiler *c, mfset *set, int i, dictionary *out) { + out->count=0; + for (int k=0; icount; i++) { // Loop over outcomes + int nparams; value *ptypes; + signature_paramlist(set->rlist[k].sig, &nparams, &ptypes); + if (i>=nparams) continue; + value val = MORPHO_INTEGER(1); + if (dictionary_get(out, ptypes[i], &val)) val=MORPHO_INTEGER(MORPHO_GETINTEGERVALUE(val)+1); + if (!dictionary_insert(out, ptypes[i], val)) return false; + } + return true; +} + +/** Count the divergent outcomes of each parameter */ +bool mfcompile_countoutcomes(mfcompiler *c, mfset *set, int *best) { + varray_int count; + varray_intinit(&count); + + dictionary dict; + dictionary_init(&dict); + + int k=0; // Loop over parameters + do { + mfcompile_outcomes(c, set, k, &dict); + varray_intwrite(&count, dict.count); + k++; + } while (dict.count); + + dictionary_clear(&dict); + varray_intclear(&count); + + return false; } mfindx mfcompile_insertinstruction(mfcompiler *c, mfinstruction instr) { @@ -169,25 +240,53 @@ mfindx mfcompile_insertinstruction(mfcompiler *c, mfinstruction instr) { } /** Compiles a single result */ -void mfcompile_singleresult(mfcompiler *c, mfresult *result) { - /** Should check remaining args */ +mfindx mfcompile_resolve(mfcompiler *c, mfset *set) { + // Should check all arguments have been resolved + + mfinstruction instr = { .opcode=MF_RESOLVE, .data.resolvefn=set->rlist->fn }; + return mfcompile_insertinstruction(c, instr); +} + +/** Attempts to dispatch based on a parameter i */ +mfindx mfcompiler_dispatchonparam(mfcompiler *c, mfset *set, int i) { - mfinstruction instr = { .opcode=MF_RESOLVE, .data.resolvefn=result->fn }; - mfcompile_insertinstruction(c, instr); +} + +/** Attempts to dispatch based on the number of arguments */ +mfindx mfcompiler_dispatchonnarg(mfcompiler *c, mfset *set, int min, int max) { + if (set->count==2) { + + }; // else branch table } /** Attempts to discriminate between a list of possible signatures */ -void mfcompile_set(mfcompiler *fn, int nres, mfresult *rlist) { - if (nres==1) mfcompile_singleresult(fn, rlist); +mfindx mfcompile_set(mfcompiler *c, mfset *set) { + if (set->count==1) mfcompile_resolve(c, set); + + int min, max; // Count the range of possible parameters + mfcompiler_countparams(c, set, &min, &max); + + // Dispatch on the number of parameters if it's in doubt + if (min!=max) return mfcompiler_dispatchonnarg(c, set, min, max); + + // If just one parameter, dispatch on it + if (min==1) return mfcompiler_dispatchonparam(c, set, 0); + + int best; + if (mfcompile_countoutcomes(c, set, &best)) return mfcompiler_dispatchonparam(c, set, best); + + return -1; } /** Compiles the metafunction resolver */ void metafunction_compile(objectmetafunction *fn) { - int nfn = fn->fns.count; - if (!nfn) return; + mfset set; + set.count = fn->fns.count; + if (!set.count) return; - mfresult rlist[nfn]; - for (int i=0; ifns.data[i]); rlist[i].fn=fn->fns.data[i]; } @@ -195,7 +294,7 @@ void metafunction_compile(objectmetafunction *fn) { mfcompiler compiler; mfcompiler_init(&compiler, fn); - mfcompile_set(&compiler, nfn, rlist); + mfcompile_set(&compiler, &set); mfcompiler_clear(&compiler, fn); } diff --git a/src/classes/metafunction.h b/src/classes/metafunction.h index ceb7a91d..0f93a521 100644 --- a/src/classes/metafunction.h +++ b/src/classes/metafunction.h @@ -22,6 +22,7 @@ typedef struct { union { value resolvefn; } data; + indx bto; /* Branch to this instruction on failure */ } mfinstruction; DECLARE_VARRAY(mfinstruction, mfinstruction); diff --git a/test/types/multiple_dispatch_nparams.morpho b/test/types/multiple_dispatch_nparams.morpho new file mode 100644 index 00000000..ac01f14a --- /dev/null +++ b/test/types/multiple_dispatch_nparams.morpho @@ -0,0 +1,29 @@ +// Dispatch a function on multiple types + +fn f() { + return 0 +} + +fn f(x) { + return 1 +} + +fn f(x, y) { + return 2 +} + +fn f(x, y, z) { + return 3 +} + +print f() +// expect: 0 + +print f("Hi") +// expect: 1 + +print f(1, 2) +// expect: 2 + +print f(1,2,3) +// expect: 3 diff --git a/test/types/multiple_dispatch_two.morpho b/test/types/multiple_dispatch_two.morpho new file mode 100644 index 00000000..b0216500 --- /dev/null +++ b/test/types/multiple_dispatch_two.morpho @@ -0,0 +1,15 @@ +// Dispatch a function on multiple types + +fn f() { + return 0 +} + +fn f(x) { + return 1 +} + +print f() +// expect: 0 + +print f("Hi") +// expect: 1 From c2f1e0033a353973fec956a58bd89faa45564008 Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Fri, 26 Apr 2024 09:28:30 -0400 Subject: [PATCH 065/181] First compiled metafunction resolution --- src/classes/metafunction.c | 55 ++++++++++++++++++++++++++++++++-- src/classes/metafunction.h | 3 +- src/datastructures/signature.c | 2 +- 3 files changed, 56 insertions(+), 4 deletions(-) diff --git a/src/classes/metafunction.c b/src/classes/metafunction.c index 2b8e8f5e..e46cecda 100644 --- a/src/classes/metafunction.c +++ b/src/classes/metafunction.c @@ -135,12 +135,19 @@ bool metafunction_slowresolve(objectmetafunction *f, int nargs, value *args, val * ********************************************************************** */ enum { + MF_CHECKNARGS, MF_RESOLVE, MF_FAIL }; DEFINE_VARRAY(mfinstruction, mfinstruction); +#define MFINSTRUCTION_EMPTY -1 + +#define MFINSTRUCTION_FAIL { .opcode=MF_FAIL } +#define MFINSTRUCTION_RESOLVE(fn) { .opcode=MF_RESOLVE, .data.resolvefn=fn, .branch=MFINSTRUCTION_EMPTY } +#define MFINSTRUCTION_CHECKNARG(n, brnch) { .opcode=MF_CHECKNARGS, .data.nargs=n, .branch=brnch } + typedef int mfindx; typedef struct { @@ -187,6 +194,30 @@ bool mfcompiler_popset(mfcompiler *c, mfset *set) { c->set.count--; } +void mfcompiler_disassemble(mfcompiler *c) { + int ninstr = c->fn->resolver.count; + for (int i=0; ifn->resolver.data[i]; + printf("%5i : ", i) ; + switch(instr->opcode) { + case MF_RESOLVE: { + printf("resolve "); + morpho_printvalue(NULL, instr->data.resolvefn); + signature *sig = _getsignature(instr->data.resolvefn); + printf(" "); + if (sig) signature_print(sig); + break; + } + case MF_CHECKNARGS: { + printf("checkargs (%i) -> (%ti)", instr->data.nargs, i+instr->branch+1); + break; + } + case MF_FAIL: printf("fail"); break; + } + printf("\n"); + } +} + /** Counts the range of parameters for the function call */ void mfcompiler_countparams(mfcompiler *c, mfset *set, int *min, int *max) { int imin=INT_MAX, imax=INT_MIN; @@ -238,12 +269,12 @@ bool mfcompile_countoutcomes(mfcompiler *c, mfset *set, int *best) { mfindx mfcompile_insertinstruction(mfcompiler *c, mfinstruction instr) { return varray_mfinstructionwrite(&c->fn->resolver, instr); } - + /** Compiles a single result */ mfindx mfcompile_resolve(mfcompiler *c, mfset *set) { // Should check all arguments have been resolved - mfinstruction instr = { .opcode=MF_RESOLVE, .data.resolvefn=set->rlist->fn }; + mfinstruction instr = MFINSTRUCTION_RESOLVE(set->rlist->fn); return mfcompile_insertinstruction(c, instr); } @@ -255,8 +286,24 @@ mfindx mfcompiler_dispatchonparam(mfcompiler *c, mfset *set, int i) { /** Attempts to dispatch based on the number of arguments */ mfindx mfcompiler_dispatchonnarg(mfcompiler *c, mfset *set, int min, int max) { if (set->count==2) { + for (int i=0; i<2; i++) { + signature *sig = set->rlist[i].sig; + int nparams; + signature_paramlist(sig, &nparams, NULL); + mfinstruction instr = MFINSTRUCTION_CHECKNARG(nparams, 1); + mfcompile_insertinstruction(c, instr); + + mfinstruction res = MFINSTRUCTION_RESOLVE(set->rlist[i].fn); + mfcompile_insertinstruction(c, res); + } + mfinstruction fail = MFINSTRUCTION_FAIL; + mfcompile_insertinstruction(c, fail); }; // else branch table + + mfcompiler_disassemble(c); + + return 0; } /** Attempts to discriminate between a list of possible signatures */ @@ -306,12 +353,16 @@ bool metafunction_resolve(objectmetafunction *fn, int nargs, value *args, value do { switch(pc->opcode) { + case MF_CHECKNARGS: + if (pc->data.nargs!=nargs) pc+=pc->branch; + break; case MF_RESOLVE: *out = pc->data.resolvefn; return true; case MF_FAIL: return false; } + pc++; } while(true); } diff --git a/src/classes/metafunction.h b/src/classes/metafunction.h index 0f93a521..bded5632 100644 --- a/src/classes/metafunction.h +++ b/src/classes/metafunction.h @@ -20,9 +20,10 @@ extern objecttype objectmetafunctiontype; typedef struct { int opcode; union { + int nargs; value resolvefn; } data; - indx bto; /* Branch to this instruction on failure */ + indx branch; /* Branch the pc by this amount on fail */ } mfinstruction; DECLARE_VARRAY(mfinstruction, mfinstruction); diff --git a/src/datastructures/signature.c b/src/datastructures/signature.c index b21c8330..ffc25709 100644 --- a/src/datastructures/signature.c +++ b/src/datastructures/signature.c @@ -152,5 +152,5 @@ void signature_print(signature *s) { if (itypes.count-1) printf(","); } - printf(")\n"); + printf(")"); } From 8ffd55ecf29e92aadee6d6dbc37bebd5a2e75e89 Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Sun, 28 Apr 2024 12:43:23 -0400 Subject: [PATCH 066/181] mfcompiler_dispatchonnarg uses mfcompiler_resolve --- src/classes/metafunction.c | 55 +++++++++++++++++++++++--------------- src/classes/metafunction.h | 5 +++- 2 files changed, 37 insertions(+), 23 deletions(-) diff --git a/src/classes/metafunction.c b/src/classes/metafunction.c index e46cecda..02470fbe 100644 --- a/src/classes/metafunction.c +++ b/src/classes/metafunction.c @@ -148,8 +148,6 @@ DEFINE_VARRAY(mfinstruction, mfinstruction); #define MFINSTRUCTION_RESOLVE(fn) { .opcode=MF_RESOLVE, .data.resolvefn=fn, .branch=MFINSTRUCTION_EMPTY } #define MFINSTRUCTION_CHECKNARG(n, brnch) { .opcode=MF_CHECKNARGS, .data.nargs=n, .branch=brnch } -typedef int mfindx; - typedef struct { signature *sig; /** Signature of the target */ value fn; /** The target */ @@ -160,6 +158,9 @@ typedef struct { mfresult *rlist; } mfset; +/** Static intiializer for the mfset */ +#define MFSET(c, l) { .count=c, .rlist=l } + DECLARE_VARRAY(mfset, mfset) DEFINE_VARRAY(mfset, mfset) @@ -196,20 +197,21 @@ bool mfcompiler_popset(mfcompiler *c, mfset *set) { void mfcompiler_disassemble(mfcompiler *c) { int ninstr = c->fn->resolver.count; + morpho_printvalue(NULL, MORPHO_OBJECT(c->fn)); + printf(":\n"); for (int i=0; ifn->resolver.data[i]; printf("%5i : ", i) ; switch(instr->opcode) { case MF_RESOLVE: { printf("resolve "); - morpho_printvalue(NULL, instr->data.resolvefn); signature *sig = _getsignature(instr->data.resolvefn); printf(" "); if (sig) signature_print(sig); break; } case MF_CHECKNARGS: { - printf("checkargs (%i) -> (%ti)", instr->data.nargs, i+instr->branch+1); + printf("checkargs (%i) -> (%i)", instr->data.nargs, i+instr->branch+1); break; } case MF_FAIL: printf("fail"); break; @@ -269,46 +271,55 @@ bool mfcompile_countoutcomes(mfcompiler *c, mfset *set, int *best) { mfindx mfcompile_insertinstruction(mfcompiler *c, mfinstruction instr) { return varray_mfinstructionwrite(&c->fn->resolver, instr); } - + +mfindx mfcompiler_currentinstruction(mfcompiler *c) { + return c->fn->resolver.count-1; +} + +void mfcompiler_setbranch(mfcompiler *c, mfindx i, mfindx branch) { + if (i>=c->fn->resolver.count) return; + c->fn->resolver.data[i].branch=branch; +} + /** Compiles a single result */ -mfindx mfcompile_resolve(mfcompiler *c, mfset *set) { +void mfcompiler_resolve(mfcompiler *c, mfset *set) { // Should check all arguments have been resolved mfinstruction instr = MFINSTRUCTION_RESOLVE(set->rlist->fn); - return mfcompile_insertinstruction(c, instr); + mfcompile_insertinstruction(c, instr); } /** Attempts to dispatch based on a parameter i */ -mfindx mfcompiler_dispatchonparam(mfcompiler *c, mfset *set, int i) { +void mfcompiler_dispatchonparam(mfcompiler *c, mfset *set, int i) { } /** Attempts to dispatch based on the number of arguments */ -mfindx mfcompiler_dispatchonnarg(mfcompiler *c, mfset *set, int min, int max) { +void mfcompiler_dispatchonnarg(mfcompiler *c, mfset *set, int min, int max) { if (set->count==2) { for (int i=0; i<2; i++) { signature *sig = set->rlist[i].sig; int nparams; - signature_paramlist(sig, &nparams, NULL); - mfinstruction instr = MFINSTRUCTION_CHECKNARG(nparams, 1); - mfcompile_insertinstruction(c, instr); + signature_paramlist(sig, &nparams, NULL); // Get the number of parameters + mfinstruction instr = MFINSTRUCTION_CHECKNARG(nparams, 0); + mfindx cindx = mfcompile_insertinstruction(c, instr); // Write the check nargs instruction + + mfset res = MFSET(1, &set->rlist[i]); // If it works, resolve on this implementation + mfcompiler_resolve(c, &res); - mfinstruction res = MFINSTRUCTION_RESOLVE(set->rlist[i].fn); - mfcompile_insertinstruction(c, res); + // Fix the branch instruction + mfindx eindx = mfcompiler_currentinstruction(c); + mfcompiler_setbranch(c, cindx, eindx-cindx); } mfinstruction fail = MFINSTRUCTION_FAIL; mfcompile_insertinstruction(c, fail); }; // else branch table - - mfcompiler_disassemble(c); - - return 0; } /** Attempts to discriminate between a list of possible signatures */ -mfindx mfcompile_set(mfcompiler *c, mfset *set) { - if (set->count==1) mfcompile_resolve(c, set); +void mfcompile_set(mfcompiler *c, mfset *set) { + if (set->count==1) mfcompiler_resolve(c, set); int min, max; // Count the range of possible parameters mfcompiler_countparams(c, set, &min, &max); @@ -321,8 +332,6 @@ mfindx mfcompile_set(mfcompiler *c, mfset *set) { int best; if (mfcompile_countoutcomes(c, set, &best)) return mfcompiler_dispatchonparam(c, set, best); - - return -1; } /** Compiles the metafunction resolver */ @@ -343,6 +352,8 @@ void metafunction_compile(objectmetafunction *fn) { mfcompile_set(&compiler, &set); + mfcompiler_disassemble(&compiler); + mfcompiler_clear(&compiler, fn); } diff --git a/src/classes/metafunction.h b/src/classes/metafunction.h index bded5632..c4452cbb 100644 --- a/src/classes/metafunction.h +++ b/src/classes/metafunction.h @@ -16,6 +16,9 @@ extern objecttype objectmetafunctiontype; #define OBJECT_METAFUNCTION objectmetafunctiontype +/** Index type for metafunction resolver */ +typedef int mfindx; + /** Compiled metafunction instruction set */ typedef struct { int opcode; @@ -23,7 +26,7 @@ typedef struct { int nargs; value resolvefn; } data; - indx branch; /* Branch the pc by this amount on fail */ + mfindx branch; /* Branch the pc by this amount on fail */ } mfinstruction; DECLARE_VARRAY(mfinstruction, mfinstruction); From 0dbe59ad5d75105db181981a4d133ecca7d09bfb Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Mon, 29 Apr 2024 08:55:38 -0400 Subject: [PATCH 067/181] Branch on nparams --- src/classes/metafunction.c | 104 +++++++++++++++----- src/classes/metafunction.h | 1 + src/datastructures/signature.c | 5 + src/datastructures/signature.h | 1 + test/types/multiple_dispatch_nparams.morpho | 3 + 5 files changed, 91 insertions(+), 23 deletions(-) diff --git a/src/classes/metafunction.c b/src/classes/metafunction.c index 02470fbe..d85e01e6 100644 --- a/src/classes/metafunction.c +++ b/src/classes/metafunction.c @@ -136,6 +136,7 @@ bool metafunction_slowresolve(objectmetafunction *f, int nargs, value *args, val enum { MF_CHECKNARGS, + MF_BRANCHNARGS, MF_RESOLVE, MF_FAIL }; @@ -144,9 +145,10 @@ DEFINE_VARRAY(mfinstruction, mfinstruction); #define MFINSTRUCTION_EMPTY -1 -#define MFINSTRUCTION_FAIL { .opcode=MF_FAIL } +#define MFINSTRUCTION_FAIL { .opcode=MF_FAIL, .branch=MFINSTRUCTION_EMPTY } #define MFINSTRUCTION_RESOLVE(fn) { .opcode=MF_RESOLVE, .data.resolvefn=fn, .branch=MFINSTRUCTION_EMPTY } #define MFINSTRUCTION_CHECKNARG(n, brnch) { .opcode=MF_CHECKNARGS, .data.nargs=n, .branch=brnch } +#define MFINSTRUCTION_BRANCHNARG(table, brnch) { .opcode=MF_BRANCHNARGS, .data.btable=table, .branch=brnch } typedef struct { signature *sig; /** Signature of the target */ @@ -195,6 +197,7 @@ bool mfcompiler_popset(mfcompiler *c, mfset *set) { c->set.count--; } +/** Disassemble */ void mfcompiler_disassemble(mfcompiler *c) { int ninstr = c->fn->resolver.count; morpho_printvalue(NULL, MORPHO_OBJECT(c->fn)); @@ -203,6 +206,18 @@ void mfcompiler_disassemble(mfcompiler *c) { mfinstruction *instr = &c->fn->resolver.data[i]; printf("%5i : ", i) ; switch(instr->opcode) { + case MF_CHECKNARGS: { + printf("checkargs (%i) -> (%i)", instr->data.nargs, i+instr->branch+1); + break; + } + case MF_BRANCHNARGS: { + printf("branchargs (%i) -> (%i)\n", instr->data.nargs, i+instr->branch+1); + for (int k=0; kdata.btable.count; k++) { + printf(" %i -> %i\n", k, i+instr->data.btable.data[k]+1); + } + + break; + } case MF_RESOLVE: { printf("resolve "); signature *sig = _getsignature(instr->data.resolvefn); @@ -210,10 +225,6 @@ void mfcompiler_disassemble(mfcompiler *c) { if (sig) signature_print(sig); break; } - case MF_CHECKNARGS: { - printf("checkargs (%i) -> (%i)", instr->data.nargs, i+instr->branch+1); - break; - } case MF_FAIL: printf("fail"); break; } printf("\n"); @@ -221,7 +232,7 @@ void mfcompiler_disassemble(mfcompiler *c) { } /** Counts the range of parameters for the function call */ -void mfcompiler_countparams(mfcompiler *c, mfset *set, int *min, int *max) { +void mfcompile_countparams(mfcompiler *c, mfset *set, int *min, int *max) { int imin=INT_MAX, imax=INT_MIN; for (int i=0; icount; i++) { int nparams; @@ -272,17 +283,29 @@ mfindx mfcompile_insertinstruction(mfcompiler *c, mfinstruction instr) { return varray_mfinstructionwrite(&c->fn->resolver, instr); } -mfindx mfcompiler_currentinstruction(mfcompiler *c) { +mfindx mfcompile_currentinstruction(mfcompiler *c) { return c->fn->resolver.count-1; } -void mfcompiler_setbranch(mfcompiler *c, mfindx i, mfindx branch) { +void mfcompile_setbranch(mfcompiler *c, mfindx i, mfindx branch) { if (i>=c->fn->resolver.count) return; c->fn->resolver.data[i].branch=branch; } +void mfcompile_fail(mfcompiler *c); +void mfcompile_resolve(mfcompiler *c, mfset *set); +void mfcompile_dispatchonparam(mfcompiler *c, mfset *set, int i); +void mfcompile_dispatchonnarg(mfcompiler *c, mfset *set, int min, int max); +void mfcompile_set(mfcompiler *c, mfset *set); + +/** Inserts a fail instruction */ +void mfcompile_fail(mfcompiler *c) { + mfinstruction fail = MFINSTRUCTION_FAIL; + mfcompile_insertinstruction(c, fail); +} + /** Compiles a single result */ -void mfcompiler_resolve(mfcompiler *c, mfset *set) { +void mfcompile_resolve(mfcompiler *c, mfset *set) { // Should check all arguments have been resolved mfinstruction instr = MFINSTRUCTION_RESOLVE(set->rlist->fn); @@ -290,12 +313,12 @@ void mfcompiler_resolve(mfcompiler *c, mfset *set) { } /** Attempts to dispatch based on a parameter i */ -void mfcompiler_dispatchonparam(mfcompiler *c, mfset *set, int i) { +void mfcompile_dispatchonparam(mfcompiler *c, mfset *set, int i) { } /** Attempts to dispatch based on the number of arguments */ -void mfcompiler_dispatchonnarg(mfcompiler *c, mfset *set, int min, int max) { +void mfcompile_dispatchonnarg(mfcompiler *c, mfset *set, int min, int max) { if (set->count==2) { for (int i=0; i<2; i++) { signature *sig = set->rlist[i].sig; @@ -305,33 +328,63 @@ void mfcompiler_dispatchonnarg(mfcompiler *c, mfset *set, int min, int max) { mfindx cindx = mfcompile_insertinstruction(c, instr); // Write the check nargs instruction mfset res = MFSET(1, &set->rlist[i]); // If it works, resolve on this implementation - mfcompiler_resolve(c, &res); + mfcompile_resolve(c, &res); // Fix the branch instruction - mfindx eindx = mfcompiler_currentinstruction(c); - mfcompiler_setbranch(c, cindx, eindx-cindx); + mfindx eindx = mfcompile_currentinstruction(c); + mfcompile_setbranch(c, cindx, eindx-cindx); } - mfinstruction fail = MFINSTRUCTION_FAIL; - mfcompile_insertinstruction(c, fail); + mfcompile_fail(c); + } else { + varray_int btable; + varray_intinit(&btable); + for (int i=0; i<=max; i++) varray_intwrite(&btable, 0); + + // Insert the branch table instruction + mfinstruction table = MFINSTRUCTION_BRANCHNARG(btable, 0); + mfindx tindx=mfcompile_insertinstruction(c, table); + // Immediately follow by a fail instruction if this falls through + mfcompile_fail(c); - }; // else branch table + // Count the number of implementations for each parameter count + for (int k=0; kcount; k++) { + btable.data[signature_countparams(set->rlist[k].sig)]++; + } + + // Compile the outcomes for each parameter count + for (int n=0; n<=max; n++) { + int nimp = btable.data[n]; + if (!nimp) continue; + + // Select all implementations that match this parameter value + mfresult rlist[nimp]; + mfset iset = MFSET(nimp, rlist); + for (int k=0, j=0; kcount; k++) { + int m=signature_countparams(set->rlist[k].sig); + if (m==n) { rlist[j]=set->rlist[k]; j++; } + } + + btable.data[n]=mfcompile_currentinstruction(c)-tindx; + mfcompile_set(c, &iset); + } + } } /** Attempts to discriminate between a list of possible signatures */ void mfcompile_set(mfcompiler *c, mfset *set) { - if (set->count==1) mfcompiler_resolve(c, set); + if (set->count==1) mfcompile_resolve(c, set); int min, max; // Count the range of possible parameters - mfcompiler_countparams(c, set, &min, &max); + mfcompile_countparams(c, set, &min, &max); // Dispatch on the number of parameters if it's in doubt - if (min!=max) return mfcompiler_dispatchonnarg(c, set, min, max); + if (min!=max) return mfcompile_dispatchonnarg(c, set, min, max); // If just one parameter, dispatch on it - if (min==1) return mfcompiler_dispatchonparam(c, set, 0); + if (min==1) return mfcompile_dispatchonparam(c, set, 0); int best; - if (mfcompile_countoutcomes(c, set, &best)) return mfcompiler_dispatchonparam(c, set, best); + if (mfcompile_countoutcomes(c, set, &best)) return mfcompile_dispatchonparam(c, set, best); } /** Compiles the metafunction resolver */ @@ -352,7 +405,7 @@ void metafunction_compile(objectmetafunction *fn) { mfcompile_set(&compiler, &set); - mfcompiler_disassemble(&compiler); + //mfcompiler_disassemble(&compiler); mfcompiler_clear(&compiler, fn); } @@ -367,6 +420,11 @@ bool metafunction_resolve(objectmetafunction *fn, int nargs, value *args, value case MF_CHECKNARGS: if (pc->data.nargs!=nargs) pc+=pc->branch; break; + case MF_BRANCHNARGS: + if (nargs<=pc->data.btable.count) { + pc+=pc->data.btable.data[nargs]; + } else pc+=pc->branch; + break; case MF_RESOLVE: *out = pc->data.resolvefn; return true; diff --git a/src/classes/metafunction.h b/src/classes/metafunction.h index c4452cbb..6e9bad45 100644 --- a/src/classes/metafunction.h +++ b/src/classes/metafunction.h @@ -25,6 +25,7 @@ typedef struct { union { int nargs; value resolvefn; + varray_int btable; } data; mfindx branch; /* Branch the pc by this amount on fail */ } mfinstruction; diff --git a/src/datastructures/signature.c b/src/datastructures/signature.c index ffc25709..f9ecf3a9 100644 --- a/src/datastructures/signature.c +++ b/src/datastructures/signature.c @@ -51,6 +51,11 @@ bool signature_paramlist(signature *s, int *nparams, value **ptypes) { return s->types.data; } +/** @brief Count the number of parameters in a signature */ +int signature_countparams(signature *s) { + return s->types.count; +} + /* ********************************************************************** * Parse signatures * ********************************************************************** */ diff --git a/src/datastructures/signature.h b/src/datastructures/signature.h index 0f796a70..885a9991 100644 --- a/src/datastructures/signature.h +++ b/src/datastructures/signature.h @@ -20,6 +20,7 @@ void signature_clear(signature *s); bool signature_istyped(signature *s); bool signature_isequal(signature *a, signature *b); bool signature_paramlist(signature *s, int *nparams, value **ptypes); +int signature_countparams(signature *s); void signature_set(signature *s, int nparam, value *types); bool signature_parse(char *sig, signature *out); diff --git a/test/types/multiple_dispatch_nparams.morpho b/test/types/multiple_dispatch_nparams.morpho index ac01f14a..5f11f804 100644 --- a/test/types/multiple_dispatch_nparams.morpho +++ b/test/types/multiple_dispatch_nparams.morpho @@ -27,3 +27,6 @@ print f(1, 2) print f(1,2,3) // expect: 3 + +print f(1,2,3,4) +// expect error 'MltplDsptchFld' From 997c2d0e40d6c9a7c62f038d82931cc1ae4d7b77 Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Mon, 29 Apr 2024 19:54:35 -0400 Subject: [PATCH 068/181] Resolve on object --- src/builtin/builtin.h | 2 + src/classes/metafunction.c | 163 ++++++++++++++++++++-- src/classes/metafunction.h | 3 +- src/datastructures/object.c | 11 ++ src/datastructures/signature.c | 7 + src/datastructures/signature.h | 1 + src/datastructures/value.c | 11 ++ test/types/multiple_dispatch.morpho | 6 +- test/types/multiple_dispatch_value.morpho | 15 ++ 9 files changed, 201 insertions(+), 18 deletions(-) create mode 100644 test/types/multiple_dispatch_value.morpho diff --git a/src/builtin/builtin.h b/src/builtin/builtin.h index 326ec3a8..9721c6d8 100644 --- a/src/builtin/builtin.h +++ b/src/builtin/builtin.h @@ -143,9 +143,11 @@ bool builtin_enumerateloop(vm *v, value obj, builtin_loopfunction fn, void *ref) void object_setveneerclass(objecttype type, value class); objectclass *object_getveneerclass(objecttype type); +bool object_veneerclasstotype(objectclass *clss, objecttype *type); void value_setveneerclass(value type, value class); objectclass *value_getveneerclass(value type); +bool value_veneerclasstotype(objectclass *clss, int *type); /* ------------------------------------------------------- * Initialization/finalization diff --git a/src/classes/metafunction.c b/src/classes/metafunction.c index d85e01e6..cedb6deb 100644 --- a/src/classes/metafunction.c +++ b/src/classes/metafunction.c @@ -137,6 +137,9 @@ bool metafunction_slowresolve(objectmetafunction *f, int nargs, value *args, val enum { MF_CHECKNARGS, MF_BRANCHNARGS, + MF_BRANCHVALUETYPE, + MF_BRANCHOBJECTTYPE, + MF_BRANCHCLASS, MF_RESOLVE, MF_FAIL }; @@ -147,8 +150,9 @@ DEFINE_VARRAY(mfinstruction, mfinstruction); #define MFINSTRUCTION_FAIL { .opcode=MF_FAIL, .branch=MFINSTRUCTION_EMPTY } #define MFINSTRUCTION_RESOLVE(fn) { .opcode=MF_RESOLVE, .data.resolvefn=fn, .branch=MFINSTRUCTION_EMPTY } -#define MFINSTRUCTION_CHECKNARG(n, brnch) { .opcode=MF_CHECKNARGS, .data.nargs=n, .branch=brnch } +#define MFINSTRUCTION_CHECKNARG(n, brnch) { .opcode=MF_CHECKNARGS, .narg=n, .branch=brnch } #define MFINSTRUCTION_BRANCHNARG(table, brnch) { .opcode=MF_BRANCHNARGS, .data.btable=table, .branch=brnch } +#define MFINSTRUCTION_BRANCHOBJECTTYPE(n, table, brnch) { .opcode=MF_BRANCHOBJECTTYPE, .narg=n, .data.btable=table, .branch=brnch } typedef struct { signature *sig; /** Signature of the target */ @@ -166,6 +170,16 @@ typedef struct { DECLARE_VARRAY(mfset, mfset) DEFINE_VARRAY(mfset, mfset) +typedef bool (mfset_selectfn) (mfresult *res, void *ref); + +/** Select subset of elements from a set given a match function */ +void mfset_select(mfset *set, mfset *out, mfset_selectfn matchfn, void *ref) { + out->count=0; + for (int k=0; kcount; k++) { + if (matchfn(set->rlist+k, ref)) { out->rlist[out->count++]=set->rlist[k]; } + } +} + typedef struct { objectmetafunction *fn; dictionary pcount; @@ -197,6 +211,12 @@ bool mfcompiler_popset(mfcompiler *c, mfset *set) { c->set.count--; } +void _mfcompiler_disassemblebranchtable(mfinstruction *instr, mfindx i) { + for (int k=0; kdata.btable.count; k++) { + printf(" %i -> %i\n", k, i+instr->data.btable.data[k]+1); + } +} + /** Disassemble */ void mfcompiler_disassemble(mfcompiler *c) { int ninstr = c->fn->resolver.count; @@ -207,15 +227,26 @@ void mfcompiler_disassemble(mfcompiler *c) { printf("%5i : ", i) ; switch(instr->opcode) { case MF_CHECKNARGS: { - printf("checkargs (%i) -> (%i)", instr->data.nargs, i+instr->branch+1); + printf("checkargs (%i) -> (%i)", instr->narg, i+instr->branch+1); break; } case MF_BRANCHNARGS: { - printf("branchargs (%i) -> (%i)\n", instr->data.nargs, i+instr->branch+1); + printf("branchargs (%i) -> (%i)\n", instr->narg, i+instr->branch+1); + _mfcompiler_disassemblebranchtable(instr, i); + break; + } + case MF_BRANCHVALUETYPE: { + printf("branchvalue (%i)\n", instr->narg); + _mfcompiler_disassemblebranchtable(instr, i); + break; + } + case MF_BRANCHOBJECTTYPE: { + printf("branchobjtype (%i)\n", instr->narg); for (int k=0; kdata.btable.count; k++) { - printf(" %i -> %i\n", k, i+instr->data.btable.data[k]+1); + if (instr->data.btable.data[k]==0) continue; + objectclass *klass=object_getveneerclass(k); + printf(" %i [%s] -> %i\n", k, MORPHO_GETCSTRING(klass->name), i+instr->data.btable.data[k]+1); } - break; } case MF_RESOLVE: { @@ -312,11 +343,105 @@ void mfcompile_resolve(mfcompiler *c, mfset *set) { mfcompile_insertinstruction(c, instr); } +enum { + MF_VENEERVALUE, + MF_VENEEROBJECT, + MF_INSTANCE, + MF_FREE +}; + +/** Detects the kind of type */ +int _detecttype(value type, int *tindx) { + if (MORPHO_ISCLASS(type)) { + objectclass *klass = MORPHO_GETCLASS(type); + if (object_veneerclasstotype(klass, tindx)) { + return MF_VENEEROBJECT; + } else if (value_veneerclasstotype(klass, tindx)) { + return MF_VENEERVALUE; + } else return MF_INSTANCE; + } + return MF_FREE; +} + +typedef struct { + int i; + int tindx; +} _selectobjectyperef; + +bool _selectobjtype(mfresult *res, void *ref) { + _selectobjectyperef *sobj = (_selectobjectyperef *) ref; + value type; + if (!signature_getparamtype(res->sig, sobj->i, &type)) return false; + + int tindx; + if (_detecttype(type, &tindx)!=MF_VENEEROBJECT) return false; + + return (tindx==sobj->tindx); +} + +void mfcompile_dispatchveneerobj(mfcompiler *c, mfset *set, int i) { + int typecount[MORPHO_MAXIMUMOBJECTDEFNS], maxindx=0; + for (int i=0; icount; k++) { + value type; + if (!signature_getparamtype(set->rlist[k].sig, i, &type)) return; + int typindx; + if (_detecttype(type, &typindx)!=MF_VENEEROBJECT) continue; + typecount[typindx]++; + if (typindx>maxindx) maxindx=typindx; + } + + varray_int btable; + varray_intinit(&btable); + for (int i=0; i<=maxindx; i++) varray_intwrite(&btable, 0); + + // Insert the branch instruction + mfinstruction instr = MFINSTRUCTION_BRANCHOBJECTTYPE(i, btable, 0); + mfindx bindx = mfcompile_insertinstruction(c, instr); + + // Immediately follow by a fail instruction if this falls through + mfcompile_fail(c); + + mfresult rlist[set->count]; + mfset out = { .count=0, .rlist = rlist }; + + // Deal with each outcome + for (int j=0; j<=maxindx; j++) { + if (!typecount[j]) continue; + + _selectobjectyperef ref = { .i = i, .tindx = j }; + mfset_select(set, &out, _selectobjtype, &ref); + + // Set the branch point + btable.data[j]=mfcompile_currentinstruction(c)-bindx; + mfcompile_set(c, &out); + } +} + /** Attempts to dispatch based on a parameter i */ void mfcompile_dispatchonparam(mfcompiler *c, mfset *set, int i) { + int typecount[MF_FREE+1] = { 0, 0, 0, 0}; + + // Determine what types are present + for (int k=0; kcount; k++) { + value type; + if (!signature_getparamtype(set->rlist[i].sig, i, &type)) return; + typecount[_detecttype(type, NULL)]++; + } + + mfcompile_dispatchveneerobj(c, set, i); + } + +bool _selectnparam(mfresult *res, void *ref) { + int n=*(int *) ref; + int m=signature_countparams(res->sig); + return (m==n); +} + /** Attempts to dispatch based on the number of arguments */ void mfcompile_dispatchonnarg(mfcompiler *c, mfset *set, int min, int max) { if (set->count==2) { @@ -335,7 +460,7 @@ void mfcompile_dispatchonnarg(mfcompiler *c, mfset *set, int min, int max) { mfcompile_setbranch(c, cindx, eindx-cindx); } mfcompile_fail(c); - } else { + } else { // If more than two options, generate a branch table varray_int btable; varray_intinit(&btable); for (int i=0; i<=max; i++) varray_intwrite(&btable, 0); @@ -359,11 +484,9 @@ void mfcompile_dispatchonnarg(mfcompiler *c, mfset *set, int min, int max) { // Select all implementations that match this parameter value mfresult rlist[nimp]; mfset iset = MFSET(nimp, rlist); - for (int k=0, j=0; kcount; k++) { - int m=signature_countparams(set->rlist[k].sig); - if (m==n) { rlist[j]=set->rlist[k]; j++; } - } + mfset_select(set, &iset, _selectnparam, &n); + // Set the branch point btable.data[n]=mfcompile_currentinstruction(c)-tindx; mfcompile_set(c, &iset); } @@ -372,7 +495,7 @@ void mfcompile_dispatchonnarg(mfcompiler *c, mfset *set, int min, int max) { /** Attempts to discriminate between a list of possible signatures */ void mfcompile_set(mfcompiler *c, mfset *set) { - if (set->count==1) mfcompile_resolve(c, set); + if (set->count==1) return mfcompile_resolve(c, set); int min, max; // Count the range of possible parameters mfcompile_countparams(c, set, &min, &max); @@ -405,7 +528,7 @@ void metafunction_compile(objectmetafunction *fn) { mfcompile_set(&compiler, &set); - //mfcompiler_disassemble(&compiler); + mfcompiler_disassemble(&compiler); mfcompiler_clear(&compiler, fn); } @@ -418,13 +541,25 @@ bool metafunction_resolve(objectmetafunction *fn, int nargs, value *args, value do { switch(pc->opcode) { case MF_CHECKNARGS: - if (pc->data.nargs!=nargs) pc+=pc->branch; + if (pc->narg!=nargs) pc+=pc->branch; break; case MF_BRANCHNARGS: - if (nargs<=pc->data.btable.count) { + if (nargsdata.btable.count) { pc+=pc->data.btable.data[nargs]; } else pc+=pc->branch; break; + case MF_BRANCHVALUETYPE: { + int type = (int) MORPHO_GETORDEREDTYPE(args[pc->narg]); + pc+=pc->data.btable.data[type]; + } + break; + case MF_BRANCHOBJECTTYPE: { + int type = MORPHO_GETOBJECTTYPE(args[pc->narg]); + pc+=pc->data.btable.data[type]; + } + break; + case MF_BRANCHCLASS: + break; case MF_RESOLVE: *out = pc->data.resolvefn; return true; diff --git a/src/classes/metafunction.h b/src/classes/metafunction.h index 6e9bad45..a960a208 100644 --- a/src/classes/metafunction.h +++ b/src/classes/metafunction.h @@ -22,10 +22,11 @@ typedef int mfindx; /** Compiled metafunction instruction set */ typedef struct { int opcode; + int narg; union { - int nargs; value resolvefn; varray_int btable; + dictionary bdict; } data; mfindx branch; /* Branch the pc by this amount on fail */ } mfinstruction; diff --git a/src/datastructures/object.c b/src/datastructures/object.c index 61c66885..0de6160e 100644 --- a/src/datastructures/object.c +++ b/src/datastructures/object.c @@ -143,6 +143,17 @@ objectclass *object_getveneerclass(objecttype type) { return (objectclass *) _objectdefns[type].veneer; } +/** @brief Finds the object type associated with a veneer class; returns false if it is not a veneer class */ +bool object_veneerclasstotype(objectclass *clss, objecttype *type) { + for (int i=0; itypes.count; } +/** @brief Returns the type of the i'th parameter, if it exists */ +bool signature_getparamtype(signature *s, int i, value *type) { + if (i>=s->types.count) return false; + if (type) *type = s->types.data[i]; + return true; +} + /* ********************************************************************** * Parse signatures * ********************************************************************** */ diff --git a/src/datastructures/signature.h b/src/datastructures/signature.h index 885a9991..ccd87ff3 100644 --- a/src/datastructures/signature.h +++ b/src/datastructures/signature.h @@ -20,6 +20,7 @@ void signature_clear(signature *s); bool signature_istyped(signature *s); bool signature_isequal(signature *a, signature *b); bool signature_paramlist(signature *s, int *nparams, value **ptypes); +bool signature_getparamtype(signature *s, int i, value *type); int signature_countparams(signature *s); void signature_set(signature *s, int nparam, value *types); diff --git a/src/datastructures/value.c b/src/datastructures/value.c index c02eeebe..7f40af93 100644 --- a/src/datastructures/value.c +++ b/src/datastructures/value.c @@ -262,6 +262,17 @@ objectclass *value_getveneerclass(value type) { } } +/** @brief Returns an type index for the class */ +bool value_veneerclasstotype(objectclass *clss, int *type) { + for (int i=0; i Date: Mon, 29 Apr 2024 20:49:44 -0400 Subject: [PATCH 069/181] mfcompile_dispatchveneerobj --- src/classes/metafunction.c | 61 +++++++++++++---------------- test/types/multiple_dispatch.morpho | 4 +- 2 files changed, 31 insertions(+), 34 deletions(-) diff --git a/src/classes/metafunction.c b/src/classes/metafunction.c index cedb6deb..864a503e 100644 --- a/src/classes/metafunction.c +++ b/src/classes/metafunction.c @@ -157,6 +157,7 @@ DEFINE_VARRAY(mfinstruction, mfinstruction); typedef struct { signature *sig; /** Signature of the target */ value fn; /** The target */ + int indx; /** Used to sort */ } mfresult; typedef struct { @@ -363,35 +364,25 @@ int _detecttype(value type, int *tindx) { return MF_FREE; } -typedef struct { - int i; - int tindx; -} _selectobjectyperef; - -bool _selectobjtype(mfresult *res, void *ref) { - _selectobjectyperef *sobj = (_selectobjectyperef *) ref; - value type; - if (!signature_getparamtype(res->sig, sobj->i, &type)) return false; - - int tindx; - if (_detecttype(type, &tindx)!=MF_VENEEROBJECT) return false; - - return (tindx==sobj->tindx); +int _mfresultsortfn (const void *a, const void *b) { + mfresult *aa = (mfresult *) a, *bb = (mfresult *) b; + return aa->indx-bb->indx; } void mfcompile_dispatchveneerobj(mfcompiler *c, mfset *set, int i) { - int typecount[MORPHO_MAXIMUMOBJECTDEFNS], maxindx=0; - for (int i=0; icount; k++) { - value type; if (!signature_getparamtype(set->rlist[k].sig, i, &type)) return; - int typindx; - if (_detecttype(type, &typindx)!=MF_VENEEROBJECT) continue; - typecount[typindx]++; - if (typindx>maxindx) maxindx=typindx; + if (_detecttype(type, &set->rlist[k].indx)!=MF_VENEEROBJECT) set->rlist[k].indx=-1; } + // Sort the set on the type index + qsort(set->rlist, set->count, sizeof(mfresult), _mfresultsortfn); + + // Create the branch table + int maxindx=set->rlist[set->count-1].indx; varray_int btable; varray_intinit(&btable); for (int i=0; i<=maxindx; i++) varray_intwrite(&btable, 0); @@ -403,19 +394,22 @@ void mfcompile_dispatchveneerobj(mfcompiler *c, mfset *set, int i) { // Immediately follow by a fail instruction if this falls through mfcompile_fail(c); - mfresult rlist[set->count]; - mfset out = { .count=0, .rlist = rlist }; + int k=0; + // Skip anything that isn't + while (set->rlist[k].indx<0 && kcount) k++; // Deal with each outcome - for (int j=0; j<=maxindx; j++) { - if (!typecount[j]) continue; + while (kcount) { + int indx=set->rlist[k].indx, n=0; + while (k+ncount && set->rlist[k+n].indx==indx) n++; - _selectobjectyperef ref = { .i = i, .tindx = j }; - mfset_select(set, &out, _selectobjtype, &ref); + mfset out = MFSET(n, &set->rlist[k]); // Set the branch point - btable.data[j]=mfcompile_currentinstruction(c)-bindx; + btable.data[indx]=mfcompile_currentinstruction(c)-bindx; mfcompile_set(c, &out); + + k+=n; } } @@ -435,7 +429,6 @@ void mfcompile_dispatchonparam(mfcompiler *c, mfset *set, int i) { } - bool _selectnparam(mfresult *res, void *ref) { int n=*(int *) ref; int m=signature_countparams(res->sig); @@ -465,16 +458,18 @@ void mfcompile_dispatchonnarg(mfcompiler *c, mfset *set, int min, int max) { varray_intinit(&btable); for (int i=0; i<=max; i++) varray_intwrite(&btable, 0); + // Count the number of implementations for each parameter count + for (int k=0; kcount; k++) { + btable.data[signature_countparams(set->rlist[k].sig)]++; + } + // Insert the branch table instruction mfinstruction table = MFINSTRUCTION_BRANCHNARG(btable, 0); mfindx tindx=mfcompile_insertinstruction(c, table); + // Immediately follow by a fail instruction if this falls through mfcompile_fail(c); - // Count the number of implementations for each parameter count - for (int k=0; kcount; k++) { - btable.data[signature_countparams(set->rlist[k].sig)]++; - } // Compile the outcomes for each parameter count for (int n=0; n<=max; n++) { diff --git a/test/types/multiple_dispatch.morpho b/test/types/multiple_dispatch.morpho index df55a369..20273537 100644 --- a/test/types/multiple_dispatch.morpho +++ b/test/types/multiple_dispatch.morpho @@ -15,7 +15,6 @@ fn f(List x) { f("Hi") // expect: Hi - //f(1.5) // expect: 2.5 @@ -23,3 +22,6 @@ var a = [] f(a) print a // expect: [ Ho ] + +f({}) +// expect error 'MltplDsptchFld' From cee02ca54a360b75893676793ba956e0f723443c Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Mon, 29 Apr 2024 21:01:18 -0400 Subject: [PATCH 070/181] mfcompile_dispatchonnarg now uses mfcompile_branchtable --- src/classes/metafunction.c | 80 ++++++++++++++------------------------ 1 file changed, 30 insertions(+), 50 deletions(-) diff --git a/src/classes/metafunction.c b/src/classes/metafunction.c index 864a503e..40b44adf 100644 --- a/src/classes/metafunction.c +++ b/src/classes/metafunction.c @@ -171,16 +171,6 @@ typedef struct { DECLARE_VARRAY(mfset, mfset) DEFINE_VARRAY(mfset, mfset) -typedef bool (mfset_selectfn) (mfresult *res, void *ref); - -/** Select subset of elements from a set given a match function */ -void mfset_select(mfset *set, mfset *out, mfset_selectfn matchfn, void *ref) { - out->count=0; - for (int k=0; kcount; k++) { - if (matchfn(set->rlist+k, ref)) { out->rlist[out->count++]=set->rlist[k]; } - } -} - typedef struct { objectmetafunction *fn; dictionary pcount; @@ -344,6 +334,27 @@ void mfcompile_resolve(mfcompiler *c, mfset *set) { mfcompile_insertinstruction(c, instr); } +/** Compile a branch table from a sorted set */ +void mfcompile_branchtable(mfcompiler *c, mfset *set, mfindx bindx, varray_int *btable) { + int k=0; + // Values with negative indices shouldn't be included in the branch table + while (set->rlist[k].indx<0 && kcount) k++; + + // Deal with each outcome + while (kcount) { + int indx=set->rlist[k].indx, n=0; + while (k+ncount && set->rlist[k+n].indx==indx) n++; + + mfset out = MFSET(n, &set->rlist[k]); + + // Set the branch point + btable->data[indx]=mfcompile_currentinstruction(c)-bindx; + mfcompile_set(c, &out); + + k+=n; + } +} + enum { MF_VENEERVALUE, MF_VENEEROBJECT, @@ -394,23 +405,8 @@ void mfcompile_dispatchveneerobj(mfcompiler *c, mfset *set, int i) { // Immediately follow by a fail instruction if this falls through mfcompile_fail(c); - int k=0; - // Skip anything that isn't - while (set->rlist[k].indx<0 && kcount) k++; - - // Deal with each outcome - while (kcount) { - int indx=set->rlist[k].indx, n=0; - while (k+ncount && set->rlist[k+n].indx==indx) n++; - - mfset out = MFSET(n, &set->rlist[k]); - - // Set the branch point - btable.data[indx]=mfcompile_currentinstruction(c)-bindx; - mfcompile_set(c, &out); - - k+=n; - } + // Compile the branch table + mfcompile_branchtable(c, set, bindx, &btable); } /** Attempts to dispatch based on a parameter i */ @@ -429,12 +425,6 @@ void mfcompile_dispatchonparam(mfcompiler *c, mfset *set, int i) { } -bool _selectnparam(mfresult *res, void *ref) { - int n=*(int *) ref; - int m=signature_countparams(res->sig); - return (m==n); -} - /** Attempts to dispatch based on the number of arguments */ void mfcompile_dispatchonnarg(mfcompiler *c, mfset *set, int min, int max) { if (set->count==2) { @@ -460,31 +450,21 @@ void mfcompile_dispatchonnarg(mfcompiler *c, mfset *set, int min, int max) { // Count the number of implementations for each parameter count for (int k=0; kcount; k++) { - btable.data[signature_countparams(set->rlist[k].sig)]++; + set->rlist[k].indx=signature_countparams(set->rlist[k].sig); } + // Sort the set on the type index + qsort(set->rlist, set->count, sizeof(mfresult), _mfresultsortfn); + // Insert the branch table instruction mfinstruction table = MFINSTRUCTION_BRANCHNARG(btable, 0); - mfindx tindx=mfcompile_insertinstruction(c, table); + mfindx bindx = mfcompile_insertinstruction(c, table); // Immediately follow by a fail instruction if this falls through mfcompile_fail(c); - - // Compile the outcomes for each parameter count - for (int n=0; n<=max; n++) { - int nimp = btable.data[n]; - if (!nimp) continue; - - // Select all implementations that match this parameter value - mfresult rlist[nimp]; - mfset iset = MFSET(nimp, rlist); - mfset_select(set, &iset, _selectnparam, &n); - - // Set the branch point - btable.data[n]=mfcompile_currentinstruction(c)-tindx; - mfcompile_set(c, &iset); - } + // Compile the branch table + mfcompile_branchtable(c, set, bindx, &btable); } } From 32814bf8773746e0e0641a57be94f4c896ca5000 Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Tue, 30 Apr 2024 08:03:46 -0400 Subject: [PATCH 071/181] branch on value type --- src/classes/metafunction.c | 93 +++++++++++++++++++++++------ src/datastructures/value.c | 2 +- test/types/multiple_dispatch.morpho | 6 +- 3 files changed, 79 insertions(+), 22 deletions(-) diff --git a/src/classes/metafunction.c b/src/classes/metafunction.c index 40b44adf..17612acb 100644 --- a/src/classes/metafunction.c +++ b/src/classes/metafunction.c @@ -153,6 +153,7 @@ DEFINE_VARRAY(mfinstruction, mfinstruction); #define MFINSTRUCTION_CHECKNARG(n, brnch) { .opcode=MF_CHECKNARGS, .narg=n, .branch=brnch } #define MFINSTRUCTION_BRANCHNARG(table, brnch) { .opcode=MF_BRANCHNARGS, .data.btable=table, .branch=brnch } #define MFINSTRUCTION_BRANCHOBJECTTYPE(n, table, brnch) { .opcode=MF_BRANCHOBJECTTYPE, .narg=n, .data.btable=table, .branch=brnch } +#define MFINSTRUCTION_BRANCHVALUETYPE(n, table, brnch) { .opcode=MF_BRANCHVALUETYPE, .narg=n, .data.btable=table, .branch=brnch } typedef struct { signature *sig; /** Signature of the target */ @@ -227,12 +228,16 @@ void mfcompiler_disassemble(mfcompiler *c) { break; } case MF_BRANCHVALUETYPE: { - printf("branchvalue (%i)\n", instr->narg); - _mfcompiler_disassemblebranchtable(instr, i); + printf("branchvalue (%i) -> (%i)\n", instr->narg, i+instr->branch+1); + for (int k=0; kdata.btable.count; k++) { + if (instr->data.btable.data[k]==0) continue; + objectclass *klass=value_getveneerclass(k); + printf(" %i [%s] -> %i\n", k, MORPHO_GETCSTRING(klass->name), i+instr->data.btable.data[k]+1); + } break; } case MF_BRANCHOBJECTTYPE: { - printf("branchobjtype (%i)\n", instr->narg); + printf("branchobjtype (%i) -> (%i)\n", instr->narg, i+instr->branch+1); for (int k=0; kdata.btable.count; k++) { if (instr->data.btable.data[k]==0) continue; objectclass *klass=object_getveneerclass(k); @@ -314,24 +319,24 @@ void mfcompile_setbranch(mfcompiler *c, mfindx i, mfindx branch) { c->fn->resolver.data[i].branch=branch; } -void mfcompile_fail(mfcompiler *c); -void mfcompile_resolve(mfcompiler *c, mfset *set); +mfindx mfcompile_fail(mfcompiler *c); +mfindx mfcompile_resolve(mfcompiler *c, mfset *set); void mfcompile_dispatchonparam(mfcompiler *c, mfset *set, int i); void mfcompile_dispatchonnarg(mfcompiler *c, mfset *set, int min, int max); void mfcompile_set(mfcompiler *c, mfset *set); /** Inserts a fail instruction */ -void mfcompile_fail(mfcompiler *c) { +mfindx mfcompile_fail(mfcompiler *c) { mfinstruction fail = MFINSTRUCTION_FAIL; - mfcompile_insertinstruction(c, fail); + return mfcompile_insertinstruction(c, fail); } /** Compiles a single result */ -void mfcompile_resolve(mfcompiler *c, mfset *set) { +mfindx mfcompile_resolve(mfcompiler *c, mfset *set) { // Should check all arguments have been resolved mfinstruction instr = MFINSTRUCTION_RESOLVE(set->rlist->fn); - mfcompile_insertinstruction(c, instr); + return mfcompile_insertinstruction(c, instr); } /** Compile a branch table from a sorted set */ @@ -380,12 +385,13 @@ int _mfresultsortfn (const void *a, const void *b) { return aa->indx-bb->indx; } -void mfcompile_dispatchveneerobj(mfcompiler *c, mfset *set, int i) { +/** Branch table on object type */ +mfindx mfcompile_dispatchveneerobj(mfcompiler *c, mfset *set, int i) { value type; // Extract the type index for each member of the set for (int k=0; kcount; k++) { - if (!signature_getparamtype(set->rlist[k].sig, i, &type)) return; + if (!signature_getparamtype(set->rlist[k].sig, i, &type)) return MFINSTRUCTION_EMPTY; if (_detecttype(type, &set->rlist[k].indx)!=MF_VENEEROBJECT) set->rlist[k].indx=-1; } @@ -402,11 +408,45 @@ void mfcompile_dispatchveneerobj(mfcompiler *c, mfset *set, int i) { mfinstruction instr = MFINSTRUCTION_BRANCHOBJECTTYPE(i, btable, 0); mfindx bindx = mfcompile_insertinstruction(c, instr); - // Immediately follow by a fail instruction if this falls through + // Fail if an object type isn't in the table mfcompile_fail(c); // Compile the branch table mfcompile_branchtable(c, set, bindx, &btable); + + return bindx; +} + +/** Branch table on value type */ +mfindx mfcompile_dispatchveneervalue(mfcompiler *c, mfset *set, int i) { + value type; + + // Extract the type index for each member of the set + for (int k=0; kcount; k++) { + if (!signature_getparamtype(set->rlist[k].sig, i, &type)) return MFINSTRUCTION_EMPTY; + if (_detecttype(type, &set->rlist[k].indx)!=MF_VENEERVALUE) set->rlist[k].indx=-1; + } + + // Sort the set on the type index + qsort(set->rlist, set->count, sizeof(mfresult), _mfresultsortfn); + + // Create the branch table + int maxindx=set->rlist[set->count-1].indx; + varray_int btable; + varray_intinit(&btable); + for (int i=0; i<=maxindx; i++) varray_intwrite(&btable, 0); + + // Insert the branch instruction + mfinstruction instr = MFINSTRUCTION_BRANCHVALUETYPE(i, btable, 0); + mfindx bindx = mfcompile_insertinstruction(c, instr); + + // Fail if an object type isn't in the table + mfcompile_fail(c); + + // Compile the branch table + mfcompile_branchtable(c, set, bindx, &btable); + + return bindx; } /** Attempts to dispatch based on a parameter i */ @@ -416,13 +456,26 @@ void mfcompile_dispatchonparam(mfcompiler *c, mfset *set, int i) { // Determine what types are present for (int k=0; kcount; k++) { value type; - if (!signature_getparamtype(set->rlist[i].sig, i, &type)) return; + if (!signature_getparamtype(set->rlist[k].sig, i, &type)) return; typecount[_detecttype(type, NULL)]++; } - mfcompile_dispatchveneerobj(c, set, i); + mfindx bindx = MFINSTRUCTION_EMPTY; + if (typecount[MF_VENEERVALUE]) { + bindx = mfcompile_dispatchveneervalue(c, set, i); + } + if (typecount[MF_VENEEROBJECT]) { + mfindx oindx = mfcompile_dispatchveneerobj(c, set, i); + if (bindx!=MFINSTRUCTION_EMPTY) { + mfcompile_setbranch(c, bindx, oindx-bindx-1); + } + bindx=oindx; + } + + mfindx findx = mfcompile_fail(c); + mfcompile_setbranch(c, bindx, findx-bindx-1); } /** Attempts to dispatch based on the number of arguments */ @@ -524,13 +577,17 @@ bool metafunction_resolve(objectmetafunction *fn, int nargs, value *args, value } else pc+=pc->branch; break; case MF_BRANCHVALUETYPE: { - int type = (int) MORPHO_GETORDEREDTYPE(args[pc->narg]); - pc+=pc->data.btable.data[type]; + if (!MORPHO_ISOBJECT(args[pc->narg])) { + int type = (int) MORPHO_GETORDEREDTYPE(args[pc->narg]); + pc+=pc->data.btable.data[type]; + } else pc+=pc->branch; } break; case MF_BRANCHOBJECTTYPE: { - int type = MORPHO_GETOBJECTTYPE(args[pc->narg]); - pc+=pc->data.btable.data[type]; + if (MORPHO_ISOBJECT(args[pc->narg])) { + int type = MORPHO_GETOBJECTTYPE(args[pc->narg]); + pc+=pc->data.btable.data[type]; + } else pc+=pc->branch; } break; case MF_BRANCHCLASS: diff --git a/src/datastructures/value.c b/src/datastructures/value.c index 7f40af93..4d7ced19 100644 --- a/src/datastructures/value.c +++ b/src/datastructures/value.c @@ -266,7 +266,7 @@ objectclass *value_getveneerclass(value type) { bool value_veneerclasstotype(objectclass *clss, int *type) { for (int i=0; i Date: Tue, 30 Apr 2024 12:58:09 -0400 Subject: [PATCH 072/181] Correct branchvalue --- src/builtin/builtin.h | 1 + src/classes/metafunction.c | 2 +- src/datastructures/value.c | 7 +++++++ src/datastructures/value.h | 9 ++++++--- test/types/multiple_dispatch_value.morpho | 4 ++-- 5 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/builtin/builtin.h b/src/builtin/builtin.h index 9721c6d8..524d37af 100644 --- a/src/builtin/builtin.h +++ b/src/builtin/builtin.h @@ -147,6 +147,7 @@ bool object_veneerclasstotype(objectclass *clss, objecttype *type); void value_setveneerclass(value type, value class); objectclass *value_getveneerclass(value type); +objectclass *value_veneerclassfromtype(int type); bool value_veneerclasstotype(objectclass *clss, int *type); /* ------------------------------------------------------- diff --git a/src/classes/metafunction.c b/src/classes/metafunction.c index 17612acb..b7c8f4a3 100644 --- a/src/classes/metafunction.c +++ b/src/classes/metafunction.c @@ -231,7 +231,7 @@ void mfcompiler_disassemble(mfcompiler *c) { printf("branchvalue (%i) -> (%i)\n", instr->narg, i+instr->branch+1); for (int k=0; kdata.btable.count; k++) { if (instr->data.btable.data[k]==0) continue; - objectclass *klass=value_getveneerclass(k); + objectclass *klass=value_veneerclassfromtype(k); printf(" %i [%s] -> %i\n", k, MORPHO_GETCSTRING(klass->name), i+instr->data.btable.data[k]+1); } break; diff --git a/src/datastructures/value.c b/src/datastructures/value.c index 4d7ced19..51f940ef 100644 --- a/src/datastructures/value.c +++ b/src/datastructures/value.c @@ -262,6 +262,13 @@ objectclass *value_getveneerclass(value type) { } } +/** @brief Returns the veneer class given the type index */ +objectclass *value_veneerclassfromtype(int type) { + if (type>47) & 0x7) - /** Map VALUE_XXX macros to type bits */ #define VALUE_NIL (TAG_NIL) #define VALUE_INTEGER (TAG_INT) @@ -126,6 +123,12 @@ static inline bool morpho_ofsametype(value a, value b) { return false; } +/** Get a non-object's type field as an integer */ +static inline int _getorderedtype(value x) { + return (MORPHO_ISFLOAT(x) ? 0 : (((x) & TYPE_BITS)>>47) & 0x7); +} +#define MORPHO_GETORDEREDTYPE(x) _getorderedtype(x) + /** Alternatively, we represent a value through a struct. */ #else diff --git a/test/types/multiple_dispatch_value.morpho b/test/types/multiple_dispatch_value.morpho index 7b9c5c18..a433baa0 100644 --- a/test/types/multiple_dispatch_value.morpho +++ b/test/types/multiple_dispatch_value.morpho @@ -1,11 +1,11 @@ // Dipatch a function on value types fn f(Int x) { - print 0 + return 0 } fn f(Float x) { - print 1 + return 1 } print f(1) From ed10469ffab9211989d6431f40860eeb8afa2bd3 Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Tue, 30 Apr 2024 21:37:52 -0400 Subject: [PATCH 073/181] Handle parameters that can take any type --- src/classes/metafunction.c | 104 ++++++++++++++++++----- test/types/multiple_dispatch.morpho | 9 +- test/types/multiple_dispatch_free.morpho | 25 ++++++ 3 files changed, 114 insertions(+), 24 deletions(-) create mode 100644 test/types/multiple_dispatch_free.morpho diff --git a/src/classes/metafunction.c b/src/classes/metafunction.c index b7c8f4a3..dee5d4df 100644 --- a/src/classes/metafunction.c +++ b/src/classes/metafunction.c @@ -136,6 +136,7 @@ bool metafunction_slowresolve(objectmetafunction *f, int nargs, value *args, val enum { MF_CHECKNARGS, + MF_BRANCH, MF_BRANCHNARGS, MF_BRANCHVALUETYPE, MF_BRANCHOBJECTTYPE, @@ -151,6 +152,7 @@ DEFINE_VARRAY(mfinstruction, mfinstruction); #define MFINSTRUCTION_FAIL { .opcode=MF_FAIL, .branch=MFINSTRUCTION_EMPTY } #define MFINSTRUCTION_RESOLVE(fn) { .opcode=MF_RESOLVE, .data.resolvefn=fn, .branch=MFINSTRUCTION_EMPTY } #define MFINSTRUCTION_CHECKNARG(n, brnch) { .opcode=MF_CHECKNARGS, .narg=n, .branch=brnch } +#define MFINSTRUCTION_BRANCH(brnch) { .opcode=MF_BRANCH, .branch=brnch } #define MFINSTRUCTION_BRANCHNARG(table, brnch) { .opcode=MF_BRANCHNARGS, .data.btable=table, .branch=brnch } #define MFINSTRUCTION_BRANCHOBJECTTYPE(n, table, brnch) { .opcode=MF_BRANCHOBJECTTYPE, .narg=n, .data.btable=table, .branch=brnch } #define MFINSTRUCTION_BRANCHVALUETYPE(n, table, brnch) { .opcode=MF_BRANCHVALUETYPE, .narg=n, .data.btable=table, .branch=brnch } @@ -222,6 +224,10 @@ void mfcompiler_disassemble(mfcompiler *c) { printf("checkargs (%i) -> (%i)", instr->narg, i+instr->branch+1); break; } + case MF_BRANCH: { + printf("branch -> (%i)", i+instr->branch+1); + break; + } case MF_BRANCHNARGS: { printf("branchargs (%i) -> (%i)\n", instr->narg, i+instr->branch+1); _mfcompiler_disassemblebranchtable(instr, i); @@ -314,16 +320,25 @@ mfindx mfcompile_currentinstruction(mfcompiler *c) { return c->fn->resolver.count-1; } +mfindx mfcompile_nextinstruction(mfcompiler *c) { + return c->fn->resolver.count; +} + void mfcompile_setbranch(mfcompiler *c, mfindx i, mfindx branch) { if (i>=c->fn->resolver.count) return; c->fn->resolver.data[i].branch=branch; } +void mfcompile_replaceinstruction(mfcompiler *c, mfindx i, mfinstruction instr) { + if (i>=c->fn->resolver.count) return; + c->fn->resolver.data[i] = instr; +} + mfindx mfcompile_fail(mfcompiler *c); mfindx mfcompile_resolve(mfcompiler *c, mfset *set); -void mfcompile_dispatchonparam(mfcompiler *c, mfset *set, int i); -void mfcompile_dispatchonnarg(mfcompiler *c, mfset *set, int min, int max); -void mfcompile_set(mfcompiler *c, mfset *set); +mfindx mfcompile_dispatchonparam(mfcompiler *c, mfset *set, int i); +mfindx mfcompile_dispatchonnarg(mfcompiler *c, mfset *set, int min, int max); +mfindx mfcompile_set(mfcompiler *c, mfset *set); /** Inserts a fail instruction */ mfindx mfcompile_fail(mfcompiler *c) { @@ -364,7 +379,7 @@ enum { MF_VENEERVALUE, MF_VENEEROBJECT, MF_INSTANCE, - MF_FREE + MF_ANY }; /** Detects the kind of type */ @@ -377,7 +392,7 @@ int _detecttype(value type, int *tindx) { return MF_VENEERVALUE; } else return MF_INSTANCE; } - return MF_FREE; + return MF_ANY; } int _mfresultsortfn (const void *a, const void *b) { @@ -385,6 +400,8 @@ int _mfresultsortfn (const void *a, const void *b) { return aa->indx-bb->indx; } +typedef mfindx (mfcompile_dispatchfn) (mfcompiler *c, mfset *set, int i); + /** Branch table on object type */ mfindx mfcompile_dispatchveneerobj(mfcompiler *c, mfset *set, int i) { value type; @@ -449,9 +466,37 @@ mfindx mfcompile_dispatchveneervalue(mfcompiler *c, mfset *set, int i) { return bindx; } +/** Handle implementations that accept any type */ +mfindx mfcompile_dispatchany(mfcompiler *c, mfset *set, int i) { + mfresult rlist[set->count]; + int n=0; + + // Find implementations that accept any time + for (int k=0; kcount; k++) { + value type; + if (!signature_getparamtype(set->rlist[k].sig, i, &type)) return MFINSTRUCTION_EMPTY; + if (_detecttype(type, &set->rlist[k].indx)==MF_ANY) { + rlist[n] = set->rlist[k]; n++; + } + } + + mfindx bindx = mfcompile_nextinstruction(c); + + mfset anyset = MFSET(n, rlist); + mfcompile_set(c, &anyset); + + return bindx; +} + +/** Fixes a fallthrough fail */ +void mfcompile_fixfallthrough(mfcompiler *c, mfindx i, mfindx branchto) { + mfinstruction instr = MFINSTRUCTION_BRANCH(branchto-i-1); + mfcompile_replaceinstruction(c, i, instr); +} + /** Attempts to dispatch based on a parameter i */ -void mfcompile_dispatchonparam(mfcompiler *c, mfset *set, int i) { - int typecount[MF_FREE+1] = { 0, 0, 0, 0}; +mfindx mfcompile_dispatchonparam(mfcompiler *c, mfset *set, int i) { + int typecount[MF_ANY+1] = { 0, 0, 0, 0}; // Determine what types are present for (int k=0; kcount; k++) { @@ -460,40 +505,49 @@ void mfcompile_dispatchonparam(mfcompiler *c, mfset *set, int i) { typecount[_detecttype(type, NULL)]++; } - mfindx bindx = MFINSTRUCTION_EMPTY; - - if (typecount[MF_VENEERVALUE]) { - bindx = mfcompile_dispatchveneervalue(c, set, i); + mfindx bindx[MF_ANY+1]; + mfcompile_dispatchfn *dfn[MF_ANY+1] = { mfcompile_dispatchveneervalue, + mfcompile_dispatchveneerobj, + NULL, + mfcompile_dispatchany}; + + // Cycle through all value types, building a chain of branchtables + int n=0; + for (int j=0; j<=MF_ANY; j++) { + if (typecount[j] && dfn[j]) { + bindx[n]=(dfn[j]) (c, set, i); + if (n>0) mfcompile_setbranch(c, bindx[n-1], bindx[n]-bindx[n-1]-1); + n++; + } } - if (typecount[MF_VENEEROBJECT]) { - mfindx oindx = mfcompile_dispatchveneerobj(c, set, i); - if (bindx!=MFINSTRUCTION_EMPTY) { - mfcompile_setbranch(c, bindx, oindx-bindx-1); + if (typecount[MF_ANY]) { // Fix branch table fallthroughs to point to any + for (int j=0; jcount==2) { for (int i=0; i<2; i++) { signature *sig = set->rlist[i].sig; int nparams; signature_paramlist(sig, &nparams, NULL); // Get the number of parameters mfinstruction instr = MFINSTRUCTION_CHECKNARG(nparams, 0); - mfindx cindx = mfcompile_insertinstruction(c, instr); // Write the check nargs instruction + bindx = mfcompile_insertinstruction(c, instr); // Write the check nargs instruction mfset res = MFSET(1, &set->rlist[i]); // If it works, resolve on this implementation mfcompile_resolve(c, &res); // Fix the branch instruction mfindx eindx = mfcompile_currentinstruction(c); - mfcompile_setbranch(c, cindx, eindx-cindx); + mfcompile_setbranch(c, bindx, eindx-bindx); } mfcompile_fail(c); } else { // If more than two options, generate a branch table @@ -511,7 +565,7 @@ void mfcompile_dispatchonnarg(mfcompiler *c, mfset *set, int min, int max) { // Insert the branch table instruction mfinstruction table = MFINSTRUCTION_BRANCHNARG(btable, 0); - mfindx bindx = mfcompile_insertinstruction(c, table); + bindx = mfcompile_insertinstruction(c, table); // Immediately follow by a fail instruction if this falls through mfcompile_fail(c); @@ -519,10 +573,11 @@ void mfcompile_dispatchonnarg(mfcompiler *c, mfset *set, int min, int max) { // Compile the branch table mfcompile_branchtable(c, set, bindx, &btable); } + return bindx; } /** Attempts to discriminate between a list of possible signatures */ -void mfcompile_set(mfcompiler *c, mfset *set) { +mfindx mfcompile_set(mfcompiler *c, mfset *set) { if (set->count==1) return mfcompile_resolve(c, set); int min, max; // Count the range of possible parameters @@ -571,6 +626,9 @@ bool metafunction_resolve(objectmetafunction *fn, int nargs, value *args, value case MF_CHECKNARGS: if (pc->narg!=nargs) pc+=pc->branch; break; + case MF_BRANCH: + pc+=pc->branch; + break; case MF_BRANCHNARGS: if (nargsdata.btable.count) { pc+=pc->data.btable.data[nargs]; diff --git a/test/types/multiple_dispatch.morpho b/test/types/multiple_dispatch.morpho index 56ab8657..ff8cf3d4 100644 --- a/test/types/multiple_dispatch.morpho +++ b/test/types/multiple_dispatch.morpho @@ -12,9 +12,16 @@ fn f(Float x) { print x+1 } +fn f(x) { + print "Foo" +} + f("Hi") // expect: Hi +f(1) +// expect: Foo + f(1.5) // expect: 2.5 @@ -24,4 +31,4 @@ print a // expect: [ Ho ] f({}) -// expect error 'MltplDsptchFld' +// expect: Foo diff --git a/test/types/multiple_dispatch_free.morpho b/test/types/multiple_dispatch_free.morpho new file mode 100644 index 00000000..46e15fab --- /dev/null +++ b/test/types/multiple_dispatch_free.morpho @@ -0,0 +1,25 @@ +// Dispatch a function with an implementation that catches any type + +fn f(Int x) { + return 0 +} + +fn f(Float x) { + return 1 +} + +fn f(x) { + return -1 +} + +print f("Hello") +// expect: -1 + +print f(true) + +print f(1) +// expect: 0 + +print f(0.1) +// expect: 1 + From a3dbacd279bbc9f45fe486024f640ce80fdf024e Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Tue, 30 Apr 2024 22:14:13 -0400 Subject: [PATCH 074/181] branchinstance --- src/classes/clss.c | 1 + src/classes/clss.h | 1 + src/classes/metafunction.c | 59 ++++++++++++++++++-- src/core/compile.c | 2 + src/datastructures/program.c | 12 ++++ src/datastructures/program.h | 4 ++ test/types/multiple_dispatch_instance.morpho | 18 ++++++ 7 files changed, 92 insertions(+), 5 deletions(-) create mode 100644 test/types/multiple_dispatch_instance.morpho diff --git a/src/classes/clss.c b/src/classes/clss.c index 05f174b2..484f7fa2 100644 --- a/src/classes/clss.c +++ b/src/classes/clss.c @@ -48,6 +48,7 @@ objectclass *object_newclass(value name) { newclass->name=object_clonestring(name); dictionary_init(&newclass->methods); newclass->superclass=NULL; + newclass->uid=0; } return newclass; diff --git a/src/classes/clss.h b/src/classes/clss.h index e176eb0f..e8a32780 100644 --- a/src/classes/clss.h +++ b/src/classes/clss.h @@ -21,6 +21,7 @@ typedef struct sobjectclass { struct sobjectclass *superclass; value name; dictionary methods; + int uid; } objectclass; /** Tests whether an object is a class */ diff --git a/src/classes/metafunction.c b/src/classes/metafunction.c index dee5d4df..073b2af0 100644 --- a/src/classes/metafunction.c +++ b/src/classes/metafunction.c @@ -140,7 +140,7 @@ enum { MF_BRANCHNARGS, MF_BRANCHVALUETYPE, MF_BRANCHOBJECTTYPE, - MF_BRANCHCLASS, + MF_BRANCHINSTANCE, MF_RESOLVE, MF_FAIL }; @@ -156,6 +156,7 @@ DEFINE_VARRAY(mfinstruction, mfinstruction); #define MFINSTRUCTION_BRANCHNARG(table, brnch) { .opcode=MF_BRANCHNARGS, .data.btable=table, .branch=brnch } #define MFINSTRUCTION_BRANCHOBJECTTYPE(n, table, brnch) { .opcode=MF_BRANCHOBJECTTYPE, .narg=n, .data.btable=table, .branch=brnch } #define MFINSTRUCTION_BRANCHVALUETYPE(n, table, brnch) { .opcode=MF_BRANCHVALUETYPE, .narg=n, .data.btable=table, .branch=brnch } +#define MFINSTRUCTION_BRANCHINSTANCE(n, table, brnch) { .opcode=MF_BRANCHINSTANCE, .narg=n, .data.btable=table, .branch=brnch } typedef struct { signature *sig; /** Signature of the target */ @@ -251,6 +252,11 @@ void mfcompiler_disassemble(mfcompiler *c) { } break; } + case MF_BRANCHINSTANCE: { + printf("branchinstance (%i) -> (%i)\n", instr->narg, i+instr->branch+1); + _mfcompiler_disassemblebranchtable(instr, i); + break; + } case MF_RESOLVE: { printf("resolve "); signature *sig = _getsignature(instr->data.resolvefn); @@ -390,7 +396,10 @@ int _detecttype(value type, int *tindx) { return MF_VENEEROBJECT; } else if (value_veneerclasstotype(klass, tindx)) { return MF_VENEERVALUE; - } else return MF_INSTANCE; + } else { + if (tindx) *tindx=klass->uid; + return MF_INSTANCE; + } } return MF_ANY; } @@ -466,12 +475,44 @@ mfindx mfcompile_dispatchveneervalue(mfcompiler *c, mfset *set, int i) { return bindx; } +/** Branch table on instance type */ +mfindx mfcompile_dispatchinstance(mfcompiler *c, mfset *set, int i) { + value type; + + // Extract the type index for each member of the set + for (int k=0; kcount; k++) { + if (!signature_getparamtype(set->rlist[k].sig, i, &type)) return MFINSTRUCTION_EMPTY; + if (_detecttype(type, &set->rlist[k].indx)!=MF_INSTANCE) set->rlist[k].indx=-1; + } + + // Sort the set on the type index + qsort(set->rlist, set->count, sizeof(mfresult), _mfresultsortfn); + + // Create the branch table + int maxindx=set->rlist[set->count-1].indx; + varray_int btable; + varray_intinit(&btable); + for (int i=0; i<=maxindx; i++) varray_intwrite(&btable, 0); + + // Insert the branch instruction + mfinstruction instr = MFINSTRUCTION_BRANCHINSTANCE(i, btable, 0); + mfindx bindx = mfcompile_insertinstruction(c, instr); + + // Fail if an object type isn't in the table + mfcompile_fail(c); + + // Compile the branch table + mfcompile_branchtable(c, set, bindx, &btable); + + return bindx; +} + /** Handle implementations that accept any type */ mfindx mfcompile_dispatchany(mfcompiler *c, mfset *set, int i) { mfresult rlist[set->count]; int n=0; - // Find implementations that accept any time + // Find implementations that accept any type for (int k=0; kcount; k++) { value type; if (!signature_getparamtype(set->rlist[k].sig, i, &type)) return MFINSTRUCTION_EMPTY; @@ -508,7 +549,7 @@ mfindx mfcompile_dispatchonparam(mfcompiler *c, mfset *set, int i) { mfindx bindx[MF_ANY+1]; mfcompile_dispatchfn *dfn[MF_ANY+1] = { mfcompile_dispatchveneervalue, mfcompile_dispatchveneerobj, - NULL, + mfcompile_dispatchinstance, mfcompile_dispatchany}; // Cycle through all value types, building a chain of branchtables @@ -636,6 +677,7 @@ bool metafunction_resolve(objectmetafunction *fn, int nargs, value *args, value break; case MF_BRANCHVALUETYPE: { if (!MORPHO_ISOBJECT(args[pc->narg])) { + // TODO: Check for btable bound int type = (int) MORPHO_GETORDEREDTYPE(args[pc->narg]); pc+=pc->data.btable.data[type]; } else pc+=pc->branch; @@ -643,12 +685,19 @@ bool metafunction_resolve(objectmetafunction *fn, int nargs, value *args, value break; case MF_BRANCHOBJECTTYPE: { if (MORPHO_ISOBJECT(args[pc->narg])) { + // TODO: Check for btable bound int type = MORPHO_GETOBJECTTYPE(args[pc->narg]); pc+=pc->data.btable.data[type]; } else pc+=pc->branch; } break; - case MF_BRANCHCLASS: + case MF_BRANCHINSTANCE: { + if (MORPHO_ISINSTANCE(args[pc->narg])) { + // TODO: Check for btable bound + objectclass *klass = MORPHO_GETINSTANCE(args[pc->narg])->klass; + pc+=pc->data.btable.data[klass->uid]; + } else pc+=pc->branch; + } break; case MF_RESOLVE: *out = pc->data.resolvefn; diff --git a/src/core/compile.c b/src/core/compile.c index ae4847b8..c968afa5 100644 --- a/src/core/compile.c +++ b/src/core/compile.c @@ -337,6 +337,8 @@ static objectclass *compiler_getcurrentclass(compiler *c) { /** Adds an objectclass to the compilers dictionary of classes */ void compiler_addclass(compiler *c, objectclass *klass) { + klass->uid = program_addclass(c->out, MORPHO_OBJECT(klass)); + dictionary_insert(&c->classes, klass->name, MORPHO_OBJECT(klass)); } diff --git a/src/datastructures/program.c b/src/datastructures/program.c index df88898f..30ab1280 100644 --- a/src/datastructures/program.c +++ b/src/datastructures/program.c @@ -26,6 +26,7 @@ void program_init(program *p) { p->boundlist=NULL; dictionary_init(&p->symboltable); varray_globalinfoinit(&p->globals); + varray_valueinit(&p->classes); } /** @brief Clears a program, freeing associated data structures */ @@ -49,6 +50,7 @@ void program_clear(program *p) { /* Note we don't free the contents as they are already interned */ varray_globalinfoclear(&p->globals); dictionary_clear(&p->symboltable); + varray_valueclear(&p->classes); } /** @brief Creates and initializes a new program */ @@ -150,3 +152,13 @@ bool program_globalsymbol(program *p, globalindx indx, value *symbol) { int program_countglobals(program *p) { return p->globals.count; } + +/** @brief Adds a class to the program's class list */ +int program_addclass(program *p, value klass) { + return varray_valuewrite(&p->classes, klass); +} + +/** @brief Returns the number of classes allocated in the program */ +int program_countclasses(program *p, value klass) { + return p->classes.count; +} diff --git a/src/datastructures/program.h b/src/datastructures/program.h index 7450db73..6fc5394d 100644 --- a/src/datastructures/program.h +++ b/src/datastructures/program.h @@ -49,6 +49,7 @@ typedef struct { varray_debugannotation annotations; /** Information about how the code connects to the source */ objectfunction *global; /** Pseudofunction containing global data */ varray_globalinfo globals; /** Global variables */ + varray_value classes; /** Classes defined by this program */ object *boundlist; /** Linked list of static objects bound to this program */ dictionary symboltable; /** The symbol table */ } program; @@ -67,6 +68,9 @@ bool program_globaltype(program *p, globalindx indx, value *type); bool program_globalsymbol(program *p, globalindx indx, value *symbol); int program_countglobals(program *p); +int program_addclass(program *p, value klass); +int program_countclasses(program *p, value klass); + #endif /* MORPHO_CORE */ #endif /* error_h */ diff --git a/test/types/multiple_dispatch_instance.morpho b/test/types/multiple_dispatch_instance.morpho new file mode 100644 index 00000000..13c8cca7 --- /dev/null +++ b/test/types/multiple_dispatch_instance.morpho @@ -0,0 +1,18 @@ +// Dipatch a function on instance + +class A {} +class B {} + +fn f(A x) { + return "A" +} + +fn f(B x) { + return "B" +} + +print f(A()) +// expect: A + +print f(B()) +// expect: B From 41e547c4db4b46ffb834ebedf03662d998c0e7b7 Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Wed, 1 May 2024 08:14:03 -0400 Subject: [PATCH 075/181] Update multiple_dispatch_instance.morpho --- test/types/multiple_dispatch_instance.morpho | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/types/multiple_dispatch_instance.morpho b/test/types/multiple_dispatch_instance.morpho index 13c8cca7..7b9b985e 100644 --- a/test/types/multiple_dispatch_instance.morpho +++ b/test/types/multiple_dispatch_instance.morpho @@ -1,4 +1,4 @@ -// Dipatch a function on instance +// Dispatch a function on instance types class A {} class B {} From 5684e80aba3a76efe80d522308e59911837f79ea Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Wed, 1 May 2024 09:13:37 -0400 Subject: [PATCH 076/181] Best parameter checking now works --- src/classes/metafunction.c | 35 ++++++++++--------- .../types/multiple_dispatch_check_args.morpho | 19 ++++++++++ test/types/multiple_dispatch_varg.morpho | 28 +++++++++++++++ 3 files changed, 66 insertions(+), 16 deletions(-) create mode 100644 test/types/multiple_dispatch_check_args.morpho create mode 100644 test/types/multiple_dispatch_varg.morpho diff --git a/src/classes/metafunction.c b/src/classes/metafunction.c index 073b2af0..7cd010ef 100644 --- a/src/classes/metafunction.c +++ b/src/classes/metafunction.c @@ -285,19 +285,15 @@ void mfcompile_countparams(mfcompiler *c, mfset *set, int *min, int *max) { /** Places the various outcomes for a parameter into a dictionary */ bool mfcompile_outcomes(mfcompiler *c, mfset *set, int i, dictionary *out) { - out->count=0; - for (int k=0; icount; i++) { // Loop over outcomes - int nparams; value *ptypes; - signature_paramlist(set->rlist[k].sig, &nparams, &ptypes); - if (i>=nparams) continue; - value val = MORPHO_INTEGER(1); - if (dictionary_get(out, ptypes[i], &val)) val=MORPHO_INTEGER(MORPHO_GETINTEGERVALUE(val)+1); - if (!dictionary_insert(out, ptypes[i], val)) return false; + for (int k=0; kcount; k++) { // Loop over outcomes + value type; + if (!signature_getparamtype(set->rlist[k].sig, i, &type)) continue; + if (!dictionary_insert(out, (MORPHO_ISNIL(type) ? MORPHO_FALSE : type), MORPHO_NIL)) return false; } return true; } -/** Count the divergent outcomes of each parameter */ +/** Find the parameter number that has most variety in types */ bool mfcompile_countoutcomes(mfcompiler *c, mfset *set, int *best) { varray_int count; varray_intinit(&count); @@ -305,17 +301,24 @@ bool mfcompile_countoutcomes(mfcompiler *c, mfset *set, int *best) { dictionary dict; dictionary_init(&dict); - int k=0; // Loop over parameters - do { - mfcompile_outcomes(c, set, k, &dict); + // Loop over parameters, counting the number of outcomes. + while (true) { + mfcompile_outcomes(c, set, count.count, &dict); + if (!dict.count) break; varray_intwrite(&count, dict.count); - k++; - } while (dict.count); + dictionary_clear(&dict); // Not needed if dict.count was zero + }; + + // Find the parameter that has most variability + int max=count.data[0], maxindx=0; + for (int i=1; imax) { max=count.data[i]; maxindx=i; } + } + if (best) *best = maxindx; - dictionary_clear(&dict); varray_intclear(&count); - return false; + return true; } mfindx mfcompile_insertinstruction(mfcompiler *c, mfinstruction instr) { diff --git a/test/types/multiple_dispatch_check_args.morpho b/test/types/multiple_dispatch_check_args.morpho new file mode 100644 index 00000000..08cceb57 --- /dev/null +++ b/test/types/multiple_dispatch_check_args.morpho @@ -0,0 +1,19 @@ +// Arg checking + +fn f(Int x, Int y) { + return 0 +} + +fn f(Int x, Float y) { + return 1 +} + +print f(1,2) +// expect: 0 + +print f(1,0.5) +// expect: 1 + +print f(0.1,0.5) +// expect error 'MltplDsptchFld' + diff --git a/test/types/multiple_dispatch_varg.morpho b/test/types/multiple_dispatch_varg.morpho new file mode 100644 index 00000000..ac16098a --- /dev/null +++ b/test/types/multiple_dispatch_varg.morpho @@ -0,0 +1,28 @@ +// Dispatch a function with variadic args + +fn f() { + return 0 +} + +fn f(x) { + return 1 +} + +fn f(...x) { + return "V" +} + +print f() +// expect: 0 + +print f("Hi") +// expect: 1 + +print f(1, 2) +// expect: V + +print f(1,2,3) +// expect: V + +print f(1,2,3,4) +// expect: V From 43a619f49e71cadaa9c766262a86cc99f9e6884c Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Wed, 1 May 2024 10:16:51 -0400 Subject: [PATCH 077/181] Check additional arguments before dispatching --- src/classes/metafunction.c | 154 +++++++++++++++++++++++++++---------- src/classes/metafunction.h | 2 +- 2 files changed, 114 insertions(+), 42 deletions(-) diff --git a/src/classes/metafunction.c b/src/classes/metafunction.c index 7cd010ef..5532fd47 100644 --- a/src/classes/metafunction.c +++ b/src/classes/metafunction.c @@ -136,6 +136,9 @@ bool metafunction_slowresolve(objectmetafunction *f, int nargs, value *args, val enum { MF_CHECKNARGS, + MF_CHECKVALUE, + MF_CHECKOBJECT, + MF_CHECKINSTANCE, MF_BRANCH, MF_BRANCHNARGS, MF_BRANCHVALUETYPE, @@ -152,6 +155,7 @@ DEFINE_VARRAY(mfinstruction, mfinstruction); #define MFINSTRUCTION_FAIL { .opcode=MF_FAIL, .branch=MFINSTRUCTION_EMPTY } #define MFINSTRUCTION_RESOLVE(fn) { .opcode=MF_RESOLVE, .data.resolvefn=fn, .branch=MFINSTRUCTION_EMPTY } #define MFINSTRUCTION_CHECKNARG(n, brnch) { .opcode=MF_CHECKNARGS, .narg=n, .branch=brnch } +#define MFINSTRUCTION_CHECKTYPE(op, n, t, brnch) { .opcode=op, .data.tindx=t, .narg=n, .branch=brnch } #define MFINSTRUCTION_BRANCH(brnch) { .opcode=MF_BRANCH, .branch=brnch } #define MFINSTRUCTION_BRANCHNARG(table, brnch) { .opcode=MF_BRANCHNARGS, .data.btable=table, .branch=brnch } #define MFINSTRUCTION_BRANCHOBJECTTYPE(n, table, brnch) { .opcode=MF_BRANCHOBJECTTYPE, .narg=n, .data.btable=table, .branch=brnch } @@ -177,33 +181,34 @@ DEFINE_VARRAY(mfset, mfset) typedef struct { objectmetafunction *fn; - dictionary pcount; - varray_mfset set; /** A stack of possible sets */ + varray_int checked; // Stack of checked parameters } mfcompiler; /** Initialize the metafunction compiler */ void mfcompiler_init(mfcompiler *c, objectmetafunction *fn) { c->fn=fn; - dictionary_init(&c->pcount); - varray_mfsetinit(&c->set); + varray_intinit(&c->checked); } /** Clear the metafunction compiler */ void mfcompiler_clear(mfcompiler *c, objectmetafunction *fn) { - dictionary_clear(&c->pcount); - varray_mfsetclear(&c->set); + varray_intclear(&c->checked); } -/** Pushes a set onto the stack */ -void mfcompiler_pushset(mfcompiler *c, mfset *set) { - varray_mfsetadd(&c->set, set, 1); +/** Pushes a parameter check onto the stack*/ +void mfcompiler_pushcheck(mfcompiler *c, int i) { + varray_intwrite(&c->checked, i); } -/** Pops a set off the stack, optionally returning it */ -bool mfcompiler_popset(mfcompiler *c, mfset *set) { - if (c->set.count<=0) return false; - if (set) *set = c->set.data[c->set.count-1]; - c->set.count--; +/** Pops a parameter check from the stack*/ +int mfcompiler_popcheck(mfcompiler *c) { + return c->checked.data[c->checked.count--]; +} + +/** Tests if a parameter has been checked according to the check stack */ +bool mfcompiler_ischecked(mfcompiler *c, int i) { + for (int j=0; jchecked.count; j++) if (i==c->checked.data[j]) return true; + return false; } void _mfcompiler_disassemblebranchtable(mfinstruction *instr, mfindx i) { @@ -225,6 +230,20 @@ void mfcompiler_disassemble(mfcompiler *c) { printf("checkargs (%i) -> (%i)", instr->narg, i+instr->branch+1); break; } + case MF_CHECKVALUE: { + objectclass *klass=value_veneerclassfromtype(instr->data.tindx); + printf("checkvalue (%i) [%s] -> (%i)", instr->narg, MORPHO_GETCSTRING(klass->name), i+instr->branch+1); + break; + } + case MF_CHECKOBJECT: { + objectclass *klass=object_getveneerclass(instr->data.tindx); + printf("checkobject (%i) [%s] -> (%i)", instr->narg, MORPHO_GETCSTRING(klass->name), i+instr->branch+1); + break; + } + case MF_CHECKINSTANCE: { + printf("checkinstance (%i) -> (%i)", instr->narg, i+instr->branch+1); + break; + } case MF_BRANCH: { printf("branch -> (%i)", i+instr->branch+1); break; @@ -343,6 +362,29 @@ void mfcompile_replaceinstruction(mfcompiler *c, mfindx i, mfinstruction instr) c->fn->resolver.data[i] = instr; } +enum { + MF_VENEERVALUE, + MF_VENEEROBJECT, + MF_INSTANCE, + MF_ANY +}; + +/** Detects the kind of type */ +int _detecttype(value type, int *tindx) { + if (MORPHO_ISCLASS(type)) { + objectclass *klass = MORPHO_GETCLASS(type); + if (object_veneerclasstotype(klass, tindx)) { + return MF_VENEEROBJECT; + } else if (value_veneerclasstotype(klass, tindx)) { + return MF_VENEERVALUE; + } else { + if (tindx) *tindx=klass->uid; + return MF_INSTANCE; + } + } + return MF_ANY; +} + mfindx mfcompile_fail(mfcompiler *c); mfindx mfcompile_resolve(mfcompiler *c, mfset *set); mfindx mfcompile_dispatchonparam(mfcompiler *c, mfset *set, int i); @@ -355,12 +397,42 @@ mfindx mfcompile_fail(mfcompiler *c) { return mfcompile_insertinstruction(c, fail); } +/** Checks a parameter i for type */ +mfindx mfcompile_check(mfcompiler *c, int i, value type) { + int tindx; + int opcode[MF_ANY] = { MF_CHECKVALUE, MF_CHECKOBJECT, MF_CHECKINSTANCE }; + int k=_detecttype(type, &tindx); + + if (k==MF_ANY) return MFINSTRUCTION_EMPTY; + + mfinstruction check = MFINSTRUCTION_CHECKTYPE(opcode[k], i, tindx, 0); + return mfcompile_insertinstruction(c, check); +} + /** Compiles a single result */ mfindx mfcompile_resolve(mfcompiler *c, mfset *set) { - // Should check all arguments have been resolved - + mfindx start = mfcompile_nextinstruction(c); + + // Check all arguments have been resolved + signature *sig = set->rlist->sig; + for (int i=0; itypes.count; i++) { + if (MORPHO_ISNIL(sig->types.data[i]) || + mfcompiler_ischecked(c, i)) continue; + + mfcompile_check(c, i, sig->types.data[i]); + } + + mfindx end = mfcompile_nextinstruction(c); + mfinstruction instr = MFINSTRUCTION_RESOLVE(set->rlist->fn); - return mfcompile_insertinstruction(c, instr); + mfcompile_insertinstruction(c, instr); + + if (start!=end) { + mfindx fail = mfcompile_fail(c); + for (mfindx i=start; iuid; - return MF_INSTANCE; - } - } - return MF_ANY; -} - int _mfresultsortfn (const void *a, const void *b) { mfresult *aa = (mfresult *) a, *bb = (mfresult *) b; return aa->indx-bb->indx; @@ -540,12 +589,13 @@ void mfcompile_fixfallthrough(mfcompiler *c, mfindx i, mfindx branchto) { /** Attempts to dispatch based on a parameter i */ mfindx mfcompile_dispatchonparam(mfcompiler *c, mfset *set, int i) { + mfcompiler_pushcheck(c, i); int typecount[MF_ANY+1] = { 0, 0, 0, 0}; // Determine what types are present for (int k=0; kcount; k++) { value type; - if (!signature_getparamtype(set->rlist[k].sig, i, &type)) return; + if (!signature_getparamtype(set->rlist[k].sig, i, &type)) continue; typecount[_detecttype(type, NULL)]++; } @@ -571,6 +621,7 @@ mfindx mfcompile_dispatchonparam(mfcompiler *c, mfset *set, int i) { } } + mfcompiler_popcheck(c); return bindx[0]; } @@ -670,6 +721,27 @@ bool metafunction_resolve(objectmetafunction *fn, int nargs, value *args, value case MF_CHECKNARGS: if (pc->narg!=nargs) pc+=pc->branch; break; + case MF_CHECKVALUE: { + if (!MORPHO_ISOBJECT(args[pc->narg])) { + int tindx = (int) MORPHO_GETORDEREDTYPE(args[pc->narg]); + if (pc->data.tindx!=tindx) pc+=pc->branch; + } else pc+=pc->branch; + } + break; + case MF_CHECKOBJECT: { + if (MORPHO_ISOBJECT(args[pc->narg])) { + int tindx = (int) MORPHO_GETOBJECTTYPE(args[pc->narg]); + if (pc->data.tindx!=tindx) pc+=pc->branch; + } else pc+=pc->branch; + } + break; + case MF_CHECKINSTANCE: { + if (MORPHO_ISINSTANCE(args[pc->narg])) { + int tindx = MORPHO_GETINSTANCE(args[pc->narg])->klass->uid; + if (pc->data.tindx!=tindx) pc+=pc->branch; + } else pc+=pc->branch; + } + break; case MF_BRANCH: pc+=pc->branch; break; diff --git a/src/classes/metafunction.h b/src/classes/metafunction.h index a960a208..ed5f8a53 100644 --- a/src/classes/metafunction.h +++ b/src/classes/metafunction.h @@ -24,9 +24,9 @@ typedef struct { int opcode; int narg; union { + int tindx; value resolvefn; varray_int btable; - dictionary bdict; } data; mfindx branch; /* Branch the pc by this amount on fail */ } mfinstruction; From a0af89a23cf324dd8237364ee9a40f913a4440f8 Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Wed, 1 May 2024 15:45:07 -0400 Subject: [PATCH 078/181] Clear compiled instructions correctly --- src/classes/metafunction.c | 16 +++++++++++++--- src/classes/metafunction.h | 1 + test/types/multiple_dispatch_duplicate.morpho | 12 ++++++++++++ 3 files changed, 26 insertions(+), 3 deletions(-) create mode 100644 test/types/multiple_dispatch_duplicate.morpho diff --git a/src/classes/metafunction.c b/src/classes/metafunction.c index 5532fd47..364ff2b9 100644 --- a/src/classes/metafunction.c +++ b/src/classes/metafunction.c @@ -15,6 +15,7 @@ void objectmetafunction_freefn(object *obj) { objectmetafunction *f = (objectmetafunction *) obj; varray_valueclear(&f->fns); + metafunction_clearinstructions(f); } void objectmetafunction_markfn(object *obj, void *v) { @@ -135,6 +136,8 @@ bool metafunction_slowresolve(objectmetafunction *f, int nargs, value *args, val * ********************************************************************** */ enum { + MF_RESOLVE, + MF_FAIL, MF_CHECKNARGS, MF_CHECKVALUE, MF_CHECKOBJECT, @@ -143,9 +146,7 @@ enum { MF_BRANCHNARGS, MF_BRANCHVALUETYPE, MF_BRANCHOBJECTTYPE, - MF_BRANCHINSTANCE, - MF_RESOLVE, - MF_FAIL + MF_BRANCHINSTANCE }; DEFINE_VARRAY(mfinstruction, mfinstruction); @@ -688,6 +689,15 @@ mfindx mfcompile_set(mfcompiler *c, mfset *set) { if (mfcompile_countoutcomes(c, set, &best)) return mfcompile_dispatchonparam(c, set, best); } +/** Clears the compiled code from a given metafunction */ +void metafunction_clearinstructions(objectmetafunction *fn) { + for (int i=0; iresolver.count; i++) { + mfinstruction *mf = &fn->resolver.data[i]; + if (mf->opcode>=MF_BRANCHNARGS && mf->opcode<=MF_BRANCHINSTANCE) varray_intclear(&mf->data.btable); + } + varray_mfinstructionclear(&fn->resolver); +} + /** Compiles the metafunction resolver */ void metafunction_compile(objectmetafunction *fn) { mfset set; diff --git a/src/classes/metafunction.h b/src/classes/metafunction.h index ed5f8a53..dea324a7 100644 --- a/src/classes/metafunction.h +++ b/src/classes/metafunction.h @@ -64,6 +64,7 @@ typedef struct sobjectmetafunction { objectmetafunction *object_newmetafunction(value name); bool metafunction_wrap(value name, value fn, value *out); void metafunction_compile(objectmetafunction *fn); +void metafunction_clearinstructions(objectmetafunction *fn); bool metafunction_add(objectmetafunction *f, value fn); bool metafunction_typefromvalue(value v, value *out); diff --git a/test/types/multiple_dispatch_duplicate.morpho b/test/types/multiple_dispatch_duplicate.morpho new file mode 100644 index 00000000..ed992b61 --- /dev/null +++ b/test/types/multiple_dispatch_duplicate.morpho @@ -0,0 +1,12 @@ +// Duplicate implementations + +fn f(x, Int y) { + return 0 +} + +fn f(x, Int y) { + return 1 +} + +// expect error 'MltplDisptchAmbg' + From 15661e7f155486b008280825ff7dd8d545a9ceb3 Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Wed, 1 May 2024 16:04:42 -0400 Subject: [PATCH 079/181] Fix bug in program_bindobject --- src/core/compile.c | 8 ++++++-- src/datastructures/program.c | 4 +--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/core/compile.c b/src/core/compile.c index c968afa5..88ddd780 100644 --- a/src/core/compile.c +++ b/src/core/compile.c @@ -199,10 +199,14 @@ void _addmatchingfunctionref(compiler *c, value symbol, value fn, value *out) { if (MORPHO_ISNIL(in)) { // If the function has a signature, will need to wrap in a metafunction if (MORPHO_ISFUNCTION(fn) && function_hastypedparameters(MORPHO_GETFUNCTION(fn))) { - metafunction_wrap(symbol, fn, out); + if (metafunction_wrap(symbol, fn, out)) { + program_bindobject(c->out, MORPHO_GETOBJECT(*out)); + } } else *out=fn; } else if (MORPHO_ISFUNCTION(in)) { - if (metafunction_wrap(symbol, in, out)) metafunction_add(MORPHO_GETMETAFUNCTION(*out), fn); + if (metafunction_wrap(symbol, in, out)) { metafunction_add(MORPHO_GETMETAFUNCTION(*out), fn); + program_bindobject(c->out, MORPHO_GETOBJECT(*out)); + } } else if (MORPHO_ISMETAFUNCTION(in)) { metafunction_add(MORPHO_GETMETAFUNCTION(in), fn); } diff --git a/src/datastructures/program.c b/src/datastructures/program.c index 30ab1280..47bff4b9 100644 --- a/src/datastructures/program.c +++ b/src/datastructures/program.c @@ -85,10 +85,8 @@ instructionindx program_getentry(program *p) { void program_bindobject(program *p, object *obj) { if (!obj->next && /* Object is not already bound to the program (or something else) */ obj->status==OBJECT_ISUNMANAGED && /* Object is unmanaged */ - (!MORPHO_ISBUILTINFUNCTION(MORPHO_OBJECT(obj))) && /* Object is not a built in function that is freed separately */ - (p->boundlist!=obj->next && p->boundlist!=NULL) /* To handle the case where the object is the only object */ + (!MORPHO_ISBUILTINFUNCTION(MORPHO_OBJECT(obj))) /* Object is not a built in function that is freed separately */ ) { - obj->next=p->boundlist; p->boundlist=obj; } From b56225cad14f6db83d4adb4f9ad04b5cbfe21f8e Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Thu, 2 May 2024 21:59:44 -0400 Subject: [PATCH 080/181] Avoid duplicating metafunctions --- src/classes/metafunction.c | 14 +++++++++++++ src/classes/metafunction.h | 8 +++++-- src/core/compile.c | 43 ++++++++++++++++++++++++++++++-------- 3 files changed, 54 insertions(+), 11 deletions(-) diff --git a/src/classes/metafunction.c b/src/classes/metafunction.c index 364ff2b9..f3354b8f 100644 --- a/src/classes/metafunction.c +++ b/src/classes/metafunction.c @@ -102,6 +102,20 @@ bool metafunction_matchtype(value type, value val) { return false; } +/** Finds whether an implementation f occurs in a metafunction */ +bool metafunction_matchfn(objectmetafunction *fn, value f) { + for (int i=0; ifns.count; i++) if (MORPHO_ISEQUAL(fn->fns.data[i], f)) return true; + return false; +} + +/** Checks if a metafunction matches a given list of implementations */ +bool metafunction_matchset(objectmetafunction *fn, int n, value *fns) { + for (int i=0; isig; diff --git a/src/classes/metafunction.h b/src/classes/metafunction.h index dea324a7..e67d6f73 100644 --- a/src/classes/metafunction.h +++ b/src/classes/metafunction.h @@ -63,11 +63,15 @@ typedef struct sobjectmetafunction { objectmetafunction *object_newmetafunction(value name); bool metafunction_wrap(value name, value fn, value *out); +bool metafunction_add(objectmetafunction *f, value fn); +bool metafunction_typefromvalue(value v, value *out); + +bool metafunction_matchfn(objectmetafunction *fn, value f); +bool metafunction_matchset(objectmetafunction *fn, int n, value *fns); + void metafunction_compile(objectmetafunction *fn); void metafunction_clearinstructions(objectmetafunction *fn); -bool metafunction_add(objectmetafunction *f, value fn); -bool metafunction_typefromvalue(value v, value *out); bool metafunction_resolve(objectmetafunction *f, int nargs, value *args, value *fn); void metafunction_initialize(void); diff --git a/src/core/compile.c b/src/core/compile.c index 88ddd780..819b960a 100644 --- a/src/core/compile.c +++ b/src/core/compile.c @@ -212,25 +212,50 @@ void _addmatchingfunctionref(compiler *c, value symbol, value fn, value *out) { } } +/** Finds an existing metafunction in the current context that matches a given set of implementations */ +bool compiler_findmetafunction(compiler *c, value symbol, int n, value *fns, value *out) { + functionstate *f=compiler_currentfunctionstate(c); + + for (int i=0; ifunc->konst.count; i++) { + value v = f->func->konst.data[i]; + if (MORPHO_ISMETAFUNCTION(v) && + MORPHO_ISEQUAL(MORPHO_GETMETAFUNCTION(v)->name, symbol) && + metafunction_matchset(MORPHO_GETMETAFUNCTION(v), n, fns)) { + *out = v; + return true; + } + } + + return false; +} + /** Determines whether a symbol refers to one (or more) functions. If so, returns either a single function or a metafunction as appropriate. */ bool compiler_resolvefunctionref(compiler *c, value symbol, value *out) { functionstate *f=compiler_currentfunctionstate(c); - value fnd=MORPHO_NIL; - int match=0; + + varray_value fns; + varray_valueinit(&fns); for (int i=0; ifunctionref.count; i++) { functionref *ref=&f->functionref.data[i]; - if (MORPHO_ISEQUAL(ref->symbol, symbol)) { - _addmatchingfunctionref(c, ref->symbol, ref->function, &fnd); - match++; + if (MORPHO_ISEQUAL(ref->symbol, symbol)) varray_valuewrite(&fns, ref->function); + } + + if (!fns.count) return false; // No need to clear an empty varray + + if (!compiler_findmetafunction(c, symbol, fns.count, fns.data, out)) { + *out=MORPHO_NIL; + for (int i=0; i Date: Thu, 2 May 2024 22:09:10 -0400 Subject: [PATCH 081/181] Update metafunction.c --- src/classes/metafunction.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/classes/metafunction.c b/src/classes/metafunction.c index f3354b8f..638a61cd 100644 --- a/src/classes/metafunction.c +++ b/src/classes/metafunction.c @@ -730,7 +730,7 @@ void metafunction_compile(objectmetafunction *fn) { mfcompile_set(&compiler, &set); - mfcompiler_disassemble(&compiler); + //mfcompiler_disassemble(&compiler); mfcompiler_clear(&compiler, fn); } From 265aba5e56eab29d7b1787c8e12ecfc2791659dd Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Sat, 4 May 2024 14:35:04 -0400 Subject: [PATCH 082/181] Fix binding of objects to program --- src/classes/metafunction.c | 6 +++++- src/core/vm.c | 8 ++++---- src/datastructures/object.c | 2 +- src/datastructures/object.h | 4 ++++ src/datastructures/program.c | 1 + test/types/multiple_dispatch_free.morpho | 1 + 6 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/classes/metafunction.c b/src/classes/metafunction.c index 638a61cd..24155b7c 100644 --- a/src/classes/metafunction.c +++ b/src/classes/metafunction.c @@ -53,6 +53,7 @@ objectmetafunction *object_newmetafunction(value name) { new->name=MORPHO_NIL; if (MORPHO_ISSTRING(name)) new->name=object_clonestring(name); varray_valueinit(&new->fns); + varray_mfinstructioninit(&new->resolver); } return new; @@ -217,7 +218,8 @@ void mfcompiler_pushcheck(mfcompiler *c, int i) { /** Pops a parameter check from the stack*/ int mfcompiler_popcheck(mfcompiler *c) { - return c->checked.data[c->checked.count--]; + c->checked.count--; + return c->checked.data[c->checked.count]; } /** Tests if a parameter has been checked according to the check stack */ @@ -701,6 +703,8 @@ mfindx mfcompile_set(mfcompiler *c, mfset *set) { int best; if (mfcompile_countoutcomes(c, set, &best)) return mfcompile_dispatchonparam(c, set, best); + + return MFINSTRUCTION_EMPTY; } /** Clears the compiled code from a given metafunction */ diff --git a/src/core/vm.c b/src/core/vm.c index aa8adfe6..be996a4c 100644 --- a/src/core/vm.c +++ b/src/core/vm.c @@ -158,7 +158,7 @@ void vm_unbindobject(vm *v, value obj) { } } // Correct estimate of bound size. - if (ob->status!=OBJECT_ISUNMANAGED) { + if (MORPHO_ISGARBAGECOLLECTED(obj)) { v->bound-=object_size(ob); ob->status=OBJECT_ISUNMANAGED; } @@ -405,7 +405,7 @@ static inline bool vm_vargs(vm *v, ptrdiff_t iindx, objectfunction *func, unsign for (; kopt.data[k].symbol, key)) break; if (k>=nopt) { // If we didn't find a match, we're done with optional arguments if (MORPHO_ISSTRING(key) && - MORPHO_GETSTRING(key)->obj.status==OBJECT_ISUNMANAGED) { + !MORPHO_ISGARBAGECOLLECTED(key)) { // Check whether the argument looks like an optional argument, but we just don't recognize it - the test for this is whether this is a string and it's one generated by the runtime vm_runtimeerror(v, iindx, VM_UNKNWNOPTARG, MORPHO_GETCSTRING(key)); @@ -1595,7 +1595,7 @@ void morpho_bindobjects(vm *v, int nobj, value *obj) { /* Now bind the new objects in. */ for (unsigned int i=0; istatus==OBJECT_ISUNMANAGED) { + if (MORPHO_ISOBJECT(obj[i]) && ob->statusstatus=OBJECT_ISUNMARKED; ob->next=v->objects; v->objects=ob; @@ -1648,7 +1648,7 @@ void morpho_resizeobject(vm *v, object *obj, size_t oldsize, size_t newsize) { #ifdef MORPHO_DEBUG_GCSIZETRACKING dictionary_insert(&sizecheck, MORPHO_OBJECT(obj), MORPHO_INTEGER(newsize)); #endif - if (obj->status==OBJECT_ISUNMANAGED) return; + if (!MORPHO_ISGARBAGECOLLECTED(MORPHO_OBJECT(obj))) return; v->bound-=oldsize; v->bound+=newsize; } diff --git a/src/datastructures/object.c b/src/datastructures/object.c index 0de6160e..063655e8 100644 --- a/src/datastructures/object.c +++ b/src/datastructures/object.c @@ -146,7 +146,7 @@ objectclass *object_getveneerclass(objecttype type) { /** @brief Finds the object type associated with a veneer class; returns false if it is not a veneer class */ bool object_veneerclasstotype(objectclass *clss, objecttype *type) { for (int i=0; istatus>=OBJECT_ISUNMARKED) + /** Gets the type of the object associated with a value @warning: Do not use this to compare types, use an appropriate macro like MORPHO_ISXXX */ #define MORPHO_GETOBJECTTYPE(val) (MORPHO_GETOBJECT(val)->type) diff --git a/src/datastructures/program.c b/src/datastructures/program.c index 47bff4b9..4d8c7f1e 100644 --- a/src/datastructures/program.c +++ b/src/datastructures/program.c @@ -87,6 +87,7 @@ void program_bindobject(program *p, object *obj) { obj->status==OBJECT_ISUNMANAGED && /* Object is unmanaged */ (!MORPHO_ISBUILTINFUNCTION(MORPHO_OBJECT(obj))) /* Object is not a built in function that is freed separately */ ) { + obj->status=OBJECT_ISPROGRAM; obj->next=p->boundlist; p->boundlist=obj; } diff --git a/test/types/multiple_dispatch_free.morpho b/test/types/multiple_dispatch_free.morpho index 46e15fab..3241dfda 100644 --- a/test/types/multiple_dispatch_free.morpho +++ b/test/types/multiple_dispatch_free.morpho @@ -16,6 +16,7 @@ print f("Hello") // expect: -1 print f(true) +// expect: -1 print f(1) // expect: 0 From 96c1d77a3fa4c0367d930fc84c41bba080c44993 Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Sat, 4 May 2024 15:26:37 -0400 Subject: [PATCH 083/181] Variadic args from binary choice --- src/classes/metafunction.c | 40 ++++++++++++++---------- src/core/compile.c | 1 + src/datastructures/signature.c | 10 ++++++ src/datastructures/signature.h | 4 +++ test/types/multiple_dispatch_varg.morpho | 7 ----- 5 files changed, 39 insertions(+), 23 deletions(-) diff --git a/src/classes/metafunction.c b/src/classes/metafunction.c index 24155b7c..9b4c5144 100644 --- a/src/classes/metafunction.c +++ b/src/classes/metafunction.c @@ -153,7 +153,8 @@ bool metafunction_slowresolve(objectmetafunction *f, int nargs, value *args, val enum { MF_RESOLVE, MF_FAIL, - MF_CHECKNARGS, + MF_CHECKNARGSNEQ, + MF_CHECKNARGSLT, MF_CHECKVALUE, MF_CHECKOBJECT, MF_CHECKINSTANCE, @@ -170,7 +171,7 @@ DEFINE_VARRAY(mfinstruction, mfinstruction); #define MFINSTRUCTION_FAIL { .opcode=MF_FAIL, .branch=MFINSTRUCTION_EMPTY } #define MFINSTRUCTION_RESOLVE(fn) { .opcode=MF_RESOLVE, .data.resolvefn=fn, .branch=MFINSTRUCTION_EMPTY } -#define MFINSTRUCTION_CHECKNARG(n, brnch) { .opcode=MF_CHECKNARGS, .narg=n, .branch=brnch } +#define MFINSTRUCTION_CHECKNARGS(op, n, brnch) { .opcode=op, .narg=n, .branch=brnch } #define MFINSTRUCTION_CHECKTYPE(op, n, t, brnch) { .opcode=op, .data.tindx=t, .narg=n, .branch=brnch } #define MFINSTRUCTION_BRANCH(brnch) { .opcode=MF_BRANCH, .branch=brnch } #define MFINSTRUCTION_BRANCHNARG(table, brnch) { .opcode=MF_BRANCHNARGS, .data.btable=table, .branch=brnch } @@ -243,8 +244,12 @@ void mfcompiler_disassemble(mfcompiler *c) { mfinstruction *instr = &c->fn->resolver.data[i]; printf("%5i : ", i) ; switch(instr->opcode) { - case MF_CHECKNARGS: { - printf("checkargs (%i) -> (%i)", instr->narg, i+instr->branch+1); + case MF_CHECKNARGSNEQ: { + printf("checknargsneq %i -> (%i)", instr->narg, i+instr->branch+1); + break; + } + case MF_CHECKNARGSLT: { + printf("checknargslt %i -> (%i)", instr->narg, i+instr->branch+1); break; } case MF_CHECKVALUE: { @@ -312,6 +317,7 @@ void mfcompile_countparams(mfcompiler *c, mfset *set, int *min, int *max) { for (int i=0; icount; i++) { int nparams; signature_paramlist(set->rlist[i].sig, &nparams, NULL); + if (signature_isvarg(set->rlist[i].sig)) imax=INT_MAX; if (nparamsimax) imax=nparams; } @@ -646,12 +652,19 @@ mfindx mfcompile_dispatchonparam(mfcompiler *c, mfset *set, int i) { mfindx mfcompile_dispatchonnarg(mfcompiler *c, mfset *set, int min, int max) { mfindx bindx = MFINSTRUCTION_EMPTY; + // Sort the set into order given by number of parameters + for (int k=0; kcount; k++) { + set->rlist[k].indx=signature_countparams(set->rlist[k].sig); + } + qsort(set->rlist, set->count, sizeof(mfresult), _mfresultsortfn); + if (set->count==2) { for (int i=0; i<2; i++) { signature *sig = set->rlist[i].sig; int nparams; signature_paramlist(sig, &nparams, NULL); // Get the number of parameters - mfinstruction instr = MFINSTRUCTION_CHECKNARG(nparams, 0); + int opcode = (signature_isvarg(sig) ? MF_CHECKNARGSLT : MF_CHECKNARGSNEQ ); + mfinstruction instr = MFINSTRUCTION_CHECKNARGS(opcode, nparams, 0); bindx = mfcompile_insertinstruction(c, instr); // Write the check nargs instruction mfset res = MFSET(1, &set->rlist[i]); // If it works, resolve on this implementation @@ -667,14 +680,6 @@ mfindx mfcompile_dispatchonnarg(mfcompiler *c, mfset *set, int min, int max) { varray_intinit(&btable); for (int i=0; i<=max; i++) varray_intwrite(&btable, 0); - // Count the number of implementations for each parameter count - for (int k=0; kcount; k++) { - set->rlist[k].indx=signature_countparams(set->rlist[k].sig); - } - - // Sort the set on the type index - qsort(set->rlist, set->count, sizeof(mfresult), _mfresultsortfn); - // Insert the branch table instruction mfinstruction table = MFINSTRUCTION_BRANCHNARG(btable, 0); bindx = mfcompile_insertinstruction(c, table); @@ -734,7 +739,7 @@ void metafunction_compile(objectmetafunction *fn) { mfcompile_set(&compiler, &set); - //mfcompiler_disassemble(&compiler); + mfcompiler_disassemble(&compiler); mfcompiler_clear(&compiler, fn); } @@ -746,8 +751,11 @@ bool metafunction_resolve(objectmetafunction *fn, int nargs, value *args, value do { switch(pc->opcode) { - case MF_CHECKNARGS: - if (pc->narg!=nargs) pc+=pc->branch; + case MF_CHECKNARGSNEQ: + if (nargs!=pc->narg) pc+=pc->branch; + break; + case MF_CHECKNARGSLT: + if (nargsnarg) pc+=pc->branch; break; case MF_CHECKVALUE: { if (!MORPHO_ISOBJECT(args[pc->narg])) { diff --git a/src/core/compile.c b/src/core/compile.c index 819b960a..6ae97d4b 100644 --- a/src/core/compile.c +++ b/src/core/compile.c @@ -2811,6 +2811,7 @@ static codeinfo compiler_function(compiler *c, syntaxtreenode *node, registerind value signature[func->nargs+1]; for (int i=0; inargs; i++) compiler_regtype(c, i+1, &signature[i]); function_setsignature(func, signature); + signature_setvarg(&func->sig, object_functionhasvargs(func)); /* Check we don't have too many arguments */ if (func->nargs>MORPHO_MAXARGS) { diff --git a/src/datastructures/signature.c b/src/datastructures/signature.c index 0c216d6c..f42de18a 100644 --- a/src/datastructures/signature.c +++ b/src/datastructures/signature.c @@ -31,6 +31,16 @@ void signature_set(signature *s, int nparam, value *types) { varray_valueadd(&s->types, types, nparam); } +/** @brief Sets whether a signature contains variadic arguments */ +void signature_setvarg(signature *s, bool varg) { + s->varg=varg; +} + +/** @brief Sets whether a signature contains variadic arguments */ +bool signature_isvarg(signature *s) { + return s->varg; +} + /** @brief Returns true if any entries in the signature are typed*/ bool signature_istyped(signature *s) { for (int i=0; itypes.count; i++) if (!MORPHO_ISNIL(s->types.data[i])) return true; diff --git a/src/datastructures/signature.h b/src/datastructures/signature.h index ccd87ff3..d93a8914 100644 --- a/src/datastructures/signature.h +++ b/src/datastructures/signature.h @@ -12,11 +12,15 @@ typedef struct { varray_value types; + bool varg; /** Is the function variadic? */ } signature; void signature_init(signature *s); void signature_clear(signature *s); +void signature_setvarg(signature *s, bool varg); +bool signature_isvarg(signature *s); + bool signature_istyped(signature *s); bool signature_isequal(signature *a, signature *b); bool signature_paramlist(signature *s, int *nparams, value **ptypes); diff --git a/test/types/multiple_dispatch_varg.morpho b/test/types/multiple_dispatch_varg.morpho index ac16098a..19518cb3 100644 --- a/test/types/multiple_dispatch_varg.morpho +++ b/test/types/multiple_dispatch_varg.morpho @@ -1,9 +1,5 @@ // Dispatch a function with variadic args -fn f() { - return 0 -} - fn f(x) { return 1 } @@ -12,9 +8,6 @@ fn f(...x) { return "V" } -print f() -// expect: 0 - print f("Hi") // expect: 1 From 4387629132f4de0c91896bcb1d015082eaa688eb Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Sun, 5 May 2024 11:02:26 -0400 Subject: [PATCH 084/181] Multiple dispatch on variadic arguments --- src/classes/metafunction.c | 43 +++++++++++-------- .../multiple_dispatch_nparams_varg.morpho | 28 ++++++++++++ test/types/multiple_dispatch_varg.morpho | 8 ++-- 3 files changed, 56 insertions(+), 23 deletions(-) create mode 100644 test/types/multiple_dispatch_nparams_varg.morpho diff --git a/src/classes/metafunction.c b/src/classes/metafunction.c index 9b4c5144..fa0e7cf3 100644 --- a/src/classes/metafunction.c +++ b/src/classes/metafunction.c @@ -280,7 +280,7 @@ void mfcompiler_disassemble(mfcompiler *c) { for (int k=0; kdata.btable.count; k++) { if (instr->data.btable.data[k]==0) continue; objectclass *klass=value_veneerclassfromtype(k); - printf(" %i [%s] -> %i\n", k, MORPHO_GETCSTRING(klass->name), i+instr->data.btable.data[k]+1); + printf(" %i [%s] -> %i\n", k, (klass ? MORPHO_GETCSTRING(klass->name) : ""), i+instr->data.btable.data[k]+1); } break; } @@ -289,7 +289,7 @@ void mfcompiler_disassemble(mfcompiler *c) { for (int k=0; kdata.btable.count; k++) { if (instr->data.btable.data[k]==0) continue; objectclass *klass=object_getveneerclass(k); - printf(" %i [%s] -> %i\n", k, MORPHO_GETCSTRING(klass->name), i+instr->data.btable.data[k]+1); + printf(" %i [%s] -> %i\n", k, (klass ? MORPHO_GETCSTRING(klass->name) : ""), i+instr->data.btable.data[k]+1); } break; } @@ -317,7 +317,6 @@ void mfcompile_countparams(mfcompiler *c, mfset *set, int *min, int *max) { for (int i=0; icount; i++) { int nparams; signature_paramlist(set->rlist[i].sig, &nparams, NULL); - if (signature_isvarg(set->rlist[i].sig)) imax=INT_MAX; if (nparamsimax) imax=nparams; } @@ -409,7 +408,7 @@ int _detecttype(value type, int *tindx) { } mfindx mfcompile_fail(mfcompiler *c); -mfindx mfcompile_resolve(mfcompiler *c, mfset *set); +mfindx mfcompile_resolve(mfcompiler *c, mfresult *res); mfindx mfcompile_dispatchonparam(mfcompiler *c, mfset *set, int i); mfindx mfcompile_dispatchonnarg(mfcompiler *c, mfset *set, int min, int max); mfindx mfcompile_set(mfcompiler *c, mfset *set); @@ -433,11 +432,11 @@ mfindx mfcompile_check(mfcompiler *c, int i, value type) { } /** Compiles a single result */ -mfindx mfcompile_resolve(mfcompiler *c, mfset *set) { +mfindx mfcompile_resolve(mfcompiler *c, mfresult *res) { mfindx start = mfcompile_nextinstruction(c); // Check all arguments have been resolved - signature *sig = set->rlist->sig; + signature *sig = res->sig; for (int i=0; itypes.count; i++) { if (MORPHO_ISNIL(sig->types.data[i]) || mfcompiler_ischecked(c, i)) continue; @@ -447,7 +446,7 @@ mfindx mfcompile_resolve(mfcompiler *c, mfset *set) { mfindx end = mfcompile_nextinstruction(c); - mfinstruction instr = MFINSTRUCTION_RESOLVE(set->rlist->fn); + mfinstruction instr = MFINSTRUCTION_RESOLVE(res->fn); mfcompile_insertinstruction(c, instr); if (start!=end) { @@ -648,6 +647,16 @@ mfindx mfcompile_dispatchonparam(mfcompiler *c, mfset *set, int i) { return bindx[0]; } +/** Compiles an argument number check for a single result */ +mfindx mfcompile_checknarg(mfcompiler *c, mfresult *res) { + mfinstruction instr = MFINSTRUCTION_CHECKNARGS((signature_isvarg(res->sig) ? MF_CHECKNARGSLT : MF_CHECKNARGSNEQ ), signature_countparams(res->sig), 0); + mfindx bindx = mfcompile_insertinstruction(c, instr); // Write the check nargs instruction + + mfcompile_resolve(c, res); + + return bindx; +} + /** Attempts to dispatch based on the number of arguments */ mfindx mfcompile_dispatchonnarg(mfcompiler *c, mfset *set, int min, int max) { mfindx bindx = MFINSTRUCTION_EMPTY; @@ -660,17 +669,8 @@ mfindx mfcompile_dispatchonnarg(mfcompiler *c, mfset *set, int min, int max) { if (set->count==2) { for (int i=0; i<2; i++) { - signature *sig = set->rlist[i].sig; - int nparams; - signature_paramlist(sig, &nparams, NULL); // Get the number of parameters - int opcode = (signature_isvarg(sig) ? MF_CHECKNARGSLT : MF_CHECKNARGSNEQ ); - mfinstruction instr = MFINSTRUCTION_CHECKNARGS(opcode, nparams, 0); - bindx = mfcompile_insertinstruction(c, instr); // Write the check nargs instruction - - mfset res = MFSET(1, &set->rlist[i]); // If it works, resolve on this implementation - mfcompile_resolve(c, &res); + bindx = mfcompile_checknarg(c, &set->rlist[i]); - // Fix the branch instruction mfindx eindx = mfcompile_currentinstruction(c); mfcompile_setbranch(c, bindx, eindx-bindx); } @@ -689,13 +689,18 @@ mfindx mfcompile_dispatchonnarg(mfcompiler *c, mfset *set, int min, int max) { // Compile the branch table mfcompile_branchtable(c, set, bindx, &btable); + + // Correct branchargs branch destination to point to the varg resolution + if (signature_isvarg(set->rlist[set->count-1].sig)) { + mfcompile_setbranch(c, bindx, btable.data[btable.count-1]); + } } return bindx; } /** Attempts to discriminate between a list of possible signatures */ mfindx mfcompile_set(mfcompiler *c, mfset *set) { - if (set->count==1) return mfcompile_resolve(c, set); + if (set->count==1) return mfcompile_resolve(c, set->rlist); int min, max; // Count the range of possible parameters mfcompile_countparams(c, set, &min, &max); @@ -739,7 +744,7 @@ void metafunction_compile(objectmetafunction *fn) { mfcompile_set(&compiler, &set); - mfcompiler_disassemble(&compiler); + //mfcompiler_disassemble(&compiler); mfcompiler_clear(&compiler, fn); } diff --git a/test/types/multiple_dispatch_nparams_varg.morpho b/test/types/multiple_dispatch_nparams_varg.morpho new file mode 100644 index 00000000..3e79f4db --- /dev/null +++ b/test/types/multiple_dispatch_nparams_varg.morpho @@ -0,0 +1,28 @@ +// Dispatch a function with variadic args + +fn f() { + return 0 +} + +fn f(x) { + return 1 +} + +fn f(y, ...x) { + return "V" +} + +print f() +// expect: 0 + +print f("Hi") +// expect: 1 + +print f(1, 2) +// expect: V + +print f(1,2,3) +// expect: V + +print f(1,2,3,4) +// expect: V diff --git a/test/types/multiple_dispatch_varg.morpho b/test/types/multiple_dispatch_varg.morpho index 19518cb3..4f8e8704 100644 --- a/test/types/multiple_dispatch_varg.morpho +++ b/test/types/multiple_dispatch_varg.morpho @@ -1,15 +1,15 @@ // Dispatch a function with variadic args -fn f(x) { - return 1 +fn f() { + return 0 } fn f(...x) { return "V" } -print f("Hi") -// expect: 1 +print f() +// expect: 0 print f(1, 2) // expect: V From 41de82350a2769e18cc05bfa4538dfd4f20c722e Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Mon, 6 May 2024 06:44:05 -0400 Subject: [PATCH 085/181] Parse varg marker in signatures --- src/classes/metafunction.c | 2 +- src/datastructures/signature.c | 17 +++++++++++++++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/classes/metafunction.c b/src/classes/metafunction.c index fa0e7cf3..7143cf02 100644 --- a/src/classes/metafunction.c +++ b/src/classes/metafunction.c @@ -744,7 +744,7 @@ void metafunction_compile(objectmetafunction *fn) { mfcompile_set(&compiler, &set); - //mfcompiler_disassemble(&compiler); + mfcompiler_disassemble(&compiler); mfcompiler_clear(&compiler, fn); } diff --git a/src/datastructures/signature.c b/src/datastructures/signature.c index f42de18a..f8485a6a 100644 --- a/src/datastructures/signature.c +++ b/src/datastructures/signature.c @@ -121,6 +121,17 @@ bool signature_parsesymbol(parser *p, void *out) { return success; } +/** @brief Parser function to process varg */ +bool signature_parsevarg(parser *p, void *out) { + signature *sig = (signature *) out; + + value blank = MORPHO_NIL; + bool success=varray_valueadd(&sig->types, &blank, 1); + sig->varg=true; + + return success; +} + /** @brief Main parser function for signatures */ bool signature_parsesignature(parser *p, void *out) { if (parse_checktokenadvance(p, SIGNATURE_SYMBOL)) { @@ -134,7 +145,7 @@ bool signature_parsesignature(parser *p, void *out) { if (parse_checktokenadvance(p, SIGNATURE_SYMBOL)) { if (!signature_parsesymbol(p, out)) return false; } else if (parse_checktokenadvance(p, SIGNATURE_DOTDOTDOT)) { - + if (!signature_parsevarg(p, out)) return false; } else return false; parse_checktokenadvance(p, SIGNATURE_COMMA); @@ -169,7 +180,9 @@ void signature_print(signature *s) { printf("("); for (int i=0; itypes.count; i++) { value type=s->types.data[i]; - if (MORPHO_ISNIL(type)) printf("_"); + + if (s->varg && i==s->types.count-1) printf("..."); + else if (MORPHO_ISNIL(type)) printf("_"); else if (MORPHO_ISCLASS(type)) morpho_printvalue(NULL, MORPHO_GETCLASS(type)->name); if (itypes.count-1) printf(","); From 9c802e5257a73bc3791e0426d9348ef043c19865 Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Mon, 6 May 2024 07:58:25 -0400 Subject: [PATCH 086/181] Function references now obey scope --- src/classes/metafunction.c | 2 +- src/core/compile.c | 6 ++---- test/types/multiple_dispatch_scope.morpho | 24 +++++++++++++++++++++++ 3 files changed, 27 insertions(+), 5 deletions(-) create mode 100644 test/types/multiple_dispatch_scope.morpho diff --git a/src/classes/metafunction.c b/src/classes/metafunction.c index 7143cf02..fa0e7cf3 100644 --- a/src/classes/metafunction.c +++ b/src/classes/metafunction.c @@ -744,7 +744,7 @@ void metafunction_compile(objectmetafunction *fn) { mfcompile_set(&compiler, &set); - mfcompiler_disassemble(&compiler); + //mfcompiler_disassemble(&compiler); mfcompiler_clear(&compiler, fn); } diff --git a/src/core/compile.c b/src/core/compile.c index 6ae97d4b..defe591b 100644 --- a/src/core/compile.c +++ b/src/core/compile.c @@ -185,13 +185,11 @@ void compiler_addfunctionref(compiler *c, objectfunction *func) { varray_functionrefwrite(&f->functionref, ref); } -/** Removes functions only visible within a */ +/** Removes functions visible at a given scope level */ void compiler_functionreffreeatscope(compiler *c, unsigned int scope) { functionstate *f=compiler_currentfunctionstate(c); - /*for (int i=f->functionref.count-1; i>=0; i--) { - - }*/ + while (f->functionref.count>0 && f->functionref.data[f->functionref.count-1].scopedepth>=scope) f->functionref.count--; } void _addmatchingfunctionref(compiler *c, value symbol, value fn, value *out) { diff --git a/test/types/multiple_dispatch_scope.morpho b/test/types/multiple_dispatch_scope.morpho new file mode 100644 index 00000000..a09fee98 --- /dev/null +++ b/test/types/multiple_dispatch_scope.morpho @@ -0,0 +1,24 @@ +// Dispatch a function on multiple types + +fn f() { + return 0 +} + +fn f(x) { + return 1 +} + +{ + fn f(x,y) { + return 1 + } +} + +print f() +// expect: 0 + +print f(1) +// expect: 1 + +print f(1,2) +// expect error 'MltplDsptchFld' From 572fd46022329823d6539307bc532f07a3f261b8 Mon Sep 17 00:00:00 2001 From: Tim Atherton <54725421+softmattertheory@users.noreply.github.com> Date: Tue, 7 May 2024 09:25:18 -0400 Subject: [PATCH 087/181] Fix duplicate element generation --- modules/meshtools.morpho | 16 ++++++++++------ src/geometry/functional.c | 2 ++ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/modules/meshtools.morpho b/modules/meshtools.morpho index ffc827c5..16861602 100644 --- a/modules/meshtools.morpho +++ b/modules/meshtools.morpho @@ -254,7 +254,6 @@ fn _equiangulatetest (v, ev, cv) { c = (v.column(ev[1])-v.column(cv[0])).norm(), d = (v.column(ev[0])-v.column(cv[1])).norm(), // Edges of face 2 e = (v.column(ev[1])-v.column(cv[1])).norm() - return ((b*b + c*c - a*a)*d*e + (d*d + e*e - a*a)*b*c) < 0 } @@ -289,6 +288,7 @@ fn equiangulate(m, quiet=false, fix=nil) { for (iedge in 0...ne) { if (fix && fix[1,iedge]) continue var ev=edges.rowindices(iedge) // vertices for this edge + if (verttoedge.rowindices(ev[0]).count()<4 || verttoedge.rowindices(ev[1]).count()<4) continue // skip if connectivity deficient @@ -1116,17 +1116,21 @@ class MeshPruner is MeshAdaptiveRefiner { for (g in 1..m.maxgrade()) { var conn = m.connectivitymatrix(0, g) var dict = Dictionary() - var clist = [] + var dup = Dictionary() for (id in 0...m.count(g)) { var el = conn.rowindices(id) var nvcl = self.countvertices(el) // Number of vertices in a cluster if (nvcl<2 || self.countmaxverticesincluster(el)==1) { var newel = self.updateelement(vmap, el) - if (nvcl==1) { // Skip if this element is a duplicate - if (_elinlist(clist, newel)) continue - clist.append(newel) // Only track elements connected to a cluster - } + + var indices = newel.clone() + indices.sort() + indices = apply(Tuple, indices) + + if (dup.contains(indices)) continue + dup[indices]=true + var newid = mb.addelement(g, newel) dict[newid] = id } diff --git a/src/geometry/functional.c b/src/geometry/functional.c index 74bd30ab..ccf0f062 100644 --- a/src/geometry/functional.c +++ b/src/geometry/functional.c @@ -1636,6 +1636,7 @@ bool functional_elementgradient(vm *v, objectmesh *mesh, grade g, elementid id, /** Calculate area */ bool length_integrand(vm *v, objectmesh *mesh, elementid id, int nv, int *vid, void *ref, double *out) { + if (nv!=2) return false; double *x[nv], s0[mesh->dim]; for (int j=0; jvert, vid[j], &x[j]); @@ -1750,6 +1751,7 @@ MORPHO_ENDCLASS /** Calculate area */ bool area_integrand(vm *v, objectmesh *mesh, elementid id, int nv, int *vid, void *ref, double *out) { + if (nv!=3) return false; double *x[nv], s0[3], s1[3], cx[3]; for (int j=0; j<3; j++) { s0[j]=0; s1[j]=0; cx[j]=0; } for (int j=0; jvert, vid[j], &x[j]); From 78e7bb645fe3acc8b28305fb95ca4bf34f77c4b3 Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Wed, 8 May 2024 20:18:41 -0400 Subject: [PATCH 088/181] Optional arguments and multiple dispatch --- src/builtin/builtin.h | 1 + src/classes/function.c | 32 ++++++++++----- src/classes/function.h | 6 ++- src/classes/metafunction.c | 40 ++++++++++++++++--- src/core/compile.c | 8 ++-- src/core/vm.c | 8 ++++ .../apply.morpho} | 0 .../check_args.morpho} | 0 .../class_multiple_dispatch.morpho | 0 .../class_multiple_dispatch_args.morpho | 0 test/types/multiple_dispatch/closure.morpho | 16 ++++++++ .../duplicate.morpho} | 0 .../free.morpho} | 0 .../in_function.morpho} | 0 .../instance.morpho} | 0 .../multiple_dispatch.morpho | 0 test/types/multiple_dispatch/namespace.morpho | 4 ++ .../types/multiple_dispatch/namespace.xmorpho | 17 ++++++++ .../nparams.morpho} | 0 .../nparams_varg.morpho} | 0 test/types/multiple_dispatch/optional.morpho | 19 +++++++++ .../scope.morpho} | 0 .../two.morpho} | 0 .../value.morpho} | 0 .../varg.morpho} | 0 25 files changed, 128 insertions(+), 23 deletions(-) rename test/types/{apply_multiple_dispatch.morpho => multiple_dispatch/apply.morpho} (100%) rename test/types/{multiple_dispatch_check_args.morpho => multiple_dispatch/check_args.morpho} (100%) rename test/types/{ => multiple_dispatch}/class_multiple_dispatch.morpho (100%) rename test/types/{ => multiple_dispatch}/class_multiple_dispatch_args.morpho (100%) create mode 100644 test/types/multiple_dispatch/closure.morpho rename test/types/{multiple_dispatch_duplicate.morpho => multiple_dispatch/duplicate.morpho} (100%) rename test/types/{multiple_dispatch_free.morpho => multiple_dispatch/free.morpho} (100%) rename test/types/{multiple_dispatch_in_function.morpho => multiple_dispatch/in_function.morpho} (100%) rename test/types/{multiple_dispatch_instance.morpho => multiple_dispatch/instance.morpho} (100%) rename test/types/{ => multiple_dispatch}/multiple_dispatch.morpho (100%) create mode 100644 test/types/multiple_dispatch/namespace.morpho create mode 100644 test/types/multiple_dispatch/namespace.xmorpho rename test/types/{multiple_dispatch_nparams.morpho => multiple_dispatch/nparams.morpho} (100%) rename test/types/{multiple_dispatch_nparams_varg.morpho => multiple_dispatch/nparams_varg.morpho} (100%) create mode 100644 test/types/multiple_dispatch/optional.morpho rename test/types/{multiple_dispatch_scope.morpho => multiple_dispatch/scope.morpho} (100%) rename test/types/{multiple_dispatch_two.morpho => multiple_dispatch/two.morpho} (100%) rename test/types/{multiple_dispatch_value.morpho => multiple_dispatch/value.morpho} (100%) rename test/types/{multiple_dispatch_varg.morpho => multiple_dispatch/varg.morpho} (100%) diff --git a/src/builtin/builtin.h b/src/builtin/builtin.h index 524d37af..6cf71a44 100644 --- a/src/builtin/builtin.h +++ b/src/builtin/builtin.h @@ -29,6 +29,7 @@ typedef unsigned int builtinfunctionflags; #define MORPHO_FN_PUREFN (1<<1) #define MORPHO_FN_CONSTRUCTOR (1<<2) #define MORPHO_FN_REENTRANT (1<<3) +#define MORPHO_FN_OPTARGS (1<<4) /** Type of C function that implements a built in Morpho function */ typedef value (*builtinfunction) (vm *v, int nargs, value *args); diff --git a/src/classes/function.c b/src/classes/function.c index deaad6e5..41ff8c9e 100644 --- a/src/classes/function.c +++ b/src/classes/function.c @@ -108,16 +108,6 @@ varray_value *object_functiongetconstanttable(objectfunction *func) { return NULL; } -/** Does a function have variadic args? */ -bool object_functionhasvargs(objectfunction *func) { - return (func->varg>=0); -} - -/** Sets the parameter number of a variadic argument */ -void object_functionsetvarg(objectfunction *func, unsigned int varg) { - func->varg=varg; -} - /** Adds an upvalue prototype to a function * @param[in] func function object to add to * @param[in] v a varray of upvalues that will be copied into the function @@ -134,11 +124,31 @@ bool object_functionaddprototype(objectfunction *func, varray_upvalue *v, indx * return success; } +/** Returns the number of positional arguments (including a variadic arg if any) */ +int function_countpositionalargs(objectfunction *func) { + return func->nargs-func->opt.count; +} + +/** Returns the number of optional arguments */ +int function_countoptionalargs(objectfunction *func) { + return func->opt.count; +} + +/** Does a function have variadic args? */ +bool function_hasvargs(objectfunction *func) { + return (func->varg>=0); +} + +/** Sets the parameter number of a variadic argument */ +void function_setvarg(objectfunction *func, unsigned int varg) { + func->varg=varg; +} + /** Sets the signature of a function * @param[in] func function object * @param[in] signature list of types for each parameter (length from func->nargs) */ void function_setsignature(objectfunction *func, value *signature) { - signature_set(&func->sig, func->nargs, signature); + signature_set(&func->sig, function_countpositionalargs(func), signature); } /** Returns true if any of the parameters are typed */ diff --git a/src/classes/function.h b/src/classes/function.h index 3f259746..2a428245 100644 --- a/src/classes/function.h +++ b/src/classes/function.h @@ -69,8 +69,10 @@ objectfunction *object_getfunctionparent(objectfunction *func); value object_getfunctionname(objectfunction *func); varray_value *object_functiongetconstanttable(objectfunction *func); objectfunction *object_newfunction(indx entry, value name, objectfunction *parent, unsigned int nargs); -bool object_functionhasvargs(objectfunction *func); -void object_functionsetvarg(objectfunction *func, unsigned int varg); +int function_countpositionalargs(objectfunction *func); +int function_countoptionalargs(objectfunction *func); +bool function_hasvargs(objectfunction *func); +void function_setvarg(objectfunction *func, unsigned int varg); void function_setsignature(objectfunction *func, value *signature); bool function_hastypedparameters(objectfunction *func); diff --git a/src/classes/metafunction.c b/src/classes/metafunction.c index fa0e7cf3..d1d8cf7e 100644 --- a/src/classes/metafunction.c +++ b/src/classes/metafunction.c @@ -126,6 +126,15 @@ signature *_getsignature(value fn) { return NULL; } +bool _hasoptargs(value fn) { + if (MORPHO_ISFUNCTION(fn)) { + return function_countoptionalargs(MORPHO_GETFUNCTION(fn)); + } else if (MORPHO_ISBUILTINFUNCTION(fn)) { + return MORPHO_GETBUILTINFUNCTION(fn)->flags & MORPHO_FN_OPTARGS; + } + else false; +} + /** Resolves a metafunction given calling arguments */ bool metafunction_slowresolve(objectmetafunction *f, int nargs, value *args, value *out) { for (int i=0; ifns.count; i++) { @@ -153,6 +162,7 @@ bool metafunction_slowresolve(objectmetafunction *f, int nargs, value *args, val enum { MF_RESOLVE, MF_FAIL, + MF_OPTARGS, MF_CHECKNARGSNEQ, MF_CHECKNARGSLT, MF_CHECKVALUE, @@ -171,6 +181,7 @@ DEFINE_VARRAY(mfinstruction, mfinstruction); #define MFINSTRUCTION_FAIL { .opcode=MF_FAIL, .branch=MFINSTRUCTION_EMPTY } #define MFINSTRUCTION_RESOLVE(fn) { .opcode=MF_RESOLVE, .data.resolvefn=fn, .branch=MFINSTRUCTION_EMPTY } +#define MFINSTRUCTION_OPTARGS { .opcode=MF_OPTARGS, .branch=MFINSTRUCTION_EMPTY } #define MFINSTRUCTION_CHECKNARGS(op, n, brnch) { .opcode=op, .narg=n, .branch=brnch } #define MFINSTRUCTION_CHECKTYPE(op, n, t, brnch) { .opcode=op, .data.tindx=t, .narg=n, .branch=brnch } #define MFINSTRUCTION_BRANCH(brnch) { .opcode=MF_BRANCH, .branch=brnch } @@ -305,6 +316,7 @@ void mfcompiler_disassemble(mfcompiler *c) { if (sig) signature_print(sig); break; } + case MF_OPTARGS: printf("optargs"); break; case MF_FAIL: printf("fail"); break; } printf("\n"); @@ -419,6 +431,12 @@ mfindx mfcompile_fail(mfcompiler *c) { return mfcompile_insertinstruction(c, fail); } +/** Inserts resolver check for optional arguments */ +mfindx mfcompile_optargs(mfcompiler *c) { + mfinstruction optarg = MFINSTRUCTION_OPTARGS; + return mfcompile_insertinstruction(c, optarg); +} + /** Checks a parameter i for type */ mfindx mfcompile_check(mfcompiler *c, int i, value type) { int tindx; @@ -731,36 +749,46 @@ void metafunction_compile(objectmetafunction *fn) { mfset set; set.count = fn->fns.count; if (!set.count) return; + bool optargs=false; mfresult rlist[set.count]; set.rlist=rlist; for (int i=0; ifns.data[i]); rlist[i].fn=fn->fns.data[i]; + optargs |= _hasoptargs(fn->fns.data[i]); } mfcompiler compiler; mfcompiler_init(&compiler, fn); + if (optargs) mfcompile_optargs(&compiler); + mfcompile_set(&compiler, &set); - //mfcompiler_disassemble(&compiler); + mfcompiler_disassemble(&compiler); mfcompiler_clear(&compiler, fn); } +unsigned int vm_countpositionalargs(unsigned int nargs, value *args); + /** Execute the metafunction's resolver */ bool metafunction_resolve(objectmetafunction *fn, int nargs, value *args, value *out) { + int n=nargs; mfinstruction *pc = fn->resolver.data; - if (!pc) return metafunction_slowresolve(fn, nargs, args, out); + if (!pc) return metafunction_slowresolve(fn, n, args, out); do { switch(pc->opcode) { + case MF_OPTARGS: + n=vm_countpositionalargs(nargs, args); + break; case MF_CHECKNARGSNEQ: - if (nargs!=pc->narg) pc+=pc->branch; + if (n!=pc->narg) pc+=pc->branch; break; case MF_CHECKNARGSLT: - if (nargsnarg) pc+=pc->branch; + if (nnarg) pc+=pc->branch; break; case MF_CHECKVALUE: { if (!MORPHO_ISOBJECT(args[pc->narg])) { @@ -787,8 +815,8 @@ bool metafunction_resolve(objectmetafunction *fn, int nargs, value *args, value pc+=pc->branch; break; case MF_BRANCHNARGS: - if (nargsdata.btable.count) { - pc+=pc->data.btable.data[nargs]; + if (ndata.btable.count) { + pc+=pc->data.btable.data[n]; } else pc+=pc->branch; break; case MF_BRANCHVALUETYPE: { diff --git a/src/core/compile.c b/src/core/compile.c index defe591b..53cd0b94 100644 --- a/src/core/compile.c +++ b/src/core/compile.c @@ -1076,7 +1076,7 @@ static inline void compiler_addvariadicarg(compiler *c, syntaxtreenode *node, va functionstate *f = compiler_currentfunctionstate(c); if (f) { - if (object_functionhasvargs(f->func)) { + if (function_hasvargs(f->func)) { compiler_error(c, node, COMPILE_MLTVARPRMTR); return; } @@ -1084,14 +1084,14 @@ static inline void compiler_addvariadicarg(compiler *c, syntaxtreenode *node, va value sym=program_internsymbol(c->out, symbol); registerindx reg = compiler_addlocal(c, node, sym); - object_functionsetvarg(f->func, reg-1); + function_setvarg(f->func, reg-1); } } /** Check if the current function has variadic parameters */ bool compiler_hasvariadicarg(compiler *c) { functionstate *f = compiler_currentfunctionstate(c); - return object_functionhasvargs(f->func); + return function_hasvargs(f->func); } /* ------------------------------------------ @@ -2809,7 +2809,7 @@ static codeinfo compiler_function(compiler *c, syntaxtreenode *node, registerind value signature[func->nargs+1]; for (int i=0; inargs; i++) compiler_regtype(c, i+1, &signature[i]); function_setsignature(func, signature); - signature_setvarg(&func->sig, object_functionhasvargs(func)); + signature_setvarg(&func->sig, function_hasvargs(func)); /* Check we don't have too many arguments */ if (func->nargs>MORPHO_MAXARGS) { diff --git a/src/core/vm.c b/src/core/vm.c index be996a4c..f9ce36b2 100644 --- a/src/core/vm.c +++ b/src/core/vm.c @@ -371,6 +371,14 @@ static inline void vm_expandstack(vm *v, value **reg, unsigned int n) { /** Marker for optional arguments */ value vm_optmarker; +/** Counts the number of positional arguments in a call */ +unsigned int vm_countpositionalargs(unsigned int nargs, value *args) { + for (int i=0; i Date: Wed, 8 May 2024 20:55:03 -0400 Subject: [PATCH 089/181] Invalid calls --- src/classes/metafunction.c | 29 ++----------------- .../multiple_dispatch/optional_invld.morpho | 13 +++++++++ .../multiple_dispatch/optional_invld2.morpho | 6 ++++ 3 files changed, 21 insertions(+), 27 deletions(-) create mode 100644 test/types/multiple_dispatch/optional_invld.morpho create mode 100644 test/types/multiple_dispatch/optional_invld2.morpho diff --git a/src/classes/metafunction.c b/src/classes/metafunction.c index d1d8cf7e..827cc97a 100644 --- a/src/classes/metafunction.c +++ b/src/classes/metafunction.c @@ -126,15 +126,6 @@ signature *_getsignature(value fn) { return NULL; } -bool _hasoptargs(value fn) { - if (MORPHO_ISFUNCTION(fn)) { - return function_countoptionalargs(MORPHO_GETFUNCTION(fn)); - } else if (MORPHO_ISBUILTINFUNCTION(fn)) { - return MORPHO_GETBUILTINFUNCTION(fn)->flags & MORPHO_FN_OPTARGS; - } - else false; -} - /** Resolves a metafunction given calling arguments */ bool metafunction_slowresolve(objectmetafunction *f, int nargs, value *args, value *out) { for (int i=0; ifns.count; i++) { @@ -162,7 +153,6 @@ bool metafunction_slowresolve(objectmetafunction *f, int nargs, value *args, val enum { MF_RESOLVE, MF_FAIL, - MF_OPTARGS, MF_CHECKNARGSNEQ, MF_CHECKNARGSLT, MF_CHECKVALUE, @@ -316,7 +306,6 @@ void mfcompiler_disassemble(mfcompiler *c) { if (sig) signature_print(sig); break; } - case MF_OPTARGS: printf("optargs"); break; case MF_FAIL: printf("fail"); break; } printf("\n"); @@ -431,12 +420,6 @@ mfindx mfcompile_fail(mfcompiler *c) { return mfcompile_insertinstruction(c, fail); } -/** Inserts resolver check for optional arguments */ -mfindx mfcompile_optargs(mfcompiler *c) { - mfinstruction optarg = MFINSTRUCTION_OPTARGS; - return mfcompile_insertinstruction(c, optarg); -} - /** Checks a parameter i for type */ mfindx mfcompile_check(mfcompiler *c, int i, value type) { int tindx; @@ -749,24 +732,19 @@ void metafunction_compile(objectmetafunction *fn) { mfset set; set.count = fn->fns.count; if (!set.count) return; - bool optargs=false; mfresult rlist[set.count]; set.rlist=rlist; for (int i=0; ifns.data[i]); rlist[i].fn=fn->fns.data[i]; - optargs |= _hasoptargs(fn->fns.data[i]); } mfcompiler compiler; mfcompiler_init(&compiler, fn); - if (optargs) mfcompile_optargs(&compiler); - mfcompile_set(&compiler, &set); - - mfcompiler_disassemble(&compiler); + //mfcompiler_disassemble(&compiler); mfcompiler_clear(&compiler, fn); } @@ -775,15 +753,12 @@ unsigned int vm_countpositionalargs(unsigned int nargs, value *args); /** Execute the metafunction's resolver */ bool metafunction_resolve(objectmetafunction *fn, int nargs, value *args, value *out) { - int n=nargs; + int n=vm_countpositionalargs(nargs, args);; mfinstruction *pc = fn->resolver.data; if (!pc) return metafunction_slowresolve(fn, n, args, out); do { switch(pc->opcode) { - case MF_OPTARGS: - n=vm_countpositionalargs(nargs, args); - break; case MF_CHECKNARGSNEQ: if (n!=pc->narg) pc+=pc->branch; break; diff --git a/test/types/multiple_dispatch/optional_invld.morpho b/test/types/multiple_dispatch/optional_invld.morpho new file mode 100644 index 00000000..d9b75127 --- /dev/null +++ b/test/types/multiple_dispatch/optional_invld.morpho @@ -0,0 +1,13 @@ +// Multiple dispatch that doesn't include optional arguments + +fn f() { return 0 } + +fn f(x) { return x } + +fn f(x,y,z,a) { return -1 } + +print f() +// expect: 0 + +print f(1,z=3) +// expect error 'InvldArgs' diff --git a/test/types/multiple_dispatch/optional_invld2.morpho b/test/types/multiple_dispatch/optional_invld2.morpho new file mode 100644 index 00000000..9221aa75 --- /dev/null +++ b/test/types/multiple_dispatch/optional_invld2.morpho @@ -0,0 +1,6 @@ +// Ensure optional arguments are treated separately from positional arguments + +fn f(x,y,z,a) { return -1 } + +print f(1,z=3) +// expect error 'InvldArgs' From 3c80ec58ccda91670de0ab055b800ff41da0e8ea Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Fri, 10 May 2024 20:50:53 -0400 Subject: [PATCH 090/181] Unknown type error --- benchmark/Metafunction/metafunction.morpho | 34 +++++++++++++++++++ src/core/compile.c | 9 ++++- src/core/compile.h | 3 ++ .../missing_comma_in_parameters.morpho | 2 +- .../multiple_dispatch/inheritance.morpho | 17 ++++++++++ test/types/multiple_dispatch/recursion.morpho | 11 ++++++ 6 files changed, 74 insertions(+), 2 deletions(-) create mode 100644 benchmark/Metafunction/metafunction.morpho create mode 100644 test/types/multiple_dispatch/inheritance.morpho create mode 100644 test/types/multiple_dispatch/recursion.morpho diff --git a/benchmark/Metafunction/metafunction.morpho b/benchmark/Metafunction/metafunction.morpho new file mode 100644 index 00000000..5c999e28 --- /dev/null +++ b/benchmark/Metafunction/metafunction.morpho @@ -0,0 +1,34 @@ +// Call a meta function + + +fn f(Int x) { return x } + +fn f(Float x) { return x } + +fn f(String x) { return x } + +fn g(x) { return x } + +var a = "Hello" + +var n = 10000000 + +var start = System.clock() +for (i in 1..n) { + g(1) + g(2.0) + g(a) +} +var end = System.clock() + +print "Regular function ${end-start}" + +var start = System.clock() +for (i in 1..n) { + f(1) + f(2.0) + f(a) +} +var end = System.clock() + +print "Metafunction ${end-start}" diff --git a/src/core/compile.c b/src/core/compile.c index 53cd0b94..74e3c61b 100644 --- a/src/core/compile.c +++ b/src/core/compile.c @@ -2716,7 +2716,13 @@ static registerindx compiler_functionparameters(compiler *c, syntaxtreeindx indx { value type=MORPHO_NIL; syntaxtreenode *typenode = compiler_getnode(c, node->left); - compiler_findtype(c, typenode->content, &type); + if (!typenode) UNREACHABLE("Incorrectly formed type node."); + if (!MORPHO_ISSTRING(typenode->content)) UNREACHABLE("Type node should have string label."); + + if (!compiler_findtype(c, typenode->content, &type)) { + compiler_error(c, node, COMPILE_UNKNWNTYPE, MORPHO_GETCSTRING(typenode->content)); + return REGISTER_UNALLOCATED; + } registerindx reg = compiler_functionparameters(c, node->right); compiler_regsettype(c, reg, type); @@ -4070,6 +4076,7 @@ void compile_initialize(void) { morpho_defineerror(COMPILE_INVLDLBL, ERROR_COMPILE, COMPILE_INVLDLBL_MSG); morpho_defineerror(COMPILE_MSSNGINDX, ERROR_COMPILE, COMPILE_MSSNGINDX_MSG); morpho_defineerror(COMPILE_TYPEVIOLATION, ERROR_COMPILE, COMPILE_TYPEVIOLATION_MSG); + morpho_defineerror(COMPILE_UNKNWNTYPE, ERROR_COMPILE, COMPILE_UNKNWNTYPE_MSG); morpho_addfinalizefn(compile_finalize); } diff --git a/src/core/compile.h b/src/core/compile.h index 241a164a..60aceea9 100644 --- a/src/core/compile.h +++ b/src/core/compile.h @@ -108,6 +108,9 @@ #define COMPILE_TYPEVIOLATION "TypeErr" #define COMPILE_TYPEVIOLATION_MSG "Type violation: Attempting to assign type %s to %s variable %s." +#define COMPILE_UNKNWNTYPE "UnknwnType" +#define COMPILE_UNKNWNTYPE_MSG "Unknown type %s." + /* ********************************************************************** * Compiler typedefs * ********************************************************************** */ diff --git a/test/function/missing_comma_in_parameters.morpho b/test/function/missing_comma_in_parameters.morpho index 5448a84b..67fd24b7 100644 --- a/test/function/missing_comma_in_parameters.morpho +++ b/test/function/missing_comma_in_parameters.morpho @@ -1,2 +1,2 @@ fn foo(a, b c, d, e, f) {} -// expect error 'FnMssngRgtPrn' +// expect error 'UnknwnType' diff --git a/test/types/multiple_dispatch/inheritance.morpho b/test/types/multiple_dispatch/inheritance.morpho new file mode 100644 index 00000000..7bd81f53 --- /dev/null +++ b/test/types/multiple_dispatch/inheritance.morpho @@ -0,0 +1,17 @@ +// Multiple dispatch with inheritance + +class A {} + +class B is A {} + +class C is B {} + +fn f(A x) { return "A" } + +fn b(B x) { return "B" } + +print f(A()) // expect: A + +print f(B()) // expect: B + +print f(C()) // expect: B \ No newline at end of file diff --git a/test/types/multiple_dispatch/recursion.morpho b/test/types/multiple_dispatch/recursion.morpho new file mode 100644 index 00000000..b2ff9e44 --- /dev/null +++ b/test/types/multiple_dispatch/recursion.morpho @@ -0,0 +1,11 @@ +// Multiple dispatch with recursion + +fn f() { + return 0 +} + +fn f(x) { + return "V" +} + +print "Not written" \ No newline at end of file From af3fb67f25402ecee309edcea4f1cc0d2e0cd2ed Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Sat, 11 May 2024 09:59:32 -0400 Subject: [PATCH 091/181] Towards handling closures --- src/classes/function.c | 5 +++++ src/classes/function.h | 2 ++ src/core/compile.c | 2 +- test/types/multiple_dispatch/closure.morpho | 3 --- test/types/multiple_dispatch/namespace.morpho | 16 ++++++++++++++++ 5 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/classes/function.c b/src/classes/function.c index 41ff8c9e..095dc4aa 100644 --- a/src/classes/function.c +++ b/src/classes/function.c @@ -144,6 +144,11 @@ void function_setvarg(objectfunction *func, unsigned int varg) { func->varg=varg; } +/** Checks if a function is enclosed in a closure */ +bool function_isclosure(objectfunction *func) { + return (func->nupvalues>0); +} + /** Sets the signature of a function * @param[in] func function object * @param[in] signature list of types for each parameter (length from func->nargs) */ diff --git a/src/classes/function.h b/src/classes/function.h index 2a428245..6f19657a 100644 --- a/src/classes/function.h +++ b/src/classes/function.h @@ -73,6 +73,8 @@ int function_countpositionalargs(objectfunction *func); int function_countoptionalargs(objectfunction *func); bool function_hasvargs(objectfunction *func); void function_setvarg(objectfunction *func, unsigned int varg); +bool function_isclosure(objectfunction *func); + void function_setsignature(objectfunction *func, value *signature); bool function_hastypedparameters(objectfunction *func); diff --git a/src/core/compile.c b/src/core/compile.c index 74e3c61b..f13600d9 100644 --- a/src/core/compile.c +++ b/src/core/compile.c @@ -1286,8 +1286,8 @@ static codeinfo compiler_movetoupvalue(compiler *c, syntaxtreenode *node, codein indx compiler_closure(compiler *c, syntaxtreenode *node, registerindx reg) { functionstate *f=compiler_currentfunctionstate(c); objectfunction *func = f->func; + indx ix=REGISTER_UNALLOCATED; - if (f->upvalues.count>0) { object_functionaddprototype(func, &f->upvalues, &ix); } diff --git a/test/types/multiple_dispatch/closure.morpho b/test/types/multiple_dispatch/closure.morpho index bac1b9e7..5e391e8c 100644 --- a/test/types/multiple_dispatch/closure.morpho +++ b/test/types/multiple_dispatch/closure.morpho @@ -1,13 +1,10 @@ // Multiple dispatch with closures fn f(a) { - fn g() { return a } fn g(x) { return x + a } print g() - - } var p = f(1) diff --git a/test/types/multiple_dispatch/namespace.morpho b/test/types/multiple_dispatch/namespace.morpho index 89066cb9..6bf567cf 100644 --- a/test/types/multiple_dispatch/namespace.morpho +++ b/test/types/multiple_dispatch/namespace.morpho @@ -2,3 +2,19 @@ import "namespace.xmorpho" as nm +nm.f("Hi") +// expect: Hi + +nm.f(1) +// expect: Foo + +nm.f(1.5) +// expect: 2.5 + +var a = [] +f(a) +print a +// expect: [ Ho ] + +f({}) +// expect: Foo \ No newline at end of file From 98b654d973e8bcbded3e008d0c37b14544e6099e Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Sat, 11 May 2024 20:57:05 -0400 Subject: [PATCH 092/181] Metafunctions with closures --- src/builtin/builtin.c | 3 +- src/classes/function.c | 13 +- src/classes/function.h | 5 +- src/classes/list.c | 2 +- src/classes/metafunction.c | 55 ++++- src/core/compile.c | 223 ++++++++++++-------- src/core/compile.h | 1 + test/closure/veneer.morpho | 2 +- test/types/multiple_dispatch/closure.morpho | 7 +- 9 files changed, 206 insertions(+), 105 deletions(-) diff --git a/src/builtin/builtin.c b/src/builtin/builtin.c index f430f7e1..59bf74c3 100644 --- a/src/builtin/builtin.c +++ b/src/builtin/builtin.c @@ -117,7 +117,8 @@ bool builtin_iscallable(value val) { return (MORPHO_ISOBJECT(val) && (MORPHO_ISFUNCTION(val) || MORPHO_ISCLOSURE(val) || MORPHO_ISINVOCATION(val) || - MORPHO_ISBUILTINFUNCTION(val))); + MORPHO_ISBUILTINFUNCTION(val) || + MORPHO_ISMETAFUNCTION(val))); } /* ********************************************************************** diff --git a/src/classes/function.c b/src/classes/function.c index 095dc4aa..3141e482 100644 --- a/src/classes/function.c +++ b/src/classes/function.c @@ -54,7 +54,7 @@ void object_functioninit(objectfunction *func) { func->name=MORPHO_NIL; func->nargs=0; func->parent=NULL; - func->nupvalues=0; + func->creg=-1; func->nregs=0; varray_valueinit(&func->konst); varray_varray_upvalueinit(&func->prototype); @@ -140,13 +140,18 @@ bool function_hasvargs(objectfunction *func) { } /** Sets the parameter number of a variadic argument */ -void function_setvarg(objectfunction *func, unsigned int varg) { +void function_setvarg(objectfunction *func, int varg) { func->varg=varg; } -/** Checks if a function is enclosed in a closure */ +/** Sets that a function must be enclosed */ +void function_setclosure(objectfunction *func, int creg) { + func->creg=creg; +} + +/** Checks if a function is enclosed */ bool function_isclosure(objectfunction *func) { - return (func->nupvalues>0); + return (func->creg>=0); } /** Sets the signature of a function diff --git a/src/classes/function.h b/src/classes/function.h index 6f19657a..ee165411 100644 --- a/src/classes/function.h +++ b/src/classes/function.h @@ -32,8 +32,8 @@ typedef struct sobjectfunction { int varg; // The parameter number of a variadic parameter. value name; indx entry; + int creg; // Closure register struct sobjectfunction *parent; - int nupvalues; int nregs; objectclass *klass; varray_value konst; @@ -72,7 +72,8 @@ objectfunction *object_newfunction(indx entry, value name, objectfunction *paren int function_countpositionalargs(objectfunction *func); int function_countoptionalargs(objectfunction *func); bool function_hasvargs(objectfunction *func); -void function_setvarg(objectfunction *func, unsigned int varg); +void function_setvarg(objectfunction *func, int varg); +void function_setclosure(objectfunction *func, int creg); bool function_isclosure(objectfunction *func); void function_setsignature(objectfunction *func, value *signature); diff --git a/src/classes/list.c b/src/classes/list.c index e7fd7103..0cc7bac1 100644 --- a/src/classes/list.c +++ b/src/classes/list.c @@ -713,7 +713,7 @@ void list_initialize(void) { // List constructor function builtin_addfunction(LIST_CLASSNAME, list_constructor, MORPHO_FN_CONSTRUCTOR); - // List constructor function + // Define List class value listclass=builtin_addclass(LIST_CLASSNAME, MORPHO_GETCLASSDEFINITION(List), objclass); object_setveneerclass(OBJECT_LIST, listclass); diff --git a/src/classes/metafunction.c b/src/classes/metafunction.c index 827cc97a..640bc15b 100644 --- a/src/classes/metafunction.c +++ b/src/classes/metafunction.c @@ -122,10 +122,23 @@ signature *_getsignature(value fn) { return &MORPHO_GETFUNCTION(fn)->sig; } else if (MORPHO_ISBUILTINFUNCTION(fn)) { return &MORPHO_GETBUILTINFUNCTION(fn)->sig; + } else if (MORPHO_ISCLOSURE(fn)) { + return &MORPHO_GETCLOSURE(fn)->func->sig; } return NULL; } +value _getname(value fn) { + if (MORPHO_ISFUNCTION(fn)) { + return MORPHO_GETFUNCTION(fn)->name; + } else if (MORPHO_ISBUILTINFUNCTION(fn)) { + return MORPHO_GETBUILTINFUNCTION(fn)->name; + } else if (MORPHO_ISCLOSURE(fn)) { + return MORPHO_GETCLOSURE(fn)->func->name; + } + return MORPHO_NIL; +} + /** Resolves a metafunction given calling arguments */ bool metafunction_slowresolve(objectmetafunction *f, int nargs, value *args, value *out) { for (int i=0; ifns.count; i++) { @@ -832,6 +845,39 @@ bool metafunction_resolve(objectmetafunction *fn, int nargs, value *args, value * Metafunction veneer class * ********************************************************************** */ +/** Constructor function for Metafunctions */ +value metafunction_constructor(vm *v, int nargs, value *args) { + value out = MORPHO_NIL; + + if (nargs==0) return MORPHO_NIL; + + value name = _getname(MORPHO_GETARG(args, 0)); + if (!MORPHO_ISSTRING(name)) return MORPHO_NIL; + + objectmetafunction *new = object_newmetafunction(name); + + if (new) { + for (int i=0; ifns.count); +} + +MORPHO_BEGINCLASS(Metafunction) +MORPHO_METHOD(MORPHO_COUNT_METHOD, Metafunction_count, BUILTIN_FLAGSEMPTY) +MORPHO_ENDCLASS /* ********************************************************************** * Initialization and finalization @@ -847,11 +893,12 @@ void metafunction_initialize(void) { objectstring objname = MORPHO_STATICSTRING(OBJECT_CLASSNAME); value objclass = builtin_findclass(MORPHO_OBJECT(&objname)); - // Create function veneer class - //value metafunctionclass=builtin_addclass(METAFUNCTION_CLASSNAME, MORPHO_GETCLASSDEFINITION(Metafunction), objclass); - //object_setveneerclass(OBJECT_FUNCTION, functionclass); + // List constructor function + builtin_addfunction(METAFUNCTION_CLASSNAME, metafunction_constructor, MORPHO_FN_CONSTRUCTOR); - // No constructor as objectmetafunctions are generated by the compiler + // Create function veneer class + value metafunctionclass=builtin_addclass(METAFUNCTION_CLASSNAME, MORPHO_GETCLASSDEFINITION(Metafunction), objclass); + object_setveneerclass(OBJECT_METAFUNCTION, metafunctionclass); // Metafunction error messages } diff --git a/src/core/compile.c b/src/core/compile.c index f13600d9..1485b735 100644 --- a/src/core/compile.c +++ b/src/core/compile.c @@ -172,90 +172,6 @@ static inline bool compiler_ininitializer(compiler *c) { return FUNCTIONTYPE_ISINITIALIZER(f->type); } -/* ------------------------------------------ - * Manage the functionref stack - * ------------------------------------------- */ - -DEFINE_VARRAY(functionref, functionref) - -/** Adds a reference to a function in the current functionstate */ -void compiler_addfunctionref(compiler *c, objectfunction *func) { - functionstate *f=compiler_currentfunctionstate(c); - functionref ref = { .function = MORPHO_OBJECT(func), .symbol = func->name, .scopedepth = f->scopedepth}; - varray_functionrefwrite(&f->functionref, ref); -} - -/** Removes functions visible at a given scope level */ -void compiler_functionreffreeatscope(compiler *c, unsigned int scope) { - functionstate *f=compiler_currentfunctionstate(c); - - while (f->functionref.count>0 && f->functionref.data[f->functionref.count-1].scopedepth>=scope) f->functionref.count--; -} - -void _addmatchingfunctionref(compiler *c, value symbol, value fn, value *out) { - value in = *out; - if (MORPHO_ISNIL(in)) { - // If the function has a signature, will need to wrap in a metafunction - if (MORPHO_ISFUNCTION(fn) && function_hastypedparameters(MORPHO_GETFUNCTION(fn))) { - if (metafunction_wrap(symbol, fn, out)) { - program_bindobject(c->out, MORPHO_GETOBJECT(*out)); - } - } else *out=fn; - } else if (MORPHO_ISFUNCTION(in)) { - if (metafunction_wrap(symbol, in, out)) { metafunction_add(MORPHO_GETMETAFUNCTION(*out), fn); - program_bindobject(c->out, MORPHO_GETOBJECT(*out)); - } - } else if (MORPHO_ISMETAFUNCTION(in)) { - metafunction_add(MORPHO_GETMETAFUNCTION(in), fn); - } -} - -/** Finds an existing metafunction in the current context that matches a given set of implementations */ -bool compiler_findmetafunction(compiler *c, value symbol, int n, value *fns, value *out) { - functionstate *f=compiler_currentfunctionstate(c); - - for (int i=0; ifunc->konst.count; i++) { - value v = f->func->konst.data[i]; - if (MORPHO_ISMETAFUNCTION(v) && - MORPHO_ISEQUAL(MORPHO_GETMETAFUNCTION(v)->name, symbol) && - metafunction_matchset(MORPHO_GETMETAFUNCTION(v), n, fns)) { - *out = v; - return true; - } - } - - return false; -} - -/** Determines whether a symbol refers to one (or more) functions. If so, returns either a single function or a metafunction as appropriate. */ -bool compiler_resolvefunctionref(compiler *c, value symbol, value *out) { - functionstate *f=compiler_currentfunctionstate(c); - - varray_value fns; - varray_valueinit(&fns); - - for (int i=0; ifunctionref.count; i++) { - functionref *ref=&f->functionref.data[i]; - if (MORPHO_ISEQUAL(ref->symbol, symbol)) varray_valuewrite(&fns, ref->function); - } - - if (!fns.count) return false; // No need to clear an empty varray - - if (!compiler_findmetafunction(c, symbol, fns.count, fns.data, out)) { - *out=MORPHO_NIL; - for (int i=0; iscopedepth++; } +void compiler_functionreffreeatscope(compiler *c, unsigned int scope); + /** Decrements the scope counter in the current functionstate */ void compiler_endscope(compiler *c) { functionstate *f=compiler_currentfunctionstate(c); @@ -1294,6 +1212,133 @@ indx compiler_closure(compiler *c, syntaxtreenode *node, registerindx reg) { return ix; } +/* ------------------------------------------ + * Manage the functionref stack + * ------------------------------------------- */ + +DEFINE_VARRAY(functionref, functionref) + +/** Adds a reference to a function in the current functionstate */ +int compiler_addfunctionref(compiler *c, objectfunction *func) { + functionstate *f=compiler_currentfunctionstate(c); + functionref ref = { .function = MORPHO_OBJECT(func), .symbol = func->name, .scopedepth = f->scopedepth}; + return varray_functionrefwrite(&f->functionref, ref); +} + +/** Removes functions visible at a given scope level */ +void compiler_functionreffreeatscope(compiler *c, unsigned int scope) { + functionstate *f=compiler_currentfunctionstate(c); + + while (f->functionref.count>0 && f->functionref.data[f->functionref.count-1].scopedepth>=scope) f->functionref.count--; +} + +void _addmatchingfunctionref(compiler *c, value symbol, value fn, value *out) { + value in = *out; + if (MORPHO_ISNIL(in)) { + // If the function has a signature, will need to wrap in a metafunction + if (MORPHO_ISFUNCTION(fn) && function_hastypedparameters(MORPHO_GETFUNCTION(fn))) { + if (metafunction_wrap(symbol, fn, out)) { + program_bindobject(c->out, MORPHO_GETOBJECT(*out)); + } + } else *out=fn; + } else if (MORPHO_ISFUNCTION(in)) { + if (metafunction_wrap(symbol, in, out)) { metafunction_add(MORPHO_GETMETAFUNCTION(*out), fn); + program_bindobject(c->out, MORPHO_GETOBJECT(*out)); + } + } else if (MORPHO_ISMETAFUNCTION(in)) { + metafunction_add(MORPHO_GETMETAFUNCTION(in), fn); + } +} + +/** Finds an existing metafunction in the current context that matches a given set of implementations */ +bool compiler_findmetafunction(compiler *c, value symbol, int n, value *fns, codeinfo *out) { + functionstate *f=compiler_currentfunctionstate(c); + + for (int i=0; ifunc->konst.count; i++) { + value v = f->func->konst.data[i]; + if (MORPHO_ISMETAFUNCTION(v) && + MORPHO_ISEQUAL(MORPHO_GETMETAFUNCTION(v)->name, symbol) && + metafunction_matchset(MORPHO_GETMETAFUNCTION(v), n, fns)) { + *out = CODEINFO(CONSTANT, i, 0); + return true; + } + } + + return false; +} + +/** Compile a metafunction constructor by setting up a call to the Metafunction() constructor */ +codeinfo compiler_metafunction(compiler *c, syntaxtreenode *node, int n, value *fns) { + codeinfo out = compiler_findbuiltin(c, node, METAFUNCTION_CLASSNAME, REGISTER_UNALLOCATED); + + for (int i=0; icreg; + } else { + val.dest = compiler_addconstant(c, node, fns[i], true, false); + } + + val=compiler_movetoregister(c, node, val, reg); + out.ninstructions+=val.ninstructions; + } + + /* Make the function call */ + compiler_addinstruction(c, ENCODE_DOUBLE(OP_CALL, out.dest, n), node); + out.ninstructions++; + compiler_regfreetoend(c, out.dest+1); + + return out; +} + +/** Determines whether a symbol refers to one (or more) functions. If so, returns either a single function or a metafunction as appropriate. */ +bool compiler_resolvefunctionref(compiler *c, syntaxtreenode *node, value symbol, codeinfo *out) { + functionstate *f=compiler_currentfunctionstate(c); + value outfn=MORPHO_NIL; + bool closure=false; // Set if one of the references contains a closure. + + varray_value fns; + varray_valueinit(&fns); + + for (int i=0; ifunctionref.count; i++) { + functionref *ref=&f->functionref.data[i]; + if (MORPHO_ISEQUAL(ref->symbol, symbol)) { + closure |= function_isclosure(MORPHO_GETFUNCTION(ref->function)); + varray_valuewrite(&fns, ref->function); + } + } + + if (!fns.count) return false; // No need to clear an empty varray + + if (closure) { + if (fns.count>1) { // If the list contains a closure, we must build the MF at runtime + *out = compiler_metafunction(c, node, fns.count, fns.data); + } else { // If just one closure, no need to build a metafunction + out->returntype=REGISTER; + out->dest=MORPHO_GETFUNCTION(fns.data[0])->creg; + out->ninstructions=0; + } + } else if (!compiler_findmetafunction(c, symbol, fns.count, fns.data, out)) { + // If a suitable MF doesn't exist in the constant table, we should build one + for (int i=0; ireturntype=CONSTANT; + out->dest=compiler_addconstant(c, node, outfn, true, false); + out->ninstructions=0; + } + + varray_valueclear(&fns); + + return true; +} + /* ------------------------------------------ * Helper function to move from a register * ------------------------------------------- */ @@ -2856,6 +2901,7 @@ static codeinfo compiler_function(compiler *c, syntaxtreenode *node, registerind /* Allocate a variable to refer to the function definition, but only in global context */ + /* TODO: Do we need to do this now functionstates capture function info? */ codeinfo fvar=CODEINFO_EMPTY; fvar.dest=compiler_regtemp(c, reqout); fvar.returntype=REGISTER; @@ -2878,7 +2924,8 @@ static codeinfo compiler_function(compiler *c, syntaxtreenode *node, registerind /* Wrap in a closure if necessary */ if (closure!=REGISTER_UNALLOCATED) { - compiler_regsetsymbol(c, reg, node->content); + // Save the register where the closure is to be found + function_setclosure(func, reg); compiler_addinstruction(c, ENCODE_DOUBLE(OP_CLOSURE, reg, (registerindx) closure), node); ninstructions++; } @@ -3389,11 +3436,7 @@ static codeinfo compiler_symbol(compiler *c, syntaxtreenode *node, registerindx } /* Is it a reference to a function? */ - value fn=MORPHO_NIL; - if (compiler_resolvefunctionref(c, node->content, &fn)) { - /* It is; so add it to the constant table */ - ret.returntype=CONSTANT; - ret.dest=compiler_addconstant(c, node, fn, false, false); + if (compiler_resolvefunctionref(c, node, node->content, &ret)) { return ret; } diff --git a/src/core/compile.h b/src/core/compile.h index 60aceea9..9d8d2ce0 100644 --- a/src/core/compile.h +++ b/src/core/compile.h @@ -197,6 +197,7 @@ typedef struct { value symbol; /** Symbol associated with the reference */ value function; /** The function itself */ unsigned int scopedepth; /** Scope depth at which the function was seen */ + registerindx reg; /** Register corresponding to the closure */ } functionref; DECLARE_VARRAY(functionref, functionref) diff --git a/test/closure/veneer.morpho b/test/closure/veneer.morpho index c7c7c5b5..c9d6ab0b 100644 --- a/test/closure/veneer.morpho +++ b/test/closure/veneer.morpho @@ -13,4 +13,4 @@ print a // expect: <> print a.clss() // expect: @Closure -print a.clone() // expect error 'ObjCantClone' \ No newline at end of file +print a.clone() // expect error 'ObjCantClone' diff --git a/test/types/multiple_dispatch/closure.morpho b/test/types/multiple_dispatch/closure.morpho index 5e391e8c..870bb972 100644 --- a/test/types/multiple_dispatch/closure.morpho +++ b/test/types/multiple_dispatch/closure.morpho @@ -4,10 +4,13 @@ fn f(a) { fn g() { return a } fn g(x) { return x + a } - print g() + return g } var p = f(1) -//print p() +print p() // expect: 1 + +print p(1) +// expect: 2 \ No newline at end of file From b0a612958592be4aacbea41caa3624913a3c957f Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Sun, 12 May 2024 08:32:41 -0400 Subject: [PATCH 093/181] Metafunction mark function --- src/classes/metafunction.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/classes/metafunction.c b/src/classes/metafunction.c index 640bc15b..b9bf1820 100644 --- a/src/classes/metafunction.c +++ b/src/classes/metafunction.c @@ -20,6 +20,12 @@ void objectmetafunction_freefn(object *obj) { void objectmetafunction_markfn(object *obj, void *v) { objectmetafunction *f = (objectmetafunction *) obj; + morpho_markvalue(v, f->name); // Mark the name + + for (int i=0; iresolver.count; i++) { // Mark any functions in the resolver + mfinstruction *instr = &f->resolver.data[i]; + if (instr->opcode==MF_RESOLVE) morpho_markvalue(v,instr->data.resolvefn); + } } size_t objectmetafunction_sizefn(object *obj) { From b29a9322b139cf81b062f3ca3075d3288eb06ed4 Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Sun, 12 May 2024 10:35:52 -0400 Subject: [PATCH 094/181] Remove slow resolver --- src/classes/metafunction.c | 57 ++++++++++++++------------------------ 1 file changed, 21 insertions(+), 36 deletions(-) diff --git a/src/classes/metafunction.c b/src/classes/metafunction.c index b9bf1820..7583f24a 100644 --- a/src/classes/metafunction.c +++ b/src/classes/metafunction.c @@ -8,6 +8,25 @@ #include "classes.h" #include "common.h" +/* ********************************************************************** + * Metafunction opcodes + * ********************************************************************** */ + +enum { + MF_RESOLVE, + MF_FAIL, + MF_CHECKNARGSNEQ, + MF_CHECKNARGSLT, + MF_CHECKVALUE, + MF_CHECKOBJECT, + MF_CHECKINSTANCE, + MF_BRANCH, + MF_BRANCHNARGS, + MF_BRANCHVALUETYPE, + MF_BRANCHOBJECTTYPE, + MF_BRANCHINSTANCE +}; + /* ********************************************************************** * objectmetafunction definitions * ********************************************************************** */ @@ -145,45 +164,10 @@ value _getname(value fn) { return MORPHO_NIL; } -/** Resolves a metafunction given calling arguments */ -bool metafunction_slowresolve(objectmetafunction *f, int nargs, value *args, value *out) { - for (int i=0; ifns.count; i++) { - signature *s = _getsignature(f->fns.data[i]); - if (!s) continue; - - int nparams; value *ptypes; - signature_paramlist(s, &nparams, &ptypes); - if (nargs!=nparams) continue; - - int j; - for (j=0; jfns.data[i]; return true; } - } - - return false; -} - /* ********************************************************************** * Fast metafunction resolver * ********************************************************************** */ -enum { - MF_RESOLVE, - MF_FAIL, - MF_CHECKNARGSNEQ, - MF_CHECKNARGSLT, - MF_CHECKVALUE, - MF_CHECKOBJECT, - MF_CHECKINSTANCE, - MF_BRANCH, - MF_BRANCHNARGS, - MF_BRANCHVALUETYPE, - MF_BRANCHOBJECTTYPE, - MF_BRANCHINSTANCE -}; - DEFINE_VARRAY(mfinstruction, mfinstruction); #define MFINSTRUCTION_EMPTY -1 @@ -773,8 +757,9 @@ unsigned int vm_countpositionalargs(unsigned int nargs, value *args); /** Execute the metafunction's resolver */ bool metafunction_resolve(objectmetafunction *fn, int nargs, value *args, value *out) { int n=vm_countpositionalargs(nargs, args);; + if (!fn->resolver.data) metafunction_compile(fn); mfinstruction *pc = fn->resolver.data; - if (!pc) return metafunction_slowresolve(fn, n, args, out); + if (!pc) return false; do { switch(pc->opcode) { From 0114dcde0596e39ff450d70b809714ce44294aa2 Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Sun, 12 May 2024 12:06:08 -0400 Subject: [PATCH 095/181] Raise an error for ambiguous implementations --- src/classes/metafunction.c | 36 +++++++++++++++---- src/classes/metafunction.h | 5 ++- src/core/compile.c | 4 ++- test/types/multiple_dispatch/duplicate.morpho | 2 ++ 4 files changed, 38 insertions(+), 9 deletions(-) diff --git a/src/classes/metafunction.c b/src/classes/metafunction.c index 7583f24a..3fe19d80 100644 --- a/src/classes/metafunction.c +++ b/src/classes/metafunction.c @@ -203,17 +203,25 @@ DEFINE_VARRAY(mfset, mfset) typedef struct { objectmetafunction *fn; varray_int checked; // Stack of checked parameters + error err; } mfcompiler; /** Initialize the metafunction compiler */ void mfcompiler_init(mfcompiler *c, objectmetafunction *fn) { c->fn=fn; varray_intinit(&c->checked); + error_init(&c->err); } /** Clear the metafunction compiler */ void mfcompiler_clear(mfcompiler *c, objectmetafunction *fn) { varray_intclear(&c->checked); + error_clear(&c->err); +} + +/** Report an error during metafunction compilation */ +void mfcompiler_error(mfcompiler *c, errorid id) { + morpho_writeerrorwithid(&c->err, id, ERROR_POSNUNIDENTIFIABLE, ERROR_POSNUNIDENTIFIABLE); } /** Pushes a parameter check onto the stack*/ @@ -354,11 +362,13 @@ bool mfcompile_countoutcomes(mfcompiler *c, mfset *set, int *best) { dictionary_clear(&dict); // Not needed if dict.count was zero }; - // Find the parameter that has most variability - int max=count.data[0], maxindx=0; - for (int i=1; imax) { max=count.data[i]; maxindx=i; } } + if (maxindx<0) return false; if (best) *best = maxindx; varray_intclear(&count); @@ -718,6 +728,7 @@ mfindx mfcompile_set(mfcompiler *c, mfset *set) { int best; if (mfcompile_countoutcomes(c, set, &best)) return mfcompile_dispatchonparam(c, set, best); + mfcompiler_error(c, METAFUNCTION_CMPLAMBGS); return MFINSTRUCTION_EMPTY; } @@ -731,10 +742,10 @@ void metafunction_clearinstructions(objectmetafunction *fn) { } /** Compiles the metafunction resolver */ -void metafunction_compile(objectmetafunction *fn) { +bool metafunction_compile(objectmetafunction *fn, error *err) { mfset set; set.count = fn->fns.count; - if (!set.count) return; + if (!set.count) return false; mfresult rlist[set.count]; set.rlist=rlist; @@ -749,7 +760,12 @@ void metafunction_compile(objectmetafunction *fn) { mfcompile_set(&compiler, &set); //mfcompiler_disassemble(&compiler); + bool success=!morpho_checkerror(&compiler.err); + if (!success && err) *err=compiler.err; + mfcompiler_clear(&compiler, fn); + + return success; } unsigned int vm_countpositionalargs(unsigned int nargs, value *args); @@ -757,7 +773,8 @@ unsigned int vm_countpositionalargs(unsigned int nargs, value *args); /** Execute the metafunction's resolver */ bool metafunction_resolve(objectmetafunction *fn, int nargs, value *args, value *out) { int n=vm_countpositionalargs(nargs, args);; - if (!fn->resolver.data) metafunction_compile(fn); + if (!fn->resolver.data && + !metafunction_compile(fn, NULL)) return false; mfinstruction *pc = fn->resolver.data; if (!pc) return false; @@ -852,7 +869,11 @@ value metafunction_constructor(vm *v, int nargs, value *args) { metafunction_add(new, MORPHO_GETARG(args, i)); } - metafunction_compile(new); + error err; + error_init(&err); + if (!metafunction_compile(new, &err)) morpho_runtimeerror(v, err.id); + error_clear(&err); + out=MORPHO_OBJECT(new); } @@ -892,4 +913,5 @@ void metafunction_initialize(void) { object_setveneerclass(OBJECT_METAFUNCTION, metafunctionclass); // Metafunction error messages + morpho_defineerror(METAFUNCTION_CMPLAMBGS, ERROR_PARSE, METAFUNCTION_CMPLAMBGS_MSG); } diff --git a/src/classes/metafunction.h b/src/classes/metafunction.h index e67d6f73..85ee4272 100644 --- a/src/classes/metafunction.h +++ b/src/classes/metafunction.h @@ -57,6 +57,9 @@ typedef struct sobjectmetafunction { * Metafunction error messages * ------------------------------------------------------- */ +#define METAFUNCTION_CMPLAMBGS "MltplDisptchAmbg" +#define METAFUNCTION_CMPLAMBGS_MSG "Ambiguous or duplicate implementations in multiple dispatch." + /* ------------------------------------------------------- * Metafunction interface * ------------------------------------------------------- */ @@ -69,7 +72,7 @@ bool metafunction_typefromvalue(value v, value *out); bool metafunction_matchfn(objectmetafunction *fn, value f); bool metafunction_matchset(objectmetafunction *fn, int n, value *fns); -void metafunction_compile(objectmetafunction *fn); +bool metafunction_compile(objectmetafunction *fn, error *err); void metafunction_clearinstructions(objectmetafunction *fn); bool metafunction_resolve(objectmetafunction *f, int nargs, value *args, value *fn); diff --git a/src/core/compile.c b/src/core/compile.c index 1485b735..b6033681 100644 --- a/src/core/compile.c +++ b/src/core/compile.c @@ -1327,7 +1327,9 @@ bool compiler_resolvefunctionref(compiler *c, syntaxtreenode *node, value symbol _addmatchingfunctionref(c, symbol, fns.data[i], &outfn); } - if (MORPHO_ISMETAFUNCTION(outfn)) metafunction_compile(MORPHO_GETMETAFUNCTION(outfn)); + if (MORPHO_ISMETAFUNCTION(outfn)) { + metafunction_compile(MORPHO_GETMETAFUNCTION(outfn), &c->err); + } out->returntype=CONSTANT; out->dest=compiler_addconstant(c, node, outfn, true, false); diff --git a/test/types/multiple_dispatch/duplicate.morpho b/test/types/multiple_dispatch/duplicate.morpho index ed992b61..20fe833e 100644 --- a/test/types/multiple_dispatch/duplicate.morpho +++ b/test/types/multiple_dispatch/duplicate.morpho @@ -8,5 +8,7 @@ fn f(x, Int y) { return 1 } +f(1,1) + // expect error 'MltplDisptchAmbg' From 9c907db877934e66d957b233a318f24676068e4e Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Sun, 12 May 2024 12:28:46 -0400 Subject: [PATCH 096/181] Prevent branch table overflows --- src/classes/metafunction.c | 8 ++--- .../multiple_dispatch/object_overflow.morpho | 31 +++++++++++++++++++ 2 files changed, 34 insertions(+), 5 deletions(-) create mode 100644 test/types/multiple_dispatch/object_overflow.morpho diff --git a/src/classes/metafunction.c b/src/classes/metafunction.c index 3fe19d80..2ee180f7 100644 --- a/src/classes/metafunction.c +++ b/src/classes/metafunction.c @@ -817,17 +817,15 @@ bool metafunction_resolve(objectmetafunction *fn, int nargs, value *args, value break; case MF_BRANCHVALUETYPE: { if (!MORPHO_ISOBJECT(args[pc->narg])) { - // TODO: Check for btable bound int type = (int) MORPHO_GETORDEREDTYPE(args[pc->narg]); - pc+=pc->data.btable.data[type]; + if (typedata.btable.count) pc+=pc->data.btable.data[type]; } else pc+=pc->branch; } break; case MF_BRANCHOBJECTTYPE: { if (MORPHO_ISOBJECT(args[pc->narg])) { - // TODO: Check for btable bound int type = MORPHO_GETOBJECTTYPE(args[pc->narg]); - pc+=pc->data.btable.data[type]; + if (typedata.btable.count) pc+=pc->data.btable.data[type]; } else pc+=pc->branch; } break; @@ -835,7 +833,7 @@ bool metafunction_resolve(objectmetafunction *fn, int nargs, value *args, value if (MORPHO_ISINSTANCE(args[pc->narg])) { // TODO: Check for btable bound objectclass *klass = MORPHO_GETINSTANCE(args[pc->narg])->klass; - pc+=pc->data.btable.data[klass->uid]; + if (klass->uiddata.btable.count) pc+=pc->data.btable.data[klass->uid]; } else pc+=pc->branch; } break; diff --git a/test/types/multiple_dispatch/object_overflow.morpho b/test/types/multiple_dispatch/object_overflow.morpho new file mode 100644 index 00000000..04aa8266 --- /dev/null +++ b/test/types/multiple_dispatch/object_overflow.morpho @@ -0,0 +1,31 @@ +// Attempt to use an object type that exceeds the bounds +// of the metafunction's branch table + +fn f(String x) { + return 0 +} + +fn f(Float x) { + return 2 +} + +try { + print f(1.0) +} catch { + "MltplDsptchFld": print "ok" +} +// expect: 2 + +try { + print f(1) +} catch { + "MltplDsptchFld": print "ok" +} +// expect: ok + +try { + print f(Matrix(1,1)) +} catch { + "MltplDsptchFld": print "ok" +} +// expect: ok From 1948924a557d695b305670f97bb8cc001893a0c7 Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Sun, 12 May 2024 16:45:48 -0400 Subject: [PATCH 097/181] Fix mutual recursion --- src/core/compile.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/core/compile.c b/src/core/compile.c index b6033681..be8e9f42 100644 --- a/src/core/compile.c +++ b/src/core/compile.c @@ -2875,17 +2875,12 @@ static codeinfo compiler_function(compiler *c, syntaxtreenode *node, registerind ninstructions+=bodyinfo.ninstructions; /* Add a return instruction if necessary */ - //if (DECODE_OP(compiler_previousinstruction(c))!=OP_RETURN) { // 8/11/21 -> fix for final return in if - if (true) { - /* Methods automatically return self unless another argument is specified */ - if (ismethod) { - compiler_addinstruction(c, ENCODE_DOUBLE(OP_RETURN, 1, 0), node); /* Add a return */ - } else { - compiler_addinstruction(c, ENCODE_BYTE(OP_RETURN), node); /* Add a return */ - } - - ninstructions++; + if (ismethod) { // Methods automatically return self unless another argument is specified + compiler_addinstruction(c, ENCODE_DOUBLE(OP_RETURN, 1, 0), node); /* Add a return */ + } else { + compiler_addinstruction(c, ENCODE_BYTE(OP_RETURN), node); /* Add a return */ } + ninstructions++; /* Verify if we have any outstanding forward references */ compiler_checkoutstandingforwardreference(c); @@ -2927,6 +2922,7 @@ static codeinfo compiler_function(compiler *c, syntaxtreenode *node, registerind /* Wrap in a closure if necessary */ if (closure!=REGISTER_UNALLOCATED) { // Save the register where the closure is to be found + compiler_regsetsymbol(c, reg, func->name); function_setclosure(func, reg); compiler_addinstruction(c, ENCODE_DOUBLE(OP_CLOSURE, reg, (registerindx) closure), node); ninstructions++; From c50e4b95b3fba9ce1f4cbeab5fc6493c1eebdedd Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Sun, 12 May 2024 19:18:00 -0400 Subject: [PATCH 098/181] Resolve function references before local variables --- src/core/compile.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/core/compile.c b/src/core/compile.c index be8e9f42..a8c1c06b 100644 --- a/src/core/compile.c +++ b/src/core/compile.c @@ -3422,6 +3422,11 @@ static codeinfo compiler_super(compiler *c, syntaxtreenode *node, registerindx r static codeinfo compiler_symbol(compiler *c, syntaxtreenode *node, registerindx reqout) { codeinfo ret=CODEINFO_EMPTY; + /* Is it a reference to a function? */ + if (compiler_resolvefunctionref(c, node, node->content, &ret)) { + return ret; + } + /* Is it a local variable? */ ret.dest=compiler_getlocal(c, node->content); if (ret.dest!=REGISTER_UNALLOCATED) return ret; @@ -3432,11 +3437,6 @@ static codeinfo compiler_symbol(compiler *c, syntaxtreenode *node, registerindx ret.returntype=UPVALUE; return ret; } - - /* Is it a reference to a function? */ - if (compiler_resolvefunctionref(c, node, node->content, &ret)) { - return ret; - } /* Is it a global variable */ ret.dest=compiler_findglobal(c, node->content, true); From 71bdf6974a67a3a280688514971b8143d43a5809 Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Sun, 12 May 2024 21:35:17 -0400 Subject: [PATCH 099/181] Functionrefs and imports --- src/core/compile.c | 9 +++++++++ test/types/multiple_dispatch/import.morpho | 20 +++++++++++++++++++ test/types/multiple_dispatch/namespace.morpho | 6 +++--- 3 files changed, 32 insertions(+), 3 deletions(-) create mode 100644 test/types/multiple_dispatch/import.morpho diff --git a/src/core/compile.c b/src/core/compile.c index a8c1c06b..548cfac1 100644 --- a/src/core/compile.c +++ b/src/core/compile.c @@ -3779,6 +3779,14 @@ void compiler_copysymbols(dictionary *src, dictionary *dest, dictionary *compare } } +/** Copies the global function ref into the destination compiler's current function ref */ +void compiler_copyfunctionref(compiler *src, compiler *dest) { + functionstate *in=compiler_currentfunctionstate(src); + functionstate *out=compiler_currentfunctionstate(dest); + + varray_functionrefadd(&out->functionref, in->functionref.data, in->functionref.count); +} + /** Searches for a module with given name, returns the file name for inclusion. */ bool compiler_findmodule(char *name, varray_char *fname) { char *ext[] = { MORPHO_EXTENSION, "" }; @@ -3900,6 +3908,7 @@ static codeinfo compiler_import(compiler *c, syntaxtreenode *node, registerindx compiler_copysymbols(&cc.classes, &nmspace->classes, (fordict.count>0 ? &fordict : NULL)); } else { // Otherwise just put it into the parent compiler's class table compiler_copysymbols(&cc.classes, &c->classes, (fordict.count>0 ? &fordict : NULL)); + compiler_copyfunctionref(&cc, c); } objectdictionary *dict = object_newdictionary(); // Preserve all symbols for further imports diff --git a/test/types/multiple_dispatch/import.morpho b/test/types/multiple_dispatch/import.morpho new file mode 100644 index 00000000..36600679 --- /dev/null +++ b/test/types/multiple_dispatch/import.morpho @@ -0,0 +1,20 @@ +// Dispatch a function defined in a module + +import "namespace.xmorpho" + +f("Hi") +// expect: Hi + +f(1) +// expect: Foo + +f(1.5) +// expect: 2.5 + +var a = [] +f(a) +print a +// expect: [ Ho ] + +f({}) +// expect: Foo diff --git a/test/types/multiple_dispatch/namespace.morpho b/test/types/multiple_dispatch/namespace.morpho index 6bf567cf..11173247 100644 --- a/test/types/multiple_dispatch/namespace.morpho +++ b/test/types/multiple_dispatch/namespace.morpho @@ -12,9 +12,9 @@ nm.f(1.5) // expect: 2.5 var a = [] -f(a) +nm.f(a) print a // expect: [ Ho ] -f({}) -// expect: Foo \ No newline at end of file +nm.f({}) +// expect: Foo From 1f509587029c47ce363537abe144b9de5d6582f1 Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Mon, 13 May 2024 08:54:13 -0400 Subject: [PATCH 100/181] Metafunctions in namespaces --- src/core/compile.c | 68 ++++++++++++------- src/core/compile.h | 2 +- .../types/multiple_dispatch/import_for.morpho | 8 +++ .../types/multiple_dispatch/namespace.xmorpho | 12 ++++ .../multiple_dispatch/namespace_for.morpho | 6 ++ 5 files changed, 72 insertions(+), 24 deletions(-) create mode 100644 test/types/multiple_dispatch/import_for.morpho create mode 100644 test/types/multiple_dispatch/namespace_for.morpho diff --git a/src/core/compile.c b/src/core/compile.c index 548cfac1..e01535b2 100644 --- a/src/core/compile.c +++ b/src/core/compile.c @@ -28,6 +28,7 @@ static optimizerfn *optimizer; * Utility functions * ------------------------------------------- */ +/** Checks if the compiler is in an error state */ static bool compiler_haserror(compiler *c) { return (c->err.cat!=ERROR_NONE); } @@ -89,16 +90,6 @@ static instructionindx compiler_currentinstructionindex(compiler *c) { return (instructionindx) c->out->code.count; } -/** Adds an instruction to the current program */ -/*static instruction compiler_previousinstruction(compiler *c) { - return c->out->code.data[c->out->code.count-1]; -}*/ - -/** Finds the current instruction index */ -/*static instructionindx compiler_currentinstruction(compiler *c) { - return c->out->code.count; -}*/ - /** Modifies the instruction at a given index */ static void compiler_setinstruction(compiler *c, instructionindx indx, instruction instr) { c->out->code.data[indx]=instr; @@ -1221,7 +1212,7 @@ DEFINE_VARRAY(functionref, functionref) /** Adds a reference to a function in the current functionstate */ int compiler_addfunctionref(compiler *c, objectfunction *func) { functionstate *f=compiler_currentfunctionstate(c); - functionref ref = { .function = MORPHO_OBJECT(func), .symbol = func->name, .scopedepth = f->scopedepth}; + functionref ref = { .function = func, .symbol = func->name, .scopedepth = f->scopedepth}; return varray_functionrefwrite(&f->functionref, ref); } @@ -1306,8 +1297,8 @@ bool compiler_resolvefunctionref(compiler *c, syntaxtreenode *node, value symbol for (int i=0; ifunctionref.count; i++) { functionref *ref=&f->functionref.data[i]; if (MORPHO_ISEQUAL(ref->symbol, symbol)) { - closure |= function_isclosure(MORPHO_GETFUNCTION(ref->function)); - varray_valuewrite(&fns, ref->function); + closure |= function_isclosure(ref->function); + varray_valuewrite(&fns, MORPHO_OBJECT(ref->function)); } } @@ -3644,7 +3635,10 @@ static codeinfo compiler_dot(compiler *c, syntaxtreenode *node, registerindx req if (MORPHO_ISINTEGER(out)) { return CODEINFO(GLOBAL, MORPHO_GETINTEGERVALUE(out), 0); - } else if (MORPHO_ISBUILTINFUNCTION(out) || MORPHO_ISCLASS(out)) { + } else if (MORPHO_ISFUNCTION(out) || + MORPHO_ISMETAFUNCTION(out) || + MORPHO_ISBUILTINFUNCTION(out) || + MORPHO_ISCLASS(out)) { registerindx indx = compiler_addconstant(c, node, out, true, false); return CODEINFO(CONSTANT, indx, 0); } else { @@ -3780,11 +3774,42 @@ void compiler_copysymbols(dictionary *src, dictionary *dest, dictionary *compare } /** Copies the global function ref into the destination compiler's current function ref */ -void compiler_copyfunctionref(compiler *src, compiler *dest) { +void compiler_copyfunctionref(compiler *src, compiler *dest, dictionary *fordict) { functionstate *in=compiler_currentfunctionstate(src); functionstate *out=compiler_currentfunctionstate(dest); - varray_functionrefadd(&out->functionref, in->functionref.data, in->functionref.count); + if (fordict) { + for (int i=0; ifunctionref.count; i++) { + functionref *ref=&in->functionref.data[i]; + if (!dictionary_get(fordict, ref->function->name, NULL)) continue; + + varray_functionrefwrite(&out->functionref, in->functionref.data[i]); + } + } else varray_functionrefadd(&out->functionref, in->functionref.data, in->functionref.count); +} + +/** Copies the global function ref into the designated namespace, checking whether the functions are present in the dictionary, and creating metafunctions where necessary */ +void compiler_copyfunctionreftonamespace(compiler *src, namespc *dest, dictionary *fordict) { + functionstate *f=compiler_currentfunctionstate(src); + + dictionary symbols; + dictionary_init(&symbols); + + for (int i=0; ifunctionref.count; i++) { + functionref *ref=&f->functionref.data[i]; + // Skip if not in the fordict + if (fordict && !dictionary_get(fordict, ref->function->name, NULL)) continue; + + value fn=MORPHO_OBJECT(ref->function); + if (dictionary_get(&symbols, ref->function->name, &fn)) { + // If the function already exists, wrap in a metafunction + _addmatchingfunctionref(src, ref->function->name, MORPHO_OBJECT(ref->function), &fn); + } + dictionary_insert(&symbols, ref->function->name, fn); + } + + compiler_copysymbols(&symbols, &dest->symbols, NULL); + dictionary_clear(&symbols); } /** Searches for a module with given name, returns the file name for inclusion. */ @@ -3906,9 +3931,10 @@ static codeinfo compiler_import(compiler *c, syntaxtreenode *node, registerindx compiler_copysymbols(&cc.globals, (nmspace ? &nmspace->symbols: &c->globals), (fordict.count>0 ? &fordict : NULL)); if (nmspace) { // If we're in a namespace, copy the class table into that compiler_copysymbols(&cc.classes, &nmspace->classes, (fordict.count>0 ? &fordict : NULL)); + compiler_copyfunctionreftonamespace(&cc, nmspace, (fordict.count>0 ? &fordict : NULL)); } else { // Otherwise just put it into the parent compiler's class table compiler_copysymbols(&cc.classes, &c->classes, (fordict.count>0 ? &fordict : NULL)); - compiler_copyfunctionref(&cc, c); + compiler_copyfunctionref(&cc, c, (fordict.count>0 ? &fordict : NULL)); } objectdictionary *dict = object_newdictionary(); // Preserve all symbols for further imports @@ -4062,9 +4088,7 @@ bool morpho_compile(char *in, compiler *c, bool opt, error *err) { compiler *morpho_newcompiler(program *out) { compiler *new = MORPHO_MALLOC(sizeof(compiler)); - if (new) { - compiler_init("", out, new); - } + if (new) compiler_init("", out, new); return new; } @@ -4080,9 +4104,7 @@ void morpho_freecompiler(compiler *c) { * ********************************************************************** */ void morpho_setbaseclass(value klss) { - if (MORPHO_ISCLASS(klss)) { - baseclass=MORPHO_GETCLASS(klss); - } + if (MORPHO_ISCLASS(klss)) baseclass=MORPHO_GETCLASS(klss); } void morpho_setoptimizer(optimizerfn *opt) { diff --git a/src/core/compile.h b/src/core/compile.h index 9d8d2ce0..35a51d10 100644 --- a/src/core/compile.h +++ b/src/core/compile.h @@ -195,7 +195,7 @@ DECLARE_VARRAY(forwardreference, forwardreference) typedef struct { value symbol; /** Symbol associated with the reference */ - value function; /** The function itself */ + objectfunction *function; /** The function itself */ unsigned int scopedepth; /** Scope depth at which the function was seen */ registerindx reg; /** Register corresponding to the closure */ } functionref; diff --git a/test/types/multiple_dispatch/import_for.morpho b/test/types/multiple_dispatch/import_for.morpho new file mode 100644 index 00000000..8f1ecc26 --- /dev/null +++ b/test/types/multiple_dispatch/import_for.morpho @@ -0,0 +1,8 @@ +// Import functions with 'for' keyword + +import "namespace.xmorpho" for f + +f("Hi") // Never gets to execute + +g() +// expect error 'SymblUndf' \ No newline at end of file diff --git a/test/types/multiple_dispatch/namespace.xmorpho b/test/types/multiple_dispatch/namespace.xmorpho index 2058f485..9ba4b16f 100644 --- a/test/types/multiple_dispatch/namespace.xmorpho +++ b/test/types/multiple_dispatch/namespace.xmorpho @@ -15,3 +15,15 @@ fn f(Float x) { fn f(x) { print "Foo" } + +fn g() { + return 0 +} + +fn g(x) { + return 1 +} + +fn h() { + return 0 +} \ No newline at end of file diff --git a/test/types/multiple_dispatch/namespace_for.morpho b/test/types/multiple_dispatch/namespace_for.morpho new file mode 100644 index 00000000..da36089b --- /dev/null +++ b/test/types/multiple_dispatch/namespace_for.morpho @@ -0,0 +1,6 @@ +// Namespace with 'for' keyword + +import "namespace.xmorpho" as nm for f + +nm.g() +// expect error 'SymblUndfNmSpc' From cb121c5140c4ede41e978ed87702fef343d717b8 Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Tue, 14 May 2024 08:56:53 -0400 Subject: [PATCH 101/181] FIx order of object checking --- src/classes/metafunction.c | 10 +++---- .../multiple_dispatch/class_visitor.morpho | 30 +++++++++++++++++++ .../multiple_dispatch/inheritance.morpho | 6 +++- .../multiple_dispatch/optional_invld2.morpho | 6 ---- 4 files changed, 40 insertions(+), 12 deletions(-) create mode 100644 test/types/multiple_dispatch/class_visitor.morpho delete mode 100644 test/types/multiple_dispatch/optional_invld2.morpho diff --git a/src/classes/metafunction.c b/src/classes/metafunction.c index 2ee180f7..48e684a1 100644 --- a/src/classes/metafunction.c +++ b/src/classes/metafunction.c @@ -275,7 +275,7 @@ void mfcompiler_disassemble(mfcompiler *c) { break; } case MF_CHECKINSTANCE: { - printf("checkinstance (%i) -> (%i)", instr->narg, i+instr->branch+1); + printf("checkinstance (%i) [%i] -> (%i)", instr->narg, instr->data.tindx, i+instr->branch+1); break; } case MF_BRANCH: { @@ -400,8 +400,8 @@ void mfcompile_replaceinstruction(mfcompiler *c, mfindx i, mfinstruction instr) enum { MF_VENEERVALUE, - MF_VENEEROBJECT, MF_INSTANCE, + MF_VENEEROBJECT, MF_ANY }; @@ -436,7 +436,7 @@ mfindx mfcompile_fail(mfcompiler *c) { /** Checks a parameter i for type */ mfindx mfcompile_check(mfcompiler *c, int i, value type) { int tindx; - int opcode[MF_ANY] = { MF_CHECKVALUE, MF_CHECKOBJECT, MF_CHECKINSTANCE }; + int opcode[MF_ANY] = { MF_CHECKVALUE, MF_CHECKINSTANCE, MF_CHECKOBJECT }; int k=_detecttype(type, &tindx); if (k==MF_ANY) return MFINSTRUCTION_EMPTY; @@ -475,7 +475,7 @@ mfindx mfcompile_resolve(mfcompiler *c, mfresult *res) { void mfcompile_branchtable(mfcompiler *c, mfset *set, mfindx bindx, varray_int *btable) { int k=0; // Values with negative indices shouldn't be included in the branch table - while (set->rlist[k].indx<0 && kcount) k++; + while (kcount && set->rlist[k].indx<0) k++; // Deal with each outcome while (kcount) { @@ -637,8 +637,8 @@ mfindx mfcompile_dispatchonparam(mfcompiler *c, mfset *set, int i) { mfindx bindx[MF_ANY+1]; mfcompile_dispatchfn *dfn[MF_ANY+1] = { mfcompile_dispatchveneervalue, - mfcompile_dispatchveneerobj, mfcompile_dispatchinstance, + mfcompile_dispatchveneerobj, mfcompile_dispatchany}; // Cycle through all value types, building a chain of branchtables diff --git a/test/types/multiple_dispatch/class_visitor.morpho b/test/types/multiple_dispatch/class_visitor.morpho new file mode 100644 index 00000000..79779b49 --- /dev/null +++ b/test/types/multiple_dispatch/class_visitor.morpho @@ -0,0 +1,30 @@ +// Multiple dispatch version of the visitor pattern + +class A { } + +class Visitor { + init(...set) { + self.lst = [] + for (s in set) self.lst.append(s) + } + + visit(A x) { return "A" } + visit(List x) { return "List" } + visit(String x) { return "String" } + visit(Int x) { return "Int" } + visit(Dictionary x) { return "Dictionary" } + + process() { + for (l in self.lst) { print self.visit(l) } + } +} + +var a = Visitor(A(), "Hello", 1, {}, [1], A()) + +a.process() +// expect: A +// expect: String +// expect: Int +// expect: Dictionary +// expect: List +// expect: A diff --git a/test/types/multiple_dispatch/inheritance.morpho b/test/types/multiple_dispatch/inheritance.morpho index 7bd81f53..38cac10b 100644 --- a/test/types/multiple_dispatch/inheritance.morpho +++ b/test/types/multiple_dispatch/inheritance.morpho @@ -6,6 +6,8 @@ class B is A {} class C is B {} +class D is Object with A { } + fn f(A x) { return "A" } fn b(B x) { return "B" } @@ -14,4 +16,6 @@ print f(A()) // expect: A print f(B()) // expect: B -print f(C()) // expect: B \ No newline at end of file +print f(C()) // expect: B + +print f(D()) // expect: A diff --git a/test/types/multiple_dispatch/optional_invld2.morpho b/test/types/multiple_dispatch/optional_invld2.morpho deleted file mode 100644 index 9221aa75..00000000 --- a/test/types/multiple_dispatch/optional_invld2.morpho +++ /dev/null @@ -1,6 +0,0 @@ -// Ensure optional arguments are treated separately from positional arguments - -fn f(x,y,z,a) { return -1 } - -print f(1,z=3) -// expect error 'InvldArgs' From 8195a66d48e5365db02d360fc845459edf084aeb Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Tue, 14 May 2024 09:49:49 -0400 Subject: [PATCH 102/181] Consolidate branch table generation --- src/classes/metafunction.c | 75 +++---------------- .../multiple_dispatch/inheritance.morpho | 8 +- test/types/multiple_dispatch/recursion.morpho | 16 ++-- 3 files changed, 29 insertions(+), 70 deletions(-) diff --git a/src/classes/metafunction.c b/src/classes/metafunction.c index 48e684a1..6afa5a82 100644 --- a/src/classes/metafunction.c +++ b/src/classes/metafunction.c @@ -179,9 +179,7 @@ DEFINE_VARRAY(mfinstruction, mfinstruction); #define MFINSTRUCTION_CHECKTYPE(op, n, t, brnch) { .opcode=op, .data.tindx=t, .narg=n, .branch=brnch } #define MFINSTRUCTION_BRANCH(brnch) { .opcode=MF_BRANCH, .branch=brnch } #define MFINSTRUCTION_BRANCHNARG(table, brnch) { .opcode=MF_BRANCHNARGS, .data.btable=table, .branch=brnch } -#define MFINSTRUCTION_BRANCHOBJECTTYPE(n, table, brnch) { .opcode=MF_BRANCHOBJECTTYPE, .narg=n, .data.btable=table, .branch=brnch } -#define MFINSTRUCTION_BRANCHVALUETYPE(n, table, brnch) { .opcode=MF_BRANCHVALUETYPE, .narg=n, .data.btable=table, .branch=brnch } -#define MFINSTRUCTION_BRANCHINSTANCE(n, table, brnch) { .opcode=MF_BRANCHINSTANCE, .narg=n, .data.btable=table, .branch=brnch } +#define MFINSTRUCTION_BRANCHTABLE(op, n, table, brnch) { .opcode=op, .narg=n, .data.btable=table, .branch=brnch } typedef struct { signature *sig; /** Signature of the target */ @@ -499,14 +497,13 @@ int _mfresultsortfn (const void *a, const void *b) { typedef mfindx (mfcompile_dispatchfn) (mfcompiler *c, mfset *set, int i); -/** Branch table on object type */ -mfindx mfcompile_dispatchveneerobj(mfcompiler *c, mfset *set, int i) { +mfindx mfcompile_dispatchtable(mfcompiler *c, mfset *set, int i, int otype, int opcode) { value type; // Extract the type index for each member of the set for (int k=0; kcount; k++) { if (!signature_getparamtype(set->rlist[k].sig, i, &type)) return MFINSTRUCTION_EMPTY; - if (_detecttype(type, &set->rlist[k].indx)!=MF_VENEEROBJECT) set->rlist[k].indx=-1; + if (_detecttype(type, &set->rlist[k].indx)!=otype) set->rlist[k].indx=-1; } // Sort the set on the type index @@ -519,7 +516,7 @@ mfindx mfcompile_dispatchveneerobj(mfcompiler *c, mfset *set, int i) { for (int i=0; i<=maxindx; i++) varray_intwrite(&btable, 0); // Insert the branch instruction - mfinstruction instr = MFINSTRUCTION_BRANCHOBJECTTYPE(i, btable, 0); + mfinstruction instr = MFINSTRUCTION_BRANCHTABLE(opcode, i, btable, 0); mfindx bindx = mfcompile_insertinstruction(c, instr); // Fail if an object type isn't in the table @@ -531,68 +528,20 @@ mfindx mfcompile_dispatchveneerobj(mfcompiler *c, mfset *set, int i) { return bindx; } + +/** Branch table on object type */ +mfindx mfcompile_dispatchveneerobj(mfcompiler *c, mfset *set, int i) { + return mfcompile_dispatchtable(c, set, i, MF_VENEEROBJECT, MF_BRANCHOBJECTTYPE); +} + /** Branch table on value type */ mfindx mfcompile_dispatchveneervalue(mfcompiler *c, mfset *set, int i) { - value type; - - // Extract the type index for each member of the set - for (int k=0; kcount; k++) { - if (!signature_getparamtype(set->rlist[k].sig, i, &type)) return MFINSTRUCTION_EMPTY; - if (_detecttype(type, &set->rlist[k].indx)!=MF_VENEERVALUE) set->rlist[k].indx=-1; - } - - // Sort the set on the type index - qsort(set->rlist, set->count, sizeof(mfresult), _mfresultsortfn); - - // Create the branch table - int maxindx=set->rlist[set->count-1].indx; - varray_int btable; - varray_intinit(&btable); - for (int i=0; i<=maxindx; i++) varray_intwrite(&btable, 0); - - // Insert the branch instruction - mfinstruction instr = MFINSTRUCTION_BRANCHVALUETYPE(i, btable, 0); - mfindx bindx = mfcompile_insertinstruction(c, instr); - - // Fail if an object type isn't in the table - mfcompile_fail(c); - - // Compile the branch table - mfcompile_branchtable(c, set, bindx, &btable); - - return bindx; + return mfcompile_dispatchtable(c, set, i, MF_VENEERVALUE, MF_BRANCHVALUETYPE); } /** Branch table on instance type */ mfindx mfcompile_dispatchinstance(mfcompiler *c, mfset *set, int i) { - value type; - - // Extract the type index for each member of the set - for (int k=0; kcount; k++) { - if (!signature_getparamtype(set->rlist[k].sig, i, &type)) return MFINSTRUCTION_EMPTY; - if (_detecttype(type, &set->rlist[k].indx)!=MF_INSTANCE) set->rlist[k].indx=-1; - } - - // Sort the set on the type index - qsort(set->rlist, set->count, sizeof(mfresult), _mfresultsortfn); - - // Create the branch table - int maxindx=set->rlist[set->count-1].indx; - varray_int btable; - varray_intinit(&btable); - for (int i=0; i<=maxindx; i++) varray_intwrite(&btable, 0); - - // Insert the branch instruction - mfinstruction instr = MFINSTRUCTION_BRANCHINSTANCE(i, btable, 0); - mfindx bindx = mfcompile_insertinstruction(c, instr); - - // Fail if an object type isn't in the table - mfcompile_fail(c); - - // Compile the branch table - mfcompile_branchtable(c, set, bindx, &btable); - - return bindx; + return mfcompile_dispatchtable(c, set, i, MF_INSTANCE, MF_BRANCHINSTANCE); } /** Handle implementations that accept any type */ diff --git a/test/types/multiple_dispatch/inheritance.morpho b/test/types/multiple_dispatch/inheritance.morpho index 38cac10b..86a560b6 100644 --- a/test/types/multiple_dispatch/inheritance.morpho +++ b/test/types/multiple_dispatch/inheritance.morpho @@ -6,7 +6,9 @@ class B is A {} class C is B {} -class D is Object with A { } +class D { } + +class E is D with A { } fn f(A x) { return "A" } @@ -18,4 +20,6 @@ print f(B()) // expect: B print f(C()) // expect: B -print f(D()) // expect: A +print f(E()) // expect: A + +print f(D()) // expect error 'MltplDsptchFld' diff --git a/test/types/multiple_dispatch/recursion.morpho b/test/types/multiple_dispatch/recursion.morpho index b2ff9e44..5a017435 100644 --- a/test/types/multiple_dispatch/recursion.morpho +++ b/test/types/multiple_dispatch/recursion.morpho @@ -1,11 +1,17 @@ // Multiple dispatch with recursion -fn f() { - return 0 +fn f(String x) { + print x } -fn f(x) { - return "V" +fn f(List x) { + if (x.count()>0) { + f(String(x.pop())) + f(x) + } } -print "Not written" \ No newline at end of file +f([1,2,3]) +// expect: 3 +// expect: 2 +// expect: 1 From c49918682e7a59d6bf93be6e1763e81bba19d10e Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Wed, 15 May 2024 10:49:02 -0400 Subject: [PATCH 103/181] Track parents and children in classes Track inheritance in types. --- src/classes/clss.c | 6 ++++++ src/classes/clss.h | 10 ++++++---- src/core/compile.c | 19 ++++++++++++++++++ test/types/type_inheritance.morpho | 21 ++++++++++++++++++++ test/types/type_violation_inheritance.morpho | 18 +++++++++++++++++ 5 files changed, 70 insertions(+), 4 deletions(-) create mode 100644 test/types/type_inheritance.morpho create mode 100644 test/types/type_violation_inheritance.morpho diff --git a/src/classes/clss.c b/src/classes/clss.c index 484f7fa2..c1e7bbeb 100644 --- a/src/classes/clss.c +++ b/src/classes/clss.c @@ -20,12 +20,16 @@ void objectclass_markfn(object *obj, void *v) { objectclass *c = (objectclass *) obj; morpho_markvalue(v, c->name); morpho_markdictionary(v, &c->methods); + morpho_markvarrayvalue(v, &c->parents); + morpho_markvarrayvalue(v, &c->children); } void objectclass_freefn(object *obj) { objectclass *klass = (objectclass *) obj; morpho_freeobject(klass->name); dictionary_clear(&klass->methods); + varray_valueclear(&klass->parents); + varray_valueclear(&klass->children); } size_t objectclass_sizefn(object *obj) { @@ -47,6 +51,8 @@ objectclass *object_newclass(value name) { if (newclass) { newclass->name=object_clonestring(name); dictionary_init(&newclass->methods); + varray_valueinit(&newclass->parents); + varray_valueinit(&newclass->children); newclass->superclass=NULL; newclass->uid=0; } diff --git a/src/classes/clss.h b/src/classes/clss.h index e8a32780..057e27c3 100644 --- a/src/classes/clss.h +++ b/src/classes/clss.h @@ -18,10 +18,12 @@ extern objecttype objectclasstype; typedef struct sobjectclass { object obj; - struct sobjectclass *superclass; - value name; - dictionary methods; - int uid; + struct sobjectclass *superclass; /** The class's superclass */ + value name; /** Class name */ + dictionary methods; /** Method dictionary */ + varray_value parents; /** Classes this class inherits from */ + varray_value children; /** Classes that inherit from this class */ + int uid; } objectclass; /** Tests whether an object is a class */ diff --git a/src/core/compile.c b/src/core/compile.c index e01535b2..0aa78b58 100644 --- a/src/core/compile.c +++ b/src/core/compile.c @@ -287,6 +287,12 @@ objectclass *compiler_findclass(compiler *c, value name) { return NULL; } +/** Adds a class to a class's parent list, and also links the class into the parent's child list */ +void compiler_addparent(compiler *c, objectclass *klass, objectclass *parent) { + varray_valuewrite(&klass->parents, MORPHO_OBJECT(parent)); + varray_valuewrite(&parent->children, MORPHO_OBJECT(klass)); +} + /* ------------------------------------------ * Types * ------------------------------------------- */ @@ -316,11 +322,23 @@ bool compiler_typefromvalue(compiler *c, value v, value *out) { return metafunction_typefromvalue(v, out); } +/** Recursively searches the parents list of classes to see if the type 'match' is present */ +bool compiler_findtypeinparent(compiler *c, objectclass *type, value match) { + for (int i=0; iparents.count; i++) { + if (MORPHO_ISEQUAL(type->parents.data[i], match) || + compiler_findtypeinparent(c, MORPHO_GETCLASS(type->parents.data[i]), match)) return true; + } + return false; +} + /** Checks if type "match" matches a given type "type" */ bool compiler_checktype(compiler *c, value type, value match) { if (MORPHO_ISNIL(type) || // If type is unset, we always match MORPHO_ISEQUAL(type, match)) return true; // Or if the types are the same + // Also match if 'match' inherits from 'type' + if (MORPHO_ISCLASS(match)) return compiler_findtypeinparent(c, MORPHO_GETCLASS(match), type); + return false; } @@ -3315,6 +3333,7 @@ static codeinfo compiler_class(compiler *c, syntaxtreenode *node, registerindx r if (superclass) { if (superclass!=klass) { if (!klass->superclass) klass->superclass=superclass; // Only the first class is the super class, all others are mixins. + compiler_addparent(c, klass, superclass); dictionary_copy(&superclass->methods, &klass->methods); } else { compiler_error(c, snode, COMPILE_CLASSINHERITSELF); diff --git a/test/types/type_inheritance.morpho b/test/types/type_inheritance.morpho new file mode 100644 index 00000000..37eff54c --- /dev/null +++ b/test/types/type_inheritance.morpho @@ -0,0 +1,21 @@ +// Types and inheritance + +class A { } + +class B is A { } + +class C is B { } + +class D is A with B { } + +fn f() { + A a = B() + A x = C() + B y = D() + + print a // expect: + print x // expect: + print y // expect: +} + +f() diff --git a/test/types/type_violation_inheritance.morpho b/test/types/type_violation_inheritance.morpho new file mode 100644 index 00000000..7701f3ce --- /dev/null +++ b/test/types/type_violation_inheritance.morpho @@ -0,0 +1,18 @@ +// Types and inheritance + +class A { } + +class B is A { } + +class C is B { } + +class D is A { } + +fn f() { + A a = B() + A x = C() + B y = C() + B z = D() // expect error 'TypeErr' +} + +f() From b2a77f3934990ac19545ca844d48e403e5e59b7e Mon Sep 17 00:00:00 2001 From: Tim Atherton <54725421+softmattertheory@users.noreply.github.com> Date: Fri, 17 May 2024 15:07:17 -0400 Subject: [PATCH 104/181] Linearization --- src/classes/clss.c | 38 +++++++++++++++++++++++++++++++++++--- src/classes/clss.h | 3 +++ src/core/compile.c | 5 +++++ 3 files changed, 43 insertions(+), 3 deletions(-) diff --git a/src/classes/clss.c b/src/classes/clss.c index c1e7bbeb..379c4fa4 100644 --- a/src/classes/clss.c +++ b/src/classes/clss.c @@ -30,6 +30,7 @@ void objectclass_freefn(object *obj) { dictionary_clear(&klass->methods); varray_valueclear(&klass->parents); varray_valueclear(&klass->children); + varray_valueclear(&klass->linearization); } size_t objectclass_sizefn(object *obj) { @@ -64,10 +65,39 @@ objectclass *object_newclass(value name) { * objectclass utility functions * ********************************************************************** */ +bool _merge(varray_value *klasses, varray_value *out) { + +} + +bool _linearize(objectclass *klass, varray_value *out) { + varray_valuewrite(out, MORPHO_OBJECT(out)); + _merge(&klass->parents, out); +} + +/** Computes the linearization of a class */ +bool class_linearize(objectclass *klass) { + klass->linearization.count=0; + + return _linearize(klass, &klass->linearization); +} + /* ********************************************************************** * (Future) Class veneer class * ********************************************************************** */ +value Class_linearize(vm *v, int nargs, value *args) { + objectclass *klass=MORPHO_GETCLASS(MORPHO_SELF(args)); + value out = MORPHO_NIL; + + + + return out; +} + +MORPHO_BEGINCLASS(Class) +MORPHO_METHOD(MORPHO_TOSTRING_METHOD, Class_linearize, BUILTIN_FLAGSEMPTY) +MORPHO_ENDCLASS + /* ********************************************************************** * Initialization and finalization * ********************************************************************** */ @@ -77,10 +107,12 @@ objecttype objectclasstype; void class_initialize(void) { // objectclass is a core type so is intialized earlier - // TODO: Add Class veneer class // Locate the Object class to use as the parent class of Class - //objectstring objname = MORPHO_STATICSTRING(OBJECT_CLASSNAME); - //value objclass = builtin_findclass(MORPHO_OBJECT(&objname)); + objectstring objname = MORPHO_STATICSTRING(OBJECT_CLASSNAME); + value objclass = builtin_findclass(MORPHO_OBJECT(&objname)); + + value classclass=builtin_addclass(CLASS_CLASSNAME, MORPHO_GETCLASSDEFINITION(Class), objclass); + object_setveneerclass(OBJECT_CLASS, classclass); // No constructor function; classes are generated by the compiler diff --git a/src/classes/clss.h b/src/classes/clss.h index 057e27c3..6b7b1a26 100644 --- a/src/classes/clss.h +++ b/src/classes/clss.h @@ -23,6 +23,7 @@ typedef struct sobjectclass { dictionary methods; /** Method dictionary */ varray_value parents; /** Classes this class inherits from */ varray_value children; /** Classes that inherit from this class */ + varray_value linearization; /** Classes that inherit from this class */ int uid; } objectclass; @@ -55,6 +56,8 @@ typedef struct sobjectclass { objectclass *object_newclass(value name); objectclass *morpho_lookupclass(value obj); +bool class_linearize(objectclass *klass); + void class_initialize(void); #endif diff --git a/src/core/compile.c b/src/core/compile.c index 0aa78b58..50d16db1 100644 --- a/src/core/compile.c +++ b/src/core/compile.c @@ -3350,6 +3350,11 @@ static codeinfo compiler_class(compiler *c, syntaxtreenode *node, registerindx r klass->superclass=baseclass; if (baseclass) dictionary_copy(&baseclass->methods, &klass->methods); } + + /* Now compute the class linearization */ + if (!class_linearize(klass)) { + // TODO: Raise an error + } /* Compile method declarations */ if (node->right!=SYNTAXTREE_UNCONNECTED) { From 210caaa6744bcad601077b56b2c14bbe58e36b64 Mon Sep 17 00:00:00 2001 From: Tim Atherton <54725421+softmattertheory@users.noreply.github.com> Date: Sat, 18 May 2024 11:50:28 -0400 Subject: [PATCH 105/181] C3 Linearization algorithm --- src/classes/clss.c | 103 +++++++++++++++++++++++++------- src/classes/clss.h | 3 +- src/classes/instance.c | 22 ++++++- src/morpho.h | 1 + test/class/linearization.morpho | 23 +++++++ test/object/responds_to.morpho | 4 +- 6 files changed, 132 insertions(+), 24 deletions(-) create mode 100644 test/class/linearization.morpho diff --git a/src/classes/clss.c b/src/classes/clss.c index 379c4fa4..e8a769ef 100644 --- a/src/classes/clss.c +++ b/src/classes/clss.c @@ -65,38 +65,101 @@ objectclass *object_newclass(value name) { * objectclass utility functions * ********************************************************************** */ -bool _merge(varray_value *klasses, varray_value *out) { - +void _print(varray_value *list) { + printf("[ "); + for (int i=0; icount; i++) { + morpho_printvalue(NULL, list->data[i]); + if (icount-1) printf(", "); + } + printf(" ]"); +} + +/** Check if value v is in the tail of a list? */ +bool _intail(varray_value *list, value v) { + for (int i=1; icount; i++) { + if (MORPHO_ISEQUAL(list->data[i], v)) return true; + } + return false; +} + +/** Remove value v from the list in */ +void _remove(varray_value *in, value v) { + for (int i=0; icount; i++) { + if (MORPHO_ISEQUAL(in->data[i], v)) { + memcpy(in->data+i, in->data+i+1, sizeof(value)*in->count-i-1); + in->count--; + } + } +} + +/** Check if value v is in any tail of the set of lists */ +bool _inanytail(int n, varray_value *in, value v) { + for (int i=0; icount>0) return false; + return true; +} + +/** Performs one C3 merge operation for a set of lists */ +bool _merge(int n, varray_value *in, varray_value *out) { + for (int i=0; ilinearization.count) varray_valueadd(out, parent->linearization.data, parent->linearization.count); } bool _linearize(objectclass *klass, varray_value *out) { - varray_valuewrite(out, MORPHO_OBJECT(out)); - _merge(&klass->parents, out); + // Add this class to the start of the list + varray_valuewrite(out, MORPHO_OBJECT(klass)); + + int n = klass->parents.count; + if (n==0) return true; + + // Start with the linearizations of the parent classes + varray_value lin[n]; + for (int i=0; iparents.data[i]), &lin[i]); + + bool success=true; + while (success && !_done(n, lin)) { + success=_merge(n, lin, out); + } + + for (int i=0; ilinearization.count=0; - return _linearize(klass, &klass->linearization); } /* ********************************************************************** - * (Future) Class veneer class + * (Possible future) Class veneer class * ********************************************************************** */ -value Class_linearize(vm *v, int nargs, value *args) { - objectclass *klass=MORPHO_GETCLASS(MORPHO_SELF(args)); - value out = MORPHO_NIL; - - - - return out; -} - -MORPHO_BEGINCLASS(Class) -MORPHO_METHOD(MORPHO_TOSTRING_METHOD, Class_linearize, BUILTIN_FLAGSEMPTY) -MORPHO_ENDCLASS +//MORPHO_BEGINCLASS(Class) +//MORPHO_ENDCLASS /* ********************************************************************** * Initialization and finalization @@ -108,11 +171,11 @@ void class_initialize(void) { // objectclass is a core type so is intialized earlier // Locate the Object class to use as the parent class of Class - objectstring objname = MORPHO_STATICSTRING(OBJECT_CLASSNAME); + /*objectstring objname = MORPHO_STATICSTRING(OBJECT_CLASSNAME); value objclass = builtin_findclass(MORPHO_OBJECT(&objname)); value classclass=builtin_addclass(CLASS_CLASSNAME, MORPHO_GETCLASSDEFINITION(Class), objclass); - object_setveneerclass(OBJECT_CLASS, classclass); + object_setveneerclass(OBJECT_CLASS, classclass);*/ // No constructor function; classes are generated by the compiler diff --git a/src/classes/clss.h b/src/classes/clss.h index 6b7b1a26..00196bb4 100644 --- a/src/classes/clss.h +++ b/src/classes/clss.h @@ -40,7 +40,8 @@ typedef struct sobjectclass { * Class veneer class * ------------------------------------------------------- */ -#define CLASS_CLASSNAME "Class" +// #define CLASS_CLASSNAME "Class" +// #define CLASS_LINEARIZATION "linearization" /* ------------------------------------------------------- * Class error messages diff --git a/src/classes/instance.c b/src/classes/instance.c index 7b369ddb..a1ac1036 100644 --- a/src/classes/instance.c +++ b/src/classes/instance.c @@ -302,6 +302,25 @@ value Object_clone(vm *v, int nargs, value *args) { return out; } +value Object_linearization(vm *v, int nargs, value *args) { + value slf=MORPHO_SELF(args); + objectclass *klass=NULL; + + if (MORPHO_ISCLASS(slf)) klass=MORPHO_GETCLASS(slf); + else if (MORPHO_ISINSTANCE(slf)) klass=MORPHO_GETINSTANCE(slf)->klass; + else return MORPHO_NIL; + + value out = MORPHO_NIL; + + objectlist *new = object_newlist(klass->linearization.count, klass->linearization.data); + if (new) { + out = MORPHO_OBJECT(new); + morpho_bindobjects(v, 1, &out); + } + + return out; +} + MORPHO_BEGINCLASS(Object) MORPHO_METHOD(MORPHO_GETINDEX_METHOD, Object_getindex, BUILTIN_FLAGSEMPTY), MORPHO_METHOD(MORPHO_SETINDEX_METHOD, Object_setindex, BUILTIN_FLAGSEMPTY), @@ -314,7 +333,8 @@ MORPHO_METHOD(MORPHO_PRINT_METHOD, Object_print, BUILTIN_FLAGSEMPTY), MORPHO_METHOD(MORPHO_COUNT_METHOD, Object_count, BUILTIN_FLAGSEMPTY), MORPHO_METHOD(MORPHO_ENUMERATE_METHOD, Object_enumerate, BUILTIN_FLAGSEMPTY), MORPHO_METHOD(MORPHO_SERIALIZE_METHOD, Object_serialize, BUILTIN_FLAGSEMPTY), -MORPHO_METHOD(MORPHO_CLONE_METHOD, Object_clone, BUILTIN_FLAGSEMPTY) +MORPHO_METHOD(MORPHO_CLONE_METHOD, Object_clone, BUILTIN_FLAGSEMPTY), +MORPHO_METHOD(MORPHO_LINEARIZATION_METHOD, Object_linearization, BUILTIN_FLAGSEMPTY) MORPHO_ENDCLASS /* ********************************************************************** diff --git a/src/morpho.h b/src/morpho.h index 986fac1d..c3ad2307 100644 --- a/src/morpho.h +++ b/src/morpho.h @@ -79,6 +79,7 @@ typedef void compiler; /* Non-standard methods */ #define MORPHO_APPEND_METHOD "append" +#define MORPHO_LINEARIZATION_METHOD "linearization" #define MORPHO_THROW_METHOD "throw" #define MORPHO_WARNING_METHOD "warning" diff --git a/test/class/linearization.morpho b/test/class/linearization.morpho new file mode 100644 index 00000000..7bd29ad6 --- /dev/null +++ b/test/class/linearization.morpho @@ -0,0 +1,23 @@ +// Class linearization + +class O { } + +class F is O { } + +class E is O { } + +class D is O { } + +class C is D with F { } + +class B is D with E { } + +class A is B with C { } + +print O.linearization() // expect: [ @O ] +print F.linearization() // expect: [ @F, @O ] +print E.linearization() // expect: [ @E, @O ] +print D.linearization() // expect: [ @D, @O ] +print C.linearization() // expect: [ @C, @D, @F, @O ] +print B.linearization() // expect: [ @B, @D, @E, @O ] +print A.linearization() // expect: [ @A, @B, @C, @D, @E, @F, @O ] diff --git a/test/object/responds_to.morpho b/test/object/responds_to.morpho index c3508b20..5565364f 100644 --- a/test/object/responds_to.morpho +++ b/test/object/responds_to.morpho @@ -11,7 +11,7 @@ print a.respondsto("squiggle") var r = a.respondsto() r.sort() print r -// expect: [ clone, clss, count, enumerate, has, index, invoke, prnt, respondsto, serialize, setindex, superclass ] +// expect: [ clone, clss, count, enumerate, has, index, invoke, linearization, prnt, respondsto, serialize, setindex, superclass ] class bar { method1(){ 1+1 @@ -22,7 +22,7 @@ var b = bar() r = b.respondsto() r.sort() print r -// expect: [ clone, clss, count, enumerate, has, index, invoke, method1, prnt, respondsto, serialize, setindex, superclass ] +// expect: [ clone, clss, count, enumerate, has, index, invoke, linearization, method1, prnt, respondsto, serialize, setindex, superclass ] print a.respondsto("squiggle","foo") From 38fd75fdf064ff1b109ae118bdfb67d32ec328ec Mon Sep 17 00:00:00 2001 From: Tim Atherton <54725421+softmattertheory@users.noreply.github.com> Date: Sat, 18 May 2024 16:29:55 -0400 Subject: [PATCH 106/181] Relabel _remove --- src/classes/clss.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/classes/clss.c b/src/classes/clss.c index e8a769ef..791a1925 100644 --- a/src/classes/clss.c +++ b/src/classes/clss.c @@ -82,12 +82,12 @@ bool _intail(varray_value *list, value v) { return false; } -/** Remove value v from the list in */ -void _remove(varray_value *in, value v) { - for (int i=0; icount; i++) { - if (MORPHO_ISEQUAL(in->data[i], v)) { - memcpy(in->data+i, in->data+i+1, sizeof(value)*in->count-i-1); - in->count--; +/** Remove value v from a list in */ +void _remove(varray_value *list, value v) { + for (int i=0; icount; i++) { + if (MORPHO_ISEQUAL(list->data[i], v)) { + memcpy(list->data+i, list->data+i+1, sizeof(value)*list->count-i-1); + list->count--; } } } From e852397da6342484aadb55ae31c5b891143df41d Mon Sep 17 00:00:00 2001 From: Tim Atherton <54725421+softmattertheory@users.noreply.github.com> Date: Sat, 18 May 2024 16:37:54 -0400 Subject: [PATCH 107/181] Some notes on C3 linearization --- src/classes/clss.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/classes/clss.c b/src/classes/clss.c index 791a1925..048e12c5 100644 --- a/src/classes/clss.c +++ b/src/classes/clss.c @@ -62,9 +62,19 @@ objectclass *object_newclass(value name) { } /* ********************************************************************** - * objectclass utility functions + * C3 Linearization algorithm * ********************************************************************** */ +/** C3 linearization aims to provide a linear ordering for a class hierarchy. It respects: + + * 1. Consistency with the hierarchy of classes (i.e. a class should appear AFTER any of its children). + * 2. Consistency with the local precedence order for each class definition. + * 3. Consistency with the extended precedence graph. + + * see: - Barrett et al. "A Monotonic Superclass Linearization for Dylan" [https://opendylan.org/_static/c3-linearization.pdf] + * - Simionato, "The Python 2.3 Method Resolution Order" Python 2.3 [https://www.python.org/download/releases/2.3/mro/] + * - Hivert & Thierry "Controlling the C3 super class linearization algorithm for large hierarchies of classes" [https://arxiv.org/pdf/2401.12740] */ + void _print(varray_value *list) { printf("[ "); for (int i=0; icount; i++) { @@ -126,6 +136,7 @@ void _init(objectclass *parent, varray_value *out) { if (parent->linearization.count) varray_valueadd(out, parent->linearization.data, parent->linearization.count); } +/** Compute the linearization of a given class */ bool _linearize(objectclass *klass, varray_value *out) { // Add this class to the start of the list varray_valuewrite(out, MORPHO_OBJECT(klass)); @@ -148,7 +159,7 @@ bool _linearize(objectclass *klass, varray_value *out) { return success; } -/** Computes the linearization of a class */ +/** Public wrapper function to computer linearization */ bool class_linearize(objectclass *klass) { klass->linearization.count=0; return _linearize(klass, &klass->linearization); From ba0d88b9a3fde76379d5dba9793ac106e5e69f9f Mon Sep 17 00:00:00 2001 From: Tim Atherton <54725421+softmattertheory@users.noreply.github.com> Date: Sun, 19 May 2024 07:55:03 -0400 Subject: [PATCH 108/181] Include parent list in C3 --- src/classes/clss.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/classes/clss.c b/src/classes/clss.c index 048e12c5..730f8ede 100644 --- a/src/classes/clss.c +++ b/src/classes/clss.c @@ -144,17 +144,18 @@ bool _linearize(objectclass *klass, varray_value *out) { int n = klass->parents.count; if (n==0) return true; - // Start with the linearizations of the parent classes - varray_value lin[n]; - for (int i=0; iparents.data[i]), &lin[i]); + varray_valueadd(&lin[n], klass->parents.data, klass->parents.count); // Also add the parents to preserve their order bool success=true; while (success && !_done(n, lin)) { success=_merge(n, lin, out); } - for (int i=0; i Date: Sun, 19 May 2024 12:59:41 -0400 Subject: [PATCH 109/181] Fix incorrect move size --- src/classes/clss.c | 5 +++-- src/classes/metafunction.c | 31 ++++++++++++++++++++++++++++++- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/src/classes/clss.c b/src/classes/clss.c index 730f8ede..885a6161 100644 --- a/src/classes/clss.c +++ b/src/classes/clss.c @@ -54,6 +54,7 @@ objectclass *object_newclass(value name) { dictionary_init(&newclass->methods); varray_valueinit(&newclass->parents); varray_valueinit(&newclass->children); + varray_valueinit(&newclass->linearization); newclass->superclass=NULL; newclass->uid=0; } @@ -96,7 +97,7 @@ bool _intail(varray_value *list, value v) { void _remove(varray_value *list, value v) { for (int i=0; icount; i++) { if (MORPHO_ISEQUAL(list->data[i], v)) { - memcpy(list->data+i, list->data+i+1, sizeof(value)*list->count-i-1); + if (icount-1) memmove(list->data+i, list->data+i+1, sizeof(value)*(list->count-i-1)); list->count--; } } @@ -160,7 +161,7 @@ bool _linearize(objectclass *klass, varray_value *out) { return success; } -/** Public wrapper function to computer linearization */ +/** Public wrapper function to compute linearization */ bool class_linearize(objectclass *klass) { klass->linearization.count=0; return _linearize(klass, &klass->linearization); diff --git a/src/classes/metafunction.c b/src/classes/metafunction.c index 6afa5a82..52efe2b4 100644 --- a/src/classes/metafunction.c +++ b/src/classes/metafunction.c @@ -497,6 +497,7 @@ int _mfresultsortfn (const void *a, const void *b) { typedef mfindx (mfcompile_dispatchfn) (mfcompiler *c, mfset *set, int i); +/** Constructs a dispatch table from the set of implementations */ mfindx mfcompile_dispatchtable(mfcompiler *c, mfset *set, int i, int otype, int opcode) { value type; @@ -528,7 +529,6 @@ mfindx mfcompile_dispatchtable(mfcompiler *c, mfset *set, int i, int otype, int return bindx; } - /** Branch table on object type */ mfindx mfcompile_dispatchveneerobj(mfcompiler *c, mfset *set, int i) { return mfcompile_dispatchtable(c, set, i, MF_VENEEROBJECT, MF_BRANCHOBJECTTYPE); @@ -539,11 +539,40 @@ mfindx mfcompile_dispatchveneervalue(mfcompiler *c, mfset *set, int i) { return mfcompile_dispatchtable(c, set, i, MF_VENEERVALUE, MF_BRANCHVALUETYPE); } +void mfcompile_inheritance(mfcompiler *c, mfset *set, int i, int otype) { + dictionary children; + dictionary_init(&children); + + // Find children of every member of the set + for (int k=0; kcount; k++) { + value type; + if (!signature_getparamtype(set->rlist[k].sig, i, &type)) return; + if (_detecttype(type, NULL)!=otype) continue; + + if (MORPHO_ISCLASS(type)) { + objectclass *klass = MORPHO_GETCLASS(type); + for (int i=0; ichildren.count; i++) dictionary_insert(&children, type, MORPHO_NIL); + } + } + + // For each child class + for (int i=0; icount]; From 7d059a4593aa929ef50b3009ba24b0546946136c Mon Sep 17 00:00:00 2001 From: Tim Atherton <54725421+softmattertheory@users.noreply.github.com> Date: Sun, 19 May 2024 20:21:45 -0400 Subject: [PATCH 110/181] Working version of inheritance --- src/classes/clss.c | 16 ++-- src/classes/metafunction.c | 92 ++++++++++++------- .../multiple_dispatch/inheritance.morpho | 14 ++- 3 files changed, 75 insertions(+), 47 deletions(-) diff --git a/src/classes/clss.c b/src/classes/clss.c index 885a6161..941dba5f 100644 --- a/src/classes/clss.c +++ b/src/classes/clss.c @@ -113,7 +113,7 @@ bool _inanytail(int n, varray_value *in, value v) { /** Check if any of the sets contain elements */ bool _done(int n, varray_value *in) { - for (int i=0; icount>0) return false; + for (int i=0; i0) return false; return true; } @@ -142,21 +142,21 @@ bool _linearize(objectclass *klass, varray_value *out) { // Add this class to the start of the list varray_valuewrite(out, MORPHO_OBJECT(klass)); - int n = klass->parents.count; - if (n==0) return true; + if (klass->parents.count==0) return true; + int n=klass->parents.count+1; // Start with the linearizations of the parent classes & the list of parent classes themselves - varray_value lin[n+1]; - for (int i=0; iparents.data[i]), &lin[i]); - varray_valueadd(&lin[n], klass->parents.data, klass->parents.count); // Also add the parents to preserve their order + varray_value lin[n]; + for (int i=0; iparents.data[i]), &lin[i]); + varray_valueadd(&lin[n-1], klass->parents.data, klass->parents.count); // Also add the parents to preserve their order bool success=true; while (success && !_done(n, lin)) { success=_merge(n, lin, out); } - for (int i=0; ichildren.count; i++) _insertchildren(dict, klass->children.data[i]); +} + +bool _resolve(objectclass *klass, dictionary *types, value *out) { + for (int k=0; klinearization.count; k++) { + if (dictionary_get(types, klass->linearization.data[k], NULL)) { + *out = klass->linearization.data[k]; + return true; + } + } + return false; +} + +int _maxindx(dictionary *dict) { + int indx, maxindx=0; + for (int i=0; icapacity; i++) { + if (_detecttype(dict->contents[i].key, &indx) && + indx>maxindx) { + maxindx=indx; + } + } + return maxindx; +} + int _mfresultsortfn (const void *a, const void *b) { mfresult *aa = (mfresult *) a, *bb = (mfresult *) b; return aa->indx-bb->indx; } -typedef mfindx (mfcompile_dispatchfn) (mfcompiler *c, mfset *set, int i); - /** Constructs a dispatch table from the set of implementations */ mfindx mfcompile_dispatchtable(mfcompiler *c, mfset *set, int i, int otype, int opcode) { - value type; + dictionary types, children; + dictionary_init(&types); // Keep track of the available types provided by the implementation + dictionary_init(&children); // and all of their children // Extract the type index for each member of the set for (int k=0; kcount; k++) { - if (!signature_getparamtype(set->rlist[k].sig, i, &type)) return MFINSTRUCTION_EMPTY; - if (_detecttype(type, &set->rlist[k].indx)!=otype) set->rlist[k].indx=-1; + value type; + if (!signature_getparamtype(set->rlist[k].sig, i, &type)) UNREACHABLE("Incorrect parameter type"); + if (_detecttype(type, &set->rlist[k].indx)==otype) { + dictionary_insert(&types, type, MORPHO_NIL); + _insertchildren(&children, type); + } else set->rlist[k].indx=-1; // Exclude from the branch table } // Sort the set on the type index qsort(set->rlist, set->count, sizeof(mfresult), _mfresultsortfn); // Create the branch table - int maxindx=set->rlist[set->count-1].indx; + int maxindx=_maxindx(&children); varray_int btable; varray_intinit(&btable); for (int i=0; i<=maxindx; i++) varray_intwrite(&btable, 0); @@ -526,6 +560,21 @@ mfindx mfcompile_dispatchtable(mfcompiler *c, mfset *set, int i, int otype, int // Compile the branch table mfcompile_branchtable(c, set, bindx, &btable); + // Fix branch table to include child classes + for (int i=0; icount; k++) { - value type; - if (!signature_getparamtype(set->rlist[k].sig, i, &type)) return; - if (_detecttype(type, NULL)!=otype) continue; - - if (MORPHO_ISCLASS(type)) { - objectclass *klass = MORPHO_GETCLASS(type); - for (int i=0; ichildren.count; i++) dictionary_insert(&children, type, MORPHO_NIL); - } - } - - // For each child class - for (int i=0; icount]; @@ -601,6 +621,8 @@ void mfcompile_fixfallthrough(mfcompiler *c, mfindx i, mfindx branchto) { mfcompile_replaceinstruction(c, i, instr); } +typedef mfindx (mfcompile_dispatchfn) (mfcompiler *c, mfset *set, int i); + /** Attempts to dispatch based on a parameter i */ mfindx mfcompile_dispatchonparam(mfcompiler *c, mfset *set, int i) { mfcompiler_pushcheck(c, i); diff --git a/test/types/multiple_dispatch/inheritance.morpho b/test/types/multiple_dispatch/inheritance.morpho index 86a560b6..f1b53b31 100644 --- a/test/types/multiple_dispatch/inheritance.morpho +++ b/test/types/multiple_dispatch/inheritance.morpho @@ -8,13 +8,17 @@ class C is B {} class D { } -class E is D with A { } +class E is A with D { } + +class F { } fn f(A x) { return "A" } -fn b(B x) { return "B" } +fn f(B x) { return "B" } + +fn f(D x) { return "D" } -print f(A()) // expect: A +print f(A()) // expect: A print f(B()) // expect: B @@ -22,4 +26,6 @@ print f(C()) // expect: B print f(E()) // expect: A -print f(D()) // expect error 'MltplDsptchFld' +print f(D()) // expect: D + +print f(F()) // expect error 'MltplDsptchFld' From 8f9ab99ffebf839bdb726fe39684c7a73c8f2fe4 Mon Sep 17 00:00:00 2001 From: Tim Atherton <54725421+softmattertheory@users.noreply.github.com> Date: Sun, 19 May 2024 21:01:00 -0400 Subject: [PATCH 111/181] Ensure branch table is correctly sized --- src/classes/metafunction.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/classes/metafunction.c b/src/classes/metafunction.c index 88070555..14cfa126 100644 --- a/src/classes/metafunction.c +++ b/src/classes/metafunction.c @@ -510,12 +510,10 @@ bool _resolve(objectclass *klass, dictionary *types, value *out) { } int _maxindx(dictionary *dict) { - int indx, maxindx=0; + int indx=0, maxindx=0; for (int i=0; icapacity; i++) { - if (_detecttype(dict->contents[i].key, &indx) && - indx>maxindx) { - maxindx=indx; - } + _detecttype(dict->contents[i].key, &indx); + if (indx>maxindx) maxindx=indx; } return maxindx; } From cd7e8246a4f3bfa48b8d8256835e00552b693327 Mon Sep 17 00:00:00 2001 From: Tim Atherton <54725421+softmattertheory@users.noreply.github.com> Date: Sun, 19 May 2024 21:11:27 -0400 Subject: [PATCH 112/181] Minor fixes to resolve analyzer issues --- src/classes/array.c | 3 +-- src/classes/json.c | 3 +-- src/support/extensions.c | 3 --- src/support/lex.c | 1 - src/support/resources.c | 2 +- src/support/threadpool.c | 2 +- 6 files changed, 4 insertions(+), 10 deletions(-) diff --git a/src/classes/array.c b/src/classes/array.c index 81829922..8e00a396 100644 --- a/src/classes/array.c +++ b/src/classes/array.c @@ -604,8 +604,7 @@ void array_initialize(void) { value objclass = builtin_findclass(MORPHO_OBJECT(&objname)); // Array constructor function - value cons = builtin_addfunction(ARRAY_CLASSNAME, array_constructor, MORPHO_FN_CONSTRUCTOR); - //builtin_setsignature(cons, "Array (List,String,_,...)"); + builtin_addfunction(ARRAY_CLASSNAME, array_constructor, MORPHO_FN_CONSTRUCTOR); // Create Array veneer class value arrayclass=builtin_addclass(ARRAY_CLASSNAME, MORPHO_GETCLASSDEFINITION(Array), objclass); diff --git a/src/classes/json.c b/src/classes/json.c index 44dcf3be..838c5d28 100644 --- a/src/classes/json.c +++ b/src/classes/json.c @@ -102,7 +102,7 @@ bool json_lexstring(lexer *l, token *tok, error *err) { /** Record JSON numbers as a token */ bool json_lexnumber(lexer *l, token *tok, error *err) { - bool hasdot=false, hasexp=false; + bool hasexp=false; tokentype type = JSON_NUMBER; // Detect if we are missing digits (ie an isolated '-') @@ -120,7 +120,6 @@ bool json_lexnumber(lexer *l, token *tok, error *err) { // Detect fractional separator if (lex_peek(l)=='.') { lex_advance(l); - hasdot=true; type = JSON_FLOAT; // Digits are required after fractional separator diff --git a/src/support/extensions.c b/src/support/extensions.c index 0748552b..9ae148a7 100644 --- a/src/support/extensions.c +++ b/src/support/extensions.c @@ -138,9 +138,6 @@ bool extension_initialize(extension *e) { dictionary *ofunc=builtin_getfunctiontable(), *oclss=builtin_getclasstable(); - dictionary *fntable = MORPHO_GETDICTIONARYSTRUCT(e->functiontable); - dictionary *clsstable = MORPHO_GETDICTIONARYSTRUCT(e->classtable); - builtin_setfunctiontable(MORPHO_GETDICTIONARYSTRUCT(e->functiontable)); builtin_setclasstable(MORPHO_GETDICTIONARYSTRUCT(e->classtable)); diff --git a/src/support/lex.c b/src/support/lex.c index b80115c3..8c93bbd0 100644 --- a/src/support/lex.c +++ b/src/support/lex.c @@ -87,7 +87,6 @@ char lex_advance(lexer *l) { /** @brief Advances the lexer by n characters, returning the last character */ char lex_advanceby(lexer *l, size_t n) { - char c = *(l->current); l->current+=n; l->posn+=n; return *(l->current-1); diff --git a/src/support/resources.c b/src/support/resources.c index 1a0f92b9..4a323db4 100644 --- a/src/support/resources.c +++ b/src/support/resources.c @@ -190,7 +190,7 @@ void resources_loadpackagelist(void) { varray_charinit(&line); char *home = getenv("HOME"); - varray_charadd(&line, home, (int) strlen(home)); + if (home) varray_charadd(&line, home, (int) strlen(home)); varray_charwrite(&line, MORPHO_SEPARATOR); varray_charadd(&line, MORPHO_PACKAGELIST, (int) strlen(MORPHO_PACKAGELIST)); varray_charwrite(&line, '\0'); diff --git a/src/support/threadpool.c b/src/support/threadpool.c index 1912ff53..2cd951e2 100644 --- a/src/support/threadpool.c +++ b/src/support/threadpool.c @@ -110,7 +110,7 @@ bool threadpool_add_task(threadpool *pool, workfn func, void *arg) { pthread_cond_broadcast(&pool->work_available_cond); /* Signal there is work to be done */ pthread_mutex_unlock(&pool->lock_mutex); - return true; + return success; } /** Blocks until all tasks in the thread pool are complete */ From 28bad550d217eb37e52da46aea134c2dd5beae74 Mon Sep 17 00:00:00 2001 From: Tim Atherton <54725421+softmattertheory@users.noreply.github.com> Date: Mon, 20 May 2024 07:15:43 -0400 Subject: [PATCH 113/181] Add help on multiple dispatch --- help/functions.md | 64 +++++++++++++++++++ src/classes/json.c | 1 - .../inheritance_single.morpho | 15 +++++ 3 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 test/types/multiple_dispatch/inheritance_single.morpho diff --git a/help/functions.md b/help/functions.md index 75469c37..8ee88012 100644 --- a/help/functions.md +++ b/help/functions.md @@ -18,6 +18,24 @@ Once a function has been defined you can evaluate it like any other morpho funct print sqr(2) +Functions can accept optional parameters: + + fn fun(x, quiet=true ) { if (!quiet) print "Loud!" } + +Morpho functions can also be defined to restrict the type of accepted parameters: + + fn f(List x) { print "A list!" } + +Multiple implementations can be defined that accept different numbers of parameters and parameter types: + + fn f() { print "No parameters!" } + fn f(String x) { print "A string!" } + fn f(Tuple x) { print "A tuple!" } + +The correct implementation is then selected at runtime: + + f("Hello World!") // expect: A string! + [show]: # (subtopics) ## Variadic @@ -57,6 +75,8 @@ Each optional parameter must be defined with a default value (here `1`). The fun func() // a == 1 due to default value func(a=2) // a == 2 supplied by the user +Note that optional parameters may not be typed. + ## Return [tagreturn]: # (return) @@ -69,6 +89,50 @@ The `return` keyword is used to exit from a function, optionally passing a given by returning early if `n<2`, otherwise returning the result by recursively calling itself. +## Signature +[tagsignature]: # (signature) + +The *signature* of a function is a list of the types of arguments in its definition: + + fn f(x) {} // Accepts one parameter of any type + fn f(x,y) {} // Accepts two parameters of any type + fn f(List x, y) {} // The first parameter must be a list + fn f(List x, List y) {} // Both parameters must be lists + +While you can define multiple implementations of a function that accept different parameter types, you can only define one implementation with a unique signature. + +Note that optional and variadic parameters are not typed. + +# Multiple dispatch +[tagmultiple]: # (multiple) +[tagdispatch]: # (dispatch) + +Morpho supports *multiple dispatch*, whereby you can define several implementations of a function that accept different types: + + fn f(List x) { return "Accepts a list" } + fn f(String x) { return "Accepts a string" } + +Morpho chooses the appropriate implementation at runtime: + + f([1,2,3]) // Selects the List implementation + +Any classes you define can be used as types: + + class A {} + class B is A {} + class C is B {} + + fn f(A x) { return "A" } + fn f(B x) { return "B" } + +Morpho selects the *closest match*, so that: + + print f(A()) // expect: A + print f(B()) // expect: B + print f(C()) // expect: B + +Class `C` inherits from both `B` and `A`, but because it directly inherits from `B`, that's the closer match. + # Closures [tagclosures]: # (closures) [tagclosure]: # (closure) diff --git a/src/classes/json.c b/src/classes/json.c index 838c5d28..fd1ef630 100644 --- a/src/classes/json.c +++ b/src/classes/json.c @@ -621,4 +621,3 @@ void json_initialize(void) { morpho_defineerror(JSON_NMBRFRMT, ERROR_PARSE, JSON_NMBRFRMT_MSG); morpho_defineerror(JSON_BLNKELMNT, ERROR_PARSE, JSON_BLNKELMNT_MSG); } - diff --git a/test/types/multiple_dispatch/inheritance_single.morpho b/test/types/multiple_dispatch/inheritance_single.morpho new file mode 100644 index 00000000..d7bb6e4d --- /dev/null +++ b/test/types/multiple_dispatch/inheritance_single.morpho @@ -0,0 +1,15 @@ +// Single implementation with typecheck and inheritance + +class A {} + +class B is A {} + +class C {} + +fn f(A x) { return "A" } + +print f(A()) // expect: A + +print f(B()) // expect: A + +print f(C()) // expect error 'MltplDsptchFld' From 881b2a26efae43b279b6f88cff0458b8d5269436 Mon Sep 17 00:00:00 2001 From: Tim Atherton <54725421+softmattertheory@users.noreply.github.com> Date: Mon, 20 May 2024 07:35:30 -0400 Subject: [PATCH 114/181] ClssLnrz error --- help/classes.md | 20 ++++++++++++++++++++ src/core/compile.c | 3 ++- src/core/compile.h | 3 +++ test/class/unlinearizable.morpho | 13 +++++++++++++ 4 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 test/class/unlinearizable.morpho diff --git a/help/classes.md b/help/classes.md index 032159f5..2f141733 100644 --- a/help/classes.md +++ b/help/classes.md @@ -31,6 +31,26 @@ See also `Object`. [showsubtopics]: # (subtopics) +## Methods +[tagmethods]: # (methods) + +Classes in morpho can define *methods* to manipulate the objects defined by the class. Like functions, multiple implementations can be defined that accept different parameter types [see also topic: `signature`]: + + class Foo { + a(List x) { print "A list!" } + a(String x) { print "A string!" } + a(Matrix x) { print "A matrix!" } + } + +Having created an object with the class, + + var x = Foo() + +the correct implementation is selected at runtime: + + x.a([1,2]) // expect: A list! + x.a("Hello") // expect: A string! + ## Is [tagis]: # (is) diff --git a/src/core/compile.c b/src/core/compile.c index 50d16db1..37cf1393 100644 --- a/src/core/compile.c +++ b/src/core/compile.c @@ -3353,7 +3353,7 @@ static codeinfo compiler_class(compiler *c, syntaxtreenode *node, registerindx r /* Now compute the class linearization */ if (!class_linearize(klass)) { - // TODO: Raise an error + compiler_error(c, node, COMPILE_CLSSLNRZ, MORPHO_GETCSTRING(klass->name)); } /* Compile method declarations */ @@ -4173,6 +4173,7 @@ void compile_initialize(void) { morpho_defineerror(COMPILE_MSSNGINDX, ERROR_COMPILE, COMPILE_MSSNGINDX_MSG); morpho_defineerror(COMPILE_TYPEVIOLATION, ERROR_COMPILE, COMPILE_TYPEVIOLATION_MSG); morpho_defineerror(COMPILE_UNKNWNTYPE, ERROR_COMPILE, COMPILE_UNKNWNTYPE_MSG); + morpho_defineerror(COMPILE_CLSSLNRZ, ERROR_COMPILE, COMPILE_CLSSLNRZ_MSG); morpho_addfinalizefn(compile_finalize); } diff --git a/src/core/compile.h b/src/core/compile.h index 35a51d10..21cd089e 100644 --- a/src/core/compile.h +++ b/src/core/compile.h @@ -111,6 +111,9 @@ #define COMPILE_UNKNWNTYPE "UnknwnType" #define COMPILE_UNKNWNTYPE_MSG "Unknown type %s." +#define COMPILE_CLSSLNRZ "ClssLnrz" +#define COMPILE_CLSSLNRZ_MSG "Can't linearize class %s: Check parent and ancestor classes for conflicting inheritance order." + /* ********************************************************************** * Compiler typedefs * ********************************************************************** */ diff --git a/test/class/unlinearizable.morpho b/test/class/unlinearizable.morpho new file mode 100644 index 00000000..345ef4bb --- /dev/null +++ b/test/class/unlinearizable.morpho @@ -0,0 +1,13 @@ +// Unlinearizable classes + +class A { } + +class B { } + +class C is A with B { } + +class D is B with A { } + +class E is C with D { } + +// expect error 'ClssLnrz' From ef3c7e649b73d98ae851ca899dc64995dc093568 Mon Sep 17 00:00:00 2001 From: Tim Atherton <54725421+softmattertheory@users.noreply.github.com> Date: Mon, 20 May 2024 08:49:18 -0400 Subject: [PATCH 115/181] Fix error in type_inheritance test --- test/types/type_inheritance.morpho | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/types/type_inheritance.morpho b/test/types/type_inheritance.morpho index 37eff54c..54867c0e 100644 --- a/test/types/type_inheritance.morpho +++ b/test/types/type_inheritance.morpho @@ -1,12 +1,12 @@ // Types and inheritance -class A { } +class A { } class B is A { } class C is B { } -class D is A with B { } +class D is B { } fn f() { A a = B() From a4b3f40d0782841b6007e755de38a2678986971e Mon Sep 17 00:00:00 2001 From: Tim Atherton <54725421+softmattertheory@users.noreply.github.com> Date: Mon, 20 May 2024 17:14:31 -0400 Subject: [PATCH 116/181] metafunction_resolve now searches linearization --- src/build.h | 4 ++-- src/classes/metafunction.c | 18 +++++++++++++++--- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/build.h b/src/build.h index dbedacef..137fc680 100644 --- a/src/build.h +++ b/src/build.h @@ -8,11 +8,11 @@ * Version * ********************************************************************** */ -#define MORPHO_VERSIONSTRING "0.6.0" +#define MORPHO_VERSIONSTRING "0.6.1" #define MORPHO_VERSION_MAJOR 0 #define MORPHO_VERSION_MINOR 6 -#define MORPHO_VERSION_PATCH 0 +#define MORPHO_VERSION_PATCH 1 /* ********************************************************************** * Paths and file system diff --git a/src/classes/metafunction.c b/src/classes/metafunction.c index 14cfa126..3e9af58f 100644 --- a/src/classes/metafunction.c +++ b/src/classes/metafunction.c @@ -180,7 +180,7 @@ DEFINE_VARRAY(mfinstruction, mfinstruction); #define MFINSTRUCTION_BRANCH(brnch) { .opcode=MF_BRANCH, .branch=brnch } #define MFINSTRUCTION_BRANCHNARG(table, brnch) { .opcode=MF_BRANCHNARGS, .data.btable=table, .branch=brnch } #define MFINSTRUCTION_BRANCHTABLE(op, n, table, brnch) { .opcode=op, .narg=n, .data.btable=table, .branch=brnch } - + typedef struct { signature *sig; /** Signature of the target */ value fn; /** The target */ @@ -768,6 +768,14 @@ bool metafunction_compile(objectmetafunction *fn, error *err) { unsigned int vm_countpositionalargs(unsigned int nargs, value *args); +/** Attempt to find the desired class uid in the linearization of a given class */ +bool _finduidinlinearization(objectclass *klass, int uid) { + for (int k=0; klinearization.count; k++) { + if (MORPHO_GETCLASS(klass->linearization.data[k])->uid == uid) return true; + } + return false; +} + /** Execute the metafunction's resolver */ bool metafunction_resolve(objectmetafunction *fn, int nargs, value *args, value *out) { int n=vm_countpositionalargs(nargs, args);; @@ -800,8 +808,12 @@ bool metafunction_resolve(objectmetafunction *fn, int nargs, value *args, value break; case MF_CHECKINSTANCE: { if (MORPHO_ISINSTANCE(args[pc->narg])) { - int tindx = MORPHO_GETINSTANCE(args[pc->narg])->klass->uid; - if (pc->data.tindx!=tindx) pc+=pc->branch; + objectclass *klass = MORPHO_GETINSTANCE(args[pc->narg])->klass; + + if (!(klass->uid==pc->data.tindx || + _finduidinlinearization(klass, pc->data.tindx))) { + pc+=pc->branch; + } } else pc+=pc->branch; } break; From 7c8f591798e6b149e860e991e21d7a0f86f0c3d7 Mon Sep 17 00:00:00 2001 From: Tim Atherton <54725421+softmattertheory@users.noreply.github.com> Date: Mon, 20 May 2024 18:05:17 -0400 Subject: [PATCH 117/181] Update build.yml --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 137d0255..c7f6952e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -2,9 +2,9 @@ name: Build on: push: - branches: [ "main" ] + branches: [ "main", "dev" ] pull_request: - branches: [ "main" ] + branches: [ "main", "dev" ] jobs: build: From ef55370fab6d46910a4921f193c219b89b4f0715 Mon Sep 17 00:00:00 2001 From: Tim Atherton <54725421+softmattertheory@users.noreply.github.com> Date: Mon, 20 May 2024 18:24:14 -0400 Subject: [PATCH 118/181] Add missing header --- src/classes/metafunction.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/classes/metafunction.c b/src/classes/metafunction.c index 3e9af58f..0ab1ca2e 100644 --- a/src/classes/metafunction.c +++ b/src/classes/metafunction.c @@ -4,6 +4,8 @@ * @brief Implement objectmetafunctions and the Metafunction veneer class */ +#include + #include "morpho.h" #include "classes.h" #include "common.h" From 9addb9ad8000fd66ec4347023acbb0657b59b903 Mon Sep 17 00:00:00 2001 From: Tim Atherton <54725421+softmattertheory@users.noreply.github.com> Date: Mon, 20 May 2024 19:03:20 -0400 Subject: [PATCH 119/181] make _init static --- src/classes/clss.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/classes/clss.c b/src/classes/clss.c index 941dba5f..dcdaa69e 100644 --- a/src/classes/clss.c +++ b/src/classes/clss.c @@ -133,7 +133,7 @@ bool _merge(int n, varray_value *in, varray_value *out) { } /** Initialize the varray from the parent class's linearization */ -void _init(objectclass *parent, varray_value *out) { +static void _init(objectclass *parent, varray_value *out) { if (parent->linearization.count) varray_valueadd(out, parent->linearization.data, parent->linearization.count); } From 96284438f2e61f0f58d1ce5b40165bdc734bb67f Mon Sep 17 00:00:00 2001 From: Tim Atherton <54725421+softmattertheory@users.noreply.github.com> Date: Tue, 21 May 2024 08:18:03 -0400 Subject: [PATCH 120/181] Update buildandtest.yml --- .github/workflows/buildandtest.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/buildandtest.yml b/.github/workflows/buildandtest.yml index 98e7fb1d..f26306a1 100644 --- a/.github/workflows/buildandtest.yml +++ b/.github/workflows/buildandtest.yml @@ -2,9 +2,9 @@ name: Build and Test on: push: - branches: [ "main" ] + branches: [ "main", "dev" ] pull_request: - branches: [ "main" ] + branches: [ "main", "dev" ] jobs: build: From 63050accfc4a70c2e0bb6b25c033fcf8fe559833 Mon Sep 17 00:00:00 2001 From: Tim Atherton <54725421+softmattertheory@users.noreply.github.com> Date: Tue, 21 May 2024 08:21:00 -0400 Subject: [PATCH 121/181] Update nonanboxing.yml --- .github/workflows/nonanboxing.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/nonanboxing.yml b/.github/workflows/nonanboxing.yml index c41dbfc7..1818f27e 100644 --- a/.github/workflows/nonanboxing.yml +++ b/.github/workflows/nonanboxing.yml @@ -2,9 +2,9 @@ name: No NANBoxing on: push: - branches: [ "main" ] + branches: [ "main", "dev" ] pull_request: - branches: [ "main" ] + branches: [ "main", "dev" ] jobs: build: From ba1a48fba7443542eb9a9fd161a4d8f12f17a4f7 Mon Sep 17 00:00:00 2001 From: Tim Atherton <54725421+softmattertheory@users.noreply.github.com> Date: Tue, 21 May 2024 08:24:12 -0400 Subject: [PATCH 122/181] Update NoNanBoxing.yml --- .github/workflows/NoNanBoxing.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/NoNanBoxing.yml b/.github/workflows/NoNanBoxing.yml index c41dbfc7..1818f27e 100644 --- a/.github/workflows/NoNanBoxing.yml +++ b/.github/workflows/NoNanBoxing.yml @@ -2,9 +2,9 @@ name: No NANBoxing on: push: - branches: [ "main" ] + branches: [ "main", "dev" ] pull_request: - branches: [ "main" ] + branches: [ "main", "dev" ] jobs: build: From 9b05a3cc00f2177147a6f421bd21deb9ea03e5a6 Mon Sep 17 00:00:00 2001 From: Tim Atherton <54725421+softmattertheory@users.noreply.github.com> Date: Tue, 21 May 2024 10:16:57 -0400 Subject: [PATCH 123/181] Correct sparse matrix addition --- src/geometry/functional.c | 31 ++++++++++++++++--- src/geometry/functional.h | 3 ++ src/linalg/sparse.h | 5 +++ test/functionals/length/length_hessian.morpho | 4 --- 4 files changed, 34 insertions(+), 9 deletions(-) diff --git a/src/geometry/functional.c b/src/geometry/functional.c index 972f44ca..e5d6d354 100644 --- a/src/geometry/functional.c +++ b/src/geometry/functional.c @@ -1505,10 +1505,13 @@ bool functional_mapnumericalhessian(vm *v, functional_mapinfo *info, value *out) varray_elementidinit(&imageids); objectsparse *new[ntask]; // Create an output matrix for each thread - for (int i=0; imesh; meshclones[i].vert=object_clonematrix(info->mesh->vert); + if (!meshclones[i].vert) { morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED); goto functional_maphessian_cleanup; } task[i].mesh=&meshclones[i]; task[i].ref=(void *) info; // Use this to pass the info structure @@ -1530,15 +1534,32 @@ bool functional_mapnumericalhessian(vm *v, functional_mapinfo *info, value *out) functional_parallelmap(ntask, task); - /* Then add up all the matrices */ - sparse_checkformat(new[0], SPARSE_CCS, true, true); - for (int i=1; iccs); + new[0]->ccs = out.ccs; + } else { + morpho_runtimeerror(v, SPARSE_OPFAILEDERR); + goto functional_maphessian_cleanup; + } + } success=true; // Use symmetry actions //if (info->sym==SYMMETRY_ADD) functional_symmetrysumforces(info->mesh, new[0]); + sparsedok_clear(&new[0]->dok); // Remove dok info + // ...and return the result *out = MORPHO_OBJECT(new[0]); diff --git a/src/geometry/functional.h b/src/geometry/functional.h index 1cd26bf0..d0389f04 100644 --- a/src/geometry/functional.h +++ b/src/geometry/functional.h @@ -83,6 +83,9 @@ #define FUNC_INTEGRAND_MESH "FnctlIntMsh" #define FUNC_INTEGRAND_MESH_MSG "Method 'integrand' requires a mesh as the argument." +#define FUNC_INTEGRAND_MESH "FnctlIntMsh" +#define FUNC_INTEGRAND_MESH_MSG "Method 'integrand' requires a mesh as the argument." + #define FUNC_ELNTFND "FnctlELNtFnd" #define FUNC_ELNTFND_MSG "Mesh does not provide elements of grade %u." diff --git a/src/linalg/sparse.h b/src/linalg/sparse.h index 984b5d37..36431590 100644 --- a/src/linalg/sparse.h +++ b/src/linalg/sparse.h @@ -76,6 +76,9 @@ typedef struct { /** Gets the object as a sparse matrix */ #define MORPHO_GETSPARSE(val) ((objectsparse *) MORPHO_GETOBJECT(val)) +/** @brief Use to create static sparse matrices on the C stack. Note that the entries should be initialized */ +#define MORPHO_STATICSPARSE() { .obj.type=OBJECT_SPARSE, .obj.status=OBJECT_ISUNMANAGED, .obj.next=NULL } + objectsparse *object_newsparse(int *nrows, int *ncols); objectsparse *sparse_sparsefromarray(objectarray *array); @@ -130,6 +133,7 @@ bool sparsedok_copy(sparsedok *src, sparsedok *dest); bool sparsedok_copyat(sparsedok *src, sparsedok *dest, int row0, int col0); bool sparsedok_copymatrixat(objectmatrix *src, sparsedok *dest, int row0, int col0); bool sparsedok_copytomatrix(sparsedok *src, objectmatrix *dest, int row0, int col0); +void sparsedok_print(vm *v, sparsedok *dok); /* *************************************** * Compressed Column Storage Format @@ -149,6 +153,7 @@ bool sparseccs_doktoccs(sparsedok *in, sparseccs *out, bool copyvals); bool sparseccs_copy(sparseccs *src, sparseccs *dest); bool sparseccs_copytodok(sparseccs *src, sparsedok *dest, int row0, int col0); bool sparseccs_copytomatrix(sparseccs *src, objectmatrix *dest, int row0, int col0); +void sparseccs_print(vm *v, sparseccs *ccs); typedef enum { SPARSE_DOK, SPARSE_CCS } objectsparseformat; diff --git a/test/functionals/length/length_hessian.morpho b/test/functionals/length/length_hessian.morpho index b9fbe887..c78a89bf 100644 --- a/test/functionals/length/length_hessian.morpho +++ b/test/functionals/length/length_hessian.morpho @@ -11,9 +11,5 @@ print abs(a.total(m) - 4*sqrt(2)) < 1e-10 print (a.gradient(m)-numericalgradient(a, m)).norm()<1e-6 // expect: true -//print Matrix(a.hessian(m)) -//print "" -//print numericalhessian(a, m, eps=1e-4) - print (Matrix(a.hessian(m))-numericalhessian(a, m, eps=1e-4)).norm() < 1e-2 // expect: true From 4287bc3e7dde49c07d512d1f84065bf0e15cc9d7 Mon Sep 17 00:00:00 2001 From: Chaitanya Joshi Date: Tue, 21 May 2024 16:13:30 -0400 Subject: [PATCH 124/181] Add automated testing + CI with two threads --- .../workflows/buildandtestmultithreaded.yml | 42 +++++++++++++++++++ .gitignore | 2 +- test/test.py | 18 ++++++-- 3 files changed, 58 insertions(+), 4 deletions(-) create mode 100644 .github/workflows/buildandtestmultithreaded.yml diff --git a/.github/workflows/buildandtestmultithreaded.yml b/.github/workflows/buildandtestmultithreaded.yml new file mode 100644 index 00000000..f03e3691 --- /dev/null +++ b/.github/workflows/buildandtestmultithreaded.yml @@ -0,0 +1,42 @@ +name: Build and Test + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - name: configure + run: | + sudo apt update + sudo apt install libsuitesparse-dev + sudo apt install liblapacke-dev + sudo apt install libunistring-dev + python -m pip install --upgrade pip + python -m pip install regex colored + - name: make + run: | + mkdir build + cd build + cmake -DCMAKE_BUILD_TYPE=Release .. + sudo make install + sudo mkdir /usr/local/lib/morpho + - name: getcli + run: | + git clone https://github.com/Morpho-lang/morpho-cli.git + cd morpho-cli + mkdir build + cd build + cmake .. + sudo make install + - name: test + run: | + cd test + python3 test.py -c -m diff --git a/.gitignore b/.gitignore index 8301e574..e09f8565 100644 --- a/.gitignore +++ b/.gitignore @@ -58,7 +58,7 @@ dkms.conf .entitlements .vscode/settings.json -test/FailedTests.txt +test/FailedTests*.txt *.png *.out manual/src/manual.lyx~ diff --git a/test/test.py b/test/test.py index 049bfe9d..81e5d994 100755 --- a/test/test.py +++ b/test/test.py @@ -183,11 +183,23 @@ def test(file,testLog,CI): # look for a command line arguement that says # this is being run for continous integration CI = False -if (len(sys.argv) > 1): - CI = sys.argv[1] == '-c' +# Also look for a command line argument that says this is being run with multiple threads +MP = False +if (len(sys.argv) == 2): + CI = sys.argv[1] == '-c' # if the argument is -c, then we are running in CI mode + MP = sys.argv[1] == '-m' # if the argument is -m, then we are running in multi-thread mode +elif (len(sys.argv) == 3): + CI = sys.argv[1] == '-c' or sys.argv[2] == '-c' + MP = sys.argv[1] == '-m' or sys.argv[2] == '-m' + +failedTestsFileName = "FailedTests.txt" +if MP: + failedTestsFileName = "FailedTestsMultiThreaded.txt" + command = "morpho6 -w2" + print("Running tests with 2 threads") files=glob.glob('**/**.'+ext, recursive=True) -with open("FailedTests.txt",'w') as testLog: +with open(failedTestsFileName,'w') as testLog: for f in files: # print(f) From 178e2e7636893dec43446480523fdd3db392854b Mon Sep 17 00:00:00 2001 From: Tim Atherton <54725421+softmattertheory@users.noreply.github.com> Date: Tue, 21 May 2024 21:14:26 -0400 Subject: [PATCH 125/181] Fix gradvector test --- src/geometry/functional.c | 9 +++++++++ test/functionals/areaintegral/gradvector.morpho | 15 +++++---------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/geometry/functional.c b/src/geometry/functional.c index e5d6d354..e54d951e 100644 --- a/src/geometry/functional.c +++ b/src/geometry/functional.c @@ -1494,6 +1494,12 @@ bool functional_numericalhessianmapfn(vm *v, objectmesh *mesh, elementid id, int return success; } +static int _sparsecmp(const void *a, const void *b) { + objectsparse *aa = *(objectsparse **) a; + objectsparse *bb = *(objectsparse **) b; + return bb->dok.dict.count - aa->dok.dict.count; +} + /** Compute the hessian numerically */ bool functional_mapnumericalhessian(vm *v, functional_mapinfo *info, value *out) { int success=false; @@ -1534,6 +1540,8 @@ bool functional_mapnumericalhessian(vm *v, functional_mapinfo *info, value *out) functional_parallelmap(ntask, task); + qsort(new, ntask, sizeof(objectsparse *), _sparsecmp); + if (!sparse_checkformat(new[0], SPARSE_CCS, true, true)) { morpho_runtimeerror(v, SPARSE_OPFAILEDERR); goto functional_maphessian_cleanup; @@ -1541,6 +1549,7 @@ bool functional_mapnumericalhessian(vm *v, functional_mapinfo *info, value *out) /* Then add up all the matrices */ for (int i=1; idok.dict.count) continue; objectsparse out = MORPHO_STATICSPARSE(); sparsedok_init(&out.dok); sparseccs_init(&out.ccs); diff --git a/test/functionals/areaintegral/gradvector.morpho b/test/functionals/areaintegral/gradvector.morpho index 3b407ce5..6f0b70c4 100644 --- a/test/functionals/areaintegral/gradvector.morpho +++ b/test/functionals/areaintegral/gradvector.morpho @@ -10,22 +10,17 @@ mb.addface([0,1,2]) var m = mb.build() var f = Field(m, fn (x,y) Matrix([x,2*y])) -var r + +var r = [ Matrix([[1],[0]]), Matrix([[0],[2]]) ] +var out = true fn integrand(x, n) { var g = grad(n) - r = [] - for (u in g) r.append(u.clone()) + for (gg, k in g) if ((gg-r[k]).norm()>1e-4) out = false return 0 } print AreaIntegral(integrand, f).total(m) // expect: 0 -print r[0] -// expect: [ 1 ] -// expect: [ 0 ] - -print r[1] -// expect: [ 0 ] -// expect: [ 2 ] +print out // expect: true From 74ffd286dfc3acebde954a9356c22ad96ea05a0b Mon Sep 17 00:00:00 2001 From: Tim Atherton <54725421+softmattertheory@users.noreply.github.com> Date: Tue, 21 May 2024 21:49:55 -0400 Subject: [PATCH 126/181] Ensure meshes have CCS matrices --- src/geometry/functional.c | 11 ++++++++++- test/test.py | 2 +- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/geometry/functional.c b/src/geometry/functional.c index e54d951e..9807253b 100644 --- a/src/geometry/functional.c +++ b/src/geometry/functional.c @@ -881,6 +881,15 @@ int functional_preparetasks(vm *v, functional_mapinfo *info, int ntask, function int bins[ntask+1]; functional_binbounds(cmax, ntask, bins); + /* Ensure all mesh topology matrices have CCS */ + int maxgrade=mesh_maxgrade(info->mesh); + for (int i=0; i<=maxgrade; i++) { + for (int j=0; j<=maxgrade; j++) { + objectsparse *s = mesh_getconnectivityelement(info->mesh, i, j); + if (s) sparse_checkformat(s, SPARSE_CCS, true, false); + } + } + /* Find any image elements so they can be skipped */ functional_symmetryimagelist(info->mesh, info->g, true, imageids); if (info->field) field_addpool(info->field); @@ -1549,7 +1558,7 @@ bool functional_mapnumericalhessian(vm *v, functional_mapinfo *info, value *out) /* Then add up all the matrices */ for (int i=1; idok.dict.count) continue; + if (!new[i]->dok.dict.count) continue; objectsparse out = MORPHO_STATICSPARSE(); sparsedok_init(&out.dok); sparseccs_init(&out.ccs); diff --git a/test/test.py b/test/test.py index 049bfe9d..9bbeeb58 100755 --- a/test/test.py +++ b/test/test.py @@ -16,7 +16,7 @@ from colored import stylize # define what command to use to invoke the interpreter -command = 'morpho6' +command = 'morpho6 -w4' # define the file extension to test ext = 'morpho' From 057dcd98fdcb8de3dec26929211211dc60aeb755 Mon Sep 17 00:00:00 2001 From: Chaitanya Joshi Date: Wed, 22 May 2024 08:37:54 -0400 Subject: [PATCH 127/181] Simplify sys argv and change to 4 threads --- test/test.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/test/test.py b/test/test.py index 81e5d994..eb22fb6b 100755 --- a/test/test.py +++ b/test/test.py @@ -185,18 +185,17 @@ def test(file,testLog,CI): CI = False # Also look for a command line argument that says this is being run with multiple threads MP = False -if (len(sys.argv) == 2): - CI = sys.argv[1] == '-c' # if the argument is -c, then we are running in CI mode - MP = sys.argv[1] == '-m' # if the argument is -m, then we are running in multi-thread mode -elif (len(sys.argv) == 3): - CI = sys.argv[1] == '-c' or sys.argv[2] == '-c' - MP = sys.argv[1] == '-m' or sys.argv[2] == '-m' +for arg in sys.argv: + if arg == '-c': # if the argument is -c, then we are running in CI mode + CI = True + if arg == '-m': # if the argument is -m, then we are running in multi-thread mode + MP = True failedTestsFileName = "FailedTests.txt" if MP: failedTestsFileName = "FailedTestsMultiThreaded.txt" - command = "morpho6 -w2" - print("Running tests with 2 threads") + command += " -w4" + print("Running tests with 4 threads") files=glob.glob('**/**.'+ext, recursive=True) with open(failedTestsFileName,'w') as testLog: From 6e9c82bd2fa27fe4d38e8f3e528cd9b7690184a8 Mon Sep 17 00:00:00 2001 From: Chaitanya Joshi Date: Wed, 22 May 2024 08:42:23 -0400 Subject: [PATCH 128/181] Add dev branch for CI --- .github/workflows/buildandtestmultithreaded.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/buildandtestmultithreaded.yml b/.github/workflows/buildandtestmultithreaded.yml index f03e3691..22979cde 100644 --- a/.github/workflows/buildandtestmultithreaded.yml +++ b/.github/workflows/buildandtestmultithreaded.yml @@ -2,9 +2,9 @@ name: Build and Test on: push: - branches: [ "main" ] + branches: [ "main", "dev" ] pull_request: - branches: [ "main" ] + branches: [ "main", "dev" ] jobs: build: From c227091be3640769d94966cedd46033642bb78d6 Mon Sep 17 00:00:00 2001 From: Chaitanya Joshi Date: Wed, 22 May 2024 08:49:41 -0400 Subject: [PATCH 129/181] Update test name --- .github/workflows/buildandtestmultithreaded.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/buildandtestmultithreaded.yml b/.github/workflows/buildandtestmultithreaded.yml index 22979cde..11815d11 100644 --- a/.github/workflows/buildandtestmultithreaded.yml +++ b/.github/workflows/buildandtestmultithreaded.yml @@ -1,4 +1,4 @@ -name: Build and Test +name: TestMultiThreaded on: push: From 0af760d8fb3926a1bc00a47d890171b47b962c37 Mon Sep 17 00:00:00 2001 From: Chaitanya Joshi Date: Wed, 22 May 2024 08:51:10 -0400 Subject: [PATCH 130/181] Change MP to MT to match "MultiThreaded" --- test/test.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/test.py b/test/test.py index eb22fb6b..12d047af 100755 --- a/test/test.py +++ b/test/test.py @@ -184,15 +184,15 @@ def test(file,testLog,CI): # this is being run for continous integration CI = False # Also look for a command line argument that says this is being run with multiple threads -MP = False +MT = False for arg in sys.argv: if arg == '-c': # if the argument is -c, then we are running in CI mode CI = True if arg == '-m': # if the argument is -m, then we are running in multi-thread mode - MP = True + MT = True failedTestsFileName = "FailedTests.txt" -if MP: +if MT: failedTestsFileName = "FailedTestsMultiThreaded.txt" command += " -w4" print("Running tests with 4 threads") From 74d246c31f6030562aef39f2c87ab816e4151233 Mon Sep 17 00:00:00 2001 From: Chaitanya Joshi Date: Wed, 22 May 2024 16:40:16 -0400 Subject: [PATCH 131/181] Simplify the graphics visitor pattern using multiple dispatch --- modules/graphics.morpho | 34 ++++++++-------------------------- modules/povray.morpho | 16 ++++++++-------- 2 files changed, 16 insertions(+), 34 deletions(-) diff --git a/modules/graphics.morpho b/modules/graphics.morpho index f38d1d29..e00a050d 100644 --- a/modules/graphics.morpho +++ b/modules/graphics.morpho @@ -76,9 +76,6 @@ class TriangleComplex { self.transmit = transmit } - accept(visitor, out) { - visitor.visittrianglecomplex(self, out) - } } /** Draws a cylinder @@ -178,9 +175,6 @@ class Cylinder { return TriangleComplex(vertices, normals, col, conn) } - accept(visitor, out) { - visitor.visitcylinder(self, out) - } } var _sphere = [ PolyhedronMesh( @@ -257,9 +251,6 @@ class Sphere { return TriangleComplex(v, normals, col, conn) } - accept(visitor, out) { - visitor.visitsphere(self, out) - } } /** Draws an arrow @@ -371,9 +362,6 @@ class Arrow { return TriangleComplex(vertices, normals, col, conn) } - accept(visitor, out) { - visitor.visitarrow(self, out) - } } /** Draws a tube @@ -517,9 +505,6 @@ class Tube { return TriangleComplex(vertices, normals, col, conn, filter=self.filter, transmit=self.transmit) } - accept(visitor, out) { - visitor.visittube(self, out) - } } /* ************************************** @@ -564,9 +549,6 @@ class Text { return m.transpose() } - accept(visitor, out) { - visitor.visittext(self, out) - } } class Font { @@ -647,10 +629,10 @@ class Show { } visitgeneric(item, out) { - self.visittrianglecomplex(item.totrianglecomplex(), out) + self.visit(item.totrianglecomplex(), out) } - visitsphere(item, out) { + visit(Sphere item, out) { var n = item.refinementlevel() if (item.color) { @@ -670,9 +652,9 @@ class Show { out.write("d ${self.spheres[n].id}") } - visitcylinder(item, out) { self.visitgeneric(item, out) } - visitarrow(item, out) { self.visitgeneric(item, out) } - visittube(item, out) { self.visitgeneric(item, out) } + visit(Cylinder item, out) { self.visitgeneric(item, out) } + visit(Arrow item, out) { self.visitgeneric(item, out) } + visit(Tube item, out) { self.visitgeneric(item, out) } trianglecomplexobjectdata(item, out) { var x = item.position @@ -718,7 +700,7 @@ class Show { } } - visittrianglecomplex(item, out) { + visit(TriangleComplex item, out) { var id = self.uid() out.write("o ${id}") self.trianglecomplexobjectdata(item, out) @@ -795,7 +777,7 @@ class Show { return self.addfont(font, size, out) } - visittext(item, out) { + visit(Text item, out) { var font = item.font if (!font) font = self.defaultfontname() var id = self.findfontid(font, item.size, out) @@ -819,6 +801,6 @@ class Show { write(graphic, out) { self.preamble(graphic, out) - for (item in graphic.displaylist) item.accept(self, out) + for (item in graphic.displaylist) self.visit(item, out) } } diff --git a/modules/povray.morpho b/modules/povray.morpho index baaab27b..298a88c4 100644 --- a/modules/povray.morpho +++ b/modules/povray.morpho @@ -59,7 +59,7 @@ class POVRaytracer { return arg } - visittext(item, out) { + visit(Text item, out) { var arg = self.optionalarg(item) out.write("text {" + " ttf \"cyrvetic.ttf\" \"${item.string}\" 0.1, 0 \n" + @@ -85,7 +85,7 @@ class POVRaytracer { out.write(str) } - visitsphere(item, out) { + visit(Sphere item, out) { var arg = self.optionalarg(item) out.write("sphere {"+ " ${self.vector(item.center)}, ${item.r}"+ @@ -94,7 +94,7 @@ class POVRaytracer { "} } }") } - visitcylinder(item, out) { + visit(Cylinder item, out) { var radius = 0.5*(item.end - item.start).norm()*item.aspectratio var arg = self.optionalarg(item) out.write("cylinder {"+ @@ -104,7 +104,7 @@ class POVRaytracer { "} } }") } - visitarrow(item, out) { + visit(Arrow item, out) { var dx = (item.end - item.start).norm() var radius = 0.5*dx*item.aspectratio var cylend = item.start + (item.end - item.start)*(1-item.aspectratio) @@ -121,11 +121,11 @@ class POVRaytracer { "} } }") } - visittube(item, out) { - self.visittrianglecomplex(item.totrianglecomplex(), out) + visit(Tube item, out) { + self.visit(item.totrianglecomplex(), out) } - visittrianglecomplex(item, out) { + visit(TriangleComplex item, out) { var arg = self.optionalarg(item) @@ -197,7 +197,7 @@ class POVRaytracer { "up <0,1,0> right <-1.33,0,0> angle ${self.viewangle}"+ "look_at ${self.vector(self.look_at)} sky ${self.vector(self.sky)} }") - for (item in self.graphic.displaylist) item.accept(self, out) + for (item in self.graphic.displaylist) self.visit(item, out) var shadowless = "" if (self.shadowless) shadowless = " shadowless" From 0d8ef8d8085a9c326de42bca6a33049700468414 Mon Sep 17 00:00:00 2001 From: Chaitanya Joshi Date: Wed, 22 May 2024 16:40:31 -0400 Subject: [PATCH 132/181] Update command to morpho6 in examples.py --- examples/examples.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/examples.py b/examples/examples.py index 91274bce..ede9406a 100755 --- a/examples/examples.py +++ b/examples/examples.py @@ -14,7 +14,7 @@ sys.path.append('../test') ext = "morpho" -command = 'morpho5' +command = 'morpho6' stk = '@stacktrace' err = '@error' From 9b2398199e80294ba614d8d3439e034a25f8a912 Mon Sep 17 00:00:00 2001 From: Chaitanya Joshi Date: Wed, 22 May 2024 16:45:00 -0400 Subject: [PATCH 133/181] Remove unnecessary whitespace --- modules/graphics.morpho | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/graphics.morpho b/modules/graphics.morpho index e00a050d..ad5b144c 100644 --- a/modules/graphics.morpho +++ b/modules/graphics.morpho @@ -654,7 +654,7 @@ class Show { visit(Cylinder item, out) { self.visitgeneric(item, out) } visit(Arrow item, out) { self.visitgeneric(item, out) } - visit(Tube item, out) { self.visitgeneric(item, out) } + visit(Tube item, out) { self.visitgeneric(item, out) } trianglecomplexobjectdata(item, out) { var x = item.position From 599303b8872913169d5c1a17b5c8a1c46b75669d Mon Sep 17 00:00:00 2001 From: Tim Atherton <54725421+softmattertheory@users.noreply.github.com> Date: Fri, 24 May 2024 08:43:24 -0400 Subject: [PATCH 134/181] Functions no longer label registers --- src/core/compile.c | 38 +++++++++------- test/types/multiple_dispatch/pets.morpho | 42 ++++++++++++++++++ test/types/multiple_dispatch/pets.zmorpho | 42 ++++++++++++++++++ .../multiple_dispatch/pets_subclass.morpho | 43 +++++++++++++++++++ 4 files changed, 150 insertions(+), 15 deletions(-) create mode 100644 test/types/multiple_dispatch/pets.morpho create mode 100644 test/types/multiple_dispatch/pets.zmorpho create mode 100644 test/types/multiple_dispatch/pets_subclass.morpho diff --git a/src/core/compile.c b/src/core/compile.c index 37cf1393..71012481 100644 --- a/src/core/compile.c +++ b/src/core/compile.c @@ -1303,22 +1303,30 @@ codeinfo compiler_metafunction(compiler *c, syntaxtreenode *node, int n, value * return out; } +/** Collects function implementations that match a given symbol */ +static void _findfunctionref(compiler *c, value symbol, bool *hasclosure, varray_value *out) { + bool closure=false; + for (functionstate *f=compiler_currentfunctionstate(c); f>=c->fstack; f--) { + for (int i=0; ifunctionref.count; i++) { + functionref *ref=&f->functionref.data[i]; + if (MORPHO_ISEQUAL(ref->symbol, symbol)) { + closure |= function_isclosure(ref->function); + varray_valuewrite(out, MORPHO_OBJECT(ref->function)); + } + } + } + *hasclosure=closure; +} + /** Determines whether a symbol refers to one (or more) functions. If so, returns either a single function or a metafunction as appropriate. */ bool compiler_resolvefunctionref(compiler *c, syntaxtreenode *node, value symbol, codeinfo *out) { - functionstate *f=compiler_currentfunctionstate(c); value outfn=MORPHO_NIL; bool closure=false; // Set if one of the references contains a closure. varray_value fns; varray_valueinit(&fns); - for (int i=0; ifunctionref.count; i++) { - functionref *ref=&f->functionref.data[i]; - if (MORPHO_ISEQUAL(ref->symbol, symbol)) { - closure |= function_isclosure(ref->function); - varray_valuewrite(&fns, MORPHO_OBJECT(ref->function)); - } - } + _findfunctionref(c, symbol, &closure, &fns); if (!fns.count) return false; // No need to clear an empty varray @@ -2931,7 +2939,7 @@ static codeinfo compiler_function(compiler *c, syntaxtreenode *node, registerind /* Wrap in a closure if necessary */ if (closure!=REGISTER_UNALLOCATED) { // Save the register where the closure is to be found - compiler_regsetsymbol(c, reg, func->name); + //compiler_regsetsymbol(c, reg, func->name); function_setclosure(func, reg); compiler_addinstruction(c, ENCODE_DOUBLE(OP_CLOSURE, reg, (registerindx) closure), node); ninstructions++; @@ -3050,7 +3058,7 @@ static codeinfo compiler_call(compiler *c, syntaxtreenode *node, registerindx re // Compile the function selector syntaxtreenode *selnode=compiler_getnode(c, node->left); codeinfo func = compiler_nodetobytecode(c, node->left, (reqouttype==NODE_SYMBOL) { @@ -3437,11 +3445,6 @@ static codeinfo compiler_super(compiler *c, syntaxtreenode *node, registerindx r static codeinfo compiler_symbol(compiler *c, syntaxtreenode *node, registerindx reqout) { codeinfo ret=CODEINFO_EMPTY; - /* Is it a reference to a function? */ - if (compiler_resolvefunctionref(c, node, node->content, &ret)) { - return ret; - } - /* Is it a local variable? */ ret.dest=compiler_getlocal(c, node->content); if (ret.dest!=REGISTER_UNALLOCATED) return ret; @@ -3453,6 +3456,11 @@ static codeinfo compiler_symbol(compiler *c, syntaxtreenode *node, registerindx return ret; } + /* Is it a reference to a function? */ + if (compiler_resolvefunctionref(c, node, node->content, &ret)) { + return ret; + } + /* Is it a global variable */ ret.dest=compiler_findglobal(c, node->content, true); if (ret.dest!=REGISTER_UNALLOCATED) { diff --git a/test/types/multiple_dispatch/pets.morpho b/test/types/multiple_dispatch/pets.morpho new file mode 100644 index 00000000..18a05a8a --- /dev/null +++ b/test/types/multiple_dispatch/pets.morpho @@ -0,0 +1,42 @@ +class Dog { + init(name) { + self.name = name + } +} + +class Cat { + init(name) { + self.name = name + } +} + +fn meets(Dog a, Dog b) { + return "wags its tail" +} + +fn meets(Dog a, Cat b) { + return "barks" +} + +fn meets(Cat a, Dog b) { + return "hisses" +} + +fn meets(Cat a, Cat b) { + return "purrs" +} + +fn encounter(a, b) { + var verb = meets(a, b) + print("${a.name} meets ${b.name} and ${verb}") +} + +var fido = Dog("Fido") +var whiskers = Cat("Whiskers") +var rex = Dog("Rex") +var simba = Cat("Simba") + +encounter(fido, rex) // expect: Fido meets Rex and wags its tail +encounter(fido, whiskers) // expect: Fido meets Whiskers and barks +encounter(whiskers, rex) // expect: Whiskers meets Rex and hisses +encounter(whiskers, simba) // expect: Whiskers meets Simba and purrs diff --git a/test/types/multiple_dispatch/pets.zmorpho b/test/types/multiple_dispatch/pets.zmorpho new file mode 100644 index 00000000..e415cd2a --- /dev/null +++ b/test/types/multiple_dispatch/pets.zmorpho @@ -0,0 +1,42 @@ +class Dog { + init(name) { + self.name = name + } +} + +class Cat { + init(name) { + self.name = name + } +} + +fn encounter(a, b) { + var verb = meets(a, b) + print("${a.name} meets ${b.name} and ${verb}") +} + +fn meets(Dog a, Dog b) { + return "wags its tail" +} + +fn meets(Dog a, Cat b) { + return "barks" +} + +fn meets(Cat a, Dog b) { + return "hisses" +} + +fn meets(Cat a, Cat b) { + return "purrs" +} + +var fido = Dog("Fido") +var whiskers = Cat("Whiskers") +var rex = Dog("Rex") +var simba = Cat("Simba") + +encounter(fido, rex) +encounter(fido, whiskers) +encounter(whiskers, rex) +encounter(whiskers, simba) diff --git a/test/types/multiple_dispatch/pets_subclass.morpho b/test/types/multiple_dispatch/pets_subclass.morpho new file mode 100644 index 00000000..a1e5b0df --- /dev/null +++ b/test/types/multiple_dispatch/pets_subclass.morpho @@ -0,0 +1,43 @@ + + +class Pet { + init(name) { + self.name = name + } +} + +class Dog is Pet {} +class Cat is Pet {} + +fn meets(Dog a, Dog b) { + return "wags its tail" +} + +fn meets(Dog a, Cat b) { + return "barks" +} + +fn meets(Cat a, Dog b) { + return "hisses" +} + +fn meets(Cat a, Cat b) { + return "purrs" +} + +fn encounter(Pet a, Pet b) { + var verb = meets(a, b) + print("${a.name} meets ${b.name} and ${verb}") +} + +var fido = Dog("Fido") +var whiskers = Cat("Whiskers") +var rex = Dog("Rex") +var simba = Cat("Simba") + +encounter(fido, rex) // expect: Fido meets Rex and wags its tail +encounter(fido, whiskers) // expect: Fido meets Whiskers and barks +encounter(whiskers, rex) // expect: Whiskers meets Rex and hisses +encounter(whiskers, simba) // expect: Whiskers meets Simba and purrs + +encounter([], rex) // expect error 'MltplDsptchFld' From 47673620edca72bac41fd4988d0653f82a7004d1 Mon Sep 17 00:00:00 2001 From: Tim Atherton <54725421+softmattertheory@users.noreply.github.com> Date: Fri, 24 May 2024 11:12:06 -0400 Subject: [PATCH 135/181] FIx handling of closures --- src/core/compile.c | 48 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 38 insertions(+), 10 deletions(-) diff --git a/src/core/compile.c b/src/core/compile.c index 71012481..3ae90ffc 100644 --- a/src/core/compile.c +++ b/src/core/compile.c @@ -220,7 +220,11 @@ objectfunction *compiler_getpreviousfunction(compiler *c) { return c->prevfunction; } +/* ------------------------------------------ + * Types + * ------------------------------------------- */ +value _closuretype; /* ------------------------------------------ * Argument declarations @@ -1160,6 +1164,19 @@ registerindx compiler_addupvalue(functionstate *f, bool islocal, indx ix) { return (registerindx) f->upvalues.count-1; } +/** Propagates upvalues up the functionstate stack. + @param c the compiler + @param start the initial functionstate + @param sindx starting index + @returns register index at the top of the function state */ +registerindx compiler_propagateupvalues(compiler *c, functionstate *start, registerindx sindx) { + registerindx indx=sindx; + for (functionstate *f = start; ffstack+c->fstackp; f++) { + indx=compiler_addupvalue(f, f==start, indx); + } + return indx; +} + /** @brief Determines whether a symbol refers to something outside its scope @param c the compiler @param symbol symbol to resolve @@ -1180,9 +1197,7 @@ static registerindx compiler_resolveupvalue(compiler *c, value symbol) { } /* Now walk up the functionstate stack adding in the upvalues */ - if (found) for (functionstate *f = found; ffstack+c->fstackp; f++) { - indx=compiler_addupvalue(f, f==found, indx); - } + if (found) indx=compiler_propagateupvalues(c, found, indx); return indx; } @@ -1306,11 +1321,17 @@ codeinfo compiler_metafunction(compiler *c, syntaxtreenode *node, int n, value * /** Collects function implementations that match a given symbol */ static void _findfunctionref(compiler *c, value symbol, bool *hasclosure, varray_value *out) { bool closure=false; - for (functionstate *f=compiler_currentfunctionstate(c); f>=c->fstack; f--) { + functionstate *fc = compiler_currentfunctionstate(c); + for (functionstate *f=fc; f>=c->fstack; f--) { for (int i=0; ifunctionref.count; i++) { functionref *ref=&f->functionref.data[i]; if (MORPHO_ISEQUAL(ref->symbol, symbol)) { - closure |= function_isclosure(ref->function); + bool iscl = function_isclosure(ref->function); + if (iscl) { + UNREACHABLE("Closure in upvalue"); + } + + closure |= iscl; varray_valuewrite(out, MORPHO_OBJECT(ref->function)); } } @@ -1401,9 +1422,7 @@ static registerindx compiler_resolveself(compiler *c) { } /* Now walk up the functionstate stack adding in the upvalues */ - if (found) for (functionstate *f = found; ffstack+c->fstackp; f++) { - indx=compiler_addupvalue(f, f==found, indx); - } + if (found) indx = compiler_propagateupvalues(c, found, indx); return indx; } @@ -2939,7 +2958,8 @@ static codeinfo compiler_function(compiler *c, syntaxtreenode *node, registerind /* Wrap in a closure if necessary */ if (closure!=REGISTER_UNALLOCATED) { // Save the register where the closure is to be found - //compiler_regsetsymbol(c, reg, func->name); + compiler_regsetsymbol(c, reg, func->name); + compiler_regsettype(c, reg, _closuretype); function_setclosure(func, reg); compiler_addinstruction(c, ENCODE_DOUBLE(OP_CLOSURE, reg, (registerindx) closure), node); ninstructions++; @@ -3444,10 +3464,15 @@ static codeinfo compiler_super(compiler *c, syntaxtreenode *node, registerindx r /** Lookup a symbol */ static codeinfo compiler_symbol(compiler *c, syntaxtreenode *node, registerindx reqout) { codeinfo ret=CODEINFO_EMPTY; + value type; /* Is it a local variable? */ ret.dest=compiler_getlocal(c, node->content); - if (ret.dest!=REGISTER_UNALLOCATED) return ret; + if (ret.dest!=REGISTER_UNALLOCATED && + compiler_regtype(c, ret.dest, &type) && // If it's a closure it should be resolved later + !MORPHO_ISEQUAL(type, _closuretype)) { + return ret; + } /* Is it an upvalue? */ ret.dest = compiler_resolveupvalue(c, node->content); @@ -4147,6 +4172,9 @@ void morpho_setoptimizer(optimizerfn *opt) { void compile_initialize(void) { _selfsymbol=builtin_internsymbolascstring("self"); + /** Types we need to refer to */ + _closuretype = MORPHO_OBJECT(object_getveneerclass(OBJECT_CLOSURE)); + optimizer = NULL; /* Compile errors */ From 9dc86743be7bd4090b8c7da41044540bc9c6991d Mon Sep 17 00:00:00 2001 From: Tim Atherton <54725421+softmattertheory@users.noreply.github.com> Date: Fri, 24 May 2024 11:20:28 -0400 Subject: [PATCH 136/181] Remove UNREACHABLE --- src/core/compile.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/compile.c b/src/core/compile.c index 3ae90ffc..6962c193 100644 --- a/src/core/compile.c +++ b/src/core/compile.c @@ -1328,7 +1328,7 @@ static void _findfunctionref(compiler *c, value symbol, bool *hasclosure, varray if (MORPHO_ISEQUAL(ref->symbol, symbol)) { bool iscl = function_isclosure(ref->function); if (iscl) { - UNREACHABLE("Closure in upvalue"); + //UNREACHABLE("Closure in upvalue"); } closure |= iscl; From ec5e80bda5711545433851eae3547bf786e319fa Mon Sep 17 00:00:00 2001 From: Tim Atherton <54725421+softmattertheory@users.noreply.github.com> Date: Fri, 24 May 2024 15:19:14 -0400 Subject: [PATCH 137/181] Test for namespaces --- test/types/type_in_function_namespace.morpho | 11 +++++++++++ test/types/type_namespace.xmorpho | 15 +++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 test/types/type_in_function_namespace.morpho create mode 100644 test/types/type_namespace.xmorpho diff --git a/test/types/type_in_function_namespace.morpho b/test/types/type_in_function_namespace.morpho new file mode 100644 index 00000000..54885edb --- /dev/null +++ b/test/types/type_in_function_namespace.morpho @@ -0,0 +1,11 @@ +// Use types from a namespace in a function definition + +import "type_namespace.xmorpho" as ns + +print ns.Cat // expect: @Cat + +fn f(ns.Cat x) { + x.hiss() +} + +f(ns.Cat("Moggies")) // expect: Moggies hisses diff --git a/test/types/type_namespace.xmorpho b/test/types/type_namespace.xmorpho new file mode 100644 index 00000000..99ac1261 --- /dev/null +++ b/test/types/type_namespace.xmorpho @@ -0,0 +1,15 @@ +// Define some classes in a namespace + +class Pet { + init(name) { + self.name = name + } +} + +class Dog is Pet { } + +class Cat is Pet { + hiss() { + print "${self.name} hisses" + } +} From 77a453373bbd3ba91d5cb3ba19a973d57a5329dd Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Fri, 24 May 2024 21:59:59 -0400 Subject: [PATCH 138/181] Types from namespaces in function defintions --- src/core/compile.c | 34 +++++++++++++++---- src/core/compile.h | 8 ++++- src/support/parse.c | 11 ++++++ src/support/parse.h | 3 ++ .../type_in_function_fake_namespace.morpho | 9 +++++ ...ype_in_function_namespace_not_found.morpho | 9 +++++ 6 files changed, 67 insertions(+), 7 deletions(-) create mode 100644 test/types/type_in_function_fake_namespace.morpho create mode 100644 test/types/type_in_function_namespace_not_found.morpho diff --git a/src/core/compile.c b/src/core/compile.c index 6962c193..5c980c64 100644 --- a/src/core/compile.c +++ b/src/core/compile.c @@ -2800,12 +2800,31 @@ static registerindx compiler_functionparameters(compiler *c, syntaxtreeindx indx value type=MORPHO_NIL; syntaxtreenode *typenode = compiler_getnode(c, node->left); if (!typenode) UNREACHABLE("Incorrectly formed type node."); - if (!MORPHO_ISSTRING(typenode->content)) UNREACHABLE("Type node should have string label."); - - if (!compiler_findtype(c, typenode->content, &type)) { - compiler_error(c, node, COMPILE_UNKNWNTYPE, MORPHO_GETCSTRING(typenode->content)); - return REGISTER_UNALLOCATED; - } + if (typenode->type==NODE_DOT) { + syntaxtreenode *nsnode = compiler_getnode(c, typenode->left); + syntaxtreenode *labelnode = compiler_getnode(c, typenode->right); + + if (!(nsnode && + labelnode && + MORPHO_ISSTRING(nsnode->content) && + MORPHO_ISSTRING(labelnode->content))) UNREACHABLE("Incorrectly formed type namespace node."); + + if (!compiler_isnamespace(c, nsnode->content)) { + compiler_error(c, nsnode, COMPILE_UNKNWNNMSPC, MORPHO_GETCSTRING(nsnode->content)); + return REGISTER_UNALLOCATED; + } + + if (!compiler_findclasswithnamespace(c, typenode, nsnode->content, labelnode->content, &type)) { + compiler_error(c, typenode, COMPILE_SYMBOLNOTDEFINEDNMSPC, MORPHO_GETCSTRING(nsnode->content), MORPHO_GETCSTRING(labelnode->content)); + return REGISTER_UNALLOCATED; + } + + } else if (MORPHO_ISSTRING(typenode->content)) { + if (!compiler_findtype(c, typenode->content, &type)) { + compiler_error(c, node, COMPILE_UNKNWNTYPE, MORPHO_GETCSTRING(typenode->content)); + return REGISTER_UNALLOCATED; + } + } else UNREACHABLE("Type node should have string label."); registerindx reg = compiler_functionparameters(c, node->right); compiler_regsettype(c, reg, type); @@ -4209,6 +4228,9 @@ void compile_initialize(void) { morpho_defineerror(COMPILE_MSSNGINDX, ERROR_COMPILE, COMPILE_MSSNGINDX_MSG); morpho_defineerror(COMPILE_TYPEVIOLATION, ERROR_COMPILE, COMPILE_TYPEVIOLATION_MSG); morpho_defineerror(COMPILE_UNKNWNTYPE, ERROR_COMPILE, COMPILE_UNKNWNTYPE_MSG); + morpho_defineerror(COMPILE_UNKNWNNMSPC, ERROR_COMPILE, COMPILE_UNKNWNNMSPC_MSG); + morpho_defineerror(COMPILE_UNKNWNTYPENMSPC, ERROR_COMPILE, COMPILE_UNKNWNTYPENMSPC_MSG); + morpho_defineerror(COMPILE_CLSSLNRZ, ERROR_COMPILE, COMPILE_CLSSLNRZ_MSG); morpho_addfinalizefn(compile_finalize); diff --git a/src/core/compile.h b/src/core/compile.h index 21cd089e..c5adc07a 100644 --- a/src/core/compile.h +++ b/src/core/compile.h @@ -109,7 +109,13 @@ #define COMPILE_TYPEVIOLATION_MSG "Type violation: Attempting to assign type %s to %s variable %s." #define COMPILE_UNKNWNTYPE "UnknwnType" -#define COMPILE_UNKNWNTYPE_MSG "Unknown type %s." +#define COMPILE_UNKNWNTYPE_MSG "Unknown type '%s'." + +#define COMPILE_UNKNWNNMSPC "UnknwnNmSpc" +#define COMPILE_UNKNWNNMSPC_MSG "Unknown namespace '%s'." + +#define COMPILE_UNKNWNTYPENMSPC "UnknwnTypeNmSpc" +#define COMPILE_UNKNWNTYPENMSPC_MSG "Unknown type '%s' in namespace '%s'." #define COMPILE_CLSSLNRZ "ClssLnrz" #define COMPILE_CLSSLNRZ_MSG "Can't linearize class %s: Check parent and ancestor classes for conflicting inheritance order." diff --git a/src/support/parse.c b/src/support/parse.c index a1fb18b2..89ccc68a 100644 --- a/src/support/parse.c +++ b/src/support/parse.c @@ -432,6 +432,16 @@ bool parse_arglist(parser *p, tokentype rightdelimiter, unsigned int *nargs, voi syntaxtreeindx label; PARSE_CHECK(parse_symbol(p, &label)); + PARSE_CHECK(parse_addnode(p, NODE_TYPE, MORPHO_NIL, &start, current, label, ¤t)); + } else if (parse_checktokenadvance(p, TOKEN_DOT)) { // Symbol followed by dot is a type in a namespace + syntaxtreeindx type, label; + + PARSE_CHECK(parse_checkrequiredtoken(p, TOKEN_SYMBOL, PARSE_SYMBLEXPECTED)); + PARSE_CHECK(parse_symbol(p, &type)); + PARSE_CHECK(parse_checkrequiredtoken(p, TOKEN_SYMBOL, PARSE_SYMBLEXPECTED)); + PARSE_CHECK(parse_symbol(p, &label)); + + PARSE_CHECK(parse_addnode(p, NODE_DOT, MORPHO_NIL, &start, current, type, ¤t)); PARSE_CHECK(parse_addnode(p, NODE_TYPE, MORPHO_NIL, &start, current, label, ¤t)); } else if (parse_checktokenadvance(p, TOKEN_EQUAL)) { // Symbol followed by equals is an optional argument syntaxtreeindx val; @@ -1817,6 +1827,7 @@ void parse_initialize(void) { morpho_defineerror(PARSE_MISSINGSEMICOLONEXP, ERROR_PARSE, PARSE_MISSINGSEMICOLONEXP_MSG); morpho_defineerror(PARSE_MISSINGSEMICOLONVAR, ERROR_PARSE, PARSE_MISSINGSEMICOLONVAR_MSG); morpho_defineerror(PARSE_VAREXPECTED, ERROR_PARSE, PARSE_VAREXPECTED_MSG); + morpho_defineerror(PARSE_SYMBLEXPECTED, ERROR_PARSE, PARSE_SYMBLEXPECTED_MSG); morpho_defineerror(PARSE_BLOCKTERMINATOREXP, ERROR_PARSE, PARSE_BLOCKTERMINATOREXP_MSG); morpho_defineerror(PARSE_MSSNGSQBRC, ERROR_PARSE, PARSE_MSSNGSQBRC_MSG); morpho_defineerror(PARSE_MSSNGCOMMA, ERROR_PARSE, PARSE_MSSNGCOMMA_MSG); diff --git a/src/support/parse.h b/src/support/parse.h index f507a2ef..c9120362 100644 --- a/src/support/parse.h +++ b/src/support/parse.h @@ -124,6 +124,9 @@ struct sparser { #define PARSE_VAREXPECTED "VarExpct" #define PARSE_VAREXPECTED_MSG "Variable name expected after var." +#define PARSE_SYMBLEXPECTED "SymblExpct" +#define PARSE_SYMBLEXPECTED_MSG "Symbol expected." + #define PARSE_BLOCKTERMINATOREXP "MssngBrc" #define PARSE_BLOCKTERMINATOREXP_MSG "Expected '}' to finish block." diff --git a/test/types/type_in_function_fake_namespace.morpho b/test/types/type_in_function_fake_namespace.morpho new file mode 100644 index 00000000..664cb921 --- /dev/null +++ b/test/types/type_in_function_fake_namespace.morpho @@ -0,0 +1,9 @@ +// Use a type that doesn't exist from a namespace in a function definition + +import "type_namespace.xmorpho" as ns + +fn f(foo.Dog x) { + x.hiss() +} + +f(ns.Cat("Moggies")) // expect error 'UnknwnNmSpc' \ No newline at end of file diff --git a/test/types/type_in_function_namespace_not_found.morpho b/test/types/type_in_function_namespace_not_found.morpho new file mode 100644 index 00000000..25892dd9 --- /dev/null +++ b/test/types/type_in_function_namespace_not_found.morpho @@ -0,0 +1,9 @@ +// Use a type that doesn't exist from a namespace in a function definition + +import "type_namespace.xmorpho" as ns + +fn f(ns.Hamster x) { + x.hiss() +} + +f(ns.Cat("Moggies")) // expect error 'SymblUndfNmSpc' \ No newline at end of file From b0864a427b1b3b1e6c96546fef58fa3cfc75a1c5 Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Sat, 25 May 2024 06:55:04 -0400 Subject: [PATCH 139/181] Add todo --- src/core/compile.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/compile.c b/src/core/compile.c index 5c980c64..06bad852 100644 --- a/src/core/compile.c +++ b/src/core/compile.c @@ -1328,7 +1328,7 @@ static void _findfunctionref(compiler *c, value symbol, bool *hasclosure, varray if (MORPHO_ISEQUAL(ref->symbol, symbol)) { bool iscl = function_isclosure(ref->function); if (iscl) { - //UNREACHABLE("Closure in upvalue"); + // TODO: Handle closures correctly deep in the stack } closure |= iscl; From b8e028c44f58066c338d5cc9a8694065d6509315 Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Sun, 26 May 2024 18:10:12 -0400 Subject: [PATCH 140/181] Typed variable from namespace --- src/support/parse.c | 23 ++++++++++++++++++++--- test/types/type_from_namespace.morpho | 7 +++++++ 2 files changed, 27 insertions(+), 3 deletions(-) create mode 100644 test/types/type_from_namespace.morpho diff --git a/src/support/parse.c b/src/support/parse.c index 89ccc68a..a3446b60 100644 --- a/src/support/parse.c +++ b/src/support/parse.c @@ -70,7 +70,8 @@ void parse_savestate(parser *p, parser *op, lexer *ol) { *op = *p; } -/** Restores the parser from a saved position */ +/** Restores the parser from a saved position. + @warning: You must take care to ensure no new objects have been allocated prior to calling this. */ void parse_restorestate(parser *op, lexer *ol, parser *out) { *out = *op; *out->lex = *ol; @@ -1010,7 +1011,6 @@ bool parse_typedvardeclaration(parser *p, void *out) { lexer ol; // Store the state of the parser parser op; parse_savestate(p, &op, &ol); - parse_advance(p); if (parse_checktoken(p, TOKEN_SYMBOL)) { // It is a typed variable declaration @@ -1018,7 +1018,24 @@ bool parse_typedvardeclaration(parser *p, void *out) { PARSE_CHECK(parse_symbol(p, &type)); PARSE_CHECK(parse_vardeclaration(p, &var)); PARSE_CHECK(parse_addnode(p, NODE_TYPE, MORPHO_NIL, &start, type, var, &new)); - } else { // It was really an expression statement + } else if (parse_checktokenadvance(p, TOKEN_DOT) && // Check that we actually match a var declaration + parse_checktokenadvance(p, TOKEN_SYMBOL) && + parse_checktokenadvance(p, TOKEN_SYMBOL)) { + parse_restorestate(&op, &ol, p); // Match successful, so rewind the parser + parse_advance(p); + + syntaxtreeindx namespace=SYNTAXTREE_UNCONNECTED, type=SYNTAXTREE_UNCONNECTED, var=SYNTAXTREE_UNCONNECTED; + + PARSE_CHECK(parse_symbol(p, &namespace)); + parse_advance(p); + parse_advance(p); // Advance over TOKEN_DOT + + PARSE_CHECK(parse_symbol(p, &type)); + PARSE_CHECK(parse_vardeclaration(p, &var)); + + PARSE_CHECK(parse_addnode(p, NODE_DOT, MORPHO_NIL, &start, namespace, type, &type)); + PARSE_CHECK(parse_addnode(p, NODE_TYPE, MORPHO_NIL, &start, type, var, &new)); + } else { // Perhaps it was really an expression statement parse_restorestate(&op, &ol, p); PARSE_CHECK(parse_statement(p, &new)); } diff --git a/test/types/type_from_namespace.morpho b/test/types/type_from_namespace.morpho new file mode 100644 index 00000000..f5f53df1 --- /dev/null +++ b/test/types/type_from_namespace.morpho @@ -0,0 +1,7 @@ +// Use a type that doesn't exist from a namespace in a function definition + +import "type_namespace.xmorpho" as ns + +ns.Cat x = ns.Cat("Phineas") + +x.hiss() // expect: Phineas hisses From 7539cf431344f1eb3b7509637a909b8fd94d13a1 Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Mon, 27 May 2024 08:46:11 -0400 Subject: [PATCH 141/181] Infers type from constructors in namespaces --- src/core/compile.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/core/compile.c b/src/core/compile.c index 06bad852..6c05a100 100644 --- a/src/core/compile.c +++ b/src/core/compile.c @@ -3094,16 +3094,25 @@ static codeinfo compiler_call(compiler *c, syntaxtreenode *node, registerindx re compiler_beginargs(c); - // Compile the function selector + // Check if the call is a constructor syntaxtreenode *selnode=compiler_getnode(c, node->left); - codeinfo func = compiler_nodetobytecode(c, node->left, (reqouttype==NODE_SYMBOL) { + if (selnode->type==NODE_SYMBOL) { // A regular call from a symbol compiler_findtype(c, selnode->content, &rtype); + } else if (selnode->type==NODE_DOT) { // An constructor in a namespace? + syntaxtreenode *nsnode = compiler_getnode(c, selnode->left); + syntaxtreenode *snode = compiler_getnode(c, selnode->right); + if (nsnode && snode && + compiler_isnamespace(c, nsnode->content)) { + compiler_findclasswithnamespace(c, snode, nsnode->content, snode->content, &rtype); + compiler_catch(c, COMPILE_SYMBOLNOTDEFINEDNMSPC); // We don't care if it wasn't there + } } + // Compile the selector + codeinfo func = compiler_nodetobytecode(c, node->left, (reqouttype==NODE_SYMBOL && compiler_catch(c, COMPILE_SYMBOLNOTDEFINED)) { syntaxtreenode *symbol=compiler_getnode(c, node->left); From c83f8461c70a08a071da33eee30f8aef55277905 Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Mon, 27 May 2024 18:02:41 -0400 Subject: [PATCH 142/181] Duplicate implementations separated by scope --- src/core/compile.c | 34 ++++++++++++++++--- src/datastructures/signature.c | 4 +-- test/types/multiple_dispatch/duplicate.morpho | 3 +- .../multiple_dispatch/duplicate_scope.morpho | 13 +++++++ 4 files changed, 46 insertions(+), 8 deletions(-) create mode 100644 test/types/multiple_dispatch/duplicate_scope.morpho diff --git a/src/core/compile.c b/src/core/compile.c index 6c05a100..08158a10 100644 --- a/src/core/compile.c +++ b/src/core/compile.c @@ -1318,24 +1318,50 @@ codeinfo compiler_metafunction(compiler *c, syntaxtreenode *node, int n, value * return out; } +/** Checks for duplicate function refs */ +static bool _checkduplicateref(varray_functionref *refs, functionref *match) { + for (int i=0; icount; i++) { + functionref *r = &refs->data[i]; + // Match functionrefs with the same signature, but only if they're + // in a different scope or parent + if (signature_isequal(&r->function->sig, &match->function->sig) && + (r->function->parent!=match->function->parent || + r->scopedepth!=match->scopedepth)) return true; + } + return false; +} + /** Collects function implementations that match a given symbol */ static void _findfunctionref(compiler *c, value symbol, bool *hasclosure, varray_value *out) { bool closure=false; + + varray_functionref refs; + varray_functionrefinit(&refs); + functionstate *fc = compiler_currentfunctionstate(c); - for (functionstate *f=fc; f>=c->fstack; f--) { - for (int i=0; ifunctionref.count; i++) { + for (functionstate *f=fc; f>=c->fstack; f--) { // Go backwards to prioritize recent def'ns + for (int i=f->functionref.count-1; i>=0; i--) { // Go backwards functionref *ref=&f->functionref.data[i]; - if (MORPHO_ISEQUAL(ref->symbol, symbol)) { + if (MORPHO_ISEQUAL(ref->symbol, symbol) && + !_checkduplicateref(&refs, ref)) { bool iscl = function_isclosure(ref->function); if (iscl) { // TODO: Handle closures correctly deep in the stack } closure |= iscl; - varray_valuewrite(out, MORPHO_OBJECT(ref->function)); + varray_functionrefadd(&refs, ref, 1); } } } + + // Return the collected implementations + for (int i=0; itypes.count!=b->types.count) return false; - for (int i=0; itypes.count; i++) if (!MORPHO_ISEQUAL(a->types.data[i], a->types.data[i])) return false; - return false; + for (int i=0; itypes.count; i++) if (!MORPHO_ISEQUAL(a->types.data[i], b->types.data[i])) return false; + return true; } /** @brief Return list of types */ diff --git a/test/types/multiple_dispatch/duplicate.morpho b/test/types/multiple_dispatch/duplicate.morpho index 20fe833e..5ccb282d 100644 --- a/test/types/multiple_dispatch/duplicate.morpho +++ b/test/types/multiple_dispatch/duplicate.morpho @@ -8,7 +8,6 @@ fn f(x, Int y) { return 1 } -f(1,1) - +print f(1,1) // expect error 'MltplDisptchAmbg' diff --git a/test/types/multiple_dispatch/duplicate_scope.morpho b/test/types/multiple_dispatch/duplicate_scope.morpho new file mode 100644 index 00000000..83d7bb41 --- /dev/null +++ b/test/types/multiple_dispatch/duplicate_scope.morpho @@ -0,0 +1,13 @@ +// Duplicate implementations separated by scope + +fn f(Int x, Int y) { + return 0 +} + +{ + fn f(Int x, Int y) { + return 1 + } + + print f(1,1) // expect: 1 +} From da2609f7c250c86ea6afad05918d96b08125524c Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Mon, 27 May 2024 21:55:12 -0400 Subject: [PATCH 143/181] Nested closure tests --- .../multi_nested_closure.morpho | 22 +++++++++++++++++++ .../multiple_dispatch/nested_closure.morpho | 17 ++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 test/types/multiple_dispatch/multi_nested_closure.morpho create mode 100644 test/types/multiple_dispatch/nested_closure.morpho diff --git a/test/types/multiple_dispatch/multi_nested_closure.morpho b/test/types/multiple_dispatch/multi_nested_closure.morpho new file mode 100644 index 00000000..c9b6e55c --- /dev/null +++ b/test/types/multiple_dispatch/multi_nested_closure.morpho @@ -0,0 +1,22 @@ +// Nested closures + +fn g(String x) { + return x.count() +} + +fn f(Int x) { + fn g(Int y) { + return y + x + } + + fn g(List y) { + return y.count() + x + } + + return g +} + +var a = f(2) +print a(1) // expect: 3 +print a([1,1,1]) // expect: 5 +print a("Hello!") // expect: 6 diff --git a/test/types/multiple_dispatch/nested_closure.morpho b/test/types/multiple_dispatch/nested_closure.morpho new file mode 100644 index 00000000..ea0399f0 --- /dev/null +++ b/test/types/multiple_dispatch/nested_closure.morpho @@ -0,0 +1,17 @@ +// Nested closures + +fn f(Int x) { + fn g(Int y) { + return y + x + } + + fn g(List y) { + return y.count() + x + } + + return g +} + +var a = f(2) +print a(1) // expect: 3 +print a([1,1,1]) // expect: 5 From f870700339519f4c97ec1611c43f02e00becff1d Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Mon, 27 May 2024 22:25:05 -0400 Subject: [PATCH 144/181] Deeply nested closures --- src/core/compile.c | 45 ++++++++++++------- .../deep_nested_closure.morpho | 28 ++++++++++++ 2 files changed, 58 insertions(+), 15 deletions(-) create mode 100644 test/types/multiple_dispatch/deep_nested_closure.morpho diff --git a/src/core/compile.c b/src/core/compile.c index 08158a10..8c5e0cb2 100644 --- a/src/core/compile.c +++ b/src/core/compile.c @@ -1291,6 +1291,28 @@ bool compiler_findmetafunction(compiler *c, value symbol, int n, value *fns, cod return false; } +/** Finds a closure, looking back up the functionstate stack and propagating upvalues as necessary */ +void _findclosure(compiler *c, objectfunction *closure, codeinfo *out) { + functionstate *fc = compiler_currentfunctionstate(c); + + if (fc->func==closure->parent) { + out->returntype=REGISTER; + out->dest = (registerindx) closure->creg; + } else { + out->returntype=UPVALUE; + + for (functionstate *f=fc; f>=c->fstack; f--) { + if (f->func==closure->parent) { + f->registers.data[closure->creg].iscaptured=true; + out->dest=compiler_propagateupvalues(c, f, closure->creg); + return; + } + } + + UNREACHABLE("Couldn't locate parent of closure."); + } +} + /** Compile a metafunction constructor by setting up a call to the Metafunction() constructor */ codeinfo compiler_metafunction(compiler *c, syntaxtreenode *node, int n, value *fns) { codeinfo out = compiler_findbuiltin(c, node, METAFUNCTION_CLASSNAME, REGISTER_UNALLOCATED); @@ -1300,8 +1322,7 @@ codeinfo compiler_metafunction(compiler *c, syntaxtreenode *node, int n, value * codeinfo val = CODEINFO(CONSTANT, REGISTER_UNALLOCATED, 0); if (MORPHO_ISFUNCTION(fns[i]) && function_isclosure(MORPHO_GETFUNCTION(fns[i]))) { - val.returntype=REGISTER; - val.dest = (registerindx) MORPHO_GETFUNCTION(fns[i])->creg; + _findclosure(c, MORPHO_GETFUNCTION(fns[i]), &val); } else { val.dest = compiler_addconstant(c, node, fns[i], true, false); } @@ -1344,12 +1365,7 @@ static void _findfunctionref(compiler *c, value symbol, bool *hasclosure, varray functionref *ref=&f->functionref.data[i]; if (MORPHO_ISEQUAL(ref->symbol, symbol) && !_checkduplicateref(&refs, ref)) { - bool iscl = function_isclosure(ref->function); - if (iscl) { - // TODO: Handle closures correctly deep in the stack - } - - closure |= iscl; + closure |= function_isclosure(ref->function); varray_functionrefadd(&refs, ref, 1); } } @@ -1381,8 +1397,7 @@ bool compiler_resolvefunctionref(compiler *c, syntaxtreenode *node, value symbol if (fns.count>1) { // If the list contains a closure, we must build the MF at runtime *out = compiler_metafunction(c, node, fns.count, fns.data); } else { // If just one closure, no need to build a metafunction - out->returntype=REGISTER; - out->dest=MORPHO_GETFUNCTION(fns.data[0])->creg; + _findclosure(c, MORPHO_GETFUNCTION(fns.data[0]), out); out->ninstructions=0; } } else if (!compiler_findmetafunction(c, symbol, fns.count, fns.data, out)) { @@ -3528,17 +3543,17 @@ static codeinfo compiler_symbol(compiler *c, syntaxtreenode *node, registerindx return ret; } + /* Is it a reference to a function? */ + if (compiler_resolvefunctionref(c, node, node->content, &ret)) { + return ret; + } + /* Is it an upvalue? */ ret.dest = compiler_resolveupvalue(c, node->content); if (ret.dest!=REGISTER_UNALLOCATED) { ret.returntype=UPVALUE; return ret; } - - /* Is it a reference to a function? */ - if (compiler_resolvefunctionref(c, node, node->content, &ret)) { - return ret; - } /* Is it a global variable */ ret.dest=compiler_findglobal(c, node->content, true); diff --git a/test/types/multiple_dispatch/deep_nested_closure.morpho b/test/types/multiple_dispatch/deep_nested_closure.morpho new file mode 100644 index 00000000..88286b9f --- /dev/null +++ b/test/types/multiple_dispatch/deep_nested_closure.morpho @@ -0,0 +1,28 @@ +// Multiple implementations with nested closures + +fn foo(List x) { + return x.count() +} + +fn f(Int x) { + fn foo(Int y) { + return y + x + } + + fn g() { + fn foo(Float y) { + return y + 2*x + } + + return foo + } + + return g +} + +var a = f(1) +var b = a() + +print b(1) // expect: 2 +print b(1.5) // expect: 3.5 +print b([1,2,3]) // expect: 3 From e046a13033a972e89cf16086e4fafd439e33a68e Mon Sep 17 00:00:00 2001 From: Chaitanya Joshi Date: Tue, 28 May 2024 21:13:13 -0400 Subject: [PATCH 145/181] Further simplify visits, add comments --- modules/graphics.morpho | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/modules/graphics.morpho b/modules/graphics.morpho index ad5b144c..756ead79 100644 --- a/modules/graphics.morpho +++ b/modules/graphics.morpho @@ -628,10 +628,20 @@ class Show { out.write("W \"${graphic.title}\"") } + /* The visit method for a generic `item`. + + Here, we define this separate `visitgeneric` method that performs the intended generic task, and have the actual generic `visit` method call `visitgeneric`. This enables calls to the generic fallback from *within a specific implementation*. + For example, the `visit` method for a `Sphere` object can call `self.visitgeneric(item, out)` to visit the `TriangleComplex` representation of the sphere, but calling `self.visit(item, out)` would result in an infinite loop. + */ visitgeneric(item, out) { self.visit(item.totrianglecomplex(), out) } + // Will be used by Cylinder, Arrow and Tube + visit(item, out) { + self.visitgeneric(item, out) + } + visit(Sphere item, out) { var n = item.refinementlevel() @@ -652,10 +662,6 @@ class Show { out.write("d ${self.spheres[n].id}") } - visit(Cylinder item, out) { self.visitgeneric(item, out) } - visit(Arrow item, out) { self.visitgeneric(item, out) } - visit(Tube item, out) { self.visitgeneric(item, out) } - trianglecomplexobjectdata(item, out) { var x = item.position var n = item.normals From 72225608a9cd6ae7a51e92366007678b1253e609 Mon Sep 17 00:00:00 2001 From: Chaitanya Joshi Date: Thu, 30 May 2024 10:43:35 -0400 Subject: [PATCH 146/181] Added more tests for multiple dispatch --- .../duplicate_no_type.morpho | 30 +++++++++++++++++ .../namespace_for_new.morpho | 13 ++++++++ .../namespace_for_overwrite.morpho | 33 +++++++++++++++++++ 3 files changed, 76 insertions(+) create mode 100644 test/types/multiple_dispatch/duplicate_no_type.morpho create mode 100644 test/types/multiple_dispatch/namespace_for_new.morpho create mode 100644 test/types/multiple_dispatch/namespace_for_overwrite.morpho diff --git a/test/types/multiple_dispatch/duplicate_no_type.morpho b/test/types/multiple_dispatch/duplicate_no_type.morpho new file mode 100644 index 00000000..305e3fea --- /dev/null +++ b/test/types/multiple_dispatch/duplicate_no_type.morpho @@ -0,0 +1,30 @@ +// Duplicate implementations with no Type annotations. +// This one currently segfaults due to an infinite loop. + +/* Backtrace from LLDB: (the loop goes mfcompile_set -> mfcompile_dispatchany -> mfcompile_dispatchonparam -> mfcompile_set) + frame #62738: 0x00000001005335f8 libmorpho.dylib`mfcompile_set + 252 + frame #62739: 0x0000000100533f20 libmorpho.dylib`mfcompile_dispatchany + 332 + frame #62740: 0x00000001005341c4 libmorpho.dylib`mfcompile_dispatchonparam + 520 + frame #62741: 0x00000001005335f8 libmorpho.dylib`mfcompile_set + 252 + frame #62742: 0x0000000100534878 libmorpho.dylib`metafunction_compile + 344 + frame #62743: 0x000000010053aa9c libmorpho.dylib`compiler_resolvefunctionref + 848 + frame #62744: 0x000000010053b134 libmorpho.dylib`compiler_symbol + 228 + frame #62745: 0x0000000100543198 libmorpho.dylib`compiler_call + 2156 + frame #62746: 0x000000010053d820 libmorpho.dylib`compiler_print + 88 + frame #62747: 0x0000000100541664 libmorpho.dylib`compiler_sequence + 412 + frame #62748: 0x00000001005450f0 libmorpho.dylib`morpho_compile + 220 + frame #62749: 0x0000000100001dc4 morpho6`cli_run + 224 + frame #62750: 0x000000010000bb3c morpho6`main + 424 + frame #62751: 0x000000018385a0e0 dyld`start + 2360 +*/ +fn f(x) { + return 0 +} + +fn f(x) { + return 1 +} + +print f(1) +// expect error 'MltplDisptchAmbg' + diff --git a/test/types/multiple_dispatch/namespace_for_new.morpho b/test/types/multiple_dispatch/namespace_for_new.morpho new file mode 100644 index 00000000..672f6e36 --- /dev/null +++ b/test/types/multiple_dispatch/namespace_for_new.morpho @@ -0,0 +1,13 @@ +// Namespace with 'for' keyword, with new implementation in local file + +import "namespace.xmorpho" for f + +// Here, we add a new implementation for a Matrix input + +fn f(Matrix a) { + print a.dimensions() + +} + +f(Matrix(2,2)) +// expect: [ 2, 2 ] diff --git a/test/types/multiple_dispatch/namespace_for_overwrite.morpho b/test/types/multiple_dispatch/namespace_for_overwrite.morpho new file mode 100644 index 00000000..5cfc3c29 --- /dev/null +++ b/test/types/multiple_dispatch/namespace_for_overwrite.morpho @@ -0,0 +1,33 @@ +// Namespace with 'for' keyword, and try to overwrite an existing signature + +import "namespace.xmorpho" for f + +// Here, we define an implementation for a String input, which already exists in the namespace, so it should raise an error. + +// This segfaults, with a slightly different compiler loop than in `duplicate_no_type.morpho`. + +/* Backttace from LLDB: (the loop goes mfcompile_set -> mfcompile_dispatchtable -> mfcompile_dispatchonparam -> mfcompile_set, so slightly differnt than in `duplicate_no_type.morpho`) + + frame #62739: 0x00000001005335f8 libmorpho.dylib`mfcompile_set + 252 + frame #62740: 0x0000000100533bb4 libmorpho.dylib`mfcompile_dispatchtable + 904 + frame #62741: 0x0000000100534170 libmorpho.dylib`mfcompile_dispatchonparam + 436 + frame #62742: 0x00000001005335f8 libmorpho.dylib`mfcompile_set + 252 + frame #62743: 0x0000000100534878 libmorpho.dylib`metafunction_compile + 344 + frame #62744: 0x000000010053aa9c libmorpho.dylib`compiler_resolvefunctionref + 848 + frame #62745: 0x000000010053b134 libmorpho.dylib`compiler_symbol + 228 + frame #62746: 0x0000000100543198 libmorpho.dylib`compiler_call + 2156 + frame #62747: 0x0000000100541664 libmorpho.dylib`compiler_sequence + 412 + frame #62748: 0x00000001005450f0 libmorpho.dylib`morpho_compile + 220 + frame #62749: 0x0000000100001dc4 morpho6`cli_run + 224 + frame #62750: 0x000000010000bb3c morpho6`main + 424 + frame #62751: 0x000000018385a0e0 dyld`start + 2360 + +*/ + +fn f(String a) { + print a.count() + +} + +f("Hi") +// expect error 'MltplDisptchAmbg' From 66dc72098bceafc031438c3713f31e669c4688de Mon Sep 17 00:00:00 2001 From: Chaitanya Joshi Date: Thu, 30 May 2024 10:46:31 -0400 Subject: [PATCH 147/181] Commenting being not sure of the expectation --- test/types/multiple_dispatch/namespace_for_new.morpho | 1 + 1 file changed, 1 insertion(+) diff --git a/test/types/multiple_dispatch/namespace_for_new.morpho b/test/types/multiple_dispatch/namespace_for_new.morpho index 672f6e36..f4851bf1 100644 --- a/test/types/multiple_dispatch/namespace_for_new.morpho +++ b/test/types/multiple_dispatch/namespace_for_new.morpho @@ -9,5 +9,6 @@ fn f(Matrix a) { } +// Do we want this to be a new implementation for the namespace function, or this is not intended / allowed? f(Matrix(2,2)) // expect: [ 2, 2 ] From e463decec6f16a2fcf18cb59341b3adec1eb86d6 Mon Sep 17 00:00:00 2001 From: Chaitanya Joshi Date: Thu, 30 May 2024 10:56:10 -0400 Subject: [PATCH 148/181] Duplicate with two arguments, no types --- .../multiple_dispatch/duplicate_no_type.morpho | 2 +- .../duplicate_no_type_two_arguments.morpho | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 test/types/multiple_dispatch/duplicate_no_type_two_arguments.morpho diff --git a/test/types/multiple_dispatch/duplicate_no_type.morpho b/test/types/multiple_dispatch/duplicate_no_type.morpho index 305e3fea..aaf358bd 100644 --- a/test/types/multiple_dispatch/duplicate_no_type.morpho +++ b/test/types/multiple_dispatch/duplicate_no_type.morpho @@ -1,4 +1,4 @@ -// Duplicate implementations with no Type annotations. +// Duplicate implementations with one argument no Type annotations. // This one currently segfaults due to an infinite loop. /* Backtrace from LLDB: (the loop goes mfcompile_set -> mfcompile_dispatchany -> mfcompile_dispatchonparam -> mfcompile_set) diff --git a/test/types/multiple_dispatch/duplicate_no_type_two_arguments.morpho b/test/types/multiple_dispatch/duplicate_no_type_two_arguments.morpho new file mode 100644 index 00000000..284ade6b --- /dev/null +++ b/test/types/multiple_dispatch/duplicate_no_type_two_arguments.morpho @@ -0,0 +1,13 @@ +// Duplicate implementations with two arguments and no type annotations + +fn f(x, y) { + return 0 +} + +fn f(x, y) { + return 1 +} + +print f(1,1) +// expect error 'MltplDisptchAmbg' + From db527c3d105fc95206962386317458301e9f0e42 Mon Sep 17 00:00:00 2001 From: Chaitanya Joshi Date: Thu, 30 May 2024 11:04:57 -0400 Subject: [PATCH 149/181] Create duplicate_one_arg.morpho --- .../multiple_dispatch/duplicate_one_arg.morpho | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 test/types/multiple_dispatch/duplicate_one_arg.morpho diff --git a/test/types/multiple_dispatch/duplicate_one_arg.morpho b/test/types/multiple_dispatch/duplicate_one_arg.morpho new file mode 100644 index 00000000..5caad800 --- /dev/null +++ b/test/types/multiple_dispatch/duplicate_one_arg.morpho @@ -0,0 +1,13 @@ +// Duplicate implementations + +fn f(Int x) { + return 0 +} + +fn f(Int x) { + return 1 +} + +print f(1) +// expect error 'MltplDisptchAmbg' + From d88f7679f4da2d6d4bf37a358443a252687e73c3 Mon Sep 17 00:00:00 2001 From: Chaitanya Joshi Date: Thu, 30 May 2024 11:07:53 -0400 Subject: [PATCH 150/181] Add comments to duplicate_one_arg --- test/types/multiple_dispatch/duplicate_one_arg.morpho | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/types/multiple_dispatch/duplicate_one_arg.morpho b/test/types/multiple_dispatch/duplicate_one_arg.morpho index 5caad800..0ea73c23 100644 --- a/test/types/multiple_dispatch/duplicate_one_arg.morpho +++ b/test/types/multiple_dispatch/duplicate_one_arg.morpho @@ -1,4 +1,5 @@ -// Duplicate implementations +// Duplicate implementations with a single Typed argument. +// This also segfaults similar to `duplicate_no_type.morpho` fn f(Int x) { return 0 From c127b6db4f14d03c3d50fff5a65269e2cfe4e8ba Mon Sep 17 00:00:00 2001 From: Tim Atherton <54725421+softmattertheory@users.noreply.github.com> Date: Fri, 31 May 2024 07:40:29 -0400 Subject: [PATCH 151/181] Catch recursion for single parameter functions --- src/classes/metafunction.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/classes/metafunction.c b/src/classes/metafunction.c index 0ab1ca2e..b40c8ce3 100644 --- a/src/classes/metafunction.c +++ b/src/classes/metafunction.c @@ -723,7 +723,9 @@ mfindx mfcompile_set(mfcompiler *c, mfset *set) { if (min!=max) return mfcompile_dispatchonnarg(c, set, min, max); // If just one parameter, dispatch on it - if (min==1) return mfcompile_dispatchonparam(c, set, 0); + if (min==1 && !mfcompiler_ischecked(c, 0)) { + return mfcompile_dispatchonparam(c, set, 0); + } int best; if (mfcompile_countoutcomes(c, set, &best)) return mfcompile_dispatchonparam(c, set, best); From b6114454bfe045c4a95f644eeffc51f8dec17e25 Mon Sep 17 00:00:00 2001 From: Tim Atherton <54725421+softmattertheory@users.noreply.github.com> Date: Fri, 31 May 2024 08:05:40 -0400 Subject: [PATCH 152/181] Remove stacktraces from tests --- .../duplicate_no_type.morpho | 16 -------------- .../namespace_for_overwrite.morpho | 21 ------------------- 2 files changed, 37 deletions(-) diff --git a/test/types/multiple_dispatch/duplicate_no_type.morpho b/test/types/multiple_dispatch/duplicate_no_type.morpho index aaf358bd..86b6f6eb 100644 --- a/test/types/multiple_dispatch/duplicate_no_type.morpho +++ b/test/types/multiple_dispatch/duplicate_no_type.morpho @@ -1,22 +1,6 @@ // Duplicate implementations with one argument no Type annotations. // This one currently segfaults due to an infinite loop. -/* Backtrace from LLDB: (the loop goes mfcompile_set -> mfcompile_dispatchany -> mfcompile_dispatchonparam -> mfcompile_set) - frame #62738: 0x00000001005335f8 libmorpho.dylib`mfcompile_set + 252 - frame #62739: 0x0000000100533f20 libmorpho.dylib`mfcompile_dispatchany + 332 - frame #62740: 0x00000001005341c4 libmorpho.dylib`mfcompile_dispatchonparam + 520 - frame #62741: 0x00000001005335f8 libmorpho.dylib`mfcompile_set + 252 - frame #62742: 0x0000000100534878 libmorpho.dylib`metafunction_compile + 344 - frame #62743: 0x000000010053aa9c libmorpho.dylib`compiler_resolvefunctionref + 848 - frame #62744: 0x000000010053b134 libmorpho.dylib`compiler_symbol + 228 - frame #62745: 0x0000000100543198 libmorpho.dylib`compiler_call + 2156 - frame #62746: 0x000000010053d820 libmorpho.dylib`compiler_print + 88 - frame #62747: 0x0000000100541664 libmorpho.dylib`compiler_sequence + 412 - frame #62748: 0x00000001005450f0 libmorpho.dylib`morpho_compile + 220 - frame #62749: 0x0000000100001dc4 morpho6`cli_run + 224 - frame #62750: 0x000000010000bb3c morpho6`main + 424 - frame #62751: 0x000000018385a0e0 dyld`start + 2360 -*/ fn f(x) { return 0 } diff --git a/test/types/multiple_dispatch/namespace_for_overwrite.morpho b/test/types/multiple_dispatch/namespace_for_overwrite.morpho index 5cfc3c29..ef8640e7 100644 --- a/test/types/multiple_dispatch/namespace_for_overwrite.morpho +++ b/test/types/multiple_dispatch/namespace_for_overwrite.morpho @@ -4,29 +4,8 @@ import "namespace.xmorpho" for f // Here, we define an implementation for a String input, which already exists in the namespace, so it should raise an error. -// This segfaults, with a slightly different compiler loop than in `duplicate_no_type.morpho`. - -/* Backttace from LLDB: (the loop goes mfcompile_set -> mfcompile_dispatchtable -> mfcompile_dispatchonparam -> mfcompile_set, so slightly differnt than in `duplicate_no_type.morpho`) - - frame #62739: 0x00000001005335f8 libmorpho.dylib`mfcompile_set + 252 - frame #62740: 0x0000000100533bb4 libmorpho.dylib`mfcompile_dispatchtable + 904 - frame #62741: 0x0000000100534170 libmorpho.dylib`mfcompile_dispatchonparam + 436 - frame #62742: 0x00000001005335f8 libmorpho.dylib`mfcompile_set + 252 - frame #62743: 0x0000000100534878 libmorpho.dylib`metafunction_compile + 344 - frame #62744: 0x000000010053aa9c libmorpho.dylib`compiler_resolvefunctionref + 848 - frame #62745: 0x000000010053b134 libmorpho.dylib`compiler_symbol + 228 - frame #62746: 0x0000000100543198 libmorpho.dylib`compiler_call + 2156 - frame #62747: 0x0000000100541664 libmorpho.dylib`compiler_sequence + 412 - frame #62748: 0x00000001005450f0 libmorpho.dylib`morpho_compile + 220 - frame #62749: 0x0000000100001dc4 morpho6`cli_run + 224 - frame #62750: 0x000000010000bb3c morpho6`main + 424 - frame #62751: 0x000000018385a0e0 dyld`start + 2360 - -*/ - fn f(String a) { print a.count() - } f("Hi") From 3e020f428f5831bc880da188a7734bd7d1bc86c0 Mon Sep 17 00:00:00 2001 From: Tim Atherton <54725421+softmattertheory@users.noreply.github.com> Date: Fri, 31 May 2024 10:03:28 -0400 Subject: [PATCH 153/181] Displays module information in stacktraces --- src/debug/debug.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/debug/debug.c b/src/debug/debug.c index 6b84b716..6321fe09 100644 --- a/src/debug/debug.c +++ b/src/debug/debug.c @@ -212,8 +212,16 @@ void morpho_stacktrace(vm *v) { else morpho_printf(v, "global"); int line=0; - if (debug_infofromindx(v->current, indx, NULL, &line, NULL, NULL, NULL)) { + + value module = MORPHO_NIL; + if (debug_infofromindx(v->current, indx, &module, &line, NULL, NULL, NULL)) { morpho_printf(v, " at line %u", line); + + if (!MORPHO_ISNIL(module)) { + morpho_printf(v, " in module '"); + morpho_printvalue(v, module); + morpho_printf(v, "'"); + } } morpho_printf(v, "\n"); From 01796731415e40f7a707105582b92f729e19f5ed Mon Sep 17 00:00:00 2001 From: Tim Atherton <54725421+softmattertheory@users.noreply.github.com> Date: Fri, 31 May 2024 17:53:44 -0400 Subject: [PATCH 154/181] Report file/module information in error reports --- src/classes/json.c | 4 ++-- src/core/compile.c | 7 ++++--- src/core/vm.c | 11 ++++++----- src/datastructures/error.c | 14 ++++++++------ src/datastructures/error.h | 6 +++--- src/debug/debug.c | 6 +++--- src/morpho.h | 2 +- src/support/lex.c | 6 +++--- src/support/parse.c | 4 ++-- 9 files changed, 32 insertions(+), 28 deletions(-) diff --git a/src/classes/json.c b/src/classes/json.c index 44dcf3be..3afcdb7d 100644 --- a/src/classes/json.c +++ b/src/classes/json.c @@ -90,7 +90,7 @@ bool json_lexstring(lexer *l, token *tok, error *err) { } if (lex_isatend(l)) { - morpho_writeerrorwithid(err, LEXER_UNTERMINATEDSTRING, tok->line, tok->posn); + morpho_writeerrorwithid(err, LEXER_UNTERMINATEDSTRING, NULL, tok->line, tok->posn); return false; } @@ -144,7 +144,7 @@ bool json_lexnumber(lexer *l, token *tok, error *err) { return true; json_lexnumberinvld: - morpho_writeerrorwithid(err, JSON_NMBRFRMT, tok->line, tok->posn); + morpho_writeerrorwithid(err, JSON_NMBRFRMT, NULL, tok->line, tok->posn); return false; } diff --git a/src/core/compile.c b/src/core/compile.c index dbc51067..df1cfd63 100644 --- a/src/core/compile.c +++ b/src/core/compile.c @@ -43,9 +43,10 @@ static void compiler_error(compiler *c, syntaxtreenode *node, errorid id, ... ) int line = (node ? node->line : ERROR_POSNUNIDENTIFIABLE); int posn = (node ? node->posn : ERROR_POSNUNIDENTIFIABLE); + char *file = (MORPHO_ISSTRING(c->currentmodule) ? MORPHO_GETCSTRING(c->currentmodule) : NULL); + va_start(args, id); - morpho_writeerrorwithidvalist(&c->err, id, line, posn, args); - + morpho_writeerrorwithidvalist(&c->err, id, NULL, line, posn, args); va_end(args); } @@ -3527,7 +3528,7 @@ static codeinfo compiler_import(compiler *c, syntaxtreenode *node, registerindx } } else { - c->err.module = cc.err.module; + c->err.file = (cc.err.file ? cc.err.file : MORPHO_GETCSTRING(modname)); } debugannotation_setmodule(&c->out->annotations, compiler_getmodule(c)); diff --git a/src/core/vm.c b/src/core/vm.c index 0e881b34..710b455a 100644 --- a/src/core/vm.c +++ b/src/core/vm.c @@ -216,10 +216,11 @@ static void vm_bindobjectwithoutcollect(vm *v, value obj) { void vm_runtimeerror(vm *v, ptrdiff_t iindx, errorid id, ...) { va_list args; int line=ERROR_POSNUNIDENTIFIABLE, posn=ERROR_POSNUNIDENTIFIABLE; - debug_infofromindx(v->current, iindx, NULL, &line, &posn, NULL, NULL); - + value module=MORPHO_NIL; + debug_infofromindx(v->current, iindx, &module, &line, &posn, NULL, NULL); + va_start(args, id); - morpho_writeerrorwithidvalist(&v->err, id, line, posn, args); + morpho_writeerrorwithidvalist(&v->err, id, NULL, line, posn, args); va_end(args); } @@ -1548,7 +1549,7 @@ void morpho_runtimeerror(vm *v, errorid id, ...) { error_init(&err); va_start(args, id); - morpho_writeerrorwithidvalist(&err, id, ERROR_POSNUNIDENTIFIABLE, ERROR_POSNUNIDENTIFIABLE, args); + morpho_writeerrorwithidvalist(&err, id, NULL, ERROR_POSNUNIDENTIFIABLE, ERROR_POSNUNIDENTIFIABLE, args); va_end(args); morpho_error(v, &err); @@ -1562,7 +1563,7 @@ void morpho_runtimewarning(vm *v, errorid id, ...) { error_init(&err); va_start(args, id); - morpho_writeerrorwithidvalist(&err, id, ERROR_POSNUNIDENTIFIABLE, ERROR_POSNUNIDENTIFIABLE, args); + morpho_writeerrorwithidvalist(&err, id, NULL, ERROR_POSNUNIDENTIFIABLE, ERROR_POSNUNIDENTIFIABLE, args); va_end(args); morpho_warning(v, &err); diff --git a/src/datastructures/error.c b/src/datastructures/error.c index 9fe2aa94..8c662ece 100644 --- a/src/datastructures/error.c +++ b/src/datastructures/error.c @@ -32,9 +32,10 @@ DEFINE_VARRAY(errordefinition, errordefinition) /** Prints to an error block * @param err Error struct to fill out * @param cat The category of error */ -static void error_printf(error *err, errorcategory cat, int line, int posn, char *message, va_list args) { +static void error_printf(error *err, errorcategory cat, char *file, int line, int posn, char *message, va_list args) { err->cat=cat; + err->file=file; err->line=line; err->posn=posn; @@ -47,7 +48,7 @@ static void error_printf(error *err, errorcategory cat, int line, int posn, char void error_clear(error *err) { err->cat=ERROR_NONE; err->id=NULL; - err->module=NULL; + err->file=NULL; err->line=ERROR_POSNUNIDENTIFIABLE; err->posn=ERROR_POSNUNIDENTIFIABLE; } @@ -84,14 +85,14 @@ bool morpho_getdefinitionfromid(errorid id, errordefinition **def) { * @param line The line at which the error occurred, if identifiable. * @param posn The position in the line at which the error occurred, if identifiable. * @param args Additional parameters (the data for the printf commands in the message) */ -void morpho_writeerrorwithidvalist(error *err, errorid id, int line, int posn, va_list args) { +void morpho_writeerrorwithidvalist(error *err, errorid id, char *file, int line, int posn, va_list args) { error_init(err); errordefinition *def; err->id=id; if (morpho_getdefinitionfromid(id, &def)) { /* Print the message with requested args */ - error_printf(err, def->cat, line, posn, def->msg, args); + error_printf(err, def->cat, file, line, posn, def->msg, args); } else { UNREACHABLE("Undefined error generated."); } @@ -100,13 +101,14 @@ void morpho_writeerrorwithidvalist(error *err, errorid id, int line, int posn, v /** @brief Writes an error message to an error structure * @param err The error structure * @param id The error id. + * @param file The file in which the error ocdured, if relevant. * @param line The line at which the error occurred, if identifiable. * @param posn The position in the line at which the error occurred, if identifiable. * @param ... Additional parameters (the data for the printf commands in the message) */ -void morpho_writeerrorwithid(error *err, errorid id, int line, int posn, ...) { +void morpho_writeerrorwithid(error *err, errorid id, char *file, int line, int posn, ...) { va_list args; va_start(args, posn); - morpho_writeerrorwithidvalist(err, id, line, posn, args); + morpho_writeerrorwithidvalist(err, id, file, line, posn, args); va_end(args); } diff --git a/src/datastructures/error.h b/src/datastructures/error.h index 0db334e3..756cf1f3 100644 --- a/src/datastructures/error.h +++ b/src/datastructures/error.h @@ -65,7 +65,7 @@ typedef errorcategory morphoerror; typedef struct { errorcategory cat; errorid id; - char *module; + char *file; int line, posn; char msg[MORPHO_ERRORSTRINGSIZE]; } error; @@ -195,8 +195,8 @@ void morpho_unreachable(const char *explanation); void error_init(error *err); void error_clear(error *err); -void morpho_writeerrorwithid(error *err, errorid id, int line, int posn, ...); -void morpho_writeerrorwithidvalist(error *err, errorid id, int line, int posn, va_list args); +void morpho_writeerrorwithid(error *err, errorid id, char *file, int line, int posn, ...); +void morpho_writeerrorwithidvalist(error *err, errorid id, char *file, int line, int posn, va_list args); void morpho_writeusererror(error *err, errorid id, char *message); void morpho_defineerror(errorid id, errorcategory cat, char *message); diff --git a/src/debug/debug.c b/src/debug/debug.c index 6321fe09..15c6a6f3 100644 --- a/src/debug/debug.c +++ b/src/debug/debug.c @@ -279,7 +279,7 @@ void debugger_error(debugger *debug, errorid id, ... ) { if (!debug->err || debug->err->id!=ERROR_NONE) return; // Ensure errors are not overwritten. va_list args; va_start(args, id); - morpho_writeerrorwithidvalist(debug->err, id, ERROR_POSNUNIDENTIFIABLE, ERROR_POSNUNIDENTIFIABLE, args); + morpho_writeerrorwithidvalist(debug->err, id, NULL, ERROR_POSNUNIDENTIFIABLE, ERROR_POSNUNIDENTIFIABLE, args); va_end(args); } @@ -626,9 +626,9 @@ void debugger_showlocation(debugger *debug, instructionindx indx) { else morpho_printf(v, "anonymous fn"); if (!MORPHO_ISNIL(module)) { - morpho_printf(v, " in \""); + morpho_printf(v, " in '"); morpho_printvalue(v, module); - morpho_printf(v, "\""); + morpho_printf(v, "'"); } morpho_printf(v, " at line %i [instruction %ti]", line, indx); } diff --git a/src/morpho.h b/src/morpho.h index 986fac1d..41a66277 100644 --- a/src/morpho.h +++ b/src/morpho.h @@ -103,7 +103,7 @@ extern value cloneselector; void morpho_version(version *v); /* Error handling */ -void morpho_writeerrorwithid(error *err, errorid id, int line, int position, ...); +void morpho_writeerrorwithid(error *err, errorid id, char *file, int line, int posn, ...); void morpho_defineerror(errorid id, errorcategory cat, char *message); errorid morpho_geterrorid(error *err); diff --git a/src/support/lex.c b/src/support/lex.c index b80115c3..c04773f4 100644 --- a/src/support/lex.c +++ b/src/support/lex.c @@ -291,7 +291,7 @@ bool lex_skipmultilinecomment(lexer *l, token *tok, error *err) { switch (c) { case '\0': /* If we come to the end of the file, the token is marked as incomplete. */ - morpho_writeerrorwithid(err, LEXER_UNTERMINATEDCOMMENT, startline, startpsn); + morpho_writeerrorwithid(err, LEXER_UNTERMINATEDCOMMENT, NULL, startline, startpsn); lex_recordtoken(l, TOKEN_INCOMPLETE, tok); return false; case '\n': @@ -400,7 +400,7 @@ bool lex_string(lexer *l, token *tok, error *err) { if (lex_isatend(l)) { /* Unterminated string */ - morpho_writeerrorwithid(err, LEXER_UNTERMINATEDSTRING, startline, startpsn); + morpho_writeerrorwithid(err, LEXER_UNTERMINATEDSTRING, NULL, startline, startpsn); lex_recordtoken(l, TOKEN_INCOMPLETE, tok); return false; } @@ -672,7 +672,7 @@ bool lex(lexer *l, token *tok, error *err) { if (lex_identifytoken(l, &defn)) { lex_recordtoken(l, defn->type, tok); } else { - morpho_writeerrorwithid(err, LEXER_UNRECOGNIZEDTOKEN, l->line, l->posn); + morpho_writeerrorwithid(err, LEXER_UNRECOGNIZEDTOKEN, NULL, l->line, l->posn); return false; } diff --git a/src/support/parse.c b/src/support/parse.c index 6043530f..66654d63 100644 --- a/src/support/parse.c +++ b/src/support/parse.c @@ -38,7 +38,7 @@ void parse_error(parser *p, bool use_prev, errorid id, ... ) { if (ERROR_FAILED(*p->err)) return; va_start(args, id); - morpho_writeerrorwithid(p->err, id, tok->line, tok->posn, args); + morpho_writeerrorwithid(p->err, id, NULL, tok->line, tok->posn, args); va_end(args); } @@ -1683,7 +1683,7 @@ bool parse_stringtovaluearray(char *string, unsigned int nmax, value *v, unsigne case TOKEN_EOF: break; default: - morpho_writeerrorwithid(err, PARSE_UNRECGNZEDTOK, ERROR_POSNUNIDENTIFIABLE, ERROR_POSNUNIDENTIFIABLE); + morpho_writeerrorwithid(err, PARSE_UNRECGNZEDTOK, NULL, ERROR_POSNUNIDENTIFIABLE, ERROR_POSNUNIDENTIFIABLE); return false; break; } From 4af689076ea1014462034d58aa8f8327e4592ba8 Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Sat, 1 Jun 2024 09:14:37 -0400 Subject: [PATCH 155/181] Modifications in response to review Added duplicate_no_arg test --- help/functions.md | 1 + test/types/multiple_dispatch/duplicate_no_arg.morpho | 12 ++++++++++++ .../types/multiple_dispatch/duplicate_no_type.morpho | 1 - .../types/multiple_dispatch/namespace_for_new.morpho | 1 - 4 files changed, 13 insertions(+), 2 deletions(-) create mode 100644 test/types/multiple_dispatch/duplicate_no_arg.morpho diff --git a/help/functions.md b/help/functions.md index 8ee88012..f062b6ae 100644 --- a/help/functions.md +++ b/help/functions.md @@ -106,6 +106,7 @@ Note that optional and variadic parameters are not typed. # Multiple dispatch [tagmultiple]: # (multiple) [tagdispatch]: # (dispatch) +[tagmultipledispatch]: # (multipledispatch) Morpho supports *multiple dispatch*, whereby you can define several implementations of a function that accept different types: diff --git a/test/types/multiple_dispatch/duplicate_no_arg.morpho b/test/types/multiple_dispatch/duplicate_no_arg.morpho new file mode 100644 index 00000000..edf68741 --- /dev/null +++ b/test/types/multiple_dispatch/duplicate_no_arg.morpho @@ -0,0 +1,12 @@ +// Duplicate implementations with no arguments. + +fn f() { + return 0 +} + +fn f() { + return 1 +} + +print f() +// expect error 'MltplDisptchAmbg' \ No newline at end of file diff --git a/test/types/multiple_dispatch/duplicate_no_type.morpho b/test/types/multiple_dispatch/duplicate_no_type.morpho index 86b6f6eb..da8fcb61 100644 --- a/test/types/multiple_dispatch/duplicate_no_type.morpho +++ b/test/types/multiple_dispatch/duplicate_no_type.morpho @@ -1,5 +1,4 @@ // Duplicate implementations with one argument no Type annotations. -// This one currently segfaults due to an infinite loop. fn f(x) { return 0 diff --git a/test/types/multiple_dispatch/namespace_for_new.morpho b/test/types/multiple_dispatch/namespace_for_new.morpho index f4851bf1..672f6e36 100644 --- a/test/types/multiple_dispatch/namespace_for_new.morpho +++ b/test/types/multiple_dispatch/namespace_for_new.morpho @@ -9,6 +9,5 @@ fn f(Matrix a) { } -// Do we want this to be a new implementation for the namespace function, or this is not intended / allowed? f(Matrix(2,2)) // expect: [ 2, 2 ] From 2af0d5ad4101aae03aab91a0c5b3f5f774b064eb Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Sat, 1 Jun 2024 09:24:59 -0400 Subject: [PATCH 156/181] Update metafunction error call --- src/classes/metafunction.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/classes/metafunction.c b/src/classes/metafunction.c index b40c8ce3..58cdf5e9 100644 --- a/src/classes/metafunction.c +++ b/src/classes/metafunction.c @@ -221,7 +221,7 @@ void mfcompiler_clear(mfcompiler *c, objectmetafunction *fn) { /** Report an error during metafunction compilation */ void mfcompiler_error(mfcompiler *c, errorid id) { - morpho_writeerrorwithid(&c->err, id, ERROR_POSNUNIDENTIFIABLE, ERROR_POSNUNIDENTIFIABLE); + morpho_writeerrorwithid(&c->err, id, NULL, ERROR_POSNUNIDENTIFIABLE, ERROR_POSNUNIDENTIFIABLE); } /** Pushes a parameter check onto the stack*/ From 1ad95114ec5133be9cf35d5da4f0b074861c9f4e Mon Sep 17 00:00:00 2001 From: Tim Atherton <54725421+softmattertheory@users.noreply.github.com> Date: Thu, 6 Jun 2024 08:17:00 -0400 Subject: [PATCH 157/181] Revert "Merge branch 'discretization' into dev" This reverts commit 10f2d3f14982a0d174dcfa72a2457e658a7561d8, reversing changes made to d5485a20edcf9854ac5c824508ae4464439793a5. --- src/builtin/builtin.c | 73 ++-- src/builtin/functiondefs.c | 4 +- src/geometry/CMakeLists.txt | 14 +- src/geometry/discretization.c | 461 --------------------- src/geometry/discretization.h | 85 ---- src/geometry/field.c | 360 ++++++---------- src/geometry/field.h | 12 +- src/geometry/functional.c | 101 ++--- src/geometry/geometry.c | 15 - src/geometry/geometry.h | 19 - src/geometry/integrate.c | 333 +++++++++------ src/geometry/integrate.h | 29 +- src/geometry/mesh.c | 8 +- src/geometry/mesh.h | 1 - src/geometry/selection.c | 3 +- src/linalg/matrix.c | 13 +- src/linalg/matrix.h | 1 - test/discretization/cg2.morpho | 17 - test/discretization/cg2_2d.morpho | 24 -- test/discretization/cg2_grad.morpho | 23 - test/discretization/cg2_interpolate.morpho | 15 - 21 files changed, 423 insertions(+), 1188 deletions(-) delete mode 100644 src/geometry/discretization.c delete mode 100644 src/geometry/discretization.h delete mode 100644 src/geometry/geometry.c delete mode 100644 src/geometry/geometry.h delete mode 100644 test/discretization/cg2.morpho delete mode 100644 test/discretization/cg2_2d.morpho delete mode 100644 test/discretization/cg2_grad.morpho delete mode 100644 test/discretization/cg2_interpolate.morpho diff --git a/src/builtin/builtin.c b/src/builtin/builtin.c index caedaf55..59bf74c3 100644 --- a/src/builtin/builtin.c +++ b/src/builtin/builtin.c @@ -10,9 +10,13 @@ #include "functiondefs.h" #include "file.h" #include "system.h" -#include "geometry.h" #include "classes.h" +#include "mesh.h" +#include "selection.h" +#include "functional.h" +#include "field.h" + /* ********************************************************************** * Global data * ********************************************************************** */ @@ -56,20 +60,20 @@ void builtin_init(objectbuiltinfunction *func) { bool builtin_enumerateloop(vm *v, value obj, builtin_loopfunction fn, void *ref) { value enumerate=MORPHO_NIL; value count=MORPHO_NIL, in=MORPHO_INTEGER(-1), val=MORPHO_NIL; - + if (morpho_lookupmethod(obj, enumerateselector, &enumerate)) { if (!morpho_invoke(v, obj, enumerate, 1, &in, &count)) return false; if (!MORPHO_ISINTEGER(count)) return false; - + for (indx i=0; ifunction=func; new->name=object_stringfromcstring(name, strlen(name)); new->flags=flags; out = MORPHO_OBJECT(new); - + value selector = dictionary_intern(&builtin_symboltable, new->name); - + if (dictionary_get(_currentfunctiontable, new->name, NULL)) { UNREACHABLE("Redefinition of function in same extension [in builtin.c]"); } - + dictionary_insert(_currentfunctiontable, selector, out); } - + return out; } @@ -221,16 +225,16 @@ value builtin_addclass(char *name, builtinclassentry desc[], value superclass) { objectclass *new = object_newclass(label); varray_valuewrite(&builtin_objects, MORPHO_OBJECT(new)); objectclass *superklass = NULL; - + if (!new) return MORPHO_NIL; - + /** Copy methods from superclass */ if (MORPHO_ISCLASS(superclass)) { superklass = MORPHO_GETCLASS(superclass); dictionary_copy(&superklass->methods, &new->methods); new->superclass=superklass; } - + for (unsigned int i=0; desc[i].name!=NULL; i++) { if (desc[i].type==BUILTIN_METHOD) { objectbuiltinfunction *newmethod = (objectbuiltinfunction *) object_new(sizeof(objectbuiltinfunction), OBJECT_BUILTINFUNCTION); @@ -264,13 +268,13 @@ value builtin_addclass(char *name, builtinclassentry desc[], value superclass) { } else dictionary_insert(&new->methods, selector, method); } } - + if (dictionary_get(_currentclasstable, label, NULL)) { UNREACHABLE("Redefinition of class in same extension [in builtin.c]"); } - + dictionary_insert(_currentclasstable, label, MORPHO_OBJECT(new)); - + return MORPHO_OBJECT(new); } @@ -319,18 +323,18 @@ void builtin_initialize(void) { dictionary_init(&builtin_classtable); dictionary_init(&builtin_symboltable); varray_valueinit(&builtin_objects); - + builtin_setfunctiontable(&builtin_functiontable); builtin_setclasstable(&builtin_classtable); - + // Initialize core object types objectstringtype=object_addtype(&objectstringdefn); objectclasstype=object_addtype(&objectclassdefn); objectbuiltinfunctiontype=object_addtype(&objectbuiltinfunctiondefn); - + /* Initialize builtin classes and functions */ instance_initialize(); // Must initialize first so that Object exists - + string_initialize(); // Classes function_initialize(); metafunction_initialize(); @@ -345,24 +349,27 @@ void builtin_initialize(void) { complex_initialize(); err_initialize(); tuple_initialize(); - + float_initialize();// Veneer classes int_initialize(); - + file_initialize(); system_initialize(); json_initialize(); - + // Initialize function definitions functiondefs_initialize(); - + // Initialize linear algebra matrix_initialize(); sparse_initialize(); - + // Initialize geometry - geometry_initialize(); - + mesh_initialize(); + selection_initialize(); + field_initialize(); + functional_initialize(); + morpho_addfinalizefn(builtin_finalize); } diff --git a/src/builtin/functiondefs.c b/src/builtin/functiondefs.c index 875cab1b..0dc36fbe 100644 --- a/src/builtin/functiondefs.c +++ b/src/builtin/functiondefs.c @@ -11,7 +11,9 @@ #include "builtin.h" #include "common.h" #include "matrix.h" -#include "geometry.h" +#include "mesh.h" +#include "field.h" +#include "selection.h" #include "cmplx.h" #ifndef M_PI diff --git a/src/geometry/CMakeLists.txt b/src/geometry/CMakeLists.txt index cad815fb..74d9e689 100644 --- a/src/geometry/CMakeLists.txt +++ b/src/geometry/CMakeLists.txt @@ -1,12 +1,10 @@ target_sources(morpho PRIVATE - discretization.c discretization.h - field.c field.h - functional.c functional.h - geometry.c geometry.h - integrate.c integrate.h - mesh.c mesh.h - selection.c selection.h + field.c field.h + functional.c functional.h + integrate.c integrate.h + mesh.c mesh.h + selection.c selection.h ) target_sources(morpho @@ -14,10 +12,8 @@ target_sources(morpho FILE_SET public_headers TYPE HEADERS FILES - discretization.h field.h functional.h - geometry.h integrate.h mesh.h selection.h diff --git a/src/geometry/discretization.c b/src/geometry/discretization.c deleted file mode 100644 index 2cc0bc9d..00000000 --- a/src/geometry/discretization.c +++ /dev/null @@ -1,461 +0,0 @@ -/** @file discretization.c - * @author T J Atherton - * - * @brief Finite element discretizations - */ - -#include "geometry.h" - -/* ********************************************************************** - * Discretization objects - * ********************************************************************** */ - -objecttype objectdiscretizationtype; - -/** Field object definitions */ -void objectdiscretization_printfn(object *obj, void *v) { - objectdiscretization *disc=(objectdiscretization *) obj; - morpho_printf(v, "", disc->discretization->name); -} - -size_t objectdiscretization_sizefn(object *obj) { - return sizeof(objectdiscretization); -} - -objecttypedefn objectdiscretizationdefn = { - .printfn=objectdiscretization_printfn, - .markfn=NULL, - .freefn=NULL, - .sizefn=objectdiscretization_sizefn -}; - -/** Creates a new discretization object - * @param[in] discretization - discretization definition to use */ -objectdiscretization *objectdiscretization_new(discretization *disc) { - objectdiscretization *new = (objectdiscretization *) object_new(sizeof(objectdiscretization), OBJECT_DISCRETIZATION); - if (new) new->discretization=disc; - - return new; -} - -/* ********************************************************************** - * Discretization definitions - * ********************************************************************** */ - -#define LINE_OPCODE 1 -#define AREA_OPCODE 2 -#define QUANTITY_OPCODE 255 - -#define LINE(id, v1, v2) LINE_OPCODE, id, v1, v2 // Identify a grade 1 subelement given by two vertex indices -#define AREA(id, v1, v2, v3) AREA_OPCODE, id, v1, v2, v3 // Identify a grade 2 subelement given by three vertex indices -#define QUANTITY(grade, id, qno) QUANTITY_OPCODE, grade, id, qno // Fetch quantity from subelement of grade with id and quantity number -#define ENDDEFN -1 - -/* ------------------------------------------------------- - * CG1 element in 1D - * ------------------------------------------------------- */ - -/* - * 0 - 1 // One degree of freedom per vertex - */ - -void cg1_1dinterpolate(double *lambda, double *wts) { - wts[0]=lambda[0]; - wts[1]=lambda[1]; -} - -unsigned int cg1_1dshape[] = { 1, 0 }; - -double cg1_1dnodes[] = { 0.0, 1.0 }; - -eldefninstruction cg1_1ddefn[] = { - QUANTITY(0,0,0), // Fetch quantity on vertex 0 - QUANTITY(0,1,0), // Fetch quantity on vertex 1 - ENDDEFN -}; - -discretization cg1_1d = { - .name = "CG1", - .grade = 1, - .shape = cg1_1dshape, - .degree = 1, - .nnodes = 2, - .nsubel = 0, - .nodes = cg1_1dnodes, - .ifn = cg1_1dinterpolate, - .eldefn = cg1_1ddefn -}; - -/* ------------------------------------------------------- - * CG2 element in 1D - * ------------------------------------------------------- */ - -/* - * 0 - 2 - 1 // One degree of freedom per vertex; one at the midpoint - */ - -void cg2_1dinterpolate(double *lambda, double *wts) { - double dl = (lambda[0]-lambda[1]); - wts[0]=lambda[0]*dl; - wts[1]=-lambda[1]*dl; - wts[2]=4*lambda[0]*lambda[1]; -} - -unsigned int cg2_1dshape[] = { 1, 1 }; - -double cg2_1dnodes[] = { 0.0, 1.0, 0.5 }; - -eldefninstruction cg2_1ddefn[] = { - LINE(0,0,1), // Identify line subelement with vertex indices (0,1) - QUANTITY(0,0,0), // Fetch quantity on vertex 0 - QUANTITY(0,1,0), // Fetch quantity on vertex 1 - QUANTITY(1,0,0), // Fetch quantity from line subelement - ENDDEFN -}; - -discretization cg2_1d = { - .name = "CG2", - .grade = 1, - .shape = cg2_1dshape, - .degree = 2, - .nnodes = 3, - .nsubel = 1, - .nodes = cg2_1dnodes, - .ifn = cg2_1dinterpolate, - .eldefn = cg2_1ddefn -}; - -/* ------------------------------------------------------- - * CG3 element in 1D - * ------------------------------------------------------- */ - -/* - * 0 - 2 - 3 - 1 // One degree of freedom per vertex; two on the line - */ - -void cg3_1dinterpolate(double *lambda, double *wts) { - double a = (9.0/2.0)*lambda[0]*lambda[1]; - wts[0]=lambda[0]*(1-a); - wts[1]=lambda[1]*(1-a); - wts[2]=a*(2*lambda[0]-lambda[1]); - wts[3]=a*(2*lambda[1]-lambda[0]); -} - -unsigned int cg3_1dshape[] = { 1, 2 }; - -double cg3_1dnodes[] = { 0.0, 1.0, 1.0/3.0, 2.0/3.0 }; - -eldefninstruction cg3_1ddefn[] = { - LINE(0,0,1), // Identify line subelement with vertex indices (0,1) - QUANTITY(0,0,0), // Fetch quantity on vertex 0 - QUANTITY(0,1,0), // Fetch quantity on vertex 1 - QUANTITY(1,0,0), // Fetch quantity from line subelement - QUANTITY(1,0,1), // Fetch quantity from line subelement - ENDDEFN -}; - -discretization cg3_1d = { - .name = "CG3", - .grade = 1, - .shape = cg3_1dshape, - .degree = 3, - .nnodes = 4, - .nsubel = 1, - .nodes = cg3_1dnodes, - .ifn = cg3_1dinterpolate, - .eldefn = cg3_1ddefn -}; - -/* ------------------------------------------------------- - * CG1 element in 2D - * ------------------------------------------------------- */ - -/* 2 - * |\ - * 0-1 // One degree of freedom per vertex - */ - -void cg1_2dinterpolate(double *lambda, double *wts) { - wts[0]=lambda[0]; - wts[1]=lambda[1]; - wts[2]=lambda[2]; -} - -unsigned int cg1_2dshape[] = { 1, 0, 0 }; - -double cg1_2dnodes[] = { 0.0, 0.0, - 1.0, 0.0, - 0.0, 1.0 }; - -eldefninstruction cg1_2deldefn[] = { - QUANTITY(0,0,0), // Fetch quantity on vertex 0 - QUANTITY(0,1,0), // Fetch quantity on vertex 1 - QUANTITY(0,2,0), // Fetch quantity on vertex 2 - ENDDEFN -}; - -discretization cg1_2d = { - .name = "CG1", - .grade = 2, - .shape = cg1_2dshape, - .degree = 1, - .nnodes = 3, - .nsubel = 0, - .nodes = cg1_2dnodes, - .ifn = cg1_2dinterpolate, - .eldefn = cg1_2deldefn -}; - -/* ------------------------------------------------------- - * CG2 element in 2D - * ------------------------------------------------------- */ - -/* 2 - * |\ - * 5 4 - * | \ - * 0-3-1 // One degree of freedom per vertex; one at the midpoint - */ - -void cg2_2dinterpolate(double *lambda, double *wts) { - wts[0]=lambda[0]*(2*lambda[0]-1); - wts[1]=lambda[1]*(2*lambda[1]-1); - wts[2]=lambda[2]*(2*lambda[2]-1); - wts[3]=4*lambda[0]*lambda[1]; - wts[4]=4*lambda[1]*lambda[2]; - wts[5]=4*lambda[2]*lambda[0]; -} - -void cg2_2dgrad(double *lambda, double *grad) { - double g[] = - { 4*lambda[0]-1, 0, 0, - 0, 4*lambda[1]-1, 0, - 0, 0, 4*lambda[2]-1, - 4*lambda[1], 4*lambda[0], 0, - 0, 4*lambda[2], 4*lambda[1], - 4*lambda[2], 0, 4*lambda[0] }; - memcpy(grad, g, sizeof(g)); -} - -unsigned int cg2_2dshape[] = { 1, 1, 0 }; - -double cg2_2dnodes[] = { 0.0, 0.0, - 1.0, 0.0, - 0.0, 1.0, - 0.5, 0.0, - 0.5, 0.5, - 0.0, 0.5 }; - -eldefninstruction cg2_2deldefn[] = { - LINE(0,0,1), // Identify line subelement with vertex indices (0,1) - LINE(1,1,2), // Identify line subelement with vertex indices (1,2) - LINE(2,2,0), // Identify line subelement with vertex indices (2,0) - QUANTITY(0,0,0), // Fetch quantity on vertex 0 - QUANTITY(0,1,0), // Fetch quantity on vertex 1 - QUANTITY(0,2,0), // Fetch quantity on vertex 2 - QUANTITY(1,0,0), // Fetch quantity from line 0 - QUANTITY(1,1,0), // Fetch quantity from line 1 - QUANTITY(1,2,0), // Fetch quantity from line 2 - ENDDEFN -}; - -discretization cg2_2d = { - .name = "CG2", - .grade = 2, - .shape = cg2_2dshape, - .degree = 2, - .nnodes = 6, - .nsubel = 3, - .nodes = cg2_2dnodes, - .ifn = cg2_2dinterpolate, - .gfn = cg2_2dgrad, - .eldefn = cg2_2deldefn -}; - -discretization *discretizations[] = { - &cg1_1d, - &cg2_1d, - &cg1_2d, - &cg2_2d, - NULL -}; - -/* ********************************************************************** - * Discretization functions - * ********************************************************************** */ - -/** Find a discretization definition based on a name and grade */ -discretization *discretization_find(char *name, grade g) { - for (int i=0; discretizations[i]!=NULL; i++) { - if (strcmp(name, discretizations[i]->name)==0 && - g==discretizations[i]->grade) return discretizations[i]; - } - return NULL; -} - -/** Finds a linear discretization for a given grade */ -discretization *discretization_findlinear(grade g) { - for (int i=0; discretizations[i]!=NULL; i++) { - if (discretizations[i]->grade && discretizations[i]->degree==1) return discretizations[i]; - } - return NULL; -} - -#define FETCH(instr) (*(instr++)) - -/** Steps through an element definition, generating subelements and identifying quantities */ -bool discretization_doftofieldindx(objectfield *field, discretization *disc, int nv, int *vids, int *dof) { - elementid subel[disc->nsubel+1]; // Element IDs of sub elements - int sid, svids[nv], nmatch, k=0; - - objectsparse *vmatrix[disc->grade+1]; // Vertex->elementid connectivity matrices - for (grade g=0; g<=disc->grade; g++) vmatrix[g]=mesh_addconnectivityelement(field->mesh, g, 0); - - for (eldefninstruction *instr=disc->eldefn; instr!=NULL && *instr!=ENDDEFN; ) { - eldefninstruction op=FETCH(instr); - switch(op) { - case LINE_OPCODE: // Find an element defined by n vertices - case AREA_OPCODE: // TODO: Need to cope with (mis) orientation of these subelements - { - sid = FETCH(instr); - for (int i=0; i<=op; i++) svids[i] = vids[FETCH(instr)]; - - if (!mesh_matchelements(vmatrix[1], op, op+1, svids, 1, &nmatch, &subel[sid])) return false; - } - break; - case QUANTITY_OPCODE: - { - grade g = FETCH(instr); - int sid = FETCH(instr), qno = FETCH(instr); - - if (!field_getindex(field, g, (g==0 ? vids[sid]: subel[sid]), qno, &dof[k])) return false; - k++; - } - break; - default: - UNREACHABLE("Error in finite element definition"); - } - } - return true; -} - -/** Constructs a layout matrix that maps element ids (columns) to degree of freedom indices in a field */ -bool discretization_layout(objectfield *field, discretization *disc, objectsparse **out) { - objectsparse *conn = mesh_getconnectivityelement(field->mesh, 0, disc->grade); - elementid nel=mesh_nelements(conn); - - objectsparse *new = object_newsparse(NULL, NULL); - if (!new) return false; - sparseccs_resize(&new->ccs, field->nelements, nel, nel*disc->nnodes, NULL); - - for (elementid id=0; idccs.cptr[id]=id*disc->nnodes; - if (!discretization_doftofieldindx(field, disc, nv, vids, new->ccs.rix+new->ccs.cptr[id])) goto discretization_layout_cleanup; - } - new->ccs.cptr[nel]=nel*disc->nnodes; // Last column pointer points to next column - - *out=new; - return true; - -discretization_layout_cleanup: - if (new) object_free((object *) new); - return false; -} - -void discretization_gradient(discretization *disc, double *lambda) { - int nbary = disc->grade+1; - - // Compute Jacobian for element - double jdata[disc->grade*disc->grade]; - objectmatrix jacobian = MORPHO_STATICMATRIX(jdata, disc->grade, disc->grade); - - // How the barycentric coordinates depend on the local coordinates - double ldata[disc->grade*nbary]; - memset(ldata, 0, sizeof(double)*disc->grade*nbary); - for (int i=0; igrade; i++) { - ldata[i]=-1; - ldata[disc->grade*(i+1)+i]=1; - } - objectmatrix lmat = MORPHO_STATICMATRIX(ldata, disc->grade, nbary); - - // Compute gradients of the basis functions - double gdata[disc->nnodes*nbary]; - (disc->gfn) (lambda, gdata); - objectmatrix gmat = MORPHO_STATICMATRIX(gdata, nbary, disc->nnodes); - - -} - -/* ********************************************************************** - * FunctionSpace class - * ********************************************************************** */ - -/** Constructs a functionspace object */ -value functionspace_constructor(vm *v, int nargs, value *args) { - value grd=MORPHO_INTEGER(1); - value out=MORPHO_NIL; - int nfixed; - - if (!builtin_options(v, nargs, args, &nfixed, 1, field_gradeoption, &grd)) - morpho_runtimeerror(v, FNSPC_ARGS); - - if (nfixed==1 && - MORPHO_ISSTRING(MORPHO_GETARG(args, 0)) && - MORPHO_ISINTEGER(grd)) { - char *label = MORPHO_GETCSTRING(MORPHO_GETARG(args, 0)); - - discretization *d=discretization_find(label, MORPHO_GETINTEGERVALUE(grd)); - - if (d) { - objectdiscretization *obj=objectdiscretization_new(d); - if (obj) { - out = MORPHO_OBJECT(obj); - morpho_bindobjects(v, 1, &out); - } else morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED); - } else morpho_runtimeerror(v, FNSPC_NOTFOUND, label, MORPHO_GETINTEGERVALUE(grd)); - - } else morpho_runtimeerror(v, FNSPC_ARGS); - - return out; -} - -value FunctionSpace_layout(vm *v, int nargs, value *args) { - value out=MORPHO_NIL; - objectdiscretization *slf = MORPHO_GETDISCRETIZATION(MORPHO_SELF(args)); - if (nargs==1 && MORPHO_ISFIELD(MORPHO_GETARG(args, 0))) { - objectfield *field = MORPHO_GETFIELD(MORPHO_GETARG(args, 0)); - objectsparse *new; - - if (discretization_layout(field, slf->discretization, &new)) { - out=MORPHO_OBJECT(new); - morpho_bindobjects(v, 1, &out); - } - } - return out; -} - -MORPHO_BEGINCLASS(FunctionSpace) -MORPHO_METHOD(FUNCTIONSPACE_LAYOUT_METHOD, FunctionSpace_layout, BUILTIN_FLAGSEMPTY) -MORPHO_ENDCLASS - -/* ********************************************************************** - * Initialization - * ********************************************************************** */ - -void discretization_initialize(void) { - objectdiscretizationtype=object_addtype(&objectdiscretizationdefn); - - builtin_addfunction(FUNCTIONSPACE_CLASSNAME, functionspace_constructor, BUILTIN_FLAGSEMPTY); - - objectstring objname = MORPHO_STATICSTRING(OBJECT_CLASSNAME); - value objclass = builtin_findclass(MORPHO_OBJECT(&objname)); - - value functionspaceclass=builtin_addclass(FUNCTIONSPACE_CLASSNAME, MORPHO_GETCLASSDEFINITION(FunctionSpace), objclass); - object_setveneerclass(OBJECT_DISCRETIZATION, functionspaceclass); - - morpho_defineerror(FNSPC_ARGS, ERROR_HALT, FNSPC_ARGS_MSG); - morpho_defineerror(FNSPC_NOTFOUND, ERROR_HALT, FNSPC_NOTFOUND_MSG); -} diff --git a/src/geometry/discretization.h b/src/geometry/discretization.h deleted file mode 100644 index b12bc07e..00000000 --- a/src/geometry/discretization.h +++ /dev/null @@ -1,85 +0,0 @@ -/** @file discretization.h - * @author T J Atherton - * - * @brief Finite element discretizations - */ - -#ifndef discretization_h -#define discretization_h - -#include "mesh.h" -#include "field.h" - -/* ------------------------------------------------------- - * Discretization type definitions - * ------------------------------------------------------- */ - -/** @brief Interpolation functions are called to assign weights to the nodes given barycentric coordinates */ -typedef void (*interpolationfn) (double *, double *); - -/** @brief Element definitions comprise a sequence of instructions to map field degrees of freedom to local nodes */ -typedef int eldefninstruction; - -/** @brief Discretization definitions */ -typedef struct { - char *name; /** Name of the discretization */ - grade grade; /** Grade of element this discretization is defined on */ - unsigned int *shape; /** Number of degrees of freedom on each grade; must have grade+1 entries */ - int degree; /** Highest degree of polynomial represented by this element */ - int nnodes; /** Number of nodes for this element type */ - int nsubel; /** Number of subelements used by */ - double *nodes; /** Node positions */ - interpolationfn ifn; /** Interpolation function; receives barycentric coordinates as input and returns weights per node */ - interpolationfn gfn; /** Gradient interpolation function */ - eldefninstruction *eldefn; /** Element definition */ -} discretization; - -/* ------------------------------------------------------- - * Discretization object type - * ------------------------------------------------------- */ - -extern objecttype objectdiscretizationtype; -#define OBJECT_DISCRETIZATION objectdiscretizationtype - -typedef struct { - object obj; - discretization *discretization; -} objectdiscretization; - -/** Tests whether an object is a discretization */ -#define MORPHO_ISDISCRETIZATION(val) object_istype(val, OBJECT_DISCRETIZATION) - -/** Gets the object as a discretization */ -#define MORPHO_GETDISCRETIZATION(val) ((objectdiscretization *) MORPHO_GETOBJECT(val)) - -/* ------------------------------------------------------- - * FunctionSpace veneer class - * ------------------------------------------------------- */ - -#define FUNCTIONSPACE_CLASSNAME "FunctionSpace" - -#define FUNCTIONSPACE_LAYOUT_METHOD "layout" - -/* ------------------------------------------------------- - * Discretization error messages - * ------------------------------------------------------- */ - -#define FNSPC_ARGS "FnSpcArgs" -#define FNSPC_ARGS_MSG "Function space must be initialized with a label and a grade." - -#define FNSPC_NOTFOUND "FnSpcNtFnd" -#define FNSPC_NOTFOUND_MSG "Function space '%s' on grade %i not found." - -/* ------------------------------------------------------- - * Discretization interface - * ------------------------------------------------------- */ - -discretization *discretization_find(char *name, grade g); -discretization *discretization_findlinear(grade g); - -bool discretization_doftofieldindx(objectfield *field, discretization *disc, int nv, int *vids, int *dof); -bool discretization_layout(objectfield *field, discretization *disc, objectsparse **out); - -void discretization_initialize(void); - -#endif diff --git a/src/geometry/field.c b/src/geometry/field.c index a62b7a76..99ffb685 100644 --- a/src/geometry/field.c +++ b/src/geometry/field.c @@ -9,11 +9,8 @@ #include "classes.h" #include "common.h" #include "matrix.h" -#include "sparse.h" -#include "geometry.h" -value field_gradeoption; -value field_functionspaceoption; +static value field_gradeoption; /* ********************************************************************** * Field objects @@ -29,13 +26,12 @@ void objectfield_printfn(object *obj, void *v) { void objectfield_markfn(object *obj, void *v) { objectfield *c = (objectfield *) obj; morpho_markvalue(v, c->prototype); - morpho_markvalue(v, c->fnspc); morpho_markobject(v, (object *) c->mesh); - } +} void objectfield_freefn(object *obj) { objectfield *f = (objectfield *) obj; - + if (f->dof) MORPHO_FREE(f->dof); if (f->offset) MORPHO_FREE(f->offset); if (f->pool) MORPHO_FREE(f->pool); @@ -65,12 +61,12 @@ bool field_checkprototype(value v) { unsigned int field_sizeprototype(value prototype) { unsigned int size = 1; - + if (MORPHO_ISMATRIX(prototype)) { objectmatrix *m = (MORPHO_GETMATRIX(prototype)); size = m->ncols*m->nrows; } - + return size; } @@ -85,7 +81,7 @@ unsigned int field_size(objectmesh *mesh, value prototype, unsigned int ngrades, unsigned int size = 0; unsigned int psize = field_sizeprototype(prototype); for (unsigned int i=0; i<=ngrades; i++) offsets[i]=0; - + if (!dof) { // Assume 1 element per vertex size=offsets[1]=mesh_nvertices(mesh)*psize; for (grade i=2; i<=ngrades; i++) offsets[i]=offsets[1]; @@ -96,7 +92,7 @@ unsigned int field_size(objectmesh *mesh, value prototype, unsigned int ngrades, size=offsets[i+1]*psize; } } - + return size; } @@ -104,41 +100,30 @@ unsigned int field_size(objectmesh *mesh, value prototype, unsigned int ngrades, /** Creates a new field * @param[in] mesh - Mesh the field is attached to * @param[in] prototype - a prototype object - * @param[in] disc - a prototype object - * @param[in] shape - (optional) number of degrees of freedom per entry in each grade (should be maxgrade entries) */ -objectfield *object_newfield(objectmesh *mesh, value prototype, value fnspc, unsigned int *shape) { + * @param[in] dof - umber of degrees of freedom per entry in each grade (should be maxgrade entries) */ +objectfield *object_newfield(objectmesh *mesh, value prototype, unsigned int *dof) { int ngrades=mesh_maxgrade(mesh)+1; - - unsigned int dof[ngrades+1]; // Extract shape from discretization or the provided function space - if (MORPHO_ISDISCRETIZATION(fnspc)) { - discretization *disc = MORPHO_GETDISCRETIZATION(fnspc)->discretization; - for (int i=0; i<=disc->grade; i++) dof[i]=disc->shape[i]; - for (int i=disc->grade+1; i<=ngrades; i++) dof[i]=0; - } else if (shape) { - for (int i=0; i<=ngrades; i++) dof[i]=shape[i]; - } - + unsigned int offset[ngrades+1]; unsigned int size=field_size(mesh, prototype, ngrades, dof, offset); objectfield *new=NULL; unsigned int *ndof = MORPHO_MALLOC(sizeof(int)*ngrades); unsigned int *noffset = MORPHO_MALLOC(sizeof(unsigned int)*(ngrades+1)); - + if (ndof && noffset) { new = (objectfield *) object_new(sizeof(objectfield)+sizeof(double)*size, OBJECT_FIELD); } - + if (new) { new->mesh=mesh; new->prototype=(MORPHO_ISNUMBER(prototype)? MORPHO_NIL : prototype); new->psize=field_sizeprototype(prototype); new->nelements=size/new->psize; new->ngrades=ngrades; - new->fnspc=(MORPHO_ISDISCRETIZATION(fnspc) ? fnspc : MORPHO_NIL); - + new->offset=noffset; memcpy(noffset, offset, sizeof(unsigned int)*(ngrades+1)); - + new->dof=ndof; if (dof) { memcpy(ndof, dof, sizeof(unsigned int)*ngrades); @@ -146,15 +131,15 @@ objectfield *object_newfield(objectmesh *mesh, value prototype, value fnspc, uns for (unsigned int i=0; ipool=NULL; - + /* Initialize the store */ object_init(&new->data.obj, OBJECT_MATRIX); new->data.ncols=1; new->data.nrows=size; new->data.elements=new->data.matrixdata; - + if (MORPHO_ISMATRIX(prototype)) { objectmatrix *mat = MORPHO_GETMATRIX(prototype); int mel = mat->ncols*mat->nrows; @@ -164,125 +149,61 @@ objectfield *object_newfield(objectmesh *mesh, value prototype, value fnspc, uns } else if(MORPHO_ISNUMBER(prototype)){ // if we have a number for our prototype set all the elements equal to it for (elementid i=0; ivert->ncols; i++) { - field_setelement(new, MESH_GRADE_VERTEX, i, 0, prototype); + field_setelement(new, MESH_GRADE_VERTEX, i, 0, prototype); } } else memset(new->data.elements, 0, sizeof(double)*size); - + } else { // Cleanup partially allocated structure if (noffset) MORPHO_FREE(noffset); if (ndof) MORPHO_FREE(ndof); } - + return new; } -/** Applies an initialization function to every vertex */ -bool field_applyfunctiontovertices(vm *v, objectmesh *mesh, value fn, objectfield *field) { - value coords[mesh->dim]; // Vertex coords - value ret=MORPHO_NIL; // Return value - int nv = mesh_nvertices(mesh); - - for (elementid i=0; idim, coords, &ret)) return false; - - if (!field_setelement(field, MESH_GRADE_VERTEX, i, 0, ret)) { - // if we can't set the field value to the ouptut of the function clean up - morpho_runtimeerror(v, FIELD_OPRETURN); - return false; - } - } - } - return true; -} - -/** Applies an initialization function to every DOF in an element */ -bool field_applyfunctiontoelements(vm *v, objectmesh *mesh, value fn, value fnspc, objectfield *field) { - if (!MORPHO_ISDISCRETIZATION(fnspc)) return false; - discretization *disc = MORPHO_GETDISCRETIZATION(fnspc)->discretization; - - objectsparse *conn = mesh_getconnectivityelement(mesh, 0, disc->grade); - if (!conn) return false; - elementid nel = mesh_nelements(conn); - - for (elementid id=0; idnnodes]; // Get indices corresponding to each degree of freedom per element - if (!discretization_doftofieldindx(field, disc, nv, vids, indx)) return false; - - for (int i=0; innodes; i++) { // Loop over nodes - double lambda[nv], ll=0.0; // Convert node positions in reference element to barycentric coordinates - for (int j=0; jnodes[i*disc->grade+j]; ll+=lambda[j+1]; } - lambda[0]=1-ll; - - double xx[mesh->dim]; // Interpolate position in physical space using barycentric coordinates - for (int j=0; jdim; j++) xx[j]=0.0; - for (int j=0; jdim, xx, lambda[j], x[j], xx); - - /*printf("<<"); - for (int j=0; jnodes[i*disc->grade+j]); - printf(">> "); - printf("["); - for (int j=0; jdim; j++) printf("%g ", xx[j]); - printf(": ");*/ - - value coords[mesh->dim], ret; - for (int j=0; jdim; j++) coords[j]=MORPHO_FLOAT(xx[j]); - - if (!morpho_call(v, fn, mesh->dim, coords, &ret)) return false; - - //morpho_printvalue(v, ret); - //printf(" -> %i\n", indx[i]); - - if (!field_setelementwithindex(field, indx[i], ret)) { - morpho_runtimeerror(v, FIELD_OPRETURN); - return false; - } - } - } - - return true; -} - /** Creates a field by applying a function to the vertices of a mesh * @param[in] v - virtual machine to use for function calls * @param[in] mesh - mesh to use * @param[in] fn - function to call * @returns field object or NULL on failure */ -objectfield *field_newwithfunction(vm *v, objectmesh *mesh, value fn, value fnspc) { +objectfield *field_newwithfunction(vm *v, objectmesh *mesh, value fn) { + objectmatrix *vert=mesh->vert; + int nv = vert->ncols; value ret=MORPHO_NIL; // Return value value coords[mesh->dim]; // Vertex coords objectfield *new = NULL; int handle = -1; - + /* Use the first element to find a prototype **/ if (mesh_getvertexcoordinatesasvalues(mesh, 0, coords)) { if (!morpho_call(v, fn, mesh->dim, coords, &ret)) goto field_newwithfunction_cleanup; if (MORPHO_ISOBJECT(ret)) handle=morpho_retainobjects(v, 1, &ret); } - - new=object_newfield(mesh, ret, fnspc, NULL); - + + new=object_newfield(mesh, ret, NULL); + if (new) { - if (fnspc) { - if (!field_applyfunctiontoelements(v, mesh, fn, fnspc, new)) goto field_newwithfunction_cleanup; - } else { - if (!field_applyfunctiontovertices(v, mesh, fn, new)) goto field_newwithfunction_cleanup; + for (elementid i=0; idim, coords, &ret)){ + // if the fn call fails go to clean up this should throw an error from morpho_call + goto field_newwithfunction_cleanup; + } + if (!field_setelement(new, MESH_GRADE_VERTEX, i, 0, ret)) { + // if we can't set the field value to the ouptut of the function clean up + morpho_runtimeerror(v,FIELD_OPRETURN); + goto field_newwithfunction_cleanup; + } + } } } - + if (handle>=0) morpho_releaseobjects(v, handle); return new; - + field_newwithfunction_cleanup: if (new) object_free((object *) new); if (handle>=0) morpho_releaseobjects(v, handle); @@ -316,7 +237,7 @@ bool field_addpool(objectfield *f) { /** Clones a field */ objectfield *field_clone(objectfield *f) { - objectfield *new = object_newfield(f->mesh, f->prototype, f->fnspc, f->dof); + objectfield *new = object_newfield(f->mesh, f->prototype, f->dof); if (new) memcpy(new->data.elements, f->data.elements, f->data.nrows*sizeof(double)); return new; } @@ -335,7 +256,7 @@ objectfield *field_clone(objectfield *f) { bool field_getelement(objectfield *field, grade grade, elementid el, int indx, value *out) { unsigned int ix=field->offset[grade]+field->dof[grade]*el+indx; if (!(ixoffset[grade+1] && indxdof[grade])) return false; - + if (MORPHO_ISNIL(field->prototype)) { *out=MORPHO_FLOAT(field->data.elements[ix]); return true; @@ -370,21 +291,6 @@ bool field_getelementwithindex(objectfield *field, int indx, value *out) { return false; } -/** Constructs a single index, suitable for use with fieldgetelementwithindex from the grade, element id and quantity number - * @param[in] field - field to use - * @param[in] grade - grade to access - * @param[in] el - element id - * @param[in] indx - index within the element - * @param[out] out - the retrieved index - * @return true on success */ -bool field_getindex(objectfield *field, grade grade, elementid el, int indx, int *out) { - int ix=field->offset[grade]+field->dof[grade]*el+indx; - if (!(ixoffset[grade+1] && indxdof[grade])) return false; - - *out=ix; - return true; -} - /** Retrieve the list of doubles that represent an entry in a field * @param[in] field - field to use * @param[in] grade - grade to access @@ -397,7 +303,7 @@ bool field_getelementaslist(objectfield *field, grade grade, elementid el, int i bool success=false; unsigned int ix=field->offset[grade]+field->dof[grade]*el+indx; if (!(ixoffset[grade+1] && indxdof[grade])) return false; - + if (MORPHO_ISNIL(field->prototype)) { *out = &field->data.elements[ix]; *nentries=1; @@ -420,7 +326,7 @@ bool field_getelementaslist(objectfield *field, grade grade, elementid el, int i bool field_setelement(objectfield *field, grade grade, elementid el, int indx, value val) { unsigned int ix=field->offset[grade]+field->dof[grade]*el+indx; if (!(ixoffset[grade+1] && indxdof[grade])) return false; - + if (MORPHO_ISNIL(field->prototype)) { if (MORPHO_ISNUMBER(val)) { return morpho_valuetofloat(val, &field->data.elements[ix]); @@ -445,7 +351,7 @@ bool field_setelement(objectfield *field, grade grade, elementid el, int indx, v * @return true on success */ bool field_setelementwithindex(objectfield *field, int ix, value val) { if (ix>=field->nelements) return false; - + if (MORPHO_ISNIL(field->prototype)) { if (MORPHO_ISNUMBER(val)) { return morpho_valuetofloat(val, &field->data.elements[ix]); @@ -506,31 +412,31 @@ bool field_op(vm *v, value fn, objectfield *f, int nargs, objectfield **args, va value fargs[nargs+1]; objectfield *fld=NULL; int handle = -1; - + for (int i=0; imesh, ret, f->fnspc, f->dof); + fld=object_newfield(f->mesh, ret, f->dof); if (!fld) { morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED); return false; } } else { morpho_runtimeerror(v, FIELD_OPRETURN); return false; } } - + if (!field_setelementwithindex(fld, i, ret)) return false; } else return false; } - + if (handle>=0) morpho_releaseobjects(v, handle); if (fld) *out = MORPHO_OBJECT(fld); - + return true; } @@ -545,50 +451,47 @@ value field_constructor(vm *v, int nargs, value *args) { objectmesh *mesh=NULL; // The mesh used by the object value fn = MORPHO_NIL; // A function to call value prototype=MORPHO_NIL; // Prototype object - + value grd = MORPHO_NIL; - value fnspc = MORPHO_NIL; int nfixed; - - if (!builtin_options(v, nargs, args, &nfixed, 2, field_gradeoption, &grd, field_functionspaceoption, &fnspc)) + + if (!builtin_options(v, nargs, args, &nfixed, 1, field_gradeoption, &grd)) morpho_runtimeerror(v, FIELD_ARGS); - + for (unsigned int i=0; ival.count, list->val.data, dof)) return MORPHO_NIL; } - + if (MORPHO_ISNIL(fn)) { - new = object_newfield(mesh, prototype, fnspc, dof); + new = object_newfield(mesh, prototype, (MORPHO_ISNIL(grd) ? NULL: dof)); } else { - new = field_newwithfunction(v, mesh, fn, fnspc); + new = field_newwithfunction(v, mesh, fn); } - + if (new) { out=MORPHO_OBJECT(new); morpho_bindobjects(v, 1, &out); } - + return out; } @@ -597,18 +500,18 @@ value Field_getindex(vm *v, int nargs, value *args) { objectfield *f=MORPHO_GETFIELD(MORPHO_SELF(args)); unsigned int indx[nargs]; value out = MORPHO_NIL; - + if (array_valuelisttoindices(nargs, args+1, indx)) { grade g = (nargs>1 ? indx[0] : MESH_GRADE_VERTEX); elementid el = (nargs>1 ? indx[1] : indx[0]); int elindx = (nargs>2 ? indx[2] : 0); - + /* If only one index is specified, increment g to the lowest nonempty grade */ if (nargs==1 && f->dof) while (f->dof[g]==0 && gngrades) g++; - + if (!field_getelement(f, g, el, elindx, &out)) morpho_runtimeerror(v, FIELD_INDICESOUTSIDEBOUNDS); } else morpho_runtimeerror(v, FIELD_INVLDINDICES); - + return out; } @@ -618,22 +521,20 @@ value Field_setindex(vm *v, int nargs, value *args) { unsigned int indx[nargs]; value out = MORPHO_NIL; int nindices = nargs-1; - + if (array_valuelisttoindices(nindices, args+1, indx)) { grade g = (nindices>1 ? indx[0] : MESH_GRADE_VERTEX); elementid el = (nindices>1 ? indx[1] : indx[0]); int elindx = (nindices>2 ? indx[2] : 0); - - /* If only one index is specified, treat it as a single index */ - if (nindices==1 && f->dof) { - if (!field_setelementwithindex(f, indx[0], MORPHO_GETARG(args, nargs-1))) { - morpho_runtimeerror(v, FIELD_INCOMPATIBLEVAL); - } - } else if (!field_setelement(f, g, el, elindx, MORPHO_GETARG(args, nargs-1))) { + + /* If only one index is specified, increment g to the lowest nonempty grade */ + if (nindices==1 && f->dof) while (f->dof[g]==0 && gngrades) g++; + + if (!field_setelement(f, g, el, elindx, MORPHO_GETARG(args, nargs-1))) { morpho_runtimeerror(v, FIELD_INCOMPATIBLEVAL); } } else morpho_runtimeerror(v, FIELD_INVLDINDICES); - + return out; } @@ -641,11 +542,11 @@ value Field_setindex(vm *v, int nargs, value *args) { value Field_enumerate(vm *v, int nargs, value *args) { objectfield *a=MORPHO_GETFIELD(MORPHO_SELF(args)); value out=MORPHO_NIL; - + if (nargs==1) { if (MORPHO_ISINTEGER(MORPHO_GETARG(args, 0))) { int i=MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0)); - + if (i<0) out=MORPHO_INTEGER(a->nelements); else if (inelements) { if (!field_getelementwithindex(a, i, &out)) UNREACHABLE("Could not get field element."); @@ -653,7 +554,7 @@ value Field_enumerate(vm *v, int nargs, value *args) { /* Note no need to bind as we are an object pool */ } } - + return out; } @@ -661,26 +562,26 @@ value Field_enumerate(vm *v, int nargs, value *args) { /** Number of field elements */ value Field_count(vm *v, int nargs, value *args) { objectfield *f=MORPHO_GETFIELD(MORPHO_SELF(args)); - + return MORPHO_INTEGER(f->nelements); } /** Field assign */ value Field_assign(vm *v, int nargs, value *args) { objectfield *a=MORPHO_GETFIELD(MORPHO_SELF(args)); - + if (nargs==1 && MORPHO_ISFIELD(MORPHO_GETARG(args, 0))) { objectfield *b=MORPHO_GETFIELD(MORPHO_GETARG(args, 0)); - + if (field_compareshape(a, b)) { matrix_copy(&b->data, &a->data); } else morpho_runtimeerror(v, FIELD_INCOMPATIBLEMATRICES); } else if (nargs==1 && MORPHO_ISMATRIX(MORPHO_GETARG(args, 0))) { objectmatrix *b=MORPHO_GETMATRIX(MORPHO_GETARG(args, 0)); - + if (matrix_copy(b, &a->data)!=MATRIX_OK) morpho_runtimeerror(v, FIELD_INCOMPATIBLEMATRICES); } else morpho_runtimeerror(v, FIELD_ARITHARGS); - + return MORPHO_NIL; } @@ -688,13 +589,13 @@ value Field_assign(vm *v, int nargs, value *args) { value Field_add(vm *v, int nargs, value *args) { objectfield *a=MORPHO_GETFIELD(MORPHO_SELF(args)); value out=MORPHO_NIL; - + if (nargs==1 && MORPHO_ISFIELD(MORPHO_GETARG(args, 0))) { objectfield *b=MORPHO_GETFIELD(MORPHO_GETARG(args, 0)); - + if (field_compareshape(a, b)) { - objectfield *new = object_newfield(a->mesh, a->prototype, a->fnspc, a->dof); - + objectfield *new = object_newfield(a->mesh, a->prototype, a->dof); + if (new) { out=MORPHO_OBJECT(new); field_add(a, b, new); @@ -702,25 +603,25 @@ value Field_add(vm *v, int nargs, value *args) { } } else morpho_runtimeerror(v, FIELD_INCOMPATIBLEMATRICES); } else morpho_runtimeerror(v, FIELD_ARITHARGS); - + return out; } /** Right add */ value Field_addr(vm *v, int nargs, value *args) { value out=MORPHO_NIL; - + if (nargs==1 && (MORPHO_ISNIL(MORPHO_GETARG(args, 0)) || MORPHO_ISNUMBER(MORPHO_GETARG(args, 0)))) { int i=0; if (MORPHO_ISINTEGER(MORPHO_GETARG(args, 0))) i=MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0)); if (MORPHO_ISFLOAT(MORPHO_GETARG(args, 0))) i=(fabs(MORPHO_GETFLOATVALUE(MORPHO_GETARG(args, 0)))mesh, a->prototype, a->fnspc, a->dof); - + objectfield *new = object_newfield(a->mesh, a->prototype, a->dof); + if (new) { out=MORPHO_OBJECT(new); field_sub(a, b, new); @@ -742,7 +643,7 @@ value Field_sub(vm *v, int nargs, value *args) { } } else morpho_runtimeerror(v, FIELD_INCOMPATIBLEMATRICES); } else morpho_runtimeerror(v, FIELD_ARITHARGS); - + return out; } @@ -750,11 +651,11 @@ value Field_sub(vm *v, int nargs, value *args) { value Field_subr(vm *v, int nargs, value *args) { objectfield *a=MORPHO_GETFIELD(MORPHO_SELF(args)); value out=MORPHO_NIL; - + if (nargs==1 && (MORPHO_ISNIL(MORPHO_GETARG(args, 0)) || MORPHO_ISINTEGER(MORPHO_GETARG(args, 0)))) { int i=(MORPHO_ISNIL(MORPHO_GETARG(args, 0)) ? 0 : MORPHO_GETINTEGERVALUE(MORPHO_GETARG(args, 0))); - + if (i==0) { objectfield *new=field_clone(a); if (new) { @@ -764,25 +665,25 @@ value Field_subr(vm *v, int nargs, value *args) { } } else morpho_runtimeerror(v, VM_INVALIDARGS); } else morpho_runtimeerror(v, VM_INVALIDARGS); - + return out; } /** Field accumulate */ value Field_acc(vm *v, int nargs, value *args) { objectfield *a=MORPHO_GETFIELD(MORPHO_SELF(args)); - + if (nargs==2 && MORPHO_ISNUMBER(MORPHO_GETARG(args, 0)) && MORPHO_ISFIELD(MORPHO_GETARG(args, 1))) { objectfield *b=MORPHO_GETFIELD(MORPHO_GETARG(args, 1)); - + if (field_compareshape(a, b)) { double lambda=1.0; morpho_valuetofloat(MORPHO_GETARG(args, 0), &lambda); field_accumulate(a, lambda, b); } else morpho_runtimeerror(v, FIELD_INCOMPATIBLEMATRICES); } else morpho_runtimeerror(v, FIELD_ARITHARGS); - + return MORPHO_NIL; } @@ -790,7 +691,7 @@ value Field_acc(vm *v, int nargs, value *args) { value Field_mul(vm *v, int nargs, value *args) { objectfield *a=MORPHO_GETFIELD(MORPHO_SELF(args)); value out=MORPHO_NIL; - + if (nargs==1 && MORPHO_ISNUMBER(MORPHO_GETARG(args, 0))) { double scale=1.0; if (morpho_valuetofloat(MORPHO_GETARG(args, 0), &scale)) { @@ -802,7 +703,7 @@ value Field_mul(vm *v, int nargs, value *args) { } } } else morpho_runtimeerror(v, MATRIX_ARITHARGS); - + return out; } @@ -810,13 +711,13 @@ value Field_mul(vm *v, int nargs, value *args) { value Field_div(vm *v, int nargs, value *args) { objectfield *a=MORPHO_GETFIELD(MORPHO_SELF(args)); value out=MORPHO_NIL; - + if (nargs==1 && MORPHO_ISNUMBER(MORPHO_GETARG(args, 0))) { /* Division by a scalar */ double scale=1.0; if (morpho_valuetofloat(MORPHO_GETARG(args, 0), &scale)) { if (fabs(scale)0) fn=MORPHO_GETARG(args, 0); if (morpho_iscallable(fn)) { for (unsigned int i=1; i\n"); matrix_print(v, &f->data); @@ -900,20 +801,20 @@ value Field_shape(vm *v, int nargs, value *args) { for (unsigned int i=0; ingrades; i++) { shape[i]=MORPHO_INTEGER(f->dof[i]); } - + objectlist *new=object_newlist(f->ngrades, shape); if (new) { out = MORPHO_OBJECT(new); morpho_bindobjects(v, 1, &out); } - + return out; } /** Get the mesh associated with a field */ value Field_mesh(vm *v, int nargs, value *args) { objectfield *f=MORPHO_GETFIELD(MORPHO_SELF(args)); - + return MORPHO_OBJECT(f->mesh); } @@ -921,13 +822,13 @@ value Field_mesh(vm *v, int nargs, value *args) { value Field_linearize(vm *v, int nargs, value *args) { objectfield *f=MORPHO_GETFIELD(MORPHO_SELF(args)); value out = MORPHO_NIL; - + objectmatrix *m=object_clonematrix(&f->data); if (m) { out = MORPHO_OBJECT(m); morpho_bindobjects(v, 1, &out); } - + return out; } @@ -935,7 +836,7 @@ value Field_linearize(vm *v, int nargs, value *args) { @warning only use when you know what you're doing. */ value Field_unsafelinearize(vm *v, int nargs, value *args) { objectfield *f=MORPHO_GETFIELD(MORPHO_SELF(args)); - + return MORPHO_OBJECT(&f->data); } @@ -969,18 +870,17 @@ MORPHO_ENDCLASS void field_initialize(void) { objectfieldtype=object_addtype(&objectfielddefn); - + field_gradeoption=builtin_internsymbolascstring(FIELD_GRADEOPTION); - field_functionspaceoption=builtin_internsymbolascstring(FIELD_FUNCTIONSPACEOPTION); - + builtin_addfunction(FIELD_CLASSNAME, field_constructor, BUILTIN_FLAGSEMPTY); - + objectstring objname = MORPHO_STATICSTRING(OBJECT_CLASSNAME); value objclass = builtin_findclass(MORPHO_OBJECT(&objname)); - + value fieldclass=builtin_addclass(FIELD_CLASSNAME, MORPHO_GETCLASSDEFINITION(Field), objclass); object_setveneerclass(OBJECT_FIELD, fieldclass); - + morpho_defineerror(FIELD_INDICESOUTSIDEBOUNDS, ERROR_HALT, FIELD_INDICESOUTSIDEBOUNDS_MSG); morpho_defineerror(FIELD_INVLDINDICES, ERROR_HALT, FIELD_INVLDINDICES_MSG); morpho_defineerror(FIELD_ARITHARGS, ERROR_HALT, FIELD_ARITHARGS_MSG); diff --git a/src/geometry/field.h b/src/geometry/field.h index 3cdadb5b..250bab52 100644 --- a/src/geometry/field.h +++ b/src/geometry/field.h @@ -8,8 +8,8 @@ #define field_h #include "object.h" -#include "matrix.h" #include "mesh.h" +#include "matrix.h" #include /* ------------------------------------------------------- @@ -32,8 +32,6 @@ typedef struct { unsigned int nelements; /** Total number of elements in the fireld */ void *pool; /** Pool of statically allocated objects */ - value fnspc; /** Function space used */ - objectmatrix data; /** Underlying data store */ } objectfield; @@ -44,7 +42,7 @@ typedef struct { #define MORPHO_GETFIELD(val) ((objectfield *) MORPHO_GETOBJECT(val)) /** Creates an empty field object */ -objectfield *object_newfield(objectmesh *mesh, value prototype, value disc, unsigned int *shape); +objectfield *object_newfield(objectmesh *mesh, value prototype, unsigned int *dof); /* ------------------------------------------------------- * Field class @@ -52,11 +50,7 @@ objectfield *object_newfield(objectmesh *mesh, value prototype, value disc, unsi #define FIELD_CLASSNAME "Field" -extern value field_gradeoption; - #define FIELD_GRADEOPTION "grade" -#define FIELD_FUNCTIONSPACEOPTION "functionspace" - #define FIELD_OP_METHOD "op" #define FIELD_SHAPE_METHOD "shape" #define FIELD_MESH_METHOD "mesh" @@ -99,11 +93,9 @@ unsigned int field_sizeprototype(value prototype); unsigned int field_dofforgrade(objectfield *f, grade g); bool field_getelement(objectfield *field, grade grade, elementid el, int indx, value *out); bool field_getelementwithindex(objectfield *field, int indx, value *out); -bool field_getindex(objectfield *field, grade grade, elementid el, int indx, int *out); bool field_getelementaslist(objectfield *field, grade grade, elementid el, int indx, unsigned int *nentries, double **out); bool field_setelement(objectfield *field, grade grade, elementid el, int indx, value val); -bool field_setelementwithindex(objectfield *field, int ix, value val); void field_initialize(void); diff --git a/src/geometry/functional.c b/src/geometry/functional.c index 7cbb6878..30bc4c26 100644 --- a/src/geometry/functional.c +++ b/src/geometry/functional.c @@ -14,7 +14,6 @@ #include "matrix.h" #include "sparse.h" #include "integrate.h" -#include "selection.h" #include #ifndef M_PI @@ -420,7 +419,7 @@ bool functional_mapfieldgradientX(vm *v, functional_mapinfo *info, value *out) { /* Create the output field */ if (n>0) { - grad=object_newfield(mesh, field->prototype, field->fnspc, field->dof); + grad=object_newfield(mesh, field->prototype, field->dof); if (!grad) { morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED); return false; } field_zero(grad); } @@ -647,7 +646,7 @@ bool functional_mapnumericalfieldgradientX(vm *v, functional_mapinfo *info, valu objectsparse *conn=mesh_getconnectivityelement(mesh, 0, grd); // Connectivity for the element /* Create the output field */ - objectfield *grad=object_newfield(mesh, field->prototype, field->fnspc, field->dof); + objectfield *grad=object_newfield(mesh, field->prototype, field->dof); if (!grad) return false; field_zero(grad); @@ -1252,7 +1251,7 @@ bool functional_mapfieldgradient(vm *v, functional_mapinfo *info, value *out) { /* Create output fields */ for (int i=0; imesh, info->field->prototype, info->field->fnspc, info->field->dof); + new[i]=object_newfield(info->mesh, info->field->prototype, info->field->dof); if (!new[i]) { morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED); goto functional_mapfieldgradient_cleanup; } field_zero(new[i]); @@ -1351,7 +1350,7 @@ bool functional_mapnumericalfieldgradient(vm *v, functional_mapinfo *info, value for (int i=0; imesh, info->field->prototype, info->field->fnspc, info->field->dof); + new[i] = object_newfield(info->mesh, info->field->prototype, info->field->dof); if (!new[i]) { morpho_runtimeerror(v, ERROR_ALLOCATIONFAILED); goto functional_mapfieldgradient_cleanup; } field_zero(new[i]); @@ -4212,41 +4211,6 @@ void integral_freeref(void *ref) { MORPHO_FREE(ref); } -/** Prepares quantity list */ -void integral_preparequantities(integralref *iref, int nv, int *vid, quantity *quantities) { - for (int k=0; knfields; k++) { - objectfield *f=MORPHO_GETFIELD(iref->fields[k]); - - if (MORPHO_ISDISCRETIZATION(f->fnspc)) { - discretization *disc=MORPHO_GETDISCRETIZATION(f->fnspc)->discretization; - quantities[k].nnodes=disc->nnodes; - quantities[k].ifn=disc->ifn; - - int dof[disc->nnodes]; - discretization_doftofieldindx(f, disc, nv, vid, dof); - - quantities[k].vals=MORPHO_MALLOC(sizeof(value)*disc->nnodes); - for (int i=0; innodes; i++) { - field_getelementwithindex(f, dof[i], &quantities[k].vals[i]); - } - } else { - quantities[k].nnodes=nv; - quantities[k].ifn=NULL; - quantities[k].vals=MORPHO_MALLOC(sizeof(value)*nv); - for (unsigned int i=0; idim, MESH_GRADE_LINE, x, iref.nfields, quantities, &iref, out, &err); - - integral_clearquantities(iref.nfields, quantities); - } else { // Old integrator - value q0[iref.nfields+1], q1[iref.nfields+1]; - value *q[2] = { q0, q1 }; - for (unsigned int k=0; kdim, MESH_GRADE_LINE, x, iref.nfields, q, &iref, out, &err); + } else { success=integrate_integrate(integral_integrandfn, mesh->dim, MESH_GRADE_LINE, x, iref.nfields, q, &iref, out); } @@ -4435,24 +4394,19 @@ bool areaintegral_integrand(vm *v, objectmesh *mesh, elementid id, int nv, int * /* Set up quantities */ integral_cleartlvars(v); vm_settlvar(v, elementhandle, MORPHO_OBJECT(&elref)); - + + value q0[iref.nfields+1], q1[iref.nfields+1], q2[iref.nfields+1]; + value *q[3] = { q0, q1, q2 }; + for (unsigned int k=0; kdim, MESH_GRADE_AREA, x, iref.nfields, quantities, &iref, out, &err); - - integral_clearquantities(iref.nfields, quantities); + success=integrate(integral_integrandfn, MORPHO_GETDICTIONARY(iref.method), mesh->dim, MESH_GRADE_AREA, x, iref.nfields, q, &iref, out, &err); } else { - value q0[iref.nfields+1], q1[iref.nfields+1], q2[iref.nfields+1]; - value *q[3] = { q0, q1, q2 }; - for (unsigned int k=0; kdim, MESH_GRADE_AREA, x, iref.nfields, q, &iref, out); } @@ -4544,12 +4498,7 @@ bool volumeintegral_integrand(vm *v, objectmesh *mesh, elementid id, int nv, int if (MORPHO_ISDICTIONARY(iref.method)) { double err; - quantity quantities[iref.nfields+1]; - integral_preparequantities(&iref, nv, vid, quantities); - - success=integrate(integral_integrandfn, MORPHO_GETDICTIONARY(iref.method), mesh->dim, MESH_GRADE_VOLUME, x, iref.nfields, quantities, &iref, out, &err); - - integral_clearquantities(iref.nfields, quantities); + success=integrate(integral_integrandfn, MORPHO_GETDICTIONARY(iref.method), mesh->dim, MESH_GRADE_VOLUME, x, iref.nfields, q, &iref, out, &err); } else { success=integrate_integrate(integral_integrandfn, mesh->dim, MESH_GRADE_VOLUME, x, iref.nfields, q, &iref, out); } diff --git a/src/geometry/geometry.c b/src/geometry/geometry.c deleted file mode 100644 index 31a4169c..00000000 --- a/src/geometry/geometry.c +++ /dev/null @@ -1,15 +0,0 @@ -/** @file geometry.c - * @author T J Atherton - * - * @brief Geometry wrapper - */ - -#include "geometry.h" - -void geometry_initialize(void) { - mesh_initialize(); - field_initialize(); - functional_initialize(); - discretization_initialize(); - selection_initialize(); -} diff --git a/src/geometry/geometry.h b/src/geometry/geometry.h deleted file mode 100644 index 076fe0e4..00000000 --- a/src/geometry/geometry.h +++ /dev/null @@ -1,19 +0,0 @@ -/** @file geometry.h - * @author T J Atherton - * - * @brief Geometry wrapper - */ - -#ifndef geometry_h -#define geometry_h - -#include "mesh.h" -#include "field.h" -#include "selection.h" -#include "functional.h" -#include "discretization.h" -#include "integrate.h" - -void geometry_initialize(void); - -#endif diff --git a/src/geometry/integrate.c b/src/geometry/integrate.c index 3f450cf4..db7605bd 100644 --- a/src/geometry/integrate.c +++ b/src/geometry/integrate.c @@ -11,7 +11,9 @@ #include "matrix.h" #include "sparse.h" -#include "geometry.h" +#include "mesh.h" +#include "selection.h" +#include "functional.h" bool integrate_recognizequantities(unsigned int nquantity, value *quantity, value *out) { if (nquantity>0) { @@ -1213,7 +1215,6 @@ quadraturerule cubtri19 = { .nnodes = 19, .nodes = cubtripts, .weights = cubtri19wts, - .ext = NULL }; quadraturerule cubtri7 = { @@ -1223,7 +1224,6 @@ quadraturerule cubtri7 = { .nnodes = 7, .nodes = cubtripts, .weights = cubtri7wts, - .ext = NULL }; /* -------------------------------- @@ -1768,7 +1768,7 @@ quadraturerule *quadrules[] = { &keast4, &keast5, &tet5, &tet6, - &grundmann1, &grundmann2, &grundmann3, &grundmann4, &grundmann5, + &grundmann1, &grundmann2, &grundmann3, &grundmann4, NULL }; @@ -1962,6 +1962,8 @@ void integrator_init(integrator *integrate) { integrate->dim=0; integrate->nbary=0; integrate->nquantity=0; + integrate->nqdof=0; + integrate->ndof=0; integrate->adapt=true; integrate->rule = NULL; @@ -1969,9 +1971,12 @@ void integrator_init(integrator *integrate) { integrate->subdivide = NULL; + integrate->workp = -1; + integrate->freep = -1; varray_quadratureworkiteminit(&integrate->worklist); varray_doubleinit(&integrate->vertexstack); varray_intinit(&integrate->elementstack); + varray_valueinit(&integrate->quantitystack); integrate->ztol = INTEGRATE_ZEROCHECK; integrate->tol = INTEGRATE_ACCURACYGOAL; @@ -1991,7 +1996,11 @@ void integrator_clear(integrator *integrate) { varray_quadratureworkitemclear(&integrate->worklist); varray_intclear(&integrate->elementstack); varray_doubleclear(&integrate->vertexstack); - for (int i=0; inquantity; i++) morpho_freeobject(integrate->qval[i]); + for (unsigned int i=0; iquantitystack.count; i++) { + value v=integrate->quantitystack.data[i]; + if (MORPHO_ISOBJECT(v)) morpho_freeobject(v); + } + varray_valueclear(&integrate->quantitystack); } /** Adds a vertex to the integrators vertex stack, returning the id */ @@ -2001,31 +2010,65 @@ int integrator_addvertex(integrator *integrate, int ndof, double *v) { return vid; } -/** Adds an element to the element stack, returning the id. Elements are specified by their coordinates in the reference element */ -int integrator_addelement(integrator *integrate, int *vids) { +/** Adds an element to the element stack, returning the id. Elements consist of : + - vertex ids + - a number of quantity ids. */ +int integrator_addelement(integrator *integrate, int *vids, int *qids) { int elid=integrate->elementstack.count; varray_intadd(&integrate->elementstack, vids, integrate->nbary); + if (integrate->nquantity && qids) varray_intadd(&integrate->elementstack, qids, integrate->nbary); return elid; } -/** Process the list of quantities given */ -void integrator_initializequantities(integrator *integrate, int nq, quantity *quantity) { - integrate->nquantity=nq; - integrate->quantity=quantity; - +/** + Quantities are stored as needed on the quantity stack. The first n values are used + to store interpolated values. As new vertices are added, n entries are added to the quantity stack + + | base: | q list 0: | q list 1: | + | q0, q1, ... qn | q0, q1, ... qn | q0, q1, ... | + + As elements are added, they include both vertex ids and references to entries on the quantity stack. Each element is 2*nbary entries long: + + nbary nbary + | vid0, vid1, ... : qid0, qid1, ... | + + For each element, we build an interpolation matrix, + + [ x0 y0 q0,0 q1,0 ... qn,0 ] + [ x1 y1 q0,1 q1,1 ... qn,1 ] + [ x2 y1 q0,2 q1,2 ... qn,2 ] + + which when multiplied by the barycentric coordinates yields the interpolated quantite + + [l0, l1, l2] . Interp -> [ x0, y0, q0, q1, ... qn ] */ + +/** Adds nq quantities to the quantity stack, returning the id of the first element */ +int integrator_addquantity(integrator *integrate, int nq, value *quantity) { + int qid=integrate->quantitystack.count; for (int i=0; iqval[i]=q; - } else if (MORPHO_ISMATRIX(q)) { - objectmatrix *m = MORPHO_GETMATRIX(q); - quantity[i].ndof=matrix_countdof(m); - - objectmatrix *new = object_clonematrix(m); // Use a copy of the matrix - integrate->qval[i]=MORPHO_OBJECT(new); + if (MORPHO_ISFLOAT(quantity[i])) { + varray_valuewrite(&integrate->quantitystack, quantity[i]); + } else if (MORPHO_ISMATRIX(quantity[i])) { + objectmatrix *new = object_clonematrix(MORPHO_GETMATRIX(quantity[i])); + // TODO: Raise error on fail + varray_valuewrite(&integrate->quantitystack, MORPHO_OBJECT(new)); + } else return false; + } + return qid; +} + +/** Count degrees of freedom */ +void integrator_countquantitydof(integrator *integrate, int nq, value *quantity) { + int ndof=0; + for (int i=0; inqdof=ndof; } /** Retrieves the vertex pointers given an elementid. @@ -2037,10 +2080,20 @@ void integrator_getvertices(integrator *integrate, int elementid, double **vert) } } +/** Retrieves the quantity pointers given an elementid. + @warning: The pointers returned become invalid after a subsequent call to integrator_addvertex */ +void integrator_getquantities(integrator *integrate, int elementid, value **quantities) { + for (int i=0; inbary; i++) { + int qid=integrate->elementstack.data[elementid+integrate->nbary+i]; // Note quantities stored after vertices + quantities[i]=&(integrate->quantitystack.data[qid]); + } +} + /** Retrieves an element with elementid */ -void integrator_getelement(integrator *integrate, int elementid, int *vid) { +void integrator_getelement(integrator *integrate, int elementid, int *vid, int *qid) { for (int i=0; inbary; i++) { vid[i]=integrate->elementstack.data[elementid+i]; + if (integrate->nquantity && qid) qid[i]=integrate->elementstack.data[elementid+integrate->nbary+i]; } } @@ -2117,73 +2170,74 @@ void integrator_estimate(integrator *integrate) { * Linear interpolation * -------------------------------- */ -/** Construct vertex transformation matrices - @param[in] integrate - the integrator - @param[in] vref - vertices specified in reference element (length integrate->nbary) - @param[out] r - matrix mapping local node coordinates to ref. el coordinates [r has nbary rows and nbary columns] - @param[out] v - matrix mapping ref. el coordinates to physical coordinates [v has dim rows and nbary columns] */ -void integrator_preparevertices(integrator *integrate, double **vref, double *r, double *v) { - int l=0; - if (r) for (int i=0; inbary; i++) { // Loop over vertices [defined rel. to ref. element] - for (int k=0; knbary; k++) { // Sum over barycentric coordinates - r[l]=vref[i][k]; - l++; +/*void xlinearinterpolate(int nbary, double *bary, int nels, double **v, double *x) { + for (int j=0; jnbary; i++) { // Loop over vertices [defined rel. to ref. element] - for (int j=0; jdim; j++) { // Loop over dimensions - v[l]=integrate->x[i][j]; - l++; - } - } -} +}*/ -/** Sets up interpolation matrix */ -void integrator_prepareinterpolation(integrator *integrate, int elementid, double *rmat, double *vmat) { - double *vert[integrate->nbary]; // Vertex information - integrator_getvertices(integrate, elementid, vert); - integrator_preparevertices(integrate, vert, rmat, vmat); +void linearinterpolate(integrator *integrate, double *bary, double *vmat, double *x) { + // Multiply 1 x nbary (lambda) with nbary x dim (vmat) + cblas_dgemm(CblasColMajor, CblasNoTrans, CblasNoTrans, 1, integrate->dim+integrate->nqdof, integrate->nbary, 1.0, bary, 1, vmat, integrate->nbary, 0.0, x, 1); } -/** Weighted sum of a list */ -double integrator_sumlistweighted(unsigned int nel, double *list, double *wts) { - return cblas_ddot(nel, list, 1, wts, 1); +/** Also provide a version using BLAS to accelerate multiplication */ +void preparevertices(integrator *integrate, double **v, double *vv) { + int k=0; + for (int j=0; jdim; j++) { + for (int i=0; inbary; i++) { + vv[k]=v[i][j]; + k++; + } + } } -/** Transforms local element coordinates to reference element coordinates */ -void integrator_transformtorefelement(integrator *integrate, double *rmat, double *local, double *bary) { - // Multiply nbary x nbary (rmat) with nbary x 1 (local) to get nbary x 1 (bary) - cblas_dgemm(CblasColMajor, CblasNoTrans, CblasNoTrans, integrate->nbary, 1, integrate->nbary, 1.0, rmat, integrate->nbary, local, integrate->nbary, 0.0, bary, integrate->nbary); +/** Prepares quantities for interpolation */ +void preparequantities(integrator *integrate, value **quantity, double *qmat) { + int k=0; // DOF counter + for (int i=0; inquantity; i++) { + if (MORPHO_ISFLOAT(quantity[0][i])) { + for (int j=0; jnbary; j++) morpho_valuetofloat(quantity[j][i], &qmat[k*integrate->nbary+j]); + k++; + } else if (MORPHO_ISMATRIX(quantity[0][i])) { + for (int j=0; jnbary; j++) { + objectmatrix *m = MORPHO_GETMATRIX(quantity[j][i]); + int mdof=m->ncols*m->nrows; + for (int l=0; lnbary+j]=m->elements[l]; + } + } else return; + } } -/** Transform from reference element barycentric coordinates to physical coordinates */ -void integrator_interpolatecoordinates(integrator *integrate, double *lambda, double *vmat, double *x) { - // Multiply dim x nbary (vmat) with nbary x 1 (lambda) to get dim x 1 (x) - cblas_dgemm(CblasColMajor, CblasNoTrans, CblasNoTrans, integrate->dim, 1, integrate->nbary, 1.0, vmat, integrate->dim, lambda, integrate->nbary, 0.0, x, integrate->dim); +/** Sets up interpolation matrix */ +void prepareinterpolation(integrator *integrate, int elementid, double *vmat) { + double *vert[integrate->nbary]; // Vertex information + value *quantity[integrate->nbary]; // Quantities + + integrator_getvertices(integrate, elementid, vert); + preparevertices(integrate, vert, vmat); + + if (integrate->nquantity) { + integrator_getquantities(integrate, elementid, quantity); + preparequantities(integrate, quantity, vmat+integrate->nbary*integrate->dim); + } } -/** Interpolates quantities */ -void integrator_interpolatequantities(integrator *integrate, double *bary) { +/** Processes the results of interpolation */ +void postprocessquantities(integrator *integrate, double *qout) { + int k=0; // DOF counter for (int i=0; inquantity; i++) { - int nnodes = integrate->quantity[i].nnodes; - double wts[nnodes]; - (integrate->quantity[i].ifn) (bary, wts); - - if (MORPHO_ISFLOAT(integrate->qval[i])) { - double qval[nnodes]; - for (int j=0; jquantity[i].vals[j]); - double val=integrator_sumlistweighted(nnodes, qval, wts); - integrate->qval[i]=MORPHO_FLOAT(val); - } else if (MORPHO_ISMATRIX(integrate->qval[i])) { - objectmatrix *out = MORPHO_GETMATRIX(integrate->qval[i]); - matrix_zero(out); - for (int j=0; jquantity[i].vals[j])); - } + value q = integrate->quantitystack.data[i]; + if (MORPHO_ISFLOAT(q)) { + integrate->quantitystack.data[i]=MORPHO_FLOAT(qout[k]); + } else if (MORPHO_ISMATRIX(q)) { + objectmatrix *m = MORPHO_GETMATRIX(q); + m->elements=&qout[k]; + k+=m->ncols*m->nrows; } - } } @@ -2191,23 +2245,26 @@ void integrator_interpolatequantities(integrator *integrate, double *bary) { * Function to perform quadrature * -------------------------------- */ +/** Weighted sum of a list */ +double integrate_sumlistweighted(unsigned int nel, double *list, double *wts) { + return cblas_ddot(nel, list, 1, wts, 1); +} + /** Evaluates the integrand at specified places */ -bool integrator_evalfn(integrator *integrate, quadraturerule *rule, int imin, int imax, double *rmat, double *vmat, double *x, double *f) { - double node[integrate->nbary]; - +bool integrate_evalfn(integrator *integrate, quadraturerule *rule, int imin, int imax, double *vmat, double *x, double *f) { for (int i=imin; inodes[integrate->nbary*i], node); - integrator_interpolatecoordinates(integrate, node, vmat, x); - integrator_interpolatequantities(integrate, node); + // Interpolate the point and quantities + linearinterpolate(integrate, &rule->nodes[integrate->nbary*i], vmat, x); + if (integrate->nquantity) postprocessquantities(integrate, x+integrate->dim); // Evaluate function - if (!(*integrate->integrand) (rule->grade, node, x, integrate->nquantity, integrate->qval, integrate->ref, &f[i])) return false; + if (!(*integrate->integrand) (rule->grade, &rule->nodes[integrate->nbary*i], x, integrate->nquantity, integrate->quantitystack.data, integrate->ref, &f[i])) return false; } return true; } /** Integrates a function over an element specified in work, filling out the integral and error estimate if provided */ -bool integrator_quadrature(integrator *integrate, quadraturerule *rule, quadratureworkitem *work) { +bool quadrature(integrator *integrate, quadraturerule *rule, quadratureworkitem *work) { int n = rule->nnodes; int nmax = rule->nnodes; @@ -2217,19 +2274,18 @@ bool integrator_quadrature(integrator *integrate, quadraturerule *rule, quadratu np++; } - double rmat[integrate->nbary*integrate->nbary]; // Transform local element coordinates to ref. el. - double vmat[integrate->nbary*integrate->dim]; // Transform barycentric coordinates in ref. el. to physical coordinates - integrator_prepareinterpolation(integrate, work->elementid, rmat, vmat); + double vmat[integrate->nbary*integrate->ndof]; // Interpolation matrix + prepareinterpolation(integrate, work->elementid, vmat); // Evaluate function at quadrature points - double x[integrate->nbary], f[nmax]; - if (!integrator_evalfn(integrate, rule, 0, rule->nnodes, rmat, vmat, x, f)) return false; + double x[integrate->ndof],f[nmax]; + if (!integrate_evalfn(integrate, rule, 0, rule->nnodes, vmat, x, f)) return false; double r[np+1]; double eps[np+1]; eps[0]=0.0; // Obtain estimate - r[0]=integrator_sumlistweighted(rule->nnodes, f, rule->weights); + r[0]=integrate_sumlistweighted(rule->nnodes, f, rule->weights); work->lval = work->val = work->weight*r[0]; // Estimate error @@ -2239,9 +2295,9 @@ bool integrator_quadrature(integrator *integrate, quadraturerule *rule, quadratu // Attempt p-refinement if available for (quadraturerule *q=rule->ext; q!=NULL; q=q->ext) { ip++; - if (!integrator_evalfn(integrate, q, nmin, q->nnodes, rmat, vmat, x, f)) return false; + if (!integrate_evalfn(integrate, q, nmin, q->nnodes, vmat, x, f)) return false; - r[ip]=integrator_sumlistweighted(q->nnodes, f, q->weights); + r[ip]=integrate_sumlistweighted(q->nnodes, f, q->weights); eps[ip]=fabs(r[ip]-r[ip-1]); nmin = q->nnodes; @@ -2254,7 +2310,7 @@ bool integrator_quadrature(integrator *integrate, quadraturerule *rule, quadratu } else if (integrate->errrule) { // Otherwise, use the error rule to obtain the estimate if (rule==integrate->errrule) return true; // We already are using the error rule double temp = work->val; // Retain the lower order estimate - if (!integrator_quadrature(integrate, integrate->errrule, work)) return false; + if (!quadrature(integrate, integrate->errrule, work)) return false; work->lval=temp; work->err=fabs(work->val-temp); // Estimate error from difference of rules } else { @@ -2268,22 +2324,28 @@ bool integrator_quadrature(integrator *integrate, quadraturerule *rule, quadratu * Subdivision * -------------------------------- */ -bool integrator_subdivide(integrator *integrate, quadratureworkitem *work, int *nels, quadratureworkitem *newitems) { +bool subdivide(integrator *integrate, quadratureworkitem *work, int *nels, quadratureworkitem *newitems) { subdivisionrule *rule = integrate->subdivide; // Fetch the element data - int vid[integrate->nbary+rule->npts]; - integrator_getelement(integrate, work->elementid, vid); + int npts = integrate->nbary+rule->npts; + int vid[npts], qid[npts]; + integrator_getelement(integrate, work->elementid, vid, qid); // Get ready for interpolation - double rmat[integrate->nbary*integrate->nbary]; // Vertex information - integrator_prepareinterpolation(integrate, work->elementid, rmat, NULL); + double vmat[integrate->nbary*integrate->ndof]; // Vertex information + prepareinterpolation(integrate, work->elementid, vmat); - // Interpolate vertices - double lambda[integrate->nbary]; + // Interpolate vertices and quantities, and add these new vertices and quantities + double x[integrate->ndof]; for (int j=0; jnpts; j++) { - integrator_transformtorefelement(integrate, rmat, &rule->pts[j*integrate->nbary], lambda); - vid[integrate->nbary+j]=integrator_addvertex(integrate, integrate->nbary, lambda); + linearinterpolate(integrate, &rule->pts[j*integrate->nbary], vmat, x); + vid[integrate->nbary+j]=integrator_addvertex(integrate, integrate->dim, x); + + if (integrate->nquantity) { + postprocessquantities(integrate, x+integrate->dim); + qid[integrate->nbary+j]=integrator_addquantity(integrate, integrate->nquantity, integrate->quantitystack.data); + } } // Create elements @@ -2292,14 +2354,15 @@ bool integrator_subdivide(integrator *integrate, quadratureworkitem *work, int * newitems[i].err=0.0; newitems[i].weight=work->weight*rule->weights[i]; - // Construct new element from the vertex ids - int vids[integrate->nbary]; + // Construct new element from the vertex ids and quantity ids + int vids[integrate->nbary], qids[integrate->nbary]; for (int k=0; knbary; k++) { vids[k]=vid[rule->newels[integrate->nbary*i+k]]; + if (integrate->nquantity) qids[k]=qid[rule->newels[integrate->nbary*i+k]]; } // Define the new element - newitems[i].elementid=integrator_addelement(integrate, vids); + newitems[i].elementid=integrator_addelement(integrate, vids, qids); } *nels = rule->nels; @@ -2314,7 +2377,7 @@ bool integrator_subdivide(integrator *integrate, quadratureworkitem *work, int * /** Laurie's sharper error estimator: BIT 23 (1983), 258-261 The norm of the difference between two rules |A-B| is usually too pessimistic; this attempts to extrapolate a sharper estimate if convergence looks good */ -void integrator_sharpenerrorestimate(integrator *integrate, quadratureworkitem *work, int nels, quadratureworkitem *newitems) { +void sharpenerrorestimate(integrator *integrate, quadratureworkitem *work, int nels, quadratureworkitem *newitems) { double a1=work->val, b1=work->lval, a2=0, b2=0; for (int k=0; kval-=work->val; integrate->err-=work->err; @@ -2487,40 +2550,39 @@ bool integrator_configurewithdictionary(integrator *integrate, grade g, objectdi * @param[in] quantity - List of quantities for each vertex. * @param[in] ref - a pointer to any data required by the function * @returns True on success */ -bool integrator_integrate(integrator *integrate, integrandfunction *integrand, int dim, double **x, unsigned int nquantity, quantity *quantity, void *ref) { +bool integrator_integrate(integrator *integrate, integrandfunction *integrand, int dim, double **x, unsigned int nquantity, value **quantity, void *ref) { - integrate->integrand=integrand; // Integrand function + integrate->integrand=integrand; integrate->ref=ref; - integrate->x=x; // Vertices - integrate->dim=dim; + integrate->dim=dim; // Dimensionality of vertices + integrate->nquantity=nquantity; - integrate->worklist.count=0; // Reset these + integrate->worklist.count=0; // Reset all these without deallocating integrate->vertexstack.count=0; integrate->elementstack.count=0; + integrate->quantitystack.count=0; error_clear(&integrate->emsg); - // Quantities - value qval[nquantity+1]; - integrate->qval=qval; + // Quantities used for interpolation live at the start of the quantity stack + integrator_countquantitydof(integrate, nquantity, quantity[0]); + integrate->ndof = integrate->dim+integrate->nqdof; // Number of degrees of freedom - integrator_initializequantities(integrate, nquantity, quantity); + integrator_addquantity(integrate, nquantity, quantity[0]); - // Create first element, which corresponds to the reference element - int vids[integrate->nbary]; + // Create first element + int vids[integrate->nbary], qids[integrate->nbary]; for (int i=0; inbary; i++) { - double xref[integrate->nbary]; - for (int j=0; jnbary; j++) xref[j]=0.0; - xref[i]=1.0; - vids[i]=integrator_addvertex(integrate, integrate->nbary, xref); + vids[i]=integrator_addvertex(integrate, dim, x[i]); + if (nquantity) qids[i]=integrator_addquantity(integrate, nquantity, quantity[i]); } - int elid = integrator_addelement(integrate, vids); + int elid = integrator_addelement(integrate, vids, qids); // Add it to the work list quadratureworkitem work; work.weight = 1.0; work.elementid = elid; - integrator_quadrature(integrate, integrate->rule, &work); // Perform initial quadrature + quadrature(integrate, integrate->rule, &work); // Perform initial quadrature integrator_pushworkitem(integrate, &work); integrator_estimate(integrate); // Initial estimate @@ -2536,14 +2598,14 @@ bool integrator_integrate(integrator *integrate, integrandfunction *integrand, i int nels; // Number of elements created quadratureworkitem newitems[integrate->subdivide->nels]; - integrator_subdivide(integrate, &work, &nels, newitems); - for (int k=0; krule, &newitems[k]); + subdivide(integrate, &work, &nels, newitems); + for (int k=0; krule, &newitems[k]); // Error estimate - integrator_sharpenerrorestimate(integrate, &work, nels, newitems); + sharpenerrorestimate(integrate, &work, nels, newitems); // Add new items to heap and update error estimates - integrator_update(integrate, &work, nels, newitems); + update(integrate, &work, nels, newitems); } // Final estimate by Kahan summing heap @@ -2563,11 +2625,11 @@ bool integrator_integrate(integrator *integrate, integrandfunction *integrand, i * @param[in] grade - Grade to integrate over * @param[in] x - vertices of the triangle x[0] = {x,y,z} etc. * @param[in] nquantity - number of quantities per vertex - * @param[in] quantity - List of quantities + * @param[in] quantity - List of quantities for each endpoint. * @param[in] ref - a pointer to any data required by the function * @param[out] out - value of the integral * @returns true on success. */ -bool integrate(integrandfunction *integrand, objectdictionary *method, unsigned int dim, unsigned int grade, double **x, unsigned int nquantity, quantity *quantity, void *ref, double *out, double *err) { +bool integrate(integrandfunction *integrand, objectdictionary *method, unsigned int dim, unsigned int grade, double **x, unsigned int nquantity, value **quantity, void *ref, double *out, double *err) { bool success=false; integrator integrate; integrator_init(&integrate); @@ -2589,11 +2651,10 @@ bool integrate(integrandfunction *integrand, objectdictionary *method, unsigned * Testing code * -------------------------------- */ -/* int nevals; bool test_integrand(unsigned int dim, double *t, double *x, unsigned int nquantity, value *quantity, void *data, double *fout) { - //double val = pow(sin(x[0]+x[1]),-0.5); //x[0]*x[1]*x[2]; // exp(-x[0]*x[0]); //sqrt(x[0]); //exp(-x[0]*x[0]); // *x[1]*x[2]; + //double val = pow(sin(x[0]+x[1]),-0.5); //x[0]*x[1]*x[2]; // exp(-x[0]*x[0]); //sqrt(x[0]); //exp(-x[0]*x[0]); //*x[1]*x[2]; //if (x[0]-x[1]<0.5) val=1.0; double val = sin(3*x[0]+6*x[1]); @@ -2613,7 +2674,7 @@ void integrate_test1(double *out, double *err) { double *xx[] = { x0, x1, x2, x3 }; value *quantities[] = { NULL, NULL, NULL, NULL }; - integrate(test_integrand, NULL, 3, 3, xx, 0, NULL, NULL, out, err); + integrate(test_integrand, NULL, 3, 3, xx, 0, quantities, NULL, out, err); return; } @@ -2628,7 +2689,7 @@ void integrate_test2(double *out) { double x3[3] = { 0, 0, 1 }; double *xx[] = { x0, x1, x2, x3 }; value *quantities[] = { NULL, NULL, NULL, NULL }; - integrate_integrate(test_integrand, 3, 3, xx, 0, NULL, NULL, out); + integrate_integrate(test_integrand, 3, 3, xx, 0, quantities, NULL, out); } void integrate_test(void) { @@ -2656,4 +2717,4 @@ void integrate_test(void) { printf("Old: %g (relative error %g) tol: %g\n", fabs(trueval-out), fabs(trueval-out)/trueval, INTEGRATE_ACCURACYGOAL); exit(0); -}*/ +} diff --git a/src/geometry/integrate.h b/src/geometry/integrate.h index e4ccd385..33d16f88 100644 --- a/src/geometry/integrate.h +++ b/src/geometry/integrate.h @@ -10,7 +10,6 @@ #include #include "morpho.h" #include "dict.h" -#include "discretization.h" #define INTEGRATE_RULELABEL "rule" #define INTEGRATE_DEGREELABEL "degree" @@ -99,43 +98,31 @@ typedef struct { DECLARE_VARRAY(quadratureworkitem, quadratureworkitem) -/* ---------------------------------- - * Quantities - * ---------------------------------- */ - -typedef struct { - int nnodes; /** Number of quantity values per element */ - value *vals; /** List of quantity values */ - interpolationfn ifn; /** Interpolation function */ - int ndof; /** Number of degrees of freedom (this will be filled out by the integrator) */ -} quantity; - /* ---------------------------------- * Integrator * ---------------------------------- */ typedef struct { integrandfunction *integrand; /** Function to integrate */ - void *ref; /** Reference to pass to integrand function */ int dim; /** Dimension of points in embedded space */ - double **x; /** Vertices defining the element */ - int nbary; /** Number of barycentric coordinates */ - int nquantity; /** Number of quantities to interpolate */ - quantity *quantity; /** Quantity list */ - value *qval; /** Interpolated quantity values */ + int nqdof; /** Number of quantity degrees of freedom */ + int ndof; /** Number of degrees of freedom */ + bool adapt; /** Enable adaptive integration */ quadraturerule *rule; /** Quadrature rule to use */ quadraturerule *errrule; /** Additional rule for error estimation */ - bool adapt; /** Enable adaptive integration */ subdivisionrule *subdivide; /** Subdivision rule to use */ + int workp; /** Index of largest item in the work list */ + int freep; /** Index of a free item in the work list */ varray_quadratureworkitem worklist; /** Work list */ varray_double vertexstack; /** Stack of vertices */ varray_int elementstack; /** Stack of elements */ + varray_value quantitystack; /** Stack of quantities */ double ztol; /** Tolerance for zero detection */ double tol; /** Tolerance for relative error */ @@ -146,6 +133,8 @@ typedef struct { double err; /** Estimated error of the integral */ error emsg; /** Store error messages from the integrator */ + + void *ref; /** Reference to pass to integrand */ } integrator; /* ------------------------------------------------------- @@ -164,7 +153,7 @@ typedef struct { bool integrate_integrate(integrandfunction *integrand, unsigned int dim, unsigned int grade, double **x, unsigned int nquantity, value **quantity, void *ref, double *out); -bool integrate(integrandfunction *integrand, objectdictionary *method, unsigned int dim, unsigned int grade, double **x, unsigned int nquantity, quantity *quantity, void *ref, double *out, double *err); +bool integrate(integrandfunction *integrand, objectdictionary *method, unsigned int dim, unsigned int grade, double **x, unsigned int nquantity, value **quantity, void *ref, double *out, double *err); void integrate_test(void); diff --git a/src/geometry/mesh.c b/src/geometry/mesh.c index 7af1daf2..0e6ed2c8 100644 --- a/src/geometry/mesh.c +++ b/src/geometry/mesh.c @@ -6,11 +6,15 @@ #include "morpho.h" #include "classes.h" +#include "mesh.h" #include "file.h" #include "parse.h" #include "sparse.h" #include "matrix.h" -#include "geometry.h" +#include "selection.h" + +// Temporary include +#include "integrate.h" #include @@ -529,7 +533,7 @@ static int mesh_compareid(const void *a, const void *b) { * @param[out] nmatches - the number of matches found * @param[out] matches - matched vertex ids * @returns true on success, false otherwise */ -bool mesh_matchelements(objectsparse *vmatrix, grade g, int nids, int *ids, int maxmatches, int *nmatches, int *matches) { +static bool mesh_matchelements(objectsparse *vmatrix, grade g, int nids, int *ids, int maxmatches, int *nmatches, int *matches) { int nentries[nids], *entries[nids], length=0, k=0; /* Obtain connectivity information from the columns of vertex connectivity matrix */ diff --git a/src/geometry/mesh.h b/src/geometry/mesh.h index 2e3a0295..3972271f 100644 --- a/src/geometry/mesh.h +++ b/src/geometry/mesh.h @@ -144,7 +144,6 @@ objectsparse *mesh_addgrade(objectmesh *mesh, grade g); objectsparse *mesh_addconnectivityelement(objectmesh *mesh, unsigned int row, unsigned int col); objectsparse *mesh_getconnectivityelement(objectmesh *mesh, unsigned int row, unsigned int col); -bool mesh_matchelements(objectsparse *vmatrix, grade g, int nids, int *ids, int maxmatches, int *nmatches, int *matches); bool mesh_getconnectivity(objectsparse *conn, elementid id, int *nentries, int **entries); void mesh_freezeconnectivity(objectmesh *mesh); diff --git a/src/geometry/selection.c b/src/geometry/selection.c index ca6257bb..7f776695 100644 --- a/src/geometry/selection.c +++ b/src/geometry/selection.c @@ -10,7 +10,8 @@ #include "classes.h" #include "matrix.h" #include "sparse.h" -#include "geometry.h" +#include "mesh.h" +#include "selection.h" /* ********************************************************************** * Selection object definitions diff --git a/src/linalg/matrix.c b/src/linalg/matrix.c index a4d64f3b..749d2951 100644 --- a/src/linalg/matrix.c +++ b/src/linalg/matrix.c @@ -47,7 +47,9 @@ objectmatrix *object_newmatrix(unsigned int nrows, unsigned int ncols, bool zero new->ncols=ncols; new->nrows=nrows; new->elements=new->matrixdata; - if (zero) matrix_zero(new); + if (zero) { + memset(new->elements, 0, sizeof(double)*nel); + } } return new; @@ -601,13 +603,6 @@ objectmatrixerror matrix_trace(objectmatrix *a, double *out) { /** Scale a matrix */ objectmatrixerror matrix_scale(objectmatrix *a, double scale) { cblas_dscal(a->ncols*a->nrows, scale, a->elements, 1); - - return MATRIX_OK; -} - -/** Sets a matrix to zero */ -objectmatrixerror matrix_zero(objectmatrix *a) { - memset(a->elements, 0, sizeof(double)*a->nrows*a->ncols); return MATRIX_OK; } @@ -615,7 +610,7 @@ objectmatrixerror matrix_zero(objectmatrix *a) { /** Load the indentity matrix*/ objectmatrixerror matrix_identity(objectmatrix *a) { if (a->ncols!=a->nrows) return MATRIX_NSQ; - matrix_zero(a); + memset(a->elements, 0, sizeof(double)*a->nrows*a->ncols); for (int i=0; inrows; i++) a->elements[i+a->nrows*i]=1.0; return MATRIX_OK; } diff --git a/src/linalg/matrix.h b/src/linalg/matrix.h index 04ece1a1..0573c5af 100644 --- a/src/linalg/matrix.h +++ b/src/linalg/matrix.h @@ -183,7 +183,6 @@ objectmatrixerror matrix_transpose(objectmatrix *a, objectmatrix *out); objectmatrixerror matrix_trace(objectmatrix *a, double *out); objectmatrixerror matrix_scale(objectmatrix *a, double scale); objectmatrixerror matrix_identity(objectmatrix *a); -objectmatrixerror matrix_zero(objectmatrix *a); double matrix_sum(objectmatrix *a); objectmatrixerror matrix_eigensystem(objectmatrix *a, double *wr, double *wi, objectmatrix *vec); bool matrix_eigen(vm *v, objectmatrix *a, value *evals, value *evecs); diff --git a/test/discretization/cg2.morpho b/test/discretization/cg2.morpho deleted file mode 100644 index 8902cf57..00000000 --- a/test/discretization/cg2.morpho +++ /dev/null @@ -1,17 +0,0 @@ - -import meshtools - -var m = LineMesh(fn (t) [t,0,0], 0..1:0.5) - -var l = FunctionSpace("CG2", grade=1) - -var f = Field(m, functionspace=l) -for (i in 0...5) f[i] = i - -//print f - -print l.layout(f) - -print f.shape() - -//print LineIntegral(fn (x, u) u, f).total(m) diff --git a/test/discretization/cg2_2d.morpho b/test/discretization/cg2_2d.morpho deleted file mode 100644 index 804f229d..00000000 --- a/test/discretization/cg2_2d.morpho +++ /dev/null @@ -1,24 +0,0 @@ - -import meshtools - -var mb = MeshBuilder() -mb.addvertex([0,0]) -mb.addvertex([1,0]) -mb.addvertex([0,1]) -mb.addvertex([1,1]) -mb.addface([0,1,2]) -mb.addface([1,3,2]) -var m = mb.build() -m.addgrade(1) - -var l = FunctionSpace("CG2", grade=2) - -var f = Field(m, fn (x,y) Matrix([[x,y],[-y, x]]), functionspace=l) - -print l.layout(f) - -print f.shape() - -print AreaIntegral(fn (x) sqrt(x[0]*x[1]), method={ }).total(m)-4/9 - -print AreaIntegral(fn (x, q) q.trace()^2, f, method={ }).total(m)-4/3 \ No newline at end of file diff --git a/test/discretization/cg2_grad.morpho b/test/discretization/cg2_grad.morpho deleted file mode 100644 index 15c88c7d..00000000 --- a/test/discretization/cg2_grad.morpho +++ /dev/null @@ -1,23 +0,0 @@ - -import meshtools - -var mb = MeshBuilder() -mb.addvertex([0,0]) -mb.addvertex([1,0]) -mb.addvertex([0,1]) -mb.addvertex([1,1]) -mb.addface([0,1,2]) -mb.addface([1,3,2]) -var m = mb.build() -m.addgrade(1) - -var l = FunctionSpace("CG2", grade=2) - -var f = Field(m, fn (x,y) x*y, functionspace=l) - -fn integrand(x, q) { - var g = grad(q) - return g.norm()^2 -} - -print AreaIntegral(integrand, f, method={ }).total(m)-4/3 \ No newline at end of file diff --git a/test/discretization/cg2_interpolate.morpho b/test/discretization/cg2_interpolate.morpho deleted file mode 100644 index f47a8fd2..00000000 --- a/test/discretization/cg2_interpolate.morpho +++ /dev/null @@ -1,15 +0,0 @@ - -import meshtools - -var m = LineMesh(fn (t) [t,0,0], 0..1) - -var l = FunctionSpace("CG2", grade=1) - -var f = Field(m, fn (x,y,z) x^2, functionspace=l) -print f - -print LineIntegral(fn (x) 1-x[0]^20, method={ }).total(m) - -print LineIntegral(fn (x, g) 1-x[0]^20, f, method={ }).total(m) - -print LineIntegral(fn (x, g) 1-g^10, f, method={ }).total(m) From 5395101ffa59e214a722448edd05853615927a5f Mon Sep 17 00:00:00 2001 From: Tim Atherton <54725421+softmattertheory@users.noreply.github.com> Date: Tue, 16 Jul 2024 08:39:10 -0400 Subject: [PATCH 158/181] Fixing metafunction multiple dispatch --- src/classes/function.h | 2 +- src/classes/metafunction.c | 24 ++++++++++++- src/classes/metafunction.h | 8 ++++- src/core/compile.c | 72 ++++++++++++++++++++++++++++++-------- src/core/compile.h | 3 ++ src/core/vm.c | 3 ++ 6 files changed, 95 insertions(+), 17 deletions(-) diff --git a/src/classes/function.h b/src/classes/function.h index ee165411..4017a4ca 100644 --- a/src/classes/function.h +++ b/src/classes/function.h @@ -35,7 +35,7 @@ typedef struct sobjectfunction { int creg; // Closure register struct sobjectfunction *parent; int nregs; - objectclass *klass; + objectclass *klass; // Parent class for methods varray_value konst; varray_varray_upvalue prototype; varray_optionalparam opt; diff --git a/src/classes/metafunction.c b/src/classes/metafunction.c index 58cdf5e9..720c8b1a 100644 --- a/src/classes/metafunction.c +++ b/src/classes/metafunction.c @@ -79,6 +79,7 @@ objectmetafunction *object_newmetafunction(value name) { if (new) { new->name=MORPHO_NIL; if (MORPHO_ISSTRING(name)) new->name=object_clonestring(name); + new->klass=NULL; varray_valueinit(&new->fns); varray_mfinstructioninit(&new->resolver); } @@ -86,6 +87,17 @@ objectmetafunction *object_newmetafunction(value name) { return new; } +/** Clone a metafunction */ +objectmetafunction *metafunction_clone(objectmetafunction *f) { + objectmetafunction *new = object_newmetafunction(f->name); + + if (new) { + varray_valueadd(&new->fns, f->fns.data, f->fns.count); + } + + return new; +} + /** Wraps a function in a metafunction */ bool metafunction_wrap(value name, value fn, value *out) { if (!MORPHO_ISCALLABLE(fn)) return false; @@ -130,6 +142,16 @@ bool metafunction_matchtype(value type, value val) { return false; } +/** Sets the parent class of a metafunction */ +void metafunction_setclass(objectmetafunction *f, objectclass *klass) { + f->klass=klass; +} + +/** Returns a metafunction's class if any */ +objectclass *metafunction_class(objectmetafunction *f) { + return f->klass; +} + /** Finds whether an implementation f occurs in a metafunction */ bool metafunction_matchfn(objectmetafunction *fn, value f) { for (int i=0; ifns.count; i++) if (MORPHO_ISEQUAL(fn->fns.data[i], f)) return true; @@ -760,7 +782,7 @@ bool metafunction_compile(objectmetafunction *fn, error *err) { mfcompiler_init(&compiler, fn); mfcompile_set(&compiler, &set); - //mfcompiler_disassemble(&compiler); + mfcompiler_disassemble(&compiler); bool success=!morpho_checkerror(&compiler.err); if (!success && err) *err=compiler.err; diff --git a/src/classes/metafunction.h b/src/classes/metafunction.h index 85ee4272..3cd5b25f 100644 --- a/src/classes/metafunction.h +++ b/src/classes/metafunction.h @@ -37,7 +37,8 @@ DECLARE_VARRAY(mfinstruction, mfinstruction); typedef struct sobjectmetafunction { object obj; value name; - varray_value fns; + objectclass *klass; // Parent class for metafunction methods + varray_value fns; varray_mfinstruction resolver; } objectmetafunction; @@ -65,10 +66,15 @@ typedef struct sobjectmetafunction { * ------------------------------------------------------- */ objectmetafunction *object_newmetafunction(value name); +objectmetafunction *metafunction_clone(objectmetafunction *f); + bool metafunction_wrap(value name, value fn, value *out); bool metafunction_add(objectmetafunction *f, value fn); bool metafunction_typefromvalue(value v, value *out); +void metafunction_setclass(objectmetafunction *f, objectclass *klass); +objectclass *metafunction_class(objectmetafunction *f); + bool metafunction_matchfn(objectmetafunction *fn, value f); bool metafunction_matchset(objectmetafunction *fn, int n, value *fns); diff --git a/src/core/compile.c b/src/core/compile.c index d5f73e64..7cc48f5e 100644 --- a/src/core/compile.c +++ b/src/core/compile.c @@ -3309,6 +3309,44 @@ static codeinfo compiler_return(compiler *c, syntaxtreenode *node, registerindx return CODEINFO(REGISTER, REGISTER_UNALLOCATED, ninstructions); } +/** Overrides or adds to an existing method implementation */ +void compiler_overridemethod(compiler *c, syntaxtreenode *node, objectfunction *method, value prev) { + value symbol = method->name; + objectclass *klass=compiler_getcurrentclass(c); + + if (MORPHO_ISMETAFUNCTION(prev)) { + objectmetafunction *f = MORPHO_GETMETAFUNCTION(prev); + if (f->klass!=klass) f=metafunction_clone(f); + + if (f) { + metafunction_setclass(f, klass); + metafunction_add(f, MORPHO_OBJECT(method)); + } + + } else if (MORPHO_ISFUNCTION(prev)) { + objectfunction *prevmethod = MORPHO_GETFUNCTION(prev); + + if (signature_isequal(&prevmethod->sig, &method->sig)) { // Does the method overshadow an old one? + if (prevmethod->klass!=klass) { // If so, is the old one in the parent or ancestor class? + dictionary_insert(&klass->methods, symbol, MORPHO_OBJECT(method)); + } else { // It's a redefinition + compiler_error(c, node, COMPILE_CLSSDPLCTIMPL, MORPHO_GETCSTRING(symbol), MORPHO_GETCSTRING(klass->name)); + } + } else { // It doesn't override the old definition so wrap in a metafunction + objectmetafunction *f = object_newmetafunction(symbol); + + if (f) { + metafunction_add(f, prev); + metafunction_add(f, MORPHO_OBJECT(method)); + metafunction_setclass(f, klass); + dictionary_insert(&klass->methods, symbol, MORPHO_OBJECT(f)); + } + } + } else if (MORPHO_ISBUILTINFUNCTION(prev)) { // A builtin function can only come from a parent class, so overwrite it + dictionary_insert(&klass->methods, symbol, MORPHO_OBJECT(method)); + } +} + /** Compiles a list of method declarations. */ static codeinfo compiler_method(compiler *c, syntaxtreenode *node, registerindx reqout) { codeinfo out; @@ -3334,19 +3372,7 @@ static codeinfo compiler_method(compiler *c, syntaxtreenode *node, registerindx prev=MORPHO_NIL; if (dictionary_get(&klass->methods, symbol, &prev)) { - if (MORPHO_ISMETAFUNCTION(prev)) { // Add the method to the mf. - metafunction_add(MORPHO_GETMETAFUNCTION(prev), omethod); - } else { // Check if we're replacing an inherited method - if ((MORPHO_ISFUNCTION(prev) && - MORPHO_GETFUNCTION(prev)->klass!=klass) || - (MORPHO_ISBUILTINFUNCTION(prev))) { // Just overwrite it - dictionary_insert(&klass->methods, symbol, omethod); - } else { // We're not, so wrap in a mf. - metafunction_wrap(symbol, prev, &prev); - metafunction_add(MORPHO_GETMETAFUNCTION(prev), omethod); - dictionary_insert(&klass->methods, symbol, prev); - } - } + compiler_overridemethod(c, node, method, prev); // Override or create a metafunction } else dictionary_insert(&klass->methods, symbol, omethod); // Just insert } } @@ -3454,12 +3480,30 @@ static codeinfo compiler_class(compiler *c, syntaxtreenode *node, registerindx r compiler_error(c, node, COMPILE_CLSSLNRZ, MORPHO_GETCSTRING(klass->name)); } + objectstring str = MORPHO_STATICSTRING("XProblemAdapter"); + if (MORPHO_ISEQUAL(klass->name, MORPHO_OBJECT(&str))) { + + } + /* Compile method declarations */ if (node->right!=SYNTAXTREE_UNCONNECTED) { syntaxtreenode *child = compiler_getnode(c, node->right); mout=compiler_method(c, child, reqout); ninstructions+=mout.ninstructions; } + + /* Compile any metafunctions */ + for (unsigned int i=0; imethods.capacity; i++) { + dictionaryentry *e = &klass->methods.contents[i]; + if (MORPHO_ISNIL(e->key)) continue; + if (MORPHO_ISMETAFUNCTION(e->val)) { + morpho_printvalue(NULL, klass->name); + printf("."); + morpho_printvalue(NULL, e->key); + printf("=>\n"); + metafunction_compile(MORPHO_GETMETAFUNCTION(e->val), &c->err); + } + } /* End class definition */ compiler_endclass(c); @@ -4281,8 +4325,8 @@ void compile_initialize(void) { morpho_defineerror(COMPILE_UNKNWNTYPE, ERROR_COMPILE, COMPILE_UNKNWNTYPE_MSG); morpho_defineerror(COMPILE_UNKNWNNMSPC, ERROR_COMPILE, COMPILE_UNKNWNNMSPC_MSG); morpho_defineerror(COMPILE_UNKNWNTYPENMSPC, ERROR_COMPILE, COMPILE_UNKNWNTYPENMSPC_MSG); - morpho_defineerror(COMPILE_CLSSLNRZ, ERROR_COMPILE, COMPILE_CLSSLNRZ_MSG); + morpho_defineerror(COMPILE_CLSSDPLCTIMPL, ERROR_COMPILE, COMPILE_CLSSDPLCTIMPL_MSG); morpho_addfinalizefn(compile_finalize); } diff --git a/src/core/compile.h b/src/core/compile.h index c5adc07a..23a5fe94 100644 --- a/src/core/compile.h +++ b/src/core/compile.h @@ -120,6 +120,9 @@ #define COMPILE_CLSSLNRZ "ClssLnrz" #define COMPILE_CLSSLNRZ_MSG "Can't linearize class %s: Check parent and ancestor classes for conflicting inheritance order." +#define COMPILE_CLSSDPLCTIMPL "ClssDplctImpl" +#define COMPILE_CLSSDPLCTIMPL_MSG "Duplicate implementation of method %s with same signature in class %s" + /* ********************************************************************** * Compiler typedefs * ********************************************************************** */ diff --git a/src/core/vm.c b/src/core/vm.c index c74ddb2b..d5282164 100644 --- a/src/core/vm.c +++ b/src/core/vm.c @@ -1001,6 +1001,9 @@ bool morpho_interpret(vm *v, value *rstart, instructionindx istart) { v->fp->inbuiltinfunction=NULL; #endif ERRORCHK(); + } else if (MORPHO_ISMETAFUNCTION(ifunc) && + !metafunction_resolve(MORPHO_GETMETAFUNCTION(ifunc), c, reg+a, &ifunc)) { + ERROR(VM_MLTPLDSPTCHFLD); } } else { if (c>0) { From 66ad1ae6e266efe33eb46bac0f4ce70b4ffd1d59 Mon Sep 17 00:00:00 2001 From: Tim Atherton <54725421+softmattertheory@users.noreply.github.com> Date: Tue, 16 Jul 2024 11:13:44 -0400 Subject: [PATCH 159/181] Correct dispatch to initializer --- src/classes/metafunction.c | 11 ++++++++--- src/core/vm.c | 8 +++++--- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/classes/metafunction.c b/src/classes/metafunction.c index 720c8b1a..0b634322 100644 --- a/src/classes/metafunction.c +++ b/src/classes/metafunction.c @@ -305,7 +305,7 @@ void mfcompiler_disassemble(mfcompiler *c) { break; } case MF_BRANCHNARGS: { - printf("branchargs (%i) -> (%i)\n", instr->narg, i+instr->branch+1); + printf("branchnargs (%i) -> (%i)\n", instr->narg, i+instr->branch+1); _mfcompiler_disassemblebranchtable(instr, i); break; } @@ -802,9 +802,14 @@ bool _finduidinlinearization(objectclass *klass, int uid) { return false; } -/** Execute the metafunction's resolver */ +/** Execute the metafunction's resolver + @param[in] fn - the metafunction to resolve + @param[in] nargs - number of positional arguments + @param[in] args - positional arguments @warning: the first user-visible argument should be in the zero position + @param[out] out - resolved function + @returns true if the metafunction was successfully resolved */ bool metafunction_resolve(objectmetafunction *fn, int nargs, value *args, value *out) { - int n=vm_countpositionalargs(nargs, args);; + int n=vm_countpositionalargs(nargs, args); if (!fn->resolver.data && !metafunction_compile(fn, NULL)) return false; mfinstruction *pc = fn->resolver.data; diff --git a/src/core/vm.c b/src/core/vm.c index d5282164..e9eafa97 100644 --- a/src/core/vm.c +++ b/src/core/vm.c @@ -989,6 +989,11 @@ bool morpho_interpret(vm *v, value *rstart, instructionindx istart) { /* Call the initializer if class provides one */ value ifunc; if (dictionary_getintern(&klass->methods, initselector, &ifunc)) { + if (MORPHO_ISMETAFUNCTION(ifunc) && + !metafunction_resolve(MORPHO_GETMETAFUNCTION(ifunc), c, reg+a+1, &ifunc)) { + ERROR(VM_MLTPLDSPTCHFLD); + } + /* If so, call it */ if (MORPHO_ISFUNCTION(ifunc)) { if (!vm_call(v, ifunc, a, c, NULL, &pc, ®)) goto vm_error; @@ -1001,9 +1006,6 @@ bool morpho_interpret(vm *v, value *rstart, instructionindx istart) { v->fp->inbuiltinfunction=NULL; #endif ERRORCHK(); - } else if (MORPHO_ISMETAFUNCTION(ifunc) && - !metafunction_resolve(MORPHO_GETMETAFUNCTION(ifunc), c, reg+a, &ifunc)) { - ERROR(VM_MLTPLDSPTCHFLD); } } else { if (c>0) { From 5af54856a68379d92a00832b55506e13d74a0450 Mon Sep 17 00:00:00 2001 From: Tim Atherton <54725421+softmattertheory@users.noreply.github.com> Date: Tue, 16 Jul 2024 11:15:31 -0400 Subject: [PATCH 160/181] Lazy compilation of metafunctions; disable disassembly --- src/classes/metafunction.c | 2 +- src/core/compile.c | 18 ------------------ 2 files changed, 1 insertion(+), 19 deletions(-) diff --git a/src/classes/metafunction.c b/src/classes/metafunction.c index 0b634322..d468a778 100644 --- a/src/classes/metafunction.c +++ b/src/classes/metafunction.c @@ -782,7 +782,7 @@ bool metafunction_compile(objectmetafunction *fn, error *err) { mfcompiler_init(&compiler, fn); mfcompile_set(&compiler, &set); - mfcompiler_disassemble(&compiler); + //mfcompiler_disassemble(&compiler); bool success=!morpho_checkerror(&compiler.err); if (!success && err) *err=compiler.err; diff --git a/src/core/compile.c b/src/core/compile.c index 7cc48f5e..7917ff49 100644 --- a/src/core/compile.c +++ b/src/core/compile.c @@ -3480,30 +3480,12 @@ static codeinfo compiler_class(compiler *c, syntaxtreenode *node, registerindx r compiler_error(c, node, COMPILE_CLSSLNRZ, MORPHO_GETCSTRING(klass->name)); } - objectstring str = MORPHO_STATICSTRING("XProblemAdapter"); - if (MORPHO_ISEQUAL(klass->name, MORPHO_OBJECT(&str))) { - - } - /* Compile method declarations */ if (node->right!=SYNTAXTREE_UNCONNECTED) { syntaxtreenode *child = compiler_getnode(c, node->right); mout=compiler_method(c, child, reqout); ninstructions+=mout.ninstructions; } - - /* Compile any metafunctions */ - for (unsigned int i=0; imethods.capacity; i++) { - dictionaryentry *e = &klass->methods.contents[i]; - if (MORPHO_ISNIL(e->key)) continue; - if (MORPHO_ISMETAFUNCTION(e->val)) { - morpho_printvalue(NULL, klass->name); - printf("."); - morpho_printvalue(NULL, e->key); - printf("=>\n"); - metafunction_compile(MORPHO_GETMETAFUNCTION(e->val), &c->err); - } - } /* End class definition */ compiler_endclass(c); From 883c9b75187b1011b93467e6b193d50e1300191a Mon Sep 17 00:00:00 2001 From: Tim Atherton <54725421+softmattertheory@users.noreply.github.com> Date: Wed, 24 Jul 2024 12:19:24 -0400 Subject: [PATCH 161/181] Improve reporting of metafunction compilation errors --- src/classes/metafunction.c | 14 ++++++---- src/classes/metafunction.h | 3 +- src/core/compile.c | 16 +++++++++++ src/core/vm.c | 25 ++++++++++++----- src/datastructures/error.h | 2 +- .../class_multiple_dispatch_post.morpho | 28 +++++++++++++++++++ .../class_multiple_initializer.morpho | 22 +++++++++++++++ .../duplicate_no_type_two.morpho | 13 +++++++++ 8 files changed, 108 insertions(+), 15 deletions(-) create mode 100644 test/types/multiple_dispatch/class_multiple_dispatch_post.morpho create mode 100644 test/types/multiple_dispatch/class_multiple_initializer.morpho create mode 100644 test/types/multiple_dispatch/duplicate_no_type_two.morpho diff --git a/src/classes/metafunction.c b/src/classes/metafunction.c index d468a778..02d08a4f 100644 --- a/src/classes/metafunction.c +++ b/src/classes/metafunction.c @@ -166,7 +166,7 @@ bool metafunction_matchset(objectmetafunction *fn, int n, value *fns) { return true; } -signature *_getsignature(value fn) { +signature *metafunction_getsignature(value fn) { if (MORPHO_ISFUNCTION(fn)) { return &MORPHO_GETFUNCTION(fn)->sig; } else if (MORPHO_ISBUILTINFUNCTION(fn)) { @@ -334,7 +334,7 @@ void mfcompiler_disassemble(mfcompiler *c) { } case MF_RESOLVE: { printf("resolve "); - signature *sig = _getsignature(instr->data.resolvefn); + signature *sig = metafunction_getsignature(instr->data.resolvefn); printf(" "); if (sig) signature_print(sig); break; @@ -774,7 +774,8 @@ bool metafunction_compile(objectmetafunction *fn, error *err) { mfresult rlist[set.count]; set.rlist=rlist; for (int i=0; ifns.data[i]); + rlist[i].sig=metafunction_getsignature(fn->fns.data[i]); + signature_print(rlist[i].sig); rlist[i].fn=fn->fns.data[i]; } @@ -782,7 +783,7 @@ bool metafunction_compile(objectmetafunction *fn, error *err) { mfcompiler_init(&compiler, fn); mfcompile_set(&compiler, &set); - //mfcompiler_disassemble(&compiler); + mfcompiler_disassemble(&compiler); bool success=!morpho_checkerror(&compiler.err); if (!success && err) *err=compiler.err; @@ -806,12 +807,13 @@ bool _finduidinlinearization(objectclass *klass, int uid) { @param[in] fn - the metafunction to resolve @param[in] nargs - number of positional arguments @param[in] args - positional arguments @warning: the first user-visible argument should be in the zero position + @param[out] err - error block to be filled out @param[out] out - resolved function @returns true if the metafunction was successfully resolved */ -bool metafunction_resolve(objectmetafunction *fn, int nargs, value *args, value *out) { +bool metafunction_resolve(objectmetafunction *fn, int nargs, value *args, error *err, value *out) { int n=vm_countpositionalargs(nargs, args); if (!fn->resolver.data && - !metafunction_compile(fn, NULL)) return false; + !metafunction_compile(fn, err)) return false; mfinstruction *pc = fn->resolver.data; if (!pc) return false; diff --git a/src/classes/metafunction.h b/src/classes/metafunction.h index 3cd5b25f..678bf43a 100644 --- a/src/classes/metafunction.h +++ b/src/classes/metafunction.h @@ -77,11 +77,12 @@ objectclass *metafunction_class(objectmetafunction *f); bool metafunction_matchfn(objectmetafunction *fn, value f); bool metafunction_matchset(objectmetafunction *fn, int n, value *fns); +signature *metafunction_getsignature(value fn); bool metafunction_compile(objectmetafunction *fn, error *err); void metafunction_clearinstructions(objectmetafunction *fn); -bool metafunction_resolve(objectmetafunction *f, int nargs, value *args, value *fn); +bool metafunction_resolve(objectmetafunction *f, int nargs, value *args, error *err, value *fn); void metafunction_initialize(void); diff --git a/src/core/compile.c b/src/core/compile.c index 7917ff49..129052b5 100644 --- a/src/core/compile.c +++ b/src/core/compile.c @@ -3314,12 +3314,28 @@ void compiler_overridemethod(compiler *c, syntaxtreenode *node, objectfunction * value symbol = method->name; objectclass *klass=compiler_getcurrentclass(c); + char *str = "ScaleConstraint"; + objectstring sc = MORPHO_STATICSTRING(str); + if (MORPHO_ISEQUAL(MORPHO_OBJECT(&sc), klass->name)) { + + } + if (MORPHO_ISMETAFUNCTION(prev)) { objectmetafunction *f = MORPHO_GETMETAFUNCTION(prev); if (f->klass!=klass) f=metafunction_clone(f); if (f) { metafunction_setclass(f, klass); + dictionary_insert(&klass->methods, symbol, MORPHO_OBJECT(f)); + + for (int i=0; ifns.count; i++) { // Check if this overrides + signature *sig = metafunction_getsignature(f->fns.data[i]); + if (sig && signature_isequal(sig, &method->sig)) { + // TODO: Should check for duplicate implementation here + f->fns.data[i] = MORPHO_OBJECT(method); + } + } + metafunction_add(f, MORPHO_OBJECT(method)); } diff --git a/src/core/vm.c b/src/core/vm.c index e9eafa97..cd53c989 100644 --- a/src/core/vm.c +++ b/src/core/vm.c @@ -946,7 +946,8 @@ bool morpho_interpret(vm *v, value *rstart, instructionindx istart) { c=DECODE_B(bc); // We use c for consistency between call and invoke... if (MORPHO_ISMETAFUNCTION(left) && - !metafunction_resolve(MORPHO_GETMETAFUNCTION(left), c, reg+a+1, &left)) { + !metafunction_resolve(MORPHO_GETMETAFUNCTION(left), c, reg+a+1, &v->err, &left)) { + ERRORCHK(); ERROR(VM_MLTPLDSPTCHFLD); } @@ -990,7 +991,8 @@ bool morpho_interpret(vm *v, value *rstart, instructionindx istart) { value ifunc; if (dictionary_getintern(&klass->methods, initselector, &ifunc)) { if (MORPHO_ISMETAFUNCTION(ifunc) && - !metafunction_resolve(MORPHO_GETMETAFUNCTION(ifunc), c, reg+a+1, &ifunc)) { + !metafunction_resolve(MORPHO_GETMETAFUNCTION(ifunc), c, reg+a+1, &v->err, &ifunc)) { + ERRORCHK(); ERROR(VM_MLTPLDSPTCHFLD); } @@ -1035,7 +1037,10 @@ bool morpho_interpret(vm *v, value *rstart, instructionindx istart) { if (dictionary_getintern(&instance->klass->methods, right, &ifunc)) { if (MORPHO_ISMETAFUNCTION(ifunc)) { - if (!metafunction_resolve(MORPHO_GETMETAFUNCTION(ifunc), c, reg+a+1, &ifunc)) ERROR(VM_MLTPLDSPTCHFLD); + if (!metafunction_resolve(MORPHO_GETMETAFUNCTION(ifunc), c, reg+a+1, &v->err, &ifunc)) { + ERRORCHK(); + ERROR(VM_MLTPLDSPTCHFLD); + } } /* If so, call it */ @@ -1076,7 +1081,10 @@ bool morpho_interpret(vm *v, value *rstart, instructionindx istart) { if (v->fp>v->frame) reg[a]=reg[0]; /* Copy self into r[a] and call */ if (MORPHO_ISMETAFUNCTION(ifunc)) { - if (!metafunction_resolve(MORPHO_GETMETAFUNCTION(ifunc), c, reg+a+1, &ifunc)) ERROR(VM_MLTPLDSPTCHFLD); + if (!metafunction_resolve(MORPHO_GETMETAFUNCTION(ifunc), c, reg+a+1, &v->err, &ifunc)) { + ERRORCHK(); + ERROR(VM_MLTPLDSPTCHFLD); + } } if (MORPHO_ISFUNCTION(ifunc)) { @@ -1108,7 +1116,10 @@ bool morpho_interpret(vm *v, value *rstart, instructionindx istart) { value ifunc; if (dictionary_getintern(&klass->methods, right, &ifunc)) { if (MORPHO_ISMETAFUNCTION(ifunc)) { - if (!metafunction_resolve(MORPHO_GETMETAFUNCTION(ifunc), c, reg+a+1, &ifunc)) ERROR(VM_MLTPLDSPTCHFLD); + if (!metafunction_resolve(MORPHO_GETMETAFUNCTION(ifunc), c, &v->err, reg+a+1, &ifunc)) { + ERRORCHK(); + ERROR(VM_MLTPLDSPTCHFLD); + } } if (MORPHO_ISBUILTINFUNCTION(ifunc)) { @@ -1720,8 +1731,8 @@ bool morpho_call(vm *v, value f, int nargs, value *args, value *ret) { value r0=f; if (MORPHO_ISMETAFUNCTION(fn) && - !metafunction_resolve(MORPHO_GETMETAFUNCTION(fn), nargs, args, &fn)) { - morpho_runtimeerror(v, VM_MLTPLDSPTCHFLD); + !metafunction_resolve(MORPHO_GETMETAFUNCTION(fn), nargs, args, &v->err, &fn)) { + if (!morpho_checkerror(&v->err)) morpho_runtimeerror(v, VM_MLTPLDSPTCHFLD); return false; } diff --git a/src/datastructures/error.h b/src/datastructures/error.h index f26c33b2..5579ef5a 100644 --- a/src/datastructures/error.h +++ b/src/datastructures/error.h @@ -189,7 +189,7 @@ void morpho_unreachable(const char *explanation); #define VM_GETINDEXARGS_MSG "Noninteger array index." #define VM_MLTPLDSPTCHFLD "MltplDsptchFld" -#define VM_MLTPLDSPTCHFLD_MSG "Mutiple dispatch could not find an implementation that matches these arguments." +#define VM_MLTPLDSPTCHFLD_MSG "Multiple dispatch could not find an implementation that matches these arguments." /* ------------------------------------------------------- * Error interface diff --git a/test/types/multiple_dispatch/class_multiple_dispatch_post.morpho b/test/types/multiple_dispatch/class_multiple_dispatch_post.morpho new file mode 100644 index 00000000..024a3ce3 --- /dev/null +++ b/test/types/multiple_dispatch/class_multiple_dispatch_post.morpho @@ -0,0 +1,28 @@ +// Dipatch methods on multiple types with a type defined after the class + +class A { + f(String x) { + print x + } + + f(Float x) { + print x+1 + } + + f(List x) { + print x[-1] + } + + f(x) { + print "Backup" + } +} + +class B { + +} + +var a = A() + +a.f(B()) +// expect: Backup diff --git a/test/types/multiple_dispatch/class_multiple_initializer.morpho b/test/types/multiple_dispatch/class_multiple_initializer.morpho new file mode 100644 index 00000000..395a654e --- /dev/null +++ b/test/types/multiple_dispatch/class_multiple_initializer.morpho @@ -0,0 +1,22 @@ +// Multiple dispatch in ititializer + +class A { + init(problem) { + self.problem = problem + } +} + +class B is A { + init(problem, List target) { + super.init(problem) + self.target = target + } + + init(x, String s) { self.init(x, [s]) } + init(x, Dictionary d) { self.init(x, [d]) } +} + +var b = B("", "Hello") + +print b.target +// expect: [ Hello ] \ No newline at end of file diff --git a/test/types/multiple_dispatch/duplicate_no_type_two.morpho b/test/types/multiple_dispatch/duplicate_no_type_two.morpho new file mode 100644 index 00000000..d7666be4 --- /dev/null +++ b/test/types/multiple_dispatch/duplicate_no_type_two.morpho @@ -0,0 +1,13 @@ +// Duplicate implementations with one argument no Type annotations. + +fn f(x, y) { + return 0 +} + +fn f(x, y) { + return 1 +} + +print f(1) +// expect error 'MltplDisptchAmbg' + From 00bb837b0e95956d781e8b7a2ff9cbbba1720dc2 Mon Sep 17 00:00:00 2001 From: Tim Atherton <54725421+softmattertheory@users.noreply.github.com> Date: Wed, 24 Jul 2024 21:38:45 -0400 Subject: [PATCH 162/181] Correct override code --- src/classes/metafunction.c | 3 +-- src/core/compile.c | 7 +------ 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/src/classes/metafunction.c b/src/classes/metafunction.c index 02d08a4f..5d011bcb 100644 --- a/src/classes/metafunction.c +++ b/src/classes/metafunction.c @@ -775,7 +775,6 @@ bool metafunction_compile(objectmetafunction *fn, error *err) { set.rlist=rlist; for (int i=0; ifns.data[i]); - signature_print(rlist[i].sig); rlist[i].fn=fn->fns.data[i]; } @@ -783,7 +782,7 @@ bool metafunction_compile(objectmetafunction *fn, error *err) { mfcompiler_init(&compiler, fn); mfcompile_set(&compiler, &set); - mfcompiler_disassemble(&compiler); + //mfcompiler_disassemble(&compiler); bool success=!morpho_checkerror(&compiler.err); if (!success && err) *err=compiler.err; diff --git a/src/core/compile.c b/src/core/compile.c index 129052b5..f8a0f6d4 100644 --- a/src/core/compile.c +++ b/src/core/compile.c @@ -3314,12 +3314,6 @@ void compiler_overridemethod(compiler *c, syntaxtreenode *node, objectfunction * value symbol = method->name; objectclass *klass=compiler_getcurrentclass(c); - char *str = "ScaleConstraint"; - objectstring sc = MORPHO_STATICSTRING(str); - if (MORPHO_ISEQUAL(MORPHO_OBJECT(&sc), klass->name)) { - - } - if (MORPHO_ISMETAFUNCTION(prev)) { objectmetafunction *f = MORPHO_GETMETAFUNCTION(prev); if (f->klass!=klass) f=metafunction_clone(f); @@ -3333,6 +3327,7 @@ void compiler_overridemethod(compiler *c, syntaxtreenode *node, objectfunction * if (sig && signature_isequal(sig, &method->sig)) { // TODO: Should check for duplicate implementation here f->fns.data[i] = MORPHO_OBJECT(method); + return; } } From fce3be542e08c006dd802a1b7acc6edbf1d17ad7 Mon Sep 17 00:00:00 2001 From: Tim Atherton <54725421+softmattertheory@users.noreply.github.com> Date: Tue, 30 Jul 2024 09:35:09 -0400 Subject: [PATCH 163/181] Fix incorrect grade in NormSq --- src/geometry/functional.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/geometry/functional.c b/src/geometry/functional.c index 30bc4c26..4d173a84 100644 --- a/src/geometry/functional.c +++ b/src/geometry/functional.c @@ -3839,7 +3839,7 @@ FUNCTIONAL_METHOD(NormSq, integrand, MESH_GRADE_VERTEX, fieldref, gradsq_prepare FUNCTIONAL_METHOD(NormSq, total, MESH_GRADE_VERTEX, fieldref, gradsq_prepareref, functional_sumintegrand, normsq_integrand, NULL, GRADSQ_ARGS, SYMMETRY_NONE); -FUNCTIONAL_METHOD(NormSq, gradient, MESH_GRADE_AREA, fieldref, gradsq_prepareref, functional_mapnumericalgradient, normsq_integrand, NULL, GRADSQ_ARGS, SYMMETRY_NONE); +FUNCTIONAL_METHOD(NormSq, gradient, MESH_GRADE_VERTEX, fieldref, gradsq_prepareref, functional_mapnumericalgradient, normsq_integrand, NULL, GRADSQ_ARGS, SYMMETRY_NONE); value NormSq_fieldgradient(vm *v, int nargs, value *args) { functional_mapinfo info; From 852150996bff3d4a864ea8f5e83e8ced2611a87c Mon Sep 17 00:00:00 2001 From: Tim Atherton <54725421+softmattertheory@users.noreply.github.com> Date: Thu, 1 Aug 2024 11:02:29 -0400 Subject: [PATCH 164/181] Fix broken call to metafunction_resolve --- src/core/vm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/vm.c b/src/core/vm.c index cd53c989..cf275be2 100644 --- a/src/core/vm.c +++ b/src/core/vm.c @@ -1116,7 +1116,7 @@ bool morpho_interpret(vm *v, value *rstart, instructionindx istart) { value ifunc; if (dictionary_getintern(&klass->methods, right, &ifunc)) { if (MORPHO_ISMETAFUNCTION(ifunc)) { - if (!metafunction_resolve(MORPHO_GETMETAFUNCTION(ifunc), c, &v->err, reg+a+1, &ifunc)) { + if (!metafunction_resolve(MORPHO_GETMETAFUNCTION(ifunc), c, reg+a+1, &v->err, &ifunc)) { ERRORCHK(); ERROR(VM_MLTPLDSPTCHFLD); } From 634fa666203a672df39c36a15f9e44fbbc1c5f79 Mon Sep 17 00:00:00 2001 From: Tim Atherton <54725421+softmattertheory@users.noreply.github.com> Date: Tue, 13 Aug 2024 12:19:40 -0400 Subject: [PATCH 165/181] Support calling throw and warning on the Error class. --- src/classes/err.c | 22 +++++++++++++++------- src/core/vm.c | 1 + src/datastructures/error.h | 3 +++ test/error/throw_on_class.morpho | 4 ++++ test/error/throw_on_class_notag.morpho | 4 ++++ 5 files changed, 27 insertions(+), 7 deletions(-) create mode 100644 test/error/throw_on_class.morpho create mode 100644 test/error/throw_on_class_notag.morpho diff --git a/src/classes/err.c b/src/classes/err.c index 65d66df6..cfaf7fe8 100644 --- a/src/classes/err.c +++ b/src/classes/err.c @@ -9,6 +9,7 @@ static value error_tagproperty; static value error_messageproperty; +static value error_errtag; /* ********************************************************************** * Error class @@ -34,17 +35,23 @@ value Error_init(vm *v, int nargs, value *args) { /** Extract the tag and message for an error */ bool _err_extract(vm *v, int nargs, value *args, value *tag, value *msg) { - objectinstance *slf = MORPHO_GETINSTANCE(MORPHO_SELF(args)); - if (!slf) return false; + // If an instance, extract the tag and message from the object + if (MORPHO_ISINSTANCE(MORPHO_SELF(args))) { + objectinstance *slf = MORPHO_GETINSTANCE(MORPHO_SELF(args)); + + objectinstance_getpropertyinterned(slf, error_tagproperty, tag); + if (nargs==0) objectinstance_getpropertyinterned(slf, error_messageproperty, msg); + } - objectinstance_getpropertyinterned(slf, error_tagproperty, tag); - if (nargs==0) { - objectinstance_getpropertyinterned(slf, error_messageproperty, msg); - } else { + if (nargs==1) { + if (!MORPHO_ISSTRING(*tag)) *tag = builtin_internsymbolascstring(ERROR_ERROR); *msg=MORPHO_GETARG(args, 0); + } else if (nargs==2) { + *tag=MORPHO_GETARG(args, 0); + *msg=MORPHO_GETARG(args, 1); } - return true; + return (MORPHO_ISSTRING(*tag) && MORPHO_ISSTRING(*msg)); } /** Throw an error */ @@ -106,6 +113,7 @@ void err_initialize(void) { // Create labels for Error property names error_tagproperty=builtin_internsymbolascstring(ERROR_TAG_PROPERTY); error_messageproperty=builtin_internsymbolascstring(ERROR_MESSAGE_PROPERTY); + error_errtag=builtin_internsymbolascstring(ERROR_ERROR); // Error error messages morpho_defineerror(ERROR_ARGS, ERROR_HALT, ERROR_ARGS_MSG); diff --git a/src/core/vm.c b/src/core/vm.c index cf275be2..9b3a55f1 100644 --- a/src/core/vm.c +++ b/src/core/vm.c @@ -2040,6 +2040,7 @@ void morpho_initialize(void) { morpho_defineerror(ERROR_ALLOCATIONFAILED, ERROR_EXIT, ERROR_ALLOCATIONFAILED_MSG); morpho_defineerror(ERROR_INTERNALERROR, ERROR_EXIT, ERROR_INTERNALERROR_MSG); + morpho_defineerror(ERROR_ERROR, ERROR_HALT, ERROR_ERROR_MSG); morpho_defineerror(VM_STCKOVFLW, ERROR_HALT, VM_STCKOVFLW_MSG); morpho_defineerror(VM_ERRSTCKOVFLW, ERROR_HALT, VM_ERRSTCKOVFLW_MSG); diff --git a/src/datastructures/error.h b/src/datastructures/error.h index 5579ef5a..83bad91e 100644 --- a/src/datastructures/error.h +++ b/src/datastructures/error.h @@ -115,6 +115,9 @@ void morpho_unreachable(const char *explanation); #define ERROR_INTERNALERROR "Intrnl" #define ERROR_INTERNALERROR_MSG "Internal error (contact developer)." +#define ERROR_ERROR "Err" +#define ERROR_ERROR_MSG "Error." + /* ------------------------------------------------------- * VM error messages * ------------------------------------------------------- */ diff --git a/test/error/throw_on_class.morpho b/test/error/throw_on_class.morpho new file mode 100644 index 00000000..f56969d1 --- /dev/null +++ b/test/error/throw_on_class.morpho @@ -0,0 +1,4 @@ +// Calling throw on the class + +Error.throw("FooFoo", "Can't foo foos.") +// expect: Error 'FooFoo': Can't foo foos. diff --git a/test/error/throw_on_class_notag.morpho b/test/error/throw_on_class_notag.morpho new file mode 100644 index 00000000..b3db2e56 --- /dev/null +++ b/test/error/throw_on_class_notag.morpho @@ -0,0 +1,4 @@ +// Calling throw on the class without a tag + +Error.throw("Can't foo foos.") +// expect: Error 'Err': Can't foo foos. From 3abc39585aa7bf06677ff2f53f3498a7b7fb90bc Mon Sep 17 00:00:00 2001 From: Tim Atherton <54725421+softmattertheory@users.noreply.github.com> Date: Tue, 13 Aug 2024 12:25:49 -0400 Subject: [PATCH 166/181] Add in missing tags --- help/errors.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/help/errors.md b/help/errors.md index 77245a4b..55cd1b0b 100644 --- a/help/errors.md +++ b/help/errors.md @@ -4,6 +4,8 @@ # Errors [tagerror]: # (error) [tagerrors]: # (errors) +[tagerrors]: # (throw) +[tagerrors]: # (warning) When an error occurs in running a morpho program, an error message is displayed together with an explanation of where in the program that the error happened. From 15ad90c84099a4d24d75e41e7a12897128aa11f4 Mon Sep 17 00:00:00 2001 From: Tim Atherton <54725421+softmattertheory@users.noreply.github.com> Date: Sun, 13 Oct 2024 14:06:55 -0400 Subject: [PATCH 167/181] Add morpho_addfunction as a new API that permits signatures --- src/builtin/builtin.c | 24 +++++++++++++++++------- src/builtin/builtin.h | 3 ++- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/builtin/builtin.c b/src/builtin/builtin.c index 59bf74c3..7d8f8839 100644 --- a/src/builtin/builtin.c +++ b/src/builtin/builtin.c @@ -180,6 +180,23 @@ void builtin_setclasstable(dictionary *dict) { * @param flags flags to define the function * @returns value referring to the objectbuiltinfunction */ value builtin_addfunction(char *name, builtinfunction func, builtinfunctionflags flags) { + morpho_addfunction(name, NULL, func, flags); +} + +/** Finds a builtin function from its name */ +value builtin_findfunction(value name) { + value out=MORPHO_NIL; + dictionary_get(&builtin_functiontable, name, &out); + return out; +} + +/** Add a function to the morpho runtime + * @param name name of the function + * @param signature [optional] signature for the function + * @param func the corresponding C function + * @param flags flags to define the function + * @returns value referring to the objectbuiltinfunction */ +value morpho_addfunction(char *name, char *signature, builtinfunction func, builtinfunctionflags flags) { objectbuiltinfunction *new = (objectbuiltinfunction *) object_new(sizeof(objectbuiltinfunction), OBJECT_BUILTINFUNCTION); value out = MORPHO_NIL; varray_valuewrite(&builtin_objects, MORPHO_OBJECT(new)); @@ -203,13 +220,6 @@ value builtin_addfunction(char *name, builtinfunction func, builtinfunctionflags return out; } -/** Finds a builtin function from its name */ -value builtin_findfunction(value name) { - value out=MORPHO_NIL; - dictionary_get(&builtin_functiontable, name, &out); - return out; -} - /* ********************************************************************** * Create and find builtin classes * ********************************************************************** */ diff --git a/src/builtin/builtin.h b/src/builtin/builtin.h index 6cf71a44..c2dd74c6 100644 --- a/src/builtin/builtin.h +++ b/src/builtin/builtin.h @@ -122,7 +122,8 @@ void builtin_setclasstable(dictionary *dict); value builtin_addfunction(char *name, builtinfunction func, builtinfunctionflags flags); value builtin_findfunction(value name); -bool builtin_setsignature(value fn, char *signature); + +value morpho_addfunction(char *name, char *signature, builtinfunction func, builtinfunctionflags flags); value builtin_addclass(char *name, builtinclassentry desc[], value superclass); value builtin_findclass(value name); From 41d0672776a282609b1c27b18e018e54305b03a2 Mon Sep 17 00:00:00 2001 From: Tim Atherton <54725421+softmattertheory@users.noreply.github.com> Date: Sun, 13 Oct 2024 14:32:43 -0400 Subject: [PATCH 168/181] morpho_addfunction implementation --- src/builtin/builtin.c | 65 ++++++++++++++++++++++++++++--------------- src/builtin/builtin.h | 2 +- 2 files changed, 44 insertions(+), 23 deletions(-) diff --git a/src/builtin/builtin.c b/src/builtin/builtin.c index 7d8f8839..fb7c6385 100644 --- a/src/builtin/builtin.c +++ b/src/builtin/builtin.c @@ -50,6 +50,12 @@ void builtin_init(objectbuiltinfunction *func) { signature_init(&func->sig); } +/** Clear an objectbuiltinfunction */ +void builtin_clear(objectbuiltinfunction *func) { + morpho_freeobject(func->name); + signature_clear(&func->sig); +} + /** @brief An enumerate loop. @details Successively calls enumerate on obj, passing the result to the supplied function. @param[in] v - the virtual machine @@ -132,9 +138,7 @@ void objectbuiltinfunction_printfn(object *obj, void *v) { } void objectbuiltinfunction_freefn(object *obj) { - objectbuiltinfunction *func = (objectbuiltinfunction *) obj; - signature_clear(&func->sig); - morpho_freeobject(func->name); + builtin_clear((objectbuiltinfunction *) obj); } size_t objectbuiltinfunction_sizefn(object *obj) { @@ -180,7 +184,9 @@ void builtin_setclasstable(dictionary *dict) { * @param flags flags to define the function * @returns value referring to the objectbuiltinfunction */ value builtin_addfunction(char *name, builtinfunction func, builtinfunctionflags flags) { - morpho_addfunction(name, NULL, func, flags); + value out=MORPHO_NIL; + morpho_addfunction(name, NULL, func, flags, &out); + return out; } /** Finds a builtin function from its name */ @@ -195,29 +201,44 @@ value builtin_findfunction(value name) { * @param signature [optional] signature for the function * @param func the corresponding C function * @param flags flags to define the function - * @returns value referring to the objectbuiltinfunction */ -value morpho_addfunction(char *name, char *signature, builtinfunction func, builtinfunctionflags flags) { + * @param[out] value referring to the objectbuiltinfunction + * @returns true on success */ +bool morpho_addfunction(char *name, char *signature, builtinfunction func, builtinfunctionflags flags, value *out) { objectbuiltinfunction *new = (objectbuiltinfunction *) object_new(sizeof(objectbuiltinfunction), OBJECT_BUILTINFUNCTION); - value out = MORPHO_NIL; - varray_valuewrite(&builtin_objects, MORPHO_OBJECT(new)); + if (!new) goto morpho_addfunction_cleanup; + + builtin_init(new); + new->function=func; + new->flags=flags; + + new->name=object_stringfromcstring(name, strlen(name)); + if (!name) goto morpho_addfunction_cleanup; + // Parse function signature if provided + if (signature && + !signature_parse(signature, &new->sig)) goto morpho_addfunction_cleanup; + + value selector = dictionary_intern(&builtin_symboltable, new->name); + if (dictionary_get(_currentfunctiontable, new->name, NULL)) { + UNREACHABLE("Redefinition of function in same extension [in builtin.c]"); + } + + value newfn = MORPHO_OBJECT(new); + dictionary_insert(_currentfunctiontable, selector, newfn); + + // Retain the objectbuiltinfunction in the builtin_objects table + varray_valuewrite(&builtin_objects, newfn); + *out = newfn; + + return true; + +morpho_addfunction_cleanup: if (new) { - builtin_init(new); - new->function=func; - new->name=object_stringfromcstring(name, strlen(name)); - new->flags=flags; - out = MORPHO_OBJECT(new); - - value selector = dictionary_intern(&builtin_symboltable, new->name); - - if (dictionary_get(_currentfunctiontable, new->name, NULL)) { - UNREACHABLE("Redefinition of function in same extension [in builtin.c]"); - } - - dictionary_insert(_currentfunctiontable, selector, out); + builtin_clear(new); + object_free((object *) new); } - return out; + return false; } /* ********************************************************************** diff --git a/src/builtin/builtin.h b/src/builtin/builtin.h index c2dd74c6..46b13cc8 100644 --- a/src/builtin/builtin.h +++ b/src/builtin/builtin.h @@ -123,7 +123,7 @@ void builtin_setclasstable(dictionary *dict); value builtin_addfunction(char *name, builtinfunction func, builtinfunctionflags flags); value builtin_findfunction(value name); -value morpho_addfunction(char *name, char *signature, builtinfunction func, builtinfunctionflags flags); +bool morpho_addfunction(char *name, char *signature, builtinfunction func, builtinfunctionflags flags, value *out); value builtin_addclass(char *name, builtinclassentry desc[], value superclass); value builtin_findclass(value name); From e96129ca3d39269b6fd77271f0289476ccf5a9cc Mon Sep 17 00:00:00 2001 From: Tim Atherton <54725421+softmattertheory@users.noreply.github.com> Date: Sun, 13 Oct 2024 15:09:49 -0400 Subject: [PATCH 169/181] builtin_addfunctiontodict allows addition of builtin metafunctions --- src/builtin/builtin.c | 52 +++++++++++++++++++++++++++++----- src/datastructures/signature.c | 1 + 2 files changed, 46 insertions(+), 7 deletions(-) diff --git a/src/builtin/builtin.c b/src/builtin/builtin.c index fb7c6385..e6708cde 100644 --- a/src/builtin/builtin.c +++ b/src/builtin/builtin.c @@ -196,12 +196,39 @@ value builtin_findfunction(value name) { return out; } +/** Adds a new builtinfunction to a given dictionary. + * @param[in] dict the dictionary + * @param[in] name name of the function to add + * @param[in] fn function to add + * @param[out] out the function added (which may be a metafunction) + * @returns true on success */ +bool builtin_addfunctiontodict(dictionary *dict, value name, value fn, value *out) { + bool success=false; + value entry=fn; // Dictionary entry for this name + value selector = dictionary_intern(&builtin_symboltable, name); // Use interned name + + if (dictionary_get(dict, selector, &entry)) { // There was an existing function + if (MORPHO_ISBUILTINFUNCTION(entry)) { // It was a builtinfunction, so we need to create a metafunction + if (metafunction_wrap(name, entry, &entry)) { // Wrap the old definition in a metafunction + metafunction_add(MORPHO_GETMETAFUNCTION(entry), fn); // Add the new definition + success=dictionary_insert(dict, selector, entry); + } + } else if (MORPHO_ISMETAFUNCTION(entry)) { // It was already a metafunction so simply add the new function + success=metafunction_add(MORPHO_GETMETAFUNCTION(entry), fn); + } + } else success=dictionary_insert(dict, selector, fn); + + if (success && out) *out = entry; + + return success; +} + /** Add a function to the morpho runtime * @param name name of the function * @param signature [optional] signature for the function * @param func the corresponding C function * @param flags flags to define the function - * @param[out] value referring to the objectbuiltinfunction + * @param[out] value the function created as usable with morpho_call * @returns true on success */ bool morpho_addfunction(char *name, char *signature, builtinfunction func, builtinfunctionflags flags, value *out) { objectbuiltinfunction *new = (objectbuiltinfunction *) object_new(sizeof(objectbuiltinfunction), OBJECT_BUILTINFUNCTION); @@ -218,17 +245,15 @@ bool morpho_addfunction(char *name, char *signature, builtinfunction func, built if (signature && !signature_parse(signature, &new->sig)) goto morpho_addfunction_cleanup; - value selector = dictionary_intern(&builtin_symboltable, new->name); - if (dictionary_get(_currentfunctiontable, new->name, NULL)) { + value newfn = MORPHO_OBJECT(new); + + if (!builtin_addfunctiontodict(_currentfunctiontable, new->name, newfn, NULL)) { UNREACHABLE("Redefinition of function in same extension [in builtin.c]"); } - value newfn = MORPHO_OBJECT(new); - dictionary_insert(_currentfunctiontable, selector, newfn); - // Retain the objectbuiltinfunction in the builtin_objects table varray_valuewrite(&builtin_objects, newfn); - *out = newfn; + if (out) *out = newfn; return true; @@ -349,6 +374,16 @@ extern objecttypedefn objectclassdefn; objecttype objectbuiltinfunctiontype; +value builtin_testint(vm *v, int nargs, value *args) { + printf("Int\n"); + return MORPHO_NIL; +} + +value builtin_testflt(vm *v, int nargs, value *args) { + printf("Float\n"); + return MORPHO_NIL; +} + void builtin_initialize(void) { dictionary_init(&builtin_functiontable); dictionary_init(&builtin_classtable); @@ -401,6 +436,9 @@ void builtin_initialize(void) { field_initialize(); functional_initialize(); + morpho_addfunction("hello", "(Int)", builtin_testint, MORPHO_FN_PUREFN, NULL); + morpho_addfunction("hello", "(Float)", builtin_testflt, MORPHO_FN_PUREFN, NULL); + morpho_addfinalizefn(builtin_finalize); } diff --git a/src/datastructures/signature.c b/src/datastructures/signature.c index c897bc77..c693c1fb 100644 --- a/src/datastructures/signature.c +++ b/src/datastructures/signature.c @@ -16,6 +16,7 @@ void signature_init(signature *s) { varray_valueinit(&s->types); + s->varg=false; } void signature_clear(signature *s) { From e4b3c8f187378e8e1f7fee8304acc6fe8b9a315e Mon Sep 17 00:00:00 2001 From: Tim Atherton <54725421+softmattertheory@users.noreply.github.com> Date: Mon, 14 Oct 2024 09:50:43 -0400 Subject: [PATCH 170/181] builtin_addclass now uses builtin_addfunctiontodict We also enforce redefinition of functions from parent classes --- src/builtin/builtin.c | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/src/builtin/builtin.c b/src/builtin/builtin.c index e6708cde..5ededa05 100644 --- a/src/builtin/builtin.c +++ b/src/builtin/builtin.c @@ -196,6 +196,15 @@ value builtin_findfunction(value name) { return out; } +objectclass *builtin_getparentclass(value fn) { + if (MORPHO_ISFUNCTION(fn)) return MORPHO_GETFUNCTION(fn)->klass; + else if (MORPHO_ISBUILTINFUNCTION(fn)) return MORPHO_GETBUILTINFUNCTION(fn)->klass; + else if (MORPHO_ISMETAFUNCTION(fn)) return MORPHO_GETMETAFUNCTION(fn)->klass; + else if (MORPHO_ISCLASS(fn)) return MORPHO_GETCLASS(fn)->superclass; + + return NULL; +} + /** Adds a new builtinfunction to a given dictionary. * @param[in] dict the dictionary * @param[in] name name of the function to add @@ -209,7 +218,10 @@ bool builtin_addfunctiontodict(dictionary *dict, value name, value fn, value *ou if (dictionary_get(dict, selector, &entry)) { // There was an existing function if (MORPHO_ISBUILTINFUNCTION(entry)) { // It was a builtinfunction, so we need to create a metafunction - if (metafunction_wrap(name, entry, &entry)) { // Wrap the old definition in a metafunction + if (builtin_getparentclass(fn) != + MORPHO_GETBUILTINFUNCTION(entry)->klass) { // Override superclass methods for now + dictionary_insert(dict, selector, fn); + } else if (metafunction_wrap(name, entry, &entry)) { // Wrap the old definition in a metafunction metafunction_add(MORPHO_GETMETAFUNCTION(entry), fn); // Add the new definition success=dictionary_insert(dict, selector, entry); } @@ -308,20 +320,7 @@ value builtin_addclass(char *name, builtinclassentry desc[], value superclass) { varray_valuewrite(&builtin_objects, method); - value prev=MORPHO_NIL; - if (dictionary_get(&new->methods, newmethod->name, &prev)) { - if (MORPHO_ISBUILTINFUNCTION(prev)) { - objectbuiltinfunction *func = MORPHO_GETBUILTINFUNCTION(prev); - if (func->klass!=new) { // Override superclass methods for now - dictionary_insert(&new->methods, selector, method); - } else if (metafunction_wrap(newmethod->name, prev, &prev)) { - metafunction_add(MORPHO_GETMETAFUNCTION(prev), method); - dictionary_insert(&new->methods, selector, prev); - } - } else if (MORPHO_ISMETAFUNCTION(prev)) { - metafunction_add(MORPHO_GETMETAFUNCTION(prev), method); - } - } else dictionary_insert(&new->methods, selector, method); + builtin_addfunctiontodict(&new->methods, newmethod->name, method, NULL); } } @@ -374,6 +373,11 @@ extern objecttypedefn objectclassdefn; objecttype objectbuiltinfunctiontype; +value builtin_test(vm *v, int nargs, value *args) { + printf("None\n"); + return MORPHO_NIL; +} + value builtin_testint(vm *v, int nargs, value *args) { printf("Int\n"); return MORPHO_NIL; @@ -436,6 +440,7 @@ void builtin_initialize(void) { field_initialize(); functional_initialize(); + morpho_addfunction("hello", "()", builtin_test, MORPHO_FN_PUREFN, NULL); morpho_addfunction("hello", "(Int)", builtin_testint, MORPHO_FN_PUREFN, NULL); morpho_addfunction("hello", "(Float)", builtin_testflt, MORPHO_FN_PUREFN, NULL); From 6e406aaa48824a0dcd4f72412e723b1f547f451e Mon Sep 17 00:00:00 2001 From: Tim Atherton <54725421+softmattertheory@users.noreply.github.com> Date: Mon, 14 Oct 2024 10:14:34 -0400 Subject: [PATCH 171/181] Signatures can now contain return types --- src/builtin/builtin.c | 19 ---------------- src/datastructures/signature.c | 40 ++++++++++++++++++++++++---------- src/datastructures/signature.h | 4 +++- 3 files changed, 31 insertions(+), 32 deletions(-) diff --git a/src/builtin/builtin.c b/src/builtin/builtin.c index 5ededa05..fda4132a 100644 --- a/src/builtin/builtin.c +++ b/src/builtin/builtin.c @@ -373,21 +373,6 @@ extern objecttypedefn objectclassdefn; objecttype objectbuiltinfunctiontype; -value builtin_test(vm *v, int nargs, value *args) { - printf("None\n"); - return MORPHO_NIL; -} - -value builtin_testint(vm *v, int nargs, value *args) { - printf("Int\n"); - return MORPHO_NIL; -} - -value builtin_testflt(vm *v, int nargs, value *args) { - printf("Float\n"); - return MORPHO_NIL; -} - void builtin_initialize(void) { dictionary_init(&builtin_functiontable); dictionary_init(&builtin_classtable); @@ -440,10 +425,6 @@ void builtin_initialize(void) { field_initialize(); functional_initialize(); - morpho_addfunction("hello", "()", builtin_test, MORPHO_FN_PUREFN, NULL); - morpho_addfunction("hello", "(Int)", builtin_testint, MORPHO_FN_PUREFN, NULL); - morpho_addfunction("hello", "(Float)", builtin_testflt, MORPHO_FN_PUREFN, NULL); - morpho_addfinalizefn(builtin_finalize); } diff --git a/src/datastructures/signature.c b/src/datastructures/signature.c index c693c1fb..3b9773be 100644 --- a/src/datastructures/signature.c +++ b/src/datastructures/signature.c @@ -16,6 +16,7 @@ void signature_init(signature *s) { varray_valueinit(&s->types); + s->ret=MORPHO_NIL; s->varg=false; } @@ -62,11 +63,6 @@ bool signature_paramlist(signature *s, int *nparams, value **ptypes) { return s->types.data; } -/** @brief Count the number of parameters in a signature */ -int signature_countparams(signature *s) { - return s->types.count; -} - /** @brief Returns the type of the i'th parameter, if it exists */ bool signature_getparamtype(signature *s, int i, value *type) { if (i>=s->types.count) return false; @@ -74,6 +70,16 @@ bool signature_getparamtype(signature *s, int i, value *type) { return true; } +/** @brief Returns the return type from the signature if defined */ +value signature_getreturntype(signature *s) { + return s->ret; +} + +/** @brief Count the number of parameters in a signature */ +int signature_countparams(signature *s) { + return s->types.count; +} + /* ********************************************************************** * Parse signatures * ********************************************************************** */ @@ -103,6 +109,17 @@ void signature_initializelexer(lexer *l, char *signature) { lex_setsymboltype(l, SIGNATURE_SYMBOL); } +/** @brief Parses a type name held in the parser's previous type */ +bool signature_parsetype(parser *p, value *type) { + value symbol; + if (!parse_stringfromtoken(p, 0, p->previous.length, &symbol)) return false; + value klass = builtin_findclass(symbol); + morpho_freeobject(symbol); + + if (MORPHO_ISCLASS(klass)) *type=klass; + return MORPHO_ISCLASS(klass); +} + /** @brief Parser function to process a symbol held in p->previous */ bool signature_parsesymbol(parser *p, void *out) { signature *sig = (signature *) out; @@ -112,12 +129,8 @@ bool signature_parsesymbol(parser *p, void *out) { value blank = MORPHO_NIL; success=varray_valueadd(&sig->types, &blank, 1); } else { - value symbol; - if (!parse_stringfromtoken(p, 0, p->previous.length, &symbol)) return false; - value klass = builtin_findclass(symbol); - morpho_freeobject(symbol); - - if (MORPHO_ISCLASS(klass)) success=varray_valueadd(&sig->types, &klass, 1); + value type; + if (signature_parsetype(p, &type)) success=varray_valueadd(&sig->types, &type, 1); } return success; } @@ -135,8 +148,11 @@ bool signature_parsevarg(parser *p, void *out) { /** @brief Main parser function for signatures */ bool signature_parsesignature(parser *p, void *out) { + signature *sig = (signature *) out; + if (parse_checktokenadvance(p, SIGNATURE_SYMBOL)) { - // Return type + value type; + if (signature_parsetype(p, &type)) sig->ret=type; } if (!parse_checktokenadvance(p, SIGNATURE_LEFTBRACE)) return false; diff --git a/src/datastructures/signature.h b/src/datastructures/signature.h index d93a8914..d101ae39 100644 --- a/src/datastructures/signature.h +++ b/src/datastructures/signature.h @@ -11,7 +11,8 @@ #include "varray.h" typedef struct { - varray_value types; + varray_value types; /** Signature of parameters */ + value ret; /** Return type */ bool varg; /** Is the function variadic? */ } signature; @@ -25,6 +26,7 @@ bool signature_istyped(signature *s); bool signature_isequal(signature *a, signature *b); bool signature_paramlist(signature *s, int *nparams, value **ptypes); bool signature_getparamtype(signature *s, int i, value *type); +value signature_getreturntype(signature *s); int signature_countparams(signature *s); void signature_set(signature *s, int nparam, value *types); From 7d73f46b7db27d3c8d24226e9626749997289051 Mon Sep 17 00:00:00 2001 From: Tim Atherton <54725421+softmattertheory@users.noreply.github.com> Date: Wed, 16 Oct 2024 08:44:44 -0400 Subject: [PATCH 172/181] Release notes for v0.6.1 --- releasenotes/version-0.6.1.md | 36 +++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 releasenotes/version-0.6.1.md diff --git a/releasenotes/version-0.6.1.md b/releasenotes/version-0.6.1.md new file mode 100644 index 00000000..21d444f7 --- /dev/null +++ b/releasenotes/version-0.6.1.md @@ -0,0 +1,36 @@ +# Release notes for 0.6.1 + +We're pleased to announce Morpho 0.6.1, which incorporates very important new language features and sets morpho up for future improvements. + +## Types + +Morpho now supports types. Variables can be declared with a specified type like so + + String s = "Hello" + +and the type of function parameters can be specified like + + fn f(String s, List l) { } + +## Multiple dispatch + +Morpho now supports *multiple dispatch*, whereby you can define multiple implementations of a function that accept different parameter types. The correct implementation to use is selected at runtime: + + fn f(String x) { } + fn f(List x) { } + +Methods defined on classes also support this mechanism. You can still specify parameters that without a type, in which case all types are accepted. Multiple dispatch is implemented efficiently (it incurs only a small overhead relative to a traditional function call) and is very useful to remove complex type checking. We are using this feature to improve how morpho works internally, as well as to implement new morpho packages. + +## Additional hessians + +LineCurvatureSq and LineTorsionSq now provide the hessian() method. + +## Preliminary support for finite element discretizations + +We have begun to include support for additional discretizations beyond the linear elements supported by prior versions of Morpho in the codebase. This feature is a work in progress and not yet completely ready for use; we expect to complete it in forthcoming releases. + +## Minor fixes + +* Bugfixes to parallelization. +* Error messages now refer to the module in which the error was found. +* Can now call throw() and warning() directly on the Error class. From 8016d9381574959607dc619226eb462c510d8ce3 Mon Sep 17 00:00:00 2001 From: Tim Atherton <54725421+softmattertheory@users.noreply.github.com> Date: Thu, 24 Oct 2024 09:41:28 -0500 Subject: [PATCH 173/181] Add test for syntax error in a constructor --- test/class/syntax_error_in_constructor.morpho | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 test/class/syntax_error_in_constructor.morpho diff --git a/test/class/syntax_error_in_constructor.morpho b/test/class/syntax_error_in_constructor.morpho new file mode 100644 index 00000000..f1fd69c1 --- /dev/null +++ b/test/class/syntax_error_in_constructor.morpho @@ -0,0 +1,13 @@ +// Syntax err. in call to constructor + +class A { + init(m, a=1, b=1, c=1) { + } +} + +class B {} + +var b = B() + +var a = A(b., a=1.0, b=1.0, c=1.0) +// expect error 'ExpExpr' From ffe9c683c2f79de1cbc03d3ccc3835ba4528b8a5 Mon Sep 17 00:00:00 2001 From: Tim Atherton <54725421+softmattertheory@users.noreply.github.com> Date: Thu, 24 Oct 2024 10:19:50 -0500 Subject: [PATCH 174/181] Add examples workflow --- .github/workflows/examples.yml | 42 ++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 .github/workflows/examples.yml diff --git a/.github/workflows/examples.yml b/.github/workflows/examples.yml new file mode 100644 index 00000000..7dd0fee0 --- /dev/null +++ b/.github/workflows/examples.yml @@ -0,0 +1,42 @@ +name: Build and Test + +on: + push: + branches: [ "main", "dev" ] + pull_request: + branches: [ "main", "dev" ] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - name: configure + run: | + sudo apt update + sudo apt install libsuitesparse-dev + sudo apt install liblapacke-dev + sudo apt install libunistring-dev + python -m pip install --upgrade pip + python -m pip install regex colored + - name: make + run: | + mkdir build + cd build + cmake -DCMAKE_BUILD_TYPE=Release .. + sudo make install + sudo mkdir /usr/local/lib/morpho + - name: getcli + run: | + git clone https://github.com/Morpho-lang/morpho-cli.git + cd morpho-cli + mkdir build + cd build + cmake .. + sudo make install + - name: test + run: | + cd examples + python3 examples.py -c From adcaa8f2011a23b540f2095afadeb40c79e0db8f Mon Sep 17 00:00:00 2001 From: Tim Atherton <54725421+softmattertheory@users.noreply.github.com> Date: Thu, 24 Oct 2024 10:21:46 -0500 Subject: [PATCH 175/181] Correct label for Examples CI --- .github/workflows/examples.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/examples.yml b/.github/workflows/examples.yml index 7dd0fee0..634a487a 100644 --- a/.github/workflows/examples.yml +++ b/.github/workflows/examples.yml @@ -1,4 +1,4 @@ -name: Build and Test +name: Examples on: push: From 4749cd25556f470544aeab240bb3e839ba929ed7 Mon Sep 17 00:00:00 2001 From: Tim Atherton <54725421+softmattertheory@users.noreply.github.com> Date: Thu, 24 Oct 2024 11:07:23 -0500 Subject: [PATCH 176/181] Fix check for Continuous Integration --- examples/examples.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/examples/examples.py b/examples/examples.py index ede9406a..46f3392b 100755 --- a/examples/examples.py +++ b/examples/examples.py @@ -118,8 +118,11 @@ def run(file,testLog,CI): # look for a command line arguement that says # this is being run for continous integration -CI = sys.argv == '-c' +CI = False +for arg in sys.argv: + if arg == '-c': # if the argument is -c, then we are running in CI mode + CI = True files=glob.glob('**/**.'+ext, recursive=True) with open("FailedExamples.txt",'w') as testLog: From f8cb1153dfeb217a8ee962ad8e39077d4fc677d1 Mon Sep 17 00:00:00 2001 From: Tim Atherton <54725421+softmattertheory@users.noreply.github.com> Date: Thu, 24 Oct 2024 11:48:19 -0500 Subject: [PATCH 177/181] Update CI script --- .github/workflows/examples.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.github/workflows/examples.yml b/.github/workflows/examples.yml index 634a487a..5c6b9aa0 100644 --- a/.github/workflows/examples.yml +++ b/.github/workflows/examples.yml @@ -16,6 +16,9 @@ jobs: - name: configure run: | sudo apt update + sudo apt install libglfw3-dev + sudo apt install povray + sudo apt install libfreetype6-dev sudo apt install libsuitesparse-dev sudo apt install liblapacke-dev sudo apt install libunistring-dev @@ -36,6 +39,14 @@ jobs: cd build cmake .. sudo make install + - name: morphoview + run: | + git clone https://github.com/morpho-lang/morpho-morphoview.git + cd morpho-morphoview + mkdir build + cd build + cmake .. + sudo make install - name: test run: | cd examples From 9950df1f2c79ea418dd2a8505f9fe8deb922ef25 Mon Sep 17 00:00:00 2001 From: Tim Atherton <54725421+softmattertheory@users.noreply.github.com> Date: Fri, 25 Oct 2024 09:24:49 -0700 Subject: [PATCH 178/181] Ensure we get output --- examples/examples.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/examples.py b/examples/examples.py index 46f3392b..246a4da0 100755 --- a/examples/examples.py +++ b/examples/examples.py @@ -63,6 +63,7 @@ def getoutput(filepath): lines[i+1]=stk # and remove them return list(filter(lambda x: x!=stk, lines)) + def run(file,testLog,CI): ret = 1 print(file+":", end=" ") @@ -72,7 +73,7 @@ def run(file,testLog,CI): tmp = file + '.out' # Run the test - os.system(command + ' ' +file + ' > ' + tmp) + os.system(command + ' ' +file) # + ' > ' + tmp) # If we produced output if os.path.exists(tmp): From 951c0bc491ee78f0722376bfc9cc30cf06a262d0 Mon Sep 17 00:00:00 2001 From: Tim Atherton <54725421+softmattertheory@users.noreply.github.com> Date: Fri, 25 Oct 2024 09:35:43 -0700 Subject: [PATCH 179/181] Update examples.py --- examples/examples.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/examples.py b/examples/examples.py index 246a4da0..4a7d76ec 100755 --- a/examples/examples.py +++ b/examples/examples.py @@ -68,12 +68,11 @@ def run(file,testLog,CI): ret = 1 print(file+":", end=" ") - # Create a temporary file in the same directory tmp = file + '.out' # Run the test - os.system(command + ' ' +file) # + ' > ' + tmp) + os.system(command + ' ' +file + ' > ' + tmp) # If we produced output if os.path.exists(tmp): From ebecf451134537b12c517149e8f52f7d5b635030 Mon Sep 17 00:00:00 2001 From: Tim Atherton <54725421+softmattertheory@users.noreply.github.com> Date: Fri, 25 Oct 2024 09:40:07 -0700 Subject: [PATCH 180/181] Add freefont --- .github/workflows/examples.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/examples.yml b/.github/workflows/examples.yml index 5c6b9aa0..83f81aaf 100644 --- a/.github/workflows/examples.yml +++ b/.github/workflows/examples.yml @@ -19,6 +19,7 @@ jobs: sudo apt install libglfw3-dev sudo apt install povray sudo apt install libfreetype6-dev + sudo apt install ttf-freefont sudo apt install libsuitesparse-dev sudo apt install liblapacke-dev sudo apt install libunistring-dev From 45531a5c38675520bafd3ab18095412c1466ac9e Mon Sep 17 00:00:00 2001 From: Tim Atherton <54725421+softmattertheory@users.noreply.github.com> Date: Fri, 25 Oct 2024 09:44:24 -0700 Subject: [PATCH 181/181] Fix package name --- .github/workflows/examples.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/examples.yml b/.github/workflows/examples.yml index 83f81aaf..4f2fff47 100644 --- a/.github/workflows/examples.yml +++ b/.github/workflows/examples.yml @@ -19,7 +19,7 @@ jobs: sudo apt install libglfw3-dev sudo apt install povray sudo apt install libfreetype6-dev - sudo apt install ttf-freefont + sudo apt install fonts-freefont-ttf sudo apt install libsuitesparse-dev sudo apt install liblapacke-dev sudo apt install libunistring-dev