Skip to content

Commit

Permalink
Add the "!is" operator.
Browse files Browse the repository at this point in the history
  • Loading branch information
mindhog committed Dec 4, 2019
1 parent 9c84b3e commit 20d55f5
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 43 deletions.
5 changes: 5 additions & 0 deletions doc/Manual.nml
Expand Up @@ -1272,6 +1272,11 @@ is::
object on the right. This isn't defined for numbers, only for aggregates
and primitive pointer types. It essentially checks for the equivalence of
the pointers.
!is::
True if the object on the left is not identical to the object on the
right. This isn't a real operator, it is syntactic sugar that translates
to the negation of the #is# operator. That is, #a !is b# is semantically
equivalent to #!(a is b)#.

Basic Arithmetic
''''''''''''''''
Expand Down
132 changes: 89 additions & 43 deletions parser/Parser.cc
Expand Up @@ -1007,6 +1007,42 @@ void Parser::checkForRedefine(const Token &tok, VarDef *def) const {
redefineError(tok, def);
}

ExprPtr Parser::parseBinOp(Expr *rawExpr, const Token &tok,
unsigned precedence
) {
ExprPtr expr = rawExpr;
// Parse the right-hand-side expression.
ExprPtr rhs = parseExpression(precedence);

FuncCall::ExprVec exprs(2);
exprs[0] = expr;
exprs[1] = rhs;

FuncDefPtr func = lookUpBinOp(tok.getData(), exprs);
if (!func)
error(tok,
SPUG_FSTR("Operator " << expr->getTypeDisplayName() << " " <<
tok.getData() << " " << rhs->getTypeDisplayName() <<
" undefined."
)
);
BSTATS_GO(s1)
FuncCallPtr funcCall = context->builder.createFuncCall(func.get());
BSTATS_END
funcCall->args = exprs;
if (func->flags & FuncDef::method)
funcCall->receiver =
(func->flags & FuncDef::reverse) ? rhs : expr;
expr = funcCall;
try {
return expr->foldConstants();
} catch (DivZeroError &ex) {
warn(tok, "Division by zero.");
// expr should still be the func call.
return expr;
}
}

ExprPtr Parser::parseSecondary(const Primary &primary, unsigned precedence) {
ExprPtr expr = primary.expr;
Token tok = getToken();
Expand Down Expand Up @@ -1138,57 +1174,67 @@ ExprPtr Parser::parseSecondary(const Primary &primary, unsigned precedence) {
if (newPrec <= precedence)
break;

// parse the right-hand-side expression
ExprPtr rhs = parseExpression(newPrec);

FuncCall::ExprVec exprs(2);
exprs[0] = expr;
exprs[1] = rhs;

FuncDefPtr func = lookUpBinOp(tok.getData(), exprs);
if (!func)
error(tok,
SPUG_FSTR("Operator " << expr->getTypeDisplayName() << " " <<
tok.getData() << " " << rhs->getTypeDisplayName() <<
" undefined."
)
);
BSTATS_GO(s1)
FuncCallPtr funcCall = context->builder.createFuncCall(func.get());
BSTATS_END
funcCall->args = exprs;
if (func->flags & FuncDef::method)
funcCall->receiver =
(func->flags & FuncDef::reverse) ? rhs : expr;
expr = funcCall;
try {
expr = expr->foldConstants();
} catch (DivZeroError &ex) {
warn(tok, "Division by zero.");
// expr should still be the func call.
}
expr = parseBinOp(expr.get(), tok, newPrec);
} else if (tok.isIstrBegin()) {
expr = parseIString(expr.get());
} else if (tok.isQuest()) {
if (precedence >= logOrPrec)
break;
expr = parseTernary(expr.get());
} else if (tok.isBang()) {
// this is special wacky collection syntax Type![1, 2, 3]
TypeDef *type = convertTypeRef(expr.get());
if (!type)
error(tok,
"Exclamation point can not follow a non-type expression"
);
// Check for the 'is' keyword to see if this is the "!is" sugar.
Token tok2 = toker.getToken();
if (tok2.getType() == Token::isKw) {
unsigned newPrec = getPrecedence(tok2.getData());
if (newPrec <= precedence)
break;

// check for a square bracket
tok = toker.getToken();
if (!tok.isLBracket())
error(tok,
"Sequence initializer ('[ ... ]') expected after "
"'type!'"
);
expr = parseConstSequence(type);
// parse the full "is" operation.
expr = parseBinOp(expr.get(), tok2, newPrec);

// Wrap it in a synthetic "not" operation

// Look up the unary operator, first in the methods of the "is"
// expression return type and then in the global context.
FuncCall::ExprVec args;
FuncDefPtr funcDef = context->lookUp(
"oper !",
args,
expr->getType(*context).get()
);
if (!funcDef) {
args.push_back(expr);
funcDef = context->lookUp("oper !", args);
}
if (!funcDef)
error(tok, SPUG_FSTR("'oper !' is not defined for type " <<
expr->type->name));

// Emit the function or method call.
BSTATS_GO(s1)
FuncCallPtr funcCall =
context->builder.createFuncCall(funcDef.get());
BSTATS_END
funcCall->args = args;
if (funcDef->flags & FuncDef::method)
funcCall->receiver = expr;
expr = funcCall->foldConstants();
} else {
// this is special wacky collection syntax Type![1, 2, 3]
TypeDef *type = convertTypeRef(expr.get());
if (!type)
error(tok,
"Exclamation point can not follow a non-type expression"
);

// check for a square bracket
if (!tok2.isLBracket())
error(tok2,
"Sequence initializer ('[ ... ]') expected after "
"'type!'"
);
expr = parseConstSequence(type);
}
} else if (tok.isAssign() || tok.isAugAssign()) {
ExprPtr val = parseExpression();
expr = makeAssign(expr.get(), tok, val.get());
Expand Down
11 changes: 11 additions & 0 deletions parser/Parser.h
Expand Up @@ -277,6 +277,17 @@ class Parser {
*/
void checkForRedefine(const Token &tok, model::VarDef *def) const;

/**
* Parse a binary operator.
*
* @param expr the left-hand expression
* @param tok the operator token.
* @param precedence the precedence of the new operator.
*/
model::ExprPtr parseBinOp(model::Expr *expr, const Token &tok,
unsigned precedence
);

/**
* Parse a secondary expression. Secondary expressions include a the
* dot operator, binary operators and the bracket operators and their
Expand Down
11 changes: 11 additions & 0 deletions test/test_binops.crk
Expand Up @@ -86,6 +86,17 @@ expect(2 | 3 & 1, "2 | 3 & 1", 3);
expect(2 | 3 ^ 6, "2 | 3 ^ 6", 7);
expect(2 ^ 3 & 1, "2 ^ 3 & 1", 3);

if (1) {
class A {}
a := A();
b := a;
A c;
if (a is b) 1; else cout `FAILED a is b\n`;
if (b is a) 1; else cout `FAILED b is a\n`;
if (a !is c) 1; else cout `FAILED a !is c\n`;
if (b !is c) 1; else cout `FAILED b !is c\n`;
}

# verify that the result types of binary operators are what we expect them to
# be

Expand Down

0 comments on commit 20d55f5

Please sign in to comment.