Skip to content

Commit

Permalink
Merge pull request #279 from Morpho-lang/ternary
Browse files Browse the repository at this point in the history
Ternary operator
  • Loading branch information
emmanuellfc authored Dec 11, 2024
2 parents 8ebdbee + b668230 commit 2de0fb2
Show file tree
Hide file tree
Showing 14 changed files with 186 additions and 3 deletions.
11 changes: 11 additions & 0 deletions releasenotes/version-0.5.1.md
Original file line number Diff line number Diff line change
@@ -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.
11 changes: 11 additions & 0 deletions releasenotes/version-0.5.2.md
Original file line number Diff line number Diff line change
@@ -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.
9 changes: 9 additions & 0 deletions releasenotes/version-0.5.3.md
Original file line number Diff line number Diff line change
@@ -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.
4 changes: 2 additions & 2 deletions src/build.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
78 changes: 78 additions & 0 deletions src/core/compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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
Expand All @@ -1691,6 +1693,8 @@ compilenoderule noderules[] = {

{ compiler_logical }, // NODE_AND
{ compiler_logical }, // NODE_OR

{ compiler_ternary }, // NODE_TERNARY

{ compiler_dot }, // NODE_DOT

Expand Down Expand Up @@ -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
*
* <pre>
* TERNARY
* / \
* cond SEQUENCE
* / \
* true outcome false outcome
* </pre>
* and are compiled to:
* <cond>
* bif <cond>, 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;
Expand Down
2 changes: 2 additions & 0 deletions src/datastructures/syntaxtree.c
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ static char * nodedisplay[] = {
"and", // NODE_AND
"or", // NODE_OR

"?", // NODE_TERNARY

".", // NODE_DOT

"..", // NODE_RANGE
Expand Down
2 changes: 2 additions & 0 deletions src/datastructures/syntaxtree.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ enum {

NODE_AND,
NODE_OR,

NODE_TERNARY,

NODE_DOT,

Expand Down
16 changes: 15 additions & 1 deletion src/support/parse.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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);
Expand Down
4 changes: 4 additions & 0 deletions src/support/parse.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ enum {
PREC_NONE,
PREC_LOWEST,
PREC_ASSIGN,
PREC_TERNARY,
PREC_COMMA,
PREC_OR,
PREC_AND,
Expand Down Expand Up @@ -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."

Expand Down
12 changes: 12 additions & 0 deletions test/operator/ternary.morpho
Original file line number Diff line number Diff line change
@@ -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

8 changes: 8 additions & 0 deletions test/operator/ternary_in_function.morpho
Original file line number Diff line number Diff line change
@@ -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
13 changes: 13 additions & 0 deletions test/operator/ternary_in_loop.morpho
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Ternary operator in a loop

var a = 3

for (i in 1..5) {
print (i<a ? "less" : "more")
}

// expect: less
// expect: less
// expect: more
// expect: more
// expect: more
6 changes: 6 additions & 0 deletions test/operator/ternary_missing_colon.morpho
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Ternary operator with missing colon

var a = 1
var b = 2

print (a < b ? 1) // expect error 'TrnryMssngColon'
13 changes: 13 additions & 0 deletions test/operator/ternary_nested.morpho
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Nested ternary operators

var a = 3

for (i in 1..5) {
print (i<a ? "less" : (i==a ? "same" : "more"))
}

// expect: less
// expect: less
// expect: same
// expect: more
// expect: more

0 comments on commit 2de0fb2

Please sign in to comment.