From c9985f44612c4917ca85ba99c43211d2e71475e7 Mon Sep 17 00:00:00 2001 From: softmattertheory Date: Thu, 28 Nov 2024 20:47:37 -0500 Subject: [PATCH 1/2] Ternary operator implementation and tests --- src/build.h | 4 +- src/core/compile.c | 78 ++++++++++++++++++++++ src/datastructures/syntaxtree.c | 2 + src/datastructures/syntaxtree.h | 2 + src/support/parse.c | 16 ++++- src/support/parse.h | 4 ++ test/operator/ternary.morpho | 12 ++++ test/operator/ternary_in_function.morpho | 8 +++ test/operator/ternary_in_loop.morpho | 13 ++++ test/operator/ternary_missing_colon.morpho | 6 ++ test/operator/ternary_nested.morpho | 13 ++++ 11 files changed, 155 insertions(+), 3 deletions(-) create mode 100644 test/operator/ternary.morpho create mode 100644 test/operator/ternary_in_function.morpho create mode 100644 test/operator/ternary_in_loop.morpho create mode 100644 test/operator/ternary_missing_colon.morpho create mode 100644 test/operator/ternary_nested.morpho diff --git a/src/build.h b/src/build.h index 137fc680..83ad7ced 100644 --- a/src/build.h +++ b/src/build.h @@ -8,11 +8,11 @@ * Version * ********************************************************************** */ -#define MORPHO_VERSIONSTRING "0.6.1" +#define MORPHO_VERSIONSTRING "0.6.2" #define MORPHO_VERSION_MAJOR 0 #define MORPHO_VERSION_MINOR 6 -#define MORPHO_VERSION_PATCH 1 +#define MORPHO_VERSION_PATCH 2 /* ********************************************************************** * Paths and file system diff --git a/src/core/compile.c b/src/core/compile.c index 76ccd1c8..f1f697ac 100644 --- a/src/core/compile.c +++ b/src/core/compile.c @@ -1618,6 +1618,7 @@ static codeinfo compiler_index(compiler *c, syntaxtreenode *node, registerindx r static codeinfo compiler_negate(compiler *c, syntaxtreenode *node, registerindx out); static codeinfo compiler_not(compiler *c, syntaxtreenode *node, registerindx out); static codeinfo compiler_binary(compiler *c, syntaxtreenode *node, registerindx out); +static codeinfo compiler_ternary(compiler *c, syntaxtreenode *node, registerindx reqout); static codeinfo compiler_property(compiler *c, syntaxtreenode *node, registerindx reqout); static codeinfo compiler_dot(compiler *c, syntaxtreenode *node, registerindx reqout); static codeinfo compiler_grouping(compiler *c, syntaxtreenode *node, registerindx out); @@ -1682,6 +1683,7 @@ compilenoderule noderules[] = { { compiler_binary }, // NODE_POW { compiler_assign }, // NODE_ASSIGN + { compiler_binary }, // NODE_EQ { compiler_binary }, // NODE_NEQ { compiler_binary }, // NODE_LT @@ -1691,6 +1693,8 @@ compilenoderule noderules[] = { { compiler_logical }, // NODE_AND { compiler_logical }, // NODE_OR + + { compiler_ternary }, // NODE_TERNARY { compiler_dot }, // NODE_DOT @@ -2048,6 +2052,80 @@ static codeinfo compiler_binary(compiler *c, syntaxtreenode *node, registerindx return CODEINFO(REGISTER, out, ninstructions); } +/** @brief Compiles the ternary operator + * @details Ternary operators are encoded in the syntax tree + * + *
+ *                 TERNARY
+ *                  /  \
+ *              cond    SEQUENCE
+ *                       /    \
+ *            true outcome    false outcome
+ *           
+ * and are compiled to: + * + * bif , fail: ; branch if condition isn't met + * .. true outcome + * b end ; generated if an else statement is present + * fail: + * .. false outcome + * end: + */ +static codeinfo compiler_ternary(compiler *c, syntaxtreenode *node, registerindx reqout) { + unsigned int ninstructions=0; + + // Compile the condition + codeinfo cond = compiler_nodetobytecode(c, node->left, REGISTER_UNALLOCATED); + ninstructions+=cond.ninstructions; + + // And make sure it's in a register */ + if (!CODEINFO_ISREGISTER(cond)) { + cond=compiler_movetoregister(c, node, cond, REGISTER_UNALLOCATED); + ninstructions+=cond.ninstructions; /* Keep track of instructions */ + } + + // Generate empty instruction to contain the conditional branch + instructionindx cbrnchindx=compiler_addinstruction(c, ENCODE_BYTE(OP_NOP), node); + ninstructions++; + + // We're now done with the result of the condition + compiler_releaseoperand(c, cond); + + // Claim an output register + registerindx rout=compiler_regtemp(c, reqout); + + // Get the possible outcomes + syntaxtreenode *outcomes = compiler_getnode(c, node->right); + + // Compile true outcome + codeinfo left = compiler_nodetobytecode(c, outcomes->left, rout); + ninstructions+=left.ninstructions; + + // Ensure the result is in the output register + left = compiler_movetoregister(c, outcomes, left, rout); + ninstructions+=left.ninstructions; + + // Generate empty instruction to branch to the end + instructionindx brnchendindx=compiler_addinstruction(c, ENCODE_BYTE(OP_NOP), node); + ninstructions++; + + // Compile false outcome + codeinfo right = compiler_nodetobytecode(c, outcomes->right, rout); + ninstructions+=right.ninstructions; + + // Ensure the result is in the output register + right = compiler_movetoregister(c, outcomes, right, rout); + ninstructions+=right.ninstructions; + + instructionindx end=compiler_currentinstructionindex(c); + + // Patch up branches + compiler_setinstruction(c, cbrnchindx, ENCODE_LONG(OP_BIFF, cond.dest, brnchendindx-cbrnchindx)); + compiler_setinstruction(c, brnchendindx, ENCODE_LONG(OP_B, REGISTER_UNALLOCATED, end-brnchendindx-1)); + + return CODEINFO(REGISTER, rout, ninstructions); +} + /** Compiles a group (used to modify precedence) */ static codeinfo compiler_grouping(compiler *c, syntaxtreenode *node, registerindx reqout) { codeinfo out = CODEINFO_EMPTY; diff --git a/src/datastructures/syntaxtree.c b/src/datastructures/syntaxtree.c index 76b6689b..40ae6700 100644 --- a/src/datastructures/syntaxtree.c +++ b/src/datastructures/syntaxtree.c @@ -79,6 +79,8 @@ static char * nodedisplay[] = { "and", // NODE_AND "or", // NODE_OR + "?", // NODE_TERNARY + ".", // NODE_DOT "..", // NODE_RANGE diff --git a/src/datastructures/syntaxtree.h b/src/datastructures/syntaxtree.h index fc9df63c..ffb9001e 100644 --- a/src/datastructures/syntaxtree.h +++ b/src/datastructures/syntaxtree.h @@ -94,6 +94,8 @@ enum { NODE_AND, NODE_OR, + + NODE_TERNARY, NODE_DOT, diff --git a/src/support/parse.c b/src/support/parse.c index 7327f54b..db18fb4e 100644 --- a/src/support/parse.c +++ b/src/support/parse.c @@ -816,6 +816,19 @@ bool parse_binary(parser *p, void *out) { return parse_addnode(p, nodetype, MORPHO_NIL, &start, left, right, (syntaxtreeindx *) out); } +/** Parse ternary operator */ +bool parse_ternary(parser *p, void *out) { + token start = p->previous; + syntaxtreeindx cond=p->left; + syntaxtreeindx left, right, outcomes; + PARSE_CHECK(parse_expression(p, &left)); + PARSE_CHECK(parse_checkrequiredtoken(p, TOKEN_COLON, PARSE_TRNRYMSSNGCOLON)); + PARSE_CHECK(parse_expression(p, &right)); + + PARSE_CHECK(parse_addnode(p, NODE_SEQUENCE, MORPHO_NIL, &start, left, right, &outcomes)); + return parse_addnode(p, NODE_TERNARY, MORPHO_NIL, &start, cond, outcomes, (syntaxtreeindx *) out); +} + /** Parse operators like +=, -=, *= etc. */ bool parse_assignby(parser *p, void *out) { token start = p->previous; @@ -1563,7 +1576,7 @@ bool parse_program(parser *p, void *out) { parserule rules[] = { PARSERULE_UNUSED(TOKEN_NEWLINE), - PARSERULE_UNUSED(TOKEN_QUESTION), + PARSERULE_INFIX(TOKEN_QUESTION, parse_ternary, PREC_TERNARY), PARSERULE_PREFIX(TOKEN_STRING, parse_string), PARSERULE_PREFIX(TOKEN_INTERPOLATION, parse_interpolation), @@ -1848,6 +1861,7 @@ void parse_initialize(void) { 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); + morpho_defineerror(PARSE_TRNRYMSSNGCOLON, ERROR_PARSE, PARSE_TRNRYMSSNGCOLON_MSG); morpho_defineerror(PARSE_IFLFTPARENMISSING, ERROR_PARSE, PARSE_IFLFTPARENMISSING_MSG); morpho_defineerror(PARSE_IFRGHTPARENMISSING, ERROR_PARSE, PARSE_IFRGHTPARENMISSING_MSG); morpho_defineerror(PARSE_WHILELFTPARENMISSING, ERROR_PARSE, PARSE_WHILELFTPARENMISSING_MSG); diff --git a/src/support/parse.h b/src/support/parse.h index c9120362..9c1ec984 100644 --- a/src/support/parse.h +++ b/src/support/parse.h @@ -29,6 +29,7 @@ enum { PREC_NONE, PREC_LOWEST, PREC_ASSIGN, + PREC_TERNARY, PREC_COMMA, PREC_OR, PREC_AND, @@ -136,6 +137,9 @@ struct sparser { #define PARSE_MSSNGCOMMA "MssngComma" #define PARSE_MSSNGCOMMA_MSG "Expected ','." +#define PARSE_TRNRYMSSNGCOLON "TrnryMssngColon" +#define PARSE_TRNRYMSSNGCOLON_MSG "Expected ':' after expression in ternary operator." + #define PARSE_IFLFTPARENMISSING "IfMssngLftPrn" #define PARSE_IFLFTPARENMISSING_MSG "Expected '(' after if." diff --git a/test/operator/ternary.morpho b/test/operator/ternary.morpho new file mode 100644 index 00000000..5bd3547c --- /dev/null +++ b/test/operator/ternary.morpho @@ -0,0 +1,12 @@ +// Ternary operator + +var a = 1 +var b = 2 + +print a < b ? 1 : 0 // expect: 1 +print a > b ? 1 : 0 // expect: 0 + +print (a < b ? 1 : 0) // expect: 1 + +print 2 + (a > b ? 1 : 3) // expect: 5 + diff --git a/test/operator/ternary_in_function.morpho b/test/operator/ternary_in_function.morpho new file mode 100644 index 00000000..4cd7b722 --- /dev/null +++ b/test/operator/ternary_in_function.morpho @@ -0,0 +1,8 @@ +// Ternary operator + +fn f(a,b) { + return 1 + (a < b ? 1 : 0) +} + +print f(1,2) // expect: 2 +print f(2,1) // expect: 1 diff --git a/test/operator/ternary_in_loop.morpho b/test/operator/ternary_in_loop.morpho new file mode 100644 index 00000000..e87be0fb --- /dev/null +++ b/test/operator/ternary_in_loop.morpho @@ -0,0 +1,13 @@ +// Ternary operator in a loop + +var a = 3 + +for (i in 1..5) { + print (i Date: Thu, 28 Nov 2024 21:06:40 -0500 Subject: [PATCH 2/2] Missing release notes added --- releasenotes/version-0.5.1.md | 11 +++++++++++ releasenotes/version-0.5.2.md | 11 +++++++++++ releasenotes/version-0.5.3.md | 9 +++++++++ 3 files changed, 31 insertions(+) create mode 100644 releasenotes/version-0.5.1.md create mode 100644 releasenotes/version-0.5.2.md create mode 100644 releasenotes/version-0.5.3.md diff --git a/releasenotes/version-0.5.1.md b/releasenotes/version-0.5.1.md new file mode 100644 index 00000000..c16fba2c --- /dev/null +++ b/releasenotes/version-0.5.1.md @@ -0,0 +1,11 @@ +# Release notes for 0.5.1 + +We're pleased to announce morpho 0.5.1, which contains: + +* Improvements to error handling: + - Programs can now generate errors with the Error class. + - Programs can handle errors that occur with try/catch. +* FloryHuggins functional added for hydrogel simulations. +* POVray module supports transparency. +* Improvements to arrays. +* Various bugfixes. \ No newline at end of file diff --git a/releasenotes/version-0.5.2.md b/releasenotes/version-0.5.2.md new file mode 100644 index 00000000..25e8ba98 --- /dev/null +++ b/releasenotes/version-0.5.2.md @@ -0,0 +1,11 @@ +# Release notes for 0.5.2 + +We're pleased to announce morpho 0.5.2, which contains: + +* New `meshgen` module to create meshes in 2D and 3D. +* New `delaunay` module to create Delaunay triangulations in arbitrary dimensions. +* New `vtk` module to load and save meshes and fields in VTK legacy format. +* `List`, `Array` and `Matrix` slices (e.g. `a[0..5]` means elements 0 to 5 of a list) +* Minor improvements to apply, min, max and bounds functions. +* Code improvements to facilitate future expansion of Morpho. +* Numerous bugfixes. diff --git a/releasenotes/version-0.5.3.md b/releasenotes/version-0.5.3.md new file mode 100644 index 00000000..635b90e8 --- /dev/null +++ b/releasenotes/version-0.5.3.md @@ -0,0 +1,9 @@ +# Release notes for 0.5.3 + +We're pleased to announce morpho 0.5.3, which contains: + +* Support for complex numbers via a `Complex` class. +* Meshslice module enables taking a slice through a `Mesh` and associated `Field` objects. +* Improved `File` class. +* Many bugfixes. +* Apple M1 support.