/* Copyright (C) 2003-2015 LiveCode Ltd. This file is part of LiveCode. LiveCode is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License v3 as published by the Free Software Foundation. LiveCode is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with LiveCode. If not see . */ #include "prefix.h" #include "globdefs.h" #include "objdefs.h" #include "parsedef.h" #include "filedefs.h" #include "scriptpt.h" //#include "execpt.h" #include "cmds.h" #include "handler.h" #include "chunk.h" #include "mcerror.h" #include "globals.h" #include "osspec.h" #include "exec.h" #include "syntax.h" #include "variable.h" #include //////////////////////////////////////////////////////////////////////// // inline bool MCMathOpCommandComputeOverlap(MCExpression *p_source, MCExpression *p_dest, MCVarref *p_destvar) { MCVarref *t_src_ref; t_src_ref = p_source -> getrootvarref(); if (t_src_ref == NULL) return false; if (p_destvar != NULL) return t_src_ref -> rootmatches(p_destvar); MCVarref *t_dst_ref; t_dst_ref = p_dest -> getrootvarref(); if (t_dst_ref == NULL) return false; return t_src_ref -> rootmatches(t_dst_ref); } // MCAdd::~MCAdd() { delete source; delete dest; // MW-2013-08-01: [[ Bug 10925 ]] Only delete the destvar varref if dest is NULL, // otherwise its owned by dest. if (dest == NULL) delete destvar; } Parse_stat MCAdd::parse(MCScriptPoint &sp) { initpoint(sp); if (sp.parseexp(False, True, &source) != PS_NORMAL) { MCperror->add(PE_ADD_BADEXP, sp); return PS_ERROR; } if (sp.skip_token(SP_FACTOR, TT_TO) == PS_ERROR) { MCperror->add(PE_ADD_NOTO, sp); return PS_ERROR; } Symbol_type type; // MW-2011-06-22: [[ SERVER ]] Update to use SP findvar method to take into account // execution outwith a handler. if (sp.next(type) != PS_NORMAL || type != ST_ID || sp.findvar(sp.gettoken_nameref(), &destvar) != PS_NORMAL) { sp.backup(); dest = new MCChunk(True); if (dest->parse(sp, False) != PS_NORMAL) { MCperror->add(PE_ADD_BADDEST, sp); return PS_ERROR; } } else destvar->parsearray(sp); // MW-2013-08-01: [[ Bug 10925 ]] If the dest chunk is just a var, extract the varref. if (dest != NULL && dest -> isvarchunk()) destvar = dest -> getrootvarref(); overlap = MCMathOpCommandComputeOverlap(source, dest, destvar); return PS_NORMAL; } // MW-2007-07-03: [[ Bug 5123 ]] - Strict array checking modification // Here the source can be an array or number so we use 'tona'. void MCAdd::exec_ctxt(MCExecContext &ctxt) { #ifdef /* MCAdd */ LEGACY_EXEC MCVariable *t_dst_var; MCVariableValue *t_dst_ref; t_dst_ref = NULL; if (source->eval(ep) != ES_NORMAL || ep.tona() != ES_NORMAL) { MCeerror->add(EE_ADD_BADSOURCE, line, pos); return ES_ERROR; } if (overlap) ep . grab(); if (destvar != NULL && destvar -> evalcontainer(ep, t_dst_var, t_dst_ref) != ES_NORMAL) { MCeerror->add(EE_ADD_BADDEST, line, pos); return ES_ERROR; } if (t_dst_ref != NULL && t_dst_ref -> is_array()) { if (t_dst_ref->factorarray(ep, O_PLUS) != ES_NORMAL) { MCeerror->add(EE_ADD_BADARRAY, line, pos); return ES_ERROR; } return ES_NORMAL; } if (ep.getformat() == VF_ARRAY) { MCeerror->add(EE_ADD_MISMATCH, line, pos); return ES_ERROR; } // Variable case real8 n1 = ep.getnvalue(); if (t_dst_ref != NULL) { real8 n2; if (!t_dst_ref -> get_as_real(ep, n2)) { MCeerror -> add(EE_ADD_BADDEST, line, pos); return ES_ERROR; } t_dst_ref -> assign_real(n1 + n2); if (t_dst_var != NULL) t_dst_var -> synchronize(ep, True); return ES_NORMAL; } // Chunk case if (dest->eval(ep) != ES_NORMAL || ep.ton() != ES_NORMAL) { MCeerror->add(EE_ADD_BADDEST, line, pos); return ES_ERROR; } real8 n2 = ep.getnvalue(); ep.setnvalue(n1 + n2); if (dest->set(ep, PT_INTO) != ES_NORMAL) { MCeerror->add(EE_ADD_CANTSET, line, pos); return ES_ERROR; } overlap = MCMathOpCommandComputeOverlap(source, dest, destvar); return ES_NORMAL; #endif /* MCAdd */ MCExecValue t_src; if (!ctxt . EvaluateExpression(source, EE_ADD_BADSOURCE, t_src) || !ctxt . ConvertToNumberOrArray(t_src)) { return; } MCExecValue t_dst; MCAutoPointer t_dst_container; if (destvar != nil) { bool t_success; if (destvar -> needsContainer()) t_success = destvar -> evalcontainer(ctxt, &t_dst_container) && t_dst_container -> eval_ctxt(ctxt, t_dst); else { destvar -> eval_ctxt(ctxt, t_dst); t_success = !ctxt . HasError(); } if (!t_success) { ctxt . LegacyThrow(EE_ADD_BADDEST); MCExecTypeRelease(t_src); return; } } else { if (!ctxt . EvaluateExpression(dest, EE_ADD_BADDEST, t_dst)) { MCExecTypeRelease(t_src); return; } } if (!ctxt . ConvertToNumberOrArray(t_dst)) { MCExecTypeRelease(t_src); MCExecTypeRelease(t_dst); return; } MCExecValue t_result; t_result . type = t_dst . type; if (t_src . type == kMCExecValueTypeArrayRef) { if (t_dst . type == kMCExecValueTypeArrayRef) MCMathExecAddArrayToArray(ctxt, t_src . arrayref_value, t_dst . arrayref_value, t_result . arrayref_value); else { ctxt . LegacyThrow(EE_ADD_MISMATCH); return; } } else { if (t_dst . type == kMCExecValueTypeArrayRef) MCMathExecAddNumberToArray(ctxt, t_src . double_value, t_dst . arrayref_value, t_result . arrayref_value); else MCMathExecAddNumberToNumber(ctxt, t_src . double_value, t_dst . double_value, t_result . double_value); } MCExecTypeRelease(t_src); MCExecTypeRelease(t_dst); if (!ctxt . HasError()) { if (destvar != nil) { bool t_success; if (destvar -> needsContainer()) t_success = t_dst_container -> give_value(ctxt, t_result); else t_success = destvar -> give_value(ctxt, t_result); if (!t_success) ctxt . Throw(); } else { if (dest->set(ctxt, PT_INTO, t_result)) return; ctxt . LegacyThrow(EE_ADD_CANTSET); } } } void MCAdd::compile(MCSyntaxFactoryRef ctxt) { MCSyntaxFactoryBeginStatement(ctxt, line, pos); source -> compile(ctxt); if (destvar != nil) destvar -> compile_inout(ctxt); else dest -> compile_inout(ctxt); MCSyntaxFactoryExecMethodWithArgs(ctxt, kMCMathExecAddArrayToArrayMethodInfo, 0, 1, 1); MCSyntaxFactoryExecMethodWithArgs(ctxt, kMCMathExecAddNumberToArrayMethodInfo, 0, 1, 1); MCSyntaxFactoryExecMethodWithArgs(ctxt, kMCMathExecAddNumberToNumberMethodInfo, 0, 1, 1); MCSyntaxFactoryEndStatement(ctxt); } MCDivide::~MCDivide() { delete source; delete dest; // MW-2013-08-01: [[ Bug 10925 ]] Only delete the destvar varref if dest is NULL, // otherwise its owned by dest. if (dest == NULL) delete destvar; } Parse_stat MCDivide::parse(MCScriptPoint &sp) { initpoint(sp); // MW-2011-06-22: [[ SERVER ]] Update to use SP findvar method to take into account // execution outwith a handler. Symbol_type type; if (sp.next(type) != PS_NORMAL || type != ST_ID || sp . findvar(sp.gettoken_nameref(), &destvar) != PS_NORMAL) { sp.backup(); dest = new MCChunk(True); if (dest->parse(sp, False) != PS_NORMAL) { MCperror->add(PE_DIVIDE_BADDEST, sp); return PS_ERROR; } } else destvar->parsearray(sp); if (sp.skip_token(SP_FACTOR, TT_PREP) == PS_ERROR) { MCperror->add(PE_DIVIDE_NOBY, sp); return PS_ERROR; } if (sp.parseexp(False, True, &source) != PS_NORMAL) { MCperror->add(PE_DIVIDE_BADEXP, sp); return PS_ERROR; } // MW-2013-08-01: [[ Bug 10925 ]] If the dest chunk is just a var, extract the varref. if (dest != NULL && dest -> isvarchunk()) destvar = dest -> getrootvarref(); overlap = MCMathOpCommandComputeOverlap(source, dest, destvar); return PS_NORMAL; } // MW-2007-07-03: [[ Bug 5123 ]] - Strict array checking modification // Here the source can be an array or number so we use 'tona'. void MCDivide::exec_ctxt(MCExecContext &ctxt) { #ifdef /* MCDivide */ LEGACY_EXEC MCVariable *t_dst_var; MCVariableValue *t_dst_ref; t_dst_ref = NULL; if (source->eval(ep) != ES_NORMAL || ep.tona() != ES_NORMAL) { MCeerror->add(EE_DIVIDE_BADSOURCE, line, pos); return ES_ERROR; } if (overlap) ep . grab(); if (destvar != NULL && destvar -> evalcontainer(ep, t_dst_var, t_dst_ref) != ES_NORMAL) { MCeerror->add(EE_DIVIDE_BADDEST, line, pos); return ES_ERROR; } if (t_dst_ref != NULL && t_dst_ref -> is_array()) { if (t_dst_ref->factorarray(ep, O_OVER) != ES_NORMAL) { MCeerror->add(EE_DIVIDE_BADARRAY, line, pos); return ES_ERROR; } return ES_NORMAL; } if (ep.getformat() == VF_ARRAY) { MCeerror->add(EE_DIVIDE_MISMATCH, line, pos); return ES_ERROR; } // Variable case real8 n2 = ep.getnvalue(); if (t_dst_ref != NULL) { real8 n1; if (!t_dst_ref -> get_as_real(ep, n1)) { MCeerror -> add(EE_ADD_BADDEST, line, pos); return ES_ERROR; } MCS_seterrno(0); n1 /= n2; if (n1 == MCinfinity || MCS_geterrno() != 0) { MCS_seterrno(0); if (n2 == 0.0) MCeerror->add(EE_DIVIDE_ZERO, line, pos); else MCeerror->add(EE_DIVIDE_RANGE, line, pos); return ES_ERROR; } t_dst_ref -> assign_real(n1); if (t_dst_var != NULL) t_dst_var -> synchronize(ep, True); return ES_NORMAL; } // Chunk case if (dest->eval(ep) != ES_NORMAL || ep.ton() != ES_NORMAL) { MCeerror->add(EE_DIVIDE_BADDEST, line, pos); return ES_ERROR; } real8 n1 = ep.getnvalue(); MCS_seterrno(0); n1 = n1 / n2; if (n1 == MCinfinity || MCS_geterrno() != 0) { MCS_seterrno(0); if (n2 == 0.0) MCeerror->add(EE_DIVIDE_ZERO, line, pos); else MCeerror->add(EE_DIVIDE_RANGE, line, pos); return ES_ERROR; } ep.setnvalue(n1); if (dest->set(ep, PT_INTO) != ES_NORMAL) { MCeerror->add(EE_DIVIDE_CANTSET, line, pos); return ES_ERROR; } return ES_NORMAL; #endif /* MCDivide */ MCExecValue t_src; if (!ctxt . EvaluateExpression(source, EE_DIVIDE_BADSOURCE, t_src) || !ctxt . ConvertToNumberOrArray(t_src)) { return; } MCExecValue t_dst; MCAutoPointer t_dst_container; if (destvar != nil) { bool t_success; if (destvar -> needsContainer()) t_success = destvar -> evalcontainer(ctxt, &t_dst_container) && t_dst_container -> eval_ctxt(ctxt, t_dst); else { destvar -> eval_ctxt(ctxt, t_dst); t_success = !ctxt.HasError(); } if (!t_success) { ctxt . LegacyThrow(EE_DIVIDE_BADDEST); MCExecTypeRelease(t_src); return; } } else { if (!ctxt . EvaluateExpression(dest, EE_DIVIDE_BADDEST, t_dst)) { MCExecTypeRelease(t_src); return; } } if (!ctxt . ConvertToNumberOrArray(t_dst)) { MCExecTypeRelease(t_src); MCExecTypeRelease(t_dst); return; } MCExecValue t_result; t_result . type = t_dst . type; if (t_src . type == kMCExecValueTypeArrayRef) { if (t_dst . type == kMCExecValueTypeArrayRef) MCMathExecDivideArrayByArray(ctxt, t_dst .arrayref_value, t_src . arrayref_value, t_result . arrayref_value); else { ctxt . LegacyThrow(EE_DIVIDE_MISMATCH); return; } } else { if (t_dst . type == kMCExecValueTypeArrayRef) MCMathExecDivideArrayByNumber(ctxt, t_dst . arrayref_value, t_src . double_value, t_result . arrayref_value); else MCMathExecDivideNumberByNumber(ctxt, t_dst . double_value, t_src . double_value, t_result . double_value); } MCExecTypeRelease(t_src); MCExecTypeRelease(t_dst); if (!ctxt . HasError()) { if (destvar != nil) { bool t_success; if (destvar -> needsContainer()) t_success = t_dst_container -> give_value(ctxt, t_result); else t_success = destvar -> give_value(ctxt, t_result); if (!t_success) ctxt . Throw(); } else { if (dest->set(ctxt, PT_INTO, t_result)) return; ctxt . LegacyThrow(EE_DIVIDE_CANTSET); } } } void MCDivide::compile(MCSyntaxFactoryRef ctxt) { MCSyntaxFactoryBeginStatement(ctxt, line, pos); source -> compile(ctxt); if (destvar != nil) destvar -> compile_inout(ctxt); else dest -> compile_inout(ctxt); MCSyntaxFactoryExecMethodWithArgs(ctxt, kMCMathExecDivideArrayByArrayMethodInfo, 0, 1, 1); MCSyntaxFactoryExecMethodWithArgs(ctxt, kMCMathExecDivideArrayByNumberMethodInfo, 0, 1, 1); MCSyntaxFactoryExecMethodWithArgs(ctxt, kMCMathExecDivideNumberByNumberMethodInfo, 0, 1, 1); MCSyntaxFactoryEndStatement(ctxt); } MCMultiply::~MCMultiply() { delete source; delete dest; // MW-2013-08-01: [[ Bug 10925 ]] Only delete the destvar varref if dest is NULL, // otherwise its owned by dest. if (dest == NULL) delete destvar; } Parse_stat MCMultiply::parse(MCScriptPoint &sp) { initpoint(sp); // MW-2011-06-22: [[ SERVER ]] Update to use SP findvar method to take into account // execution outwith a handler. Symbol_type type; if (sp.next(type) != PS_NORMAL || type != ST_ID || sp . findvar(sp.gettoken_nameref(), &destvar) != PS_NORMAL) { sp.backup(); dest = new MCChunk(True); if (dest->parse(sp, False) != PS_NORMAL) { MCperror->add (PE_MULTIPLY_BADDEST, sp); return PS_ERROR; } } else destvar->parsearray(sp); if (sp.skip_token(SP_FACTOR, TT_PREP) == PS_ERROR) { MCperror->add (PE_MULTIPLY_NOBY, sp); return PS_ERROR; } if (sp.parseexp(False, True, &source) != PS_NORMAL) { MCperror->add (PE_MULTIPLY_BADEXP, sp); return PS_ERROR; } // MW-2013-08-01: [[ Bug 10925 ]] If the dest chunk is just a var, extract the varref. if (dest != NULL && dest -> isvarchunk()) destvar = dest -> getrootvarref(); overlap = MCMathOpCommandComputeOverlap(source, dest, destvar); return PS_NORMAL; } // MW-2007-07-03: [[ Bug 5123 ]] - Strict array checking modification // Here the source can be an array or number so we use 'tona'. void MCMultiply::exec_ctxt(MCExecContext &ctxt) { #ifdef /* MCMultiply */ LEGACY_EXEC MCVariable *t_dst_var; MCVariableValue *t_dst_ref; t_dst_ref = NULL; if (source->eval(ep) != ES_NORMAL || ep.tona() != ES_NORMAL) { MCeerror->add(EE_MULTIPLY_BADSOURCE, line, pos); return ES_ERROR; } if (overlap) ep . grab(); if (destvar != NULL && destvar -> evalcontainer(ep, t_dst_var, t_dst_ref) != ES_NORMAL) { MCeerror->add(EE_MULTIPLY_BADDEST, line, pos); return ES_ERROR; } if (t_dst_ref != NULL && t_dst_ref -> is_array()) { if (t_dst_ref->factorarray(ep, O_TIMES) != ES_NORMAL) { MCeerror->add(EE_MULTIPLY_BADARRAY, line, pos); return ES_ERROR; } return ES_NORMAL; } if (ep.getformat() == VF_ARRAY) { MCeerror->add(EE_MULTIPLY_MISMATCH, line, pos); return ES_ERROR; } // Variable case real8 n2 = ep.getnvalue(); if (t_dst_ref != NULL) { real8 n1; if (!t_dst_ref -> get_as_real(ep, n1)) { MCeerror -> add(EE_MULTIPLY_BADDEST, line, pos); return ES_ERROR; } MCS_seterrno(0); n1 *= n2; if (n1 == MCinfinity || MCS_geterrno() != 0) { MCS_seterrno(0); MCeerror->add(EE_MULTIPLY_RANGE, line, pos); return ES_ERROR; } t_dst_ref -> assign_real(n1); if (t_dst_var != NULL) t_dst_var -> synchronize(ep, True); return ES_NORMAL; } // Chunk case if (dest->eval(ep) != ES_NORMAL || ep.ton() != ES_NORMAL) { MCeerror->add(EE_MULTIPLY_BADDEST, line, pos); return ES_ERROR; } real8 n1 = ep.getnvalue(); MCS_seterrno(0); n1 *= n2; if (n1 == MCinfinity || MCS_geterrno() != 0) { MCS_seterrno(0); MCeerror->add(EE_MULTIPLY_RANGE, line, pos); return ES_ERROR; } ep.setnvalue(n1); if (dest->set(ep, PT_INTO) != ES_NORMAL) { MCeerror->add(EE_MULTIPLY_CANTSET, line, pos); return ES_ERROR; } return ES_NORMAL; #endif /* MCMultiply */ MCExecValue t_src; if(!ctxt . EvaluateExpression(source, EE_MULTIPLY_BADSOURCE, t_src) || !ctxt . ConvertToNumberOrArray(t_src)) { return; } MCExecValue t_dst; MCAutoPointer t_dst_container; if (destvar != nil) { bool t_success; if (destvar -> needsContainer()) t_success = destvar -> evalcontainer(ctxt, &t_dst_container) && t_dst_container -> eval_ctxt(ctxt, t_dst); else { destvar -> eval_ctxt(ctxt, t_dst); t_success = !ctxt . HasError(); } if (!t_success) { ctxt . LegacyThrow(EE_MULTIPLY_BADDEST); MCExecTypeRelease(t_src); return; } } else { if (!ctxt . EvaluateExpression(dest, EE_MULTIPLY_BADDEST, t_dst)) { MCExecTypeRelease(t_src); return; } } if (!ctxt . ConvertToNumberOrArray(t_dst)) { MCExecTypeRelease(t_src); MCExecTypeRelease(t_dst); return; } MCExecValue t_result; t_result . type = t_dst . type; if (t_src . type == kMCExecValueTypeArrayRef) { if (t_dst . type == kMCExecValueTypeArrayRef) MCMathExecMultiplyArrayByArray(ctxt, t_dst . arrayref_value, t_src . arrayref_value, t_result . arrayref_value); else { ctxt . LegacyThrow(EE_MULTIPLY_MISMATCH); return; } } else { if (t_dst . type == kMCExecValueTypeArrayRef) MCMathExecMultiplyArrayByNumber(ctxt, t_dst . arrayref_value, t_src . double_value, t_result . arrayref_value); else MCMathExecMultiplyNumberByNumber(ctxt, t_dst . double_value, t_src . double_value, t_result . double_value); } MCExecTypeRelease(t_src); MCExecTypeRelease(t_dst); if (!ctxt . HasError()) { if (destvar != nil) { bool t_success; if (destvar -> needsContainer()) t_success = t_dst_container -> give_value(ctxt, t_result); else t_success = destvar -> give_value(ctxt, t_result); if (!t_success) ctxt . Throw(); } else { if (dest->set(ctxt, PT_INTO, t_result)) return; ctxt . LegacyThrow(EE_MULTIPLY_CANTSET); } } } void MCMultiply::compile(MCSyntaxFactoryRef ctxt) { MCSyntaxFactoryBeginStatement(ctxt, line, pos); source -> compile(ctxt); if (destvar != nil) destvar -> compile_inout(ctxt); else dest -> compile_inout(ctxt); MCSyntaxFactoryExecMethodWithArgs(ctxt, kMCMathExecMultiplyArrayByArrayMethodInfo, 0, 1, 1); MCSyntaxFactoryExecMethodWithArgs(ctxt, kMCMathExecMultiplyArrayByNumberMethodInfo, 0, 1, 1); MCSyntaxFactoryExecMethodWithArgs(ctxt, kMCMathExecMultiplyNumberByNumberMethodInfo, 0, 1, 1); MCSyntaxFactoryEndStatement(ctxt); } MCSubtract::~MCSubtract() { delete source; delete dest; // MW-2013-08-01: [[ Bug 10925 ]] Only delete the destvar varref if dest is NULL, // otherwise its owned by dest. if (dest == NULL) delete destvar; } Parse_stat MCSubtract::parse(MCScriptPoint &sp) { initpoint(sp); if (sp.parseexp(False, True, &source) != PS_NORMAL) { MCperror->add (PE_SUBTRACT_BADEXP, sp); return PS_ERROR; } if (sp.skip_token(SP_FACTOR, TT_FROM) == PS_ERROR) { MCperror->add (PE_SUBTRACT_NOFROM, sp); return PS_ERROR; } // MW-2011-06-22: [[ SERVER ]] Update to use SP findvar method to take into account // execution outwith a handler. Symbol_type type; if (sp.next(type) != PS_NORMAL || type != ST_ID || sp . findvar(sp.gettoken_nameref(), &destvar) != PS_NORMAL) { sp.backup(); dest = new MCChunk(True); if (dest->parse(sp, False) != PS_NORMAL) { MCperror->add (PE_SUBTRACT_BADDEST, sp); return PS_ERROR; } } else destvar->parsearray(sp); // MW-2013-08-01: [[ Bug 10925 ]] If the dest chunk is just a var, extract the varref. if (dest != NULL && dest -> isvarchunk()) destvar = dest -> getrootvarref(); overlap = MCMathOpCommandComputeOverlap(source, dest, destvar); return PS_NORMAL; } // MW-2007-07-03: [[ Bug 5123 ]] - Strict array checking modification // Here the source can be an array or number so we use 'tona'. void MCSubtract::exec_ctxt(MCExecContext &ctxt) { #ifdef /* MCSubtract */ LEGACY_EXEC MCVariable *t_dst_var; MCVariableValue *t_dst_ref; t_dst_ref = NULL; if (source->eval(ep) != ES_NORMAL || ep.tona() != ES_NORMAL) { MCeerror->add(EE_SUBTRACT_BADSOURCE, line, pos); return ES_ERROR; } if (overlap) ep . grab(); if (destvar != NULL && destvar -> evalcontainer(ep, t_dst_var, t_dst_ref) != ES_NORMAL) { MCeerror->add(EE_SUBTRACT_BADDEST, line, pos); return ES_ERROR; } if (t_dst_ref != NULL && t_dst_ref -> is_array()) { if (t_dst_ref->factorarray(ep, O_MINUS) != ES_NORMAL) { MCeerror->add(EE_SUBTRACT_BADARRAY, line, pos); return ES_ERROR; } return ES_NORMAL; } if (ep.getformat() == VF_ARRAY) { MCeerror->add(EE_SUBTRACT_MISMATCH, line, pos); return ES_ERROR; } // Variable case real8 n1 = ep.getnvalue(); if (t_dst_ref != NULL) { real8 n2; if (!t_dst_ref -> get_as_real(ep, n2)) { MCeerror -> add(EE_SUBTRACT_BADDEST, line, pos); return ES_ERROR; } t_dst_ref -> assign_real(n2 - n1); if (t_dst_var != NULL) t_dst_var -> synchronize(ep, True); return ES_NORMAL; } // Chunk case if (dest->eval(ep) != ES_NORMAL || ep.ton() != ES_NORMAL) { MCeerror->add(EE_SUBTRACT_BADDEST, line, pos); return ES_ERROR; } real8 n2 = ep.getnvalue(); ep.setnvalue(n2 - n1); if (dest->set(ep, PT_INTO) != ES_NORMAL) { MCeerror->add(EE_SUBTRACT_CANTSET, line, pos); return ES_ERROR; } return ES_NORMAL; #endif /* MCSubtract */ MCExecValue t_src; if (!ctxt . EvaluateExpression(source, EE_SUBTRACT_BADSOURCE, t_src) || !ctxt . ConvertToNumberOrArray(t_src)) { return; } MCExecValue t_dst; MCAutoPointer t_dst_container; if (destvar != nil) { bool t_success; if (destvar -> needsContainer()) t_success = destvar -> evalcontainer(ctxt, &t_dst_container) && t_dst_container -> eval_ctxt(ctxt, t_dst); else { destvar -> eval_ctxt(ctxt, t_dst); t_success = !ctxt . HasError(); } if (!t_success) { ctxt . LegacyThrow(EE_SUBTRACT_BADDEST); MCExecTypeRelease(t_src); return; } } else { if (!ctxt . EvaluateExpression(dest, EE_SUBTRACT_BADDEST, t_dst)) { MCExecTypeRelease(t_src); return; } } if (!ctxt . ConvertToNumberOrArray(t_dst)) { MCExecTypeRelease(t_src); MCExecTypeRelease(t_dst); return; } MCExecValue t_result; t_result . type = t_dst . type; if (t_src . type == kMCExecValueTypeArrayRef) { if (t_dst . type == kMCExecValueTypeArrayRef) MCMathExecSubtractArrayFromArray(ctxt, t_src . arrayref_value, t_dst . arrayref_value, t_result . arrayref_value); else { ctxt . LegacyThrow(EE_SUBTRACT_MISMATCH); return; } } else { if (t_dst . type == kMCExecValueTypeArrayRef) MCMathExecSubtractNumberFromArray(ctxt, t_src . double_value, t_dst . arrayref_value, t_result . arrayref_value); else MCMathExecSubtractNumberFromNumber(ctxt, t_src . double_value, t_dst . double_value, t_result . double_value); } MCExecTypeRelease(t_src); MCExecTypeRelease(t_dst); if (!ctxt . HasError()) { if (destvar != nil) { bool t_success; if (destvar -> needsContainer()) t_success = t_dst_container -> give_value(ctxt, t_result); else t_success = destvar -> give_value(ctxt, t_result); if (!t_success) ctxt . Throw(); } else { if (dest->set(ctxt, PT_INTO, t_result)) return; ctxt . LegacyThrow(EE_SUBTRACT_CANTSET); } } } void MCSubtract::compile(MCSyntaxFactoryRef ctxt) { MCSyntaxFactoryBeginStatement(ctxt, line, pos); source -> compile(ctxt); if (destvar != nil) destvar -> compile_inout(ctxt); else dest -> compile_inout(ctxt); MCSyntaxFactoryExecMethodWithArgs(ctxt, kMCMathExecSubtractArrayFromArrayMethodInfo, 0, 1, 1); MCSyntaxFactoryExecMethodWithArgs(ctxt, kMCMathExecSubtractNumberFromArrayMethodInfo, 0, 1, 1); MCSyntaxFactoryExecMethodWithArgs(ctxt, kMCMathExecSubtractNumberFromNumberMethodInfo, 0, 1, 1); MCSyntaxFactoryEndStatement(ctxt); } MCArrayOp::~MCArrayOp() { delete destvar; delete element; delete key; } Parse_stat MCArrayOp::parse(MCScriptPoint &sp) { initpoint(sp); Symbol_type type; // MW-2008-08-20: [[ Bug 6954 ]] Split/Combine don't work on array keys // MW-2011-06-22: [[ SERVER ]] Update to use SP findvar method to take into account // execution outwith a handler. if (sp.next(type) != PS_NORMAL || type != ST_ID || sp.findvar(sp.gettoken_nameref(), &destvar) != PS_NORMAL || destvar -> parsearray(sp) != PS_NORMAL) { MCperror->add(PE_ARRAYOP_BADARRAY, sp); return PS_ERROR; } if (sp.skip_token(SP_REPEAT, TT_UNDEFINED, RF_WITH) != PS_NORMAL && sp.skip_token(SP_FACTOR, TT_PREP, PT_BY) != PS_NORMAL && sp.skip_token(SP_START, TT_UNDEFINED, SC_USING) != PS_NORMAL) { MCperror->add(PE_ARRAYOP_NOWITH, sp); return PS_ERROR; } if (sp . next(type) == PS_NORMAL && type == ST_ID && (sp . token_is_cstring("column") || sp . token_is_cstring("row"))) { if (sp . token_is_cstring("column")) mode = TYPE_COLUMN; else mode = TYPE_ROW; } else { sp.backup(); if (sp.parseexp(True, False, &element) != PS_NORMAL) { MCperror->add(PE_ARRAYOP_BADEXP, sp); return PS_ERROR; } if (sp.skip_token(SP_FACTOR, TT_BINOP, O_AND) == PS_NORMAL) if (sp.parseexp(True, False, &key) != PS_NORMAL) { MCperror->add(PE_ARRAYOP_BADEXP, sp); return PS_ERROR; } } if (sp . skip_token(SP_FACTOR, TT_PREP, PT_AS) == PS_NORMAL) { if (sp . skip_token(SP_COMMAND, TT_STATEMENT, S_SET) != PS_NORMAL || key != nil) { MCperror -> add(PE_ARRAYOP_BADFORM, sp); return PS_ERROR; } form = FORM_SET; } return PS_NORMAL; } void MCArrayOp::exec_ctxt(MCExecContext &ctxt) { #ifdef /* MCArrayOp */ LEGACY_EXEC uint1 e; uint1 k = '\0'; uint4 chunk; chunk = mode; switch(chunk) { case TYPE_USER: if (element != NULL) { if (element->eval(ep) != ES_NORMAL || ep.tos() != ES_NORMAL || ep.getsvalue().getlength() != 1) { MCeerror->add(EE_ARRAYOP_BADEXP, line, pos); return ES_ERROR; } e = ep.getsvalue().getstring()[0]; if (key != NULL) { if (key->eval(ep) != ES_NORMAL || ep.tos() != ES_NORMAL || ep.getsvalue().getlength() != 1) { MCeerror->add(EE_ARRAYOP_BADEXP, line, pos); return ES_ERROR; } k = ep.getsvalue().getstring()[0]; } } break; case TYPE_ROW: e = ep . getrowdel(); break; case TYPE_COLUMN: e = ep . getcolumndel(); break; case TYPE_LINE: e = ep . getlinedel(); break; case TYPE_ITEM: e = ep . getitemdel(); break; case TYPE_WORD: case TYPE_TOKEN: case TYPE_CHARACTER: default: return ES_ERROR; break; } MCVariable *t_dst_var; MCVariableValue *t_dst_ref; if (destvar -> evalcontainer(ep, t_dst_var, t_dst_ref) != ES_NORMAL) { MCeerror -> add(EE_ARRAYOP_BADEXP, line, pos); return ES_ERROR; } if (is_combine) { if (form == FORM_NONE) { if (chunk == TYPE_COLUMN) t_dst_ref -> combine_column(e, ep . getrowdel(), ep); else t_dst_ref -> combine(e, k, ep); } else t_dst_ref -> combine_as_set(e, ep); } else { if (form == FORM_NONE) { if (chunk == TYPE_COLUMN) t_dst_ref -> split_column(e, ep . getrowdel(), ep); else t_dst_ref -> split(e, k, ep); } else t_dst_ref -> split_as_set(e, ep); } if (t_dst_var != NULL) t_dst_var -> synchronize(ep, True); return ES_NORMAL; #endif /* MCArrayOp */ MCAutoStringRef t_element_del; MCAutoStringRef t_key_del; uint4 chunk; chunk = mode; switch(chunk) { case TYPE_USER: if (element != NULL) { if (!ctxt . EvalExprAsStringRef(element, EE_ARRAYOP_BADEXP, &t_element_del)) return; if (!ctxt . EvalOptionalExprAsNullableStringRef(key, EE_ARRAYOP_BADEXP, &t_key_del)) return; } break; case TYPE_ROW: t_element_del = ctxt.GetRowDelimiter(); break; case TYPE_COLUMN: t_element_del = ctxt.GetColumnDelimiter(); break; case TYPE_LINE: t_element_del = ctxt.GetLineDelimiter(); break; case TYPE_ITEM: t_element_del = ctxt.GetItemDelimiter(); break; case TYPE_WORD: case TYPE_TOKEN: case TYPE_CHARACTER: default: ctxt . Throw(); return; break; } MCAutoPointer t_container; MCAutoValueRef t_container_value; if (!destvar -> evalcontainer(ctxt, &t_container)) { ctxt . LegacyThrow(EE_ARRAYOP_BADEXP); return; } if (!t_container -> eval(ctxt, &t_container_value)) { ctxt . Throw(); return; } if (is_combine) { MCAutoArrayRef t_array; if (!ctxt . ConvertToArray(*t_container_value, &t_array)) return; MCAutoStringRef t_string; if (form == FORM_NONE) { // SN-2014-09-01: [[ Bug 13297 ]] Combining by column deserves its own function as it is too // different from combining by row if (chunk == TYPE_ROW) MCArraysExecCombineByRow(ctxt, *t_array, &t_string); else if (chunk == TYPE_COLUMN) MCArraysExecCombineByColumn(ctxt, *t_array, &t_string); else MCArraysExecCombine(ctxt, *t_array, *t_element_del, *t_key_del, &t_string); } else if (form == FORM_SET) MCArraysExecCombineAsSet(ctxt, *t_array, *t_element_del, &t_string); if (!ctxt . HasError()) t_container -> set(ctxt, *t_string); } else { MCAutoStringRef t_string; if (!ctxt . ConvertToString(*t_container_value, &t_string)) return; MCAutoArrayRef t_array; if (form == FORM_NONE) { if (chunk == TYPE_COLUMN) MCArraysExecSplitByColumn(ctxt, *t_string, &t_array); else MCArraysExecSplit(ctxt, *t_string, *t_element_del, *t_key_del, &t_array); } else if (form == FORM_SET) MCArraysExecSplitAsSet(ctxt, *t_string, *t_element_del, &t_array); if (!ctxt . HasError()) t_container -> set(ctxt, *t_array); } } void MCArrayOp::compile(MCSyntaxFactoryRef ctxt) { MCSyntaxFactoryBeginStatement(ctxt, line, pos); destvar -> compile_inout(ctxt); if (mode == TYPE_USER && element != nil) element -> compile(ctxt); else MCSyntaxFactoryEvalConstantNil(ctxt); if (mode == TYPE_USER && key != nil) key -> compile(ctxt); else MCSyntaxFactoryEvalConstantNil(ctxt); if (is_combine) { if (form == FORM_NONE) { // SN-2014-09-01: [[ Bug 13297 ]] Combining by column deserves its own function as it is too // different from combining by row if (mode == TYPE_ROW) MCSyntaxFactoryExecMethodWithArgs(ctxt, kMCArraysExecCombineByRowMethodInfo, 0, 0); else if (mode == TYPE_COLUMN) MCSyntaxFactoryExecMethodWithArgs(ctxt, kMCArraysExecCombineByColumnMethodInfo, 0, 0); else MCSyntaxFactoryExecMethodWithArgs(ctxt, kMCArraysExecCombineMethodInfo, 0, 1, 2, 0); } else if (form == FORM_SET) MCSyntaxFactoryExecMethodWithArgs(ctxt, kMCArraysExecCombineAsSetMethodInfo, 0, 1, 0); } else { if (form == FORM_NONE) { if (mode == TYPE_COLUMN) MCSyntaxFactoryExecMethodWithArgs(ctxt, kMCArraysExecSplitByColumnMethodInfo, 0, 0); else MCSyntaxFactoryExecMethodWithArgs(ctxt, kMCArraysExecSplitMethodInfo, 0, 1, 2, 0); } else if (form == FORM_SET) MCSyntaxFactoryExecMethodWithArgs(ctxt, kMCArraysExecSplitAsSetMethodInfo, 0, 1, 0); } MCSyntaxFactoryEndStatement(ctxt); } MCSetOp::~MCSetOp() { delete destvar; delete source; } Parse_stat MCSetOp::parse(MCScriptPoint &sp) { initpoint(sp); // MW-2011-06-22: [[ SERVER ]] Update to use SP findvar method to take into account // execution outwith a handler. Symbol_type type; if (sp.next(type) != PS_NORMAL || type != ST_ID || sp.findvar(sp.gettoken_nameref(), &destvar) != PS_NORMAL || destvar -> parsearray(sp) != PS_NORMAL) { MCperror->add(PE_ARRAYOP_BADARRAY, sp); return PS_ERROR; } if (sp.skip_token(SP_REPEAT, TT_UNDEFINED, RF_WITH) == PS_ERROR && sp.skip_token(SP_FACTOR, TT_PREP, PT_BY) == PS_ERROR) { MCperror->add(PE_ARRAYOP_NOWITH, sp); return PS_ERROR; } if (sp.parseexp(True, False, &source) != PS_NORMAL) { MCperror->add(PE_ARRAYOP_BADEXP, sp); return PS_ERROR; } // MERG-2013-08-26: [[ RecursiveArrayOp ]] Support nested arrays in union and intersect recursive = sp.skip_token(SP_SUGAR, TT_UNDEFINED, SG_RECURSIVELY) == PS_NORMAL; return PS_NORMAL; } void MCSetOp::exec_ctxt(MCExecContext &ctxt) { #ifdef /* MCSetOp */ LEGACY_EXEC // ARRAYEVAL if (source -> eval(ep) != ES_NORMAL) { MCeerror->add(EE_ARRAYOP_BADEXP, line, pos); return ES_ERROR; } if (ep . getformat() != VF_ARRAY) ep . clear(); if (overlap) ep . grab(); MCVariable *t_dst_var; MCVariableValue *t_dst_ref; if (destvar -> evalcontainer(ep, t_dst_var, t_dst_ref) != ES_NORMAL) { MCeerror -> add(EE_ARRAYOP_BADEXP, line, pos); return ES_ERROR; } MCVariableValue *t_src_ref; t_src_ref = ep . getarray(); // Do nothing if source and dest are the same if (t_src_ref == t_dst_ref) return ES_NORMAL; if (intersect) { if (t_src_ref == NULL) t_dst_ref -> assign_empty(); else // MERG-2013-08-26: [[ RecursiveArrayOp ]] Support nested arrays in union and intersect t_dst_ref -> intersectarray(*t_src_ref,recursive); } else { if (t_src_ref == NULL) return ES_NORMAL; // MERG-2013-08-26: [[ RecursiveArrayOp ]] Support nested arrays in union and intersect t_dst_ref -> unionarray(*t_src_ref,recursive); } if (t_dst_var != NULL) t_dst_var -> synchronize(ep, True); return ES_NORMAL; #endif /* MCSetOp */ // ARRAYEVAL MCAutoValueRef t_src; if (!ctxt . EvalExprAsValueRef(source, EE_ARRAYOP_BADEXP, &t_src)) return; MCAutoPointer t_container; if (!destvar -> evalcontainer(ctxt, &t_container)) { ctxt . LegacyThrow(EE_ARRAYOP_BADEXP); return; } MCAutoValueRef t_dst; if (!t_container -> eval(ctxt, &t_dst)) return; MCAutoValueRef t_dst_value; if (intersect) { // MERG-2013-08-26: [[ RecursiveArrayOp ]] Support nested arrays in union and intersect if (recursive) MCArraysExecIntersectRecursive(ctxt, *t_dst, *t_src, &t_dst_value); else MCArraysExecIntersect(ctxt, *t_dst, *t_src, &t_dst_value); } else { // MERG-2013-08-26: [[ RecursiveArrayOp ]] Support nested arrays in union and intersect if (recursive) MCArraysExecUnionRecursive(ctxt, *t_dst, *t_src, &t_dst_value); else MCArraysExecUnion(ctxt, *t_dst, *t_src, &t_dst_value); } if (!ctxt . HasError()) t_container -> set(ctxt, *t_dst_value); } void MCSetOp::compile(MCSyntaxFactoryRef ctxt) { MCSyntaxFactoryBeginStatement(ctxt, line, pos); // MUTABLE ARRAY destvar -> compile(ctxt); source -> compile(ctxt); if (intersect) { if (recursive) MCSyntaxFactoryExecMethodWithArgs(ctxt, kMCArraysExecIntersectRecursiveMethodInfo, 0, 1, 0); else MCSyntaxFactoryExecMethodWithArgs(ctxt, kMCArraysExecIntersectMethodInfo, 0, 1, 0); } else { if (recursive) MCSyntaxFactoryExecMethodWithArgs(ctxt, kMCArraysExecUnionRecursiveMethodInfo, 0, 1, 0); else MCSyntaxFactoryExecMethodWithArgs(ctxt, kMCArraysExecUnionMethodInfo, 0, 1, 0); } MCSyntaxFactoryEndStatement(ctxt); }