/* @@@HEAD@@@ // Operators. */ #include <string.h> #include "config.h" #include "defs.h" #include "y.tab.h" #include "operators.h" #include "execute.h" #include "memory.h" #include "cdc_types.h" #include "lookup.h" #include "log.h" #include "util.h" /* // ----------------------------------------------------------------- // // The following are basic syntax operations // */ void op_comment(void) { /* Do nothing, just increment the program counter past the comment. */ cur_frame->pc++; /* actually, increment the number of ticks left too, since comments really don't do anything */ cur_frame->ticks++; /* decrement system tick */ tick--; } void op_pop(void) { pop(1); } void op_set_local(void) { data_t *var; /* Copy data in top of stack to variable. */ var = &stack[cur_frame->var_start + cur_frame->opcodes[cur_frame->pc++]]; data_discard(var); data_dup(var, &stack[stack_pos - 1]); } void op_set_obj_var(void) { long ind, id, result; data_t *val; ind = cur_frame->opcodes[cur_frame->pc++]; id = object_get_ident(cur_frame->method->object, ind); val = &stack[stack_pos - 1]; result = object_assign_var(cur_frame->object, cur_frame->method->object, id, val); if (result == paramnf_id) cthrow(paramnf_id, "No such parameter %I.", id); } void op_if(void) { /* Jump if the condition is false. */ if (!data_true(&stack[stack_pos - 1])) cur_frame->pc = cur_frame->opcodes[cur_frame->pc]; else cur_frame->pc++; pop(1); } void op_else(void) { cur_frame->pc = cur_frame->opcodes[cur_frame->pc]; } void op_for_range(void) { int var; data_t *range; var = cur_frame->var_start + cur_frame->opcodes[cur_frame->pc + 1]; range = &stack[stack_pos - 2]; /* Make sure we have an integer range. */ if (range[0].type != INTEGER || range[1].type != INTEGER) { cthrow(type_id, "Range bounds (%D, %D) are not both integers.", &range[0], &range[1]); return; } if (range[0].u.val > range[1].u.val) { /* We're finished; pop the range and jump to the end. */ pop(2); cur_frame->pc = cur_frame->opcodes[cur_frame->pc]; } else { /* Replace the index variable with the lower range bound, increment the * range, and continue. */ data_discard(&stack[var]); stack[var] = range[0]; range[0].u.val++; cur_frame->pc += 2; } } void op_for_list(void) { data_t *counter; data_t *domain; int var, len; list_t *pair; counter = &stack[stack_pos - 1]; domain = &stack[stack_pos - 2]; var = cur_frame->var_start + cur_frame->opcodes[cur_frame->pc + 1]; /* Make sure we're iterating over a list. We know the counter is okay. */ if (domain->type != LIST && domain->type != DICT) { cthrow(type_id, "Domain (%D) is not a list or dictionary.", domain); return; } len = (domain->type == LIST) ? list_length(domain->u.list) : dict_size(domain->u.dict); if (counter->u.val >= len) { /* We're finished; pop the list and counter and jump to the end. */ pop(2); cur_frame->pc = cur_frame->opcodes[cur_frame->pc]; return; } /* Replace the index variable with the next list element and increment * the counter. */ data_discard(&stack[var]); if (domain->type == LIST) { data_dup(&stack[var], list_elem(domain->u.list, counter->u.val)); } else { pair = dict_key_value_pair(domain->u.dict, counter->u.val); stack[var].type = LIST; stack[var].u.list = pair; } counter->u.val++; cur_frame->pc += 2; } void op_while(void) { if (!data_true(&stack[stack_pos - 1])) { /* The condition expression is false. Jump to the end of the loop. */ cur_frame->pc = cur_frame->opcodes[cur_frame->pc]; } else { /* The condition expression is true; continue. */ cur_frame->pc += 2; } pop(1); } void op_switch(void) { /* This opcode doesn't actually do anything; it just provides a place- * holder for a break statement. */ cur_frame->pc++; } void op_case_value(void) { /* There are two expression values on the stack: the controlling expression * for the switch statement, and the value for this case. If they are * equal, pop them off the stack and jump to the body of this case. * Otherwise, just pop the value for this case, and go on. */ if (data_cmp(&stack[stack_pos - 2], &stack[stack_pos - 1]) == 0) { pop(2); cur_frame->pc = cur_frame->opcodes[cur_frame->pc]; } else { pop(1); cur_frame->pc++; } } void op_case_range(void) { data_t *switch_expr, *range; int is_match; switch_expr = &stack[stack_pos - 3]; range = &stack[stack_pos - 2]; /* Verify that range[0] and range[1] make a value type. */ if (range[0].type != range[1].type) { cthrow(type_id, "%D and %D are not of the same type.", &range[0], &range[1]); return; } else if (range[0].type != INTEGER && range[0].type != STRING) { cthrow(type_id, "%D and %D are not integers or strings.", &range[0], &range[1]); return; } /* Decide if this is a match. In order for it to be a match, switch_expr * must be of the same type as the range expressions, must be greater than * or equal to the lower bound of the range, and must be less than or equal * to the upper bound of the range. */ is_match = (switch_expr->type == range[0].type); is_match = (is_match) && (data_cmp(switch_expr, &range[0]) >= 0); is_match = (is_match) && (data_cmp(switch_expr, &range[1]) <= 0); /* If it's a match, pop all three expressions and jump to the case body. * Otherwise, just pop the range and go on. */ if (is_match) { pop(3); cur_frame->pc = cur_frame->opcodes[cur_frame->pc]; } else { pop(2); cur_frame->pc++; } } void op_last_case_value(void) { /* There are two expression values on the stack: the controlling expression * for the switch statement, and the value for this case. If they are * equal, pop them off the stack and go on. Otherwise, just pop the value * for this case, and jump to the next case. */ if (data_cmp(&stack[stack_pos - 2], &stack[stack_pos - 1]) == 0) { pop(2); cur_frame->pc++; } else { pop(1); cur_frame->pc = cur_frame->opcodes[cur_frame->pc]; } } void op_last_case_range(void) { data_t *switch_expr, *range; int is_match; switch_expr = &stack[stack_pos - 3]; range = &stack[stack_pos - 2]; /* Verify that range[0] and range[1] make a value type. */ if (range[0].type != range[1].type) { cthrow(type_id, "%D and %D are not of the same type.", &range[0], &range[1]); return; } else if (range[0].type != INTEGER && range[0].type != STRING) { cthrow(type_id, "%D and %D are not integers or strings.", &range[0], &range[1]); return; } /* Decide if this is a match. In order for it to be a match, switch_expr * must be of the same type as the range expressions, must be greater than * or equal to the lower bound of the range, and must be less than or equal * to the upper bound of the range. */ is_match = (switch_expr->type == range[0].type); is_match = (is_match) && (data_cmp(switch_expr, &range[0]) >= 0); is_match = (is_match) && (data_cmp(switch_expr, &range[1]) <= 0); /* If it's a match, pop all three expressions and go on. Otherwise, just * pop the range and jump to the next case. */ if (is_match) { pop(3); cur_frame->pc++; } else { pop(2); cur_frame->pc = cur_frame->opcodes[cur_frame->pc]; } } void op_end_case(void) { /* Jump to end of switch statement. */ cur_frame->pc = cur_frame->opcodes[cur_frame->pc]; } void op_default(void) { /* Pop the controlling switch expression. */ pop(1); } void op_end(void) { /* Jump to the beginning of the loop or condition expression. */ cur_frame->pc = cur_frame->opcodes[cur_frame->pc]; } void op_break(void) { int n, op; /* Get loop instruction from argument. */ n = cur_frame->opcodes[cur_frame->pc]; /* If it's a for loop, pop the loop information on the stack (either a list * and an index, or two range bounds. */ op = cur_frame->opcodes[n]; if (op == FOR_LIST || op == FOR_RANGE) pop(2); /* Jump to the end of the loop. */ cur_frame->pc = cur_frame->opcodes[n + 1]; } void op_continue(void) { /* Jump back to the beginning of the loop. If it's a WHILE loop, jump back * to the beginning of the condition expression. */ cur_frame->pc = cur_frame->opcodes[cur_frame->pc]; if (cur_frame->opcodes[cur_frame->pc] == WHILE) cur_frame->pc = cur_frame->opcodes[cur_frame->pc + 2]; } void op_return(void) { long dbref; dbref = cur_frame->object->dbref; frame_return(); if (cur_frame) push_dbref(dbref); } void op_return_expr(void) { data_t *val; /* Return, and push frame onto caller stack. Transfers reference count to * caller stack. Assumes (correctly) that there is space on the caller * stack. */ val = &stack[--stack_pos]; frame_return(); if (cur_frame) { stack[stack_pos] = *val; stack_pos++; } else { data_discard(val); } } void op_catch(void) { Error_action_specifier *spec; /* Make a new error action specifier and push it onto the stack. */ spec = EMALLOC(Error_action_specifier, 1); spec->type = CATCH; spec->stack_pos = stack_pos; spec->u.ccatch.handler = cur_frame->opcodes[cur_frame->pc++]; spec->u.ccatch.error_list = cur_frame->opcodes[cur_frame->pc++]; spec->next = cur_frame->specifiers; cur_frame->specifiers = spec; } void op_catch_end(void) { /* Pop the error action specifier for the catch statement, and jump past * the handler. */ pop_error_action_specifier(); cur_frame->pc = cur_frame->opcodes[cur_frame->pc]; } void op_handler_end(void) { pop_handler_info(); } void op_zero(void) { /* Push a zero. */ push_int(0); } void op_one(void) { /* Push a one. */ push_int(1); } void op_integer(void) { push_int(cur_frame->opcodes[cur_frame->pc++]); } void op_float(void) { push_float(*((float*)(&cur_frame->opcodes[cur_frame->pc++]))); } void op_string(void) { string_t *str; int ind = cur_frame->opcodes[cur_frame->pc++]; str = object_get_string(cur_frame->method->object, ind); push_string(str); } void op_dbref(void) { int id; id = cur_frame->opcodes[cur_frame->pc++]; push_dbref(id); } void op_symbol(void) { int ind, id; ind = cur_frame->opcodes[cur_frame->pc++]; id = object_get_ident(cur_frame->method->object, ind); push_symbol(id); } void op_error(void) { int ind, id; ind = cur_frame->opcodes[cur_frame->pc++]; id = object_get_ident(cur_frame->method->object, ind); push_error(id); } void op_name(void) { int ind, id; long dbref; ind = cur_frame->opcodes[cur_frame->pc++]; id = object_get_ident(cur_frame->method->object, ind); if (lookup_retrieve_name(id, &dbref)) push_dbref(dbref); else cthrow(namenf_id, "Can't find object name %I.", id); } void op_get_local(void) { int var; /* Push value of local variable on stack. */ var = cur_frame->var_start + cur_frame->opcodes[cur_frame->pc++]; check_stack(1); data_dup(&stack[stack_pos], &stack[var]); stack_pos++; } void op_get_obj_var(void) { long ind, id, result; data_t val; /* Look for variable, and push it onto the stack if we find it. */ ind = cur_frame->opcodes[cur_frame->pc++]; id = object_get_ident(cur_frame->method->object, ind); result = object_retrieve_var(cur_frame->object, cur_frame->method->object, id, &val); if (result == paramnf_id) { cthrow(paramnf_id, "No such parameter %I.", id); } else { check_stack(1); stack[stack_pos] = val; stack_pos++; } } void op_start_args(void) { /* Resize argument stack if necessary. */ if (arg_pos == arg_size) { arg_size = arg_size * 2 + ARG_STACK_MALLOC_DELTA; arg_starts = EREALLOC(arg_starts, int, arg_size); } /* Push stack position onto argument start stack. */ arg_starts[arg_pos] = stack_pos; arg_pos++; } void op_pass(void) { int arg_start, result; arg_start = arg_starts[--arg_pos]; /* Attempt to pass the message we're processing. */ result = pass_message(arg_start, arg_start); if (result == numargs_id) interp_error(result, numargs_str); else if (result == methodnf_id) cthrow(result, "No next method found."); else if (result == maxdepth_id) cthrow(result, "Maximum call depth exceeded."); } void op_message(void) { int arg_start, result, ind; data_t *target; long message, dbref; Frob *frob; ind = cur_frame->opcodes[cur_frame->pc++]; message = object_get_ident(cur_frame->method->object, ind); arg_start = arg_starts[--arg_pos]; target = &stack[arg_start - 1]; #if 0 write_err("##message: %s[%d] #%d #%d.%I %d", ident_name(message), ind, cur_frame->object->dbref, cur_frame->method->object->dbref, (cur_frame->method->name != NOT_AN_IDENT) ? cur_frame->method->name : opcode_id, cur_frame->method->name); #endif if (target->type == DBREF) { dbref = target->u.dbref; } else if (target->type == FROB) { /* Convert the frob to its rep and pass as first argument. */ frob = target->u.frob; dbref = frob->cclass; *target = frob->rep; arg_start--; TFREE(frob, 1); } else { /* JBB - changed to support messages to all object types */ if (!lookup_retrieve_name(data_type_id(target->type), &dbref)) { cthrow(objnf_id, "No object for data type %I.", data_type_id(target->type)); return; } arg_start--; } /* Attempt to send the message. */ ident_dup(message); result = send_message(dbref, message, target - stack, arg_start); if (result == numargs_id) interp_error(result, numargs_str); else if (result == objnf_id) cthrow(result, "Target (#%l) not found.", dbref); else if (result == methodnf_id) cthrow(result, "Method %I not found.", message); else if (result == maxdepth_id) cthrow(result, "Maximum call depth exceeded."); else if (result == private_id) cthrow(result, "Method %I is private.", message); else if (result == protected_id) cthrow(result, "Method %I is protected.", message); else if (result == root_id) cthrow(result, "Method %I can only be called by $root.", message); else if (result == driver_id) cthrow(result, "Method %I can only be by the driver.", message); ident_discard(message); } void op_expr_message(void) { int arg_start, result; data_t *target, *message_data; long dbref, message; arg_start = arg_starts[--arg_pos]; target = &stack[arg_start - 2]; message_data = &stack[arg_start - 1]; if (message_data->type != SYMBOL) { cthrow(type_id, "Message (%D) is not a symbol.", message_data); return; } message = ident_dup(message_data->u.symbol); if (target->type == DBREF) { dbref = target->u.dbref; } else if (target->type == FROB) { dbref = target->u.frob->cclass; /* Pass frob rep as first argument (where the message data is now). */ data_discard(message_data); *message_data = target->u.frob->rep; arg_start--; /* Discard the frob and replace it with a dummy value. */ TFREE(target->u.frob, 1); target->type = INTEGER; target->u.val = 0; } else { /* JBB - changed to support messages to all object types */ if (!lookup_retrieve_name(data_type_id(target->type), &dbref)) { cthrow(objnf_id, "No object for data type %I", data_type_id(target->type)); ident_discard(message); return; } arg_start--; data_discard(message_data); message_data = &stack[arg_start -1]; } /* Attempt to send the message. */ ident_dup(message); result = send_message(dbref, message, target - stack, arg_start); if (result == numargs_id) interp_error(result, numargs_str); else if (result == objnf_id) cthrow(result, "Target (#%l) not found.", dbref); else if (result == methodnf_id) cthrow(result, "Method %I not found.", message); else if (result == maxdepth_id) cthrow(result, "Maximum call depth exceeded."); else if (result == private_id) cthrow(result, "Method %I is private.", message); else if (result == protected_id) cthrow(result, "Method %I is protected.", message); else if (result == root_id) cthrow(result, "Method %I can only be called by $root.", message); else if (result == driver_id) cthrow(result, "Method %I can only be by the driver.", message); ident_discard(message); } void op_list(void) { int start, len; list_t *list; data_t *d; start = arg_starts[--arg_pos]; len = stack_pos - start; /* Move the elements into a list. */ list = list_new(len); d = list_empty_spaces(list, len); MEMCPY(d, &stack[start], len); stack_pos = start; /* Push the list onto the stack where elements began. */ push_list(list); list_discard(list); } void op_dict(void) { int start, len; list_t *list; data_t *d; Dict *dict; start = arg_starts[--arg_pos]; len = stack_pos - start; /* Move the elements into a list. */ list = list_new(len); d = list_empty_spaces(list, len); MEMCPY(d, &stack[start], len); stack_pos = start; /* Construct a dictionary from the list. */ dict = dict_from_slices(list); list_discard(list); if (!dict) { cthrow(type_id, "Arguments were not all two-element lists."); } else { push_dict(dict); dict_discard(dict); } } void op_buffer(void) { int start, len, i; Buffer *buf; start = arg_starts[--arg_pos]; len = stack_pos - start; for (i = 0; i < len; i++) { if (stack[start + i].type != INTEGER) { cthrow(type_id, "Element %d (%D) is not an integer.", i + 1, &stack[start + i]); return; } } buf = buffer_new(len); for (i = 0; i < len; i++) buf->s[i] = ((unsigned long) stack[start + i].u.val) % (1 << 8); stack_pos = start; push_buffer(buf); buffer_discard(buf); } void op_frob(void) { data_t *cclass, *rep; cclass = &stack[stack_pos - 2]; rep = &stack[stack_pos - 1]; if (cclass->type != DBREF) { cthrow(type_id, "Class (%D) is not a dbref.", cclass); } else if (rep->type != LIST && rep->type != DICT) { cthrow(type_id, "Rep (%D) is not a list or dictionary.", rep); } else { Dbref dbref = cclass->u.dbref; cclass->type = FROB; cclass->u.frob = TMALLOC(Frob, 1); cclass->u.frob->cclass = dbref; data_dup(&cclass->u.frob->rep, rep); pop(1); } } void op_index(void) { data_t *d, *ind, element; int i, len; string_t *str; d = &stack[stack_pos - 2]; ind = &stack[stack_pos - 1]; if (d->type != LIST && d->type != STRING && d->type != DICT) { cthrow(type_id, "Array (%D) is not a list, string, or dictionary.", d); return; } else if (d->type != DICT && ind->type != INTEGER) { cthrow(type_id, "Offset (%D) is not an integer.", ind); return; } if (d->type == DICT) { /* Get the value corresponding to a key. */ if (dict_find(d->u.dict, ind, &element) == keynf_id) { cthrow(keynf_id, "Key (%D) is not in the dictionary.", ind); } else { pop(1); data_discard(d); *d = element; } return; } /* It's not a dictionary. Make sure ind is within bounds. */ len = (d->type == LIST) ? list_length(d->u.list) : string_length(d->u.str); i = ind->u.val - 1; if (i < 0) { cthrow(range_id, "Index (%d) is less than one.", i + 1); } else if (i > len - 1) { cthrow(range_id, "Index (%d) is greater than length (%d)", i + 1, len); } else { /* Replace d with the element of d numbered by ind. */ if (d->type == LIST) { data_dup(&element, list_elem(d->u.list, i)); pop(2); stack[stack_pos] = element; stack_pos++; } else { str = string_from_chars(string_chars(d->u.str) + i, 1); pop(2); push_string(str); string_discard(str); } } } void op_and(void) { /* Short-circuit if left side is false; otherwise discard. */ if (!data_true(&stack[stack_pos - 1])) { cur_frame->pc = cur_frame->opcodes[cur_frame->pc]; } else { cur_frame->pc++; pop(1); } } void op_or(void) { /* Short-circuit if left side is true; otherwise discard. */ if (data_true(&stack[stack_pos - 1])) { cur_frame->pc = cur_frame->opcodes[cur_frame->pc]; } else { cur_frame->pc++; pop(1); } } void op_splice(void) { int i; list_t *list; data_t *d; if (stack[stack_pos - 1].type != LIST) { cthrow(type_id, "%D is not a list.", &stack[stack_pos - 1]); return; } list = stack[stack_pos - 1].u.list; /* Splice the list onto the stack, overwriting the list. */ check_stack(list_length(list) - 1); for (d = list_first(list), i=0; d; d = list_next(list, d), i++) data_dup(&stack[stack_pos - 1 + i], d); stack_pos += list_length(list) - 1; list_discard(list); } void op_critical(void) { Error_action_specifier *spec; /* Make an error action specifier for the critical expression, and push it * onto the stack. */ spec = EMALLOC(Error_action_specifier, 1); spec->type = CRITICAL; spec->stack_pos = stack_pos; spec->u.critical.end = cur_frame->opcodes[cur_frame->pc++]; spec->next = cur_frame->specifiers; cur_frame->specifiers = spec; } void op_critical_end(void) { pop_error_action_specifier(); } void op_propagate(void) { Error_action_specifier *spec; /* Make an error action specifier for the critical expression, and push it * onto the stack. */ spec = EMALLOC(Error_action_specifier, 1); spec->type = PROPAGATE; spec->stack_pos = stack_pos; spec->u.propagate.end = cur_frame->opcodes[cur_frame->pc++]; spec->next = cur_frame->specifiers; cur_frame->specifiers = spec; } void op_propagate_end(void) { pop_error_action_specifier(); } /* // ----------------------------------------------------------------- // // The following are extended operations, math and the like // */ /* All of the following functions are interpreter opcodes, so they require that the interpreter data (the globals in execute.c) be in a state consistent with interpretation. They may modify the interpreter data by pushing and popping the data stack or by throwing exceptions. */ /* Effects: Pops the top value on the stack and pushes its logical inverse. */ void op_not(void) { data_t *d = &stack[stack_pos - 1]; int val = !data_true(d); /* Replace d with the inverse of its truth value. */ data_discard(d); d->type = INTEGER; d->u.val = val; } /* Effects: If the top value on the stack is an integer, pops it and pushes its * its arithmetic inverse. */ void op_negate(void) { data_t *d = &stack[stack_pos - 1]; /* Replace d with -d. */ if (d->type == INTEGER) { d->u.val *= -1; } else if (d->type == FLOAT) { d->u.fval *= -1; } else { cthrow(type_id, "Argument (%D) is not an integer or float.", d); } } /* Effects: If the top two values on the stack are integers, pops them and * pushes their product. */ void op_multiply(void) { data_t *d1 = &stack[stack_pos - 2]; data_t *d2 = &stack[stack_pos - 1]; /* do type conversions */ if (d1->type == FLOAT && d2->type == INTEGER) { d2->type = FLOAT; d2->u.fval = (float) d2->u.val; } else if (d1->type == INTEGER && d2->type == FLOAT) { d1->type = FLOAT; d1->u.fval = (float) d1->u.val; } if (d1->type != d2->type) { cthrow(type_id, "%D and %D are not of the same type.", d1, d2); } else if (d1->type != INTEGER && d1->type != FLOAT) { cthrow(type_id, "%D and %D are not integers or floats.", d1, d2); } else { /* Replace d1 with d1 - d2 and pop d2 */ if (d1->type == INTEGER) d1->u.val *= d2->u.val; else d1->u.fval *= d2->u.fval; pop(1); } } /* Effects: If the top two values on the stack are integers and the second is * not zero, pops them, divides the first by the second, and pushes * the quotient. */ void op_divide(void) { data_t *d1 = &stack[stack_pos - 2]; data_t *d2 = &stack[stack_pos - 1]; if (d1->type == FLOAT && d2->type == INTEGER) { d2->type = FLOAT; d2->u.fval = (float) d2->u.val; } else if (d1->type == INTEGER && d2->type == FLOAT) { d1->type = FLOAT; d1->u.fval = (float) d1->u.val; } if (d1->type != d2->type) { cthrow(type_id, "%D and %D are not of the same type.", d1, d2); } else if (d1->type != INTEGER && d1->type != FLOAT) { cthrow(type_id, "%D and %D are not integers or floats.", d1, d2); } else if (d2->type == INTEGER ? (d2->u.val == 0) : (d2->u.fval == 0.0)) { cthrow(div_id, "Attempt to divide %D by zero.", d1); } else { /* Replace d1 with d1 - d2, and pop d2. */ if (d1->type == INTEGER) d1->u.val /= d2->u.val; else d1->u.fval /= d2->u.fval; pop(1); } } /* Effects: If the top two values on the stack are integers and the second is * not zero, pops them, divides the first by the second, and pushes * the remainder. */ void op_modulo(void) { data_t *d1 = &stack[stack_pos - 2]; data_t *d2 = &stack[stack_pos - 1]; /* Make sure we're multiplying two integers. */ if (d1->type != INTEGER) { cthrow(type_id, "Left side (%D) is not an integer.", d1); } else if (d2->type != INTEGER) { cthrow(type_id, "Right side (%D) is not an integer.", d2); } else if (d2->u.val == 0) { cthrow(div_id, "Attempt to divide %D by zero.", d1); } else { /* Replace d1 with d1 % d2, and pop d2. */ d1->u.val %= d2->u.val; pop(1); } } /* Effects: If the top two values on the stack are integers, pops them and * pushes their sum. If the top two values are strings, pops them, * concatenates the second onto the first, and pushes the result. */ void op_add(void) { data_t *d1 = &stack[stack_pos - 2]; data_t *d2 = &stack[stack_pos - 1]; if (d1->type == FLOAT && d2->type == INTEGER) { d2->type = FLOAT; d2->u.fval = (float) d2->u.val; } else if (d1->type == INTEGER && d2->type == FLOAT) { d1->type = FLOAT; d1->u.fval = (float) d1->u.val; } /* If we're adding two integers or two strings, replace d1 with d1 + d2 and discard d2. */ if (d1->type == INTEGER && d2->type == INTEGER) { /* Replace d1 with d1 + d2, and pop d2. */ d1->u.val += d2->u.val; } else if (d1->type == FLOAT && d2->type == FLOAT) { d1->u.fval += d2->u.fval; } else if (d1->type == STRING && d2->type == STRING) { anticipate_assignment(); d1->u.str = string_add(d1->u.str, d2->u.str); } else if (d1->type == LIST && d2->type == LIST) { anticipate_assignment(); d1->u.list = list_append(d1->u.list, d2->u.list); } else { cthrow(type_id, "Cannot add %D and %D.", d1, d2); return; } pop(1); } /* Effects: Adds two lists. (This is used for [@foo, ...];) */ void op_splice_add(void) { data_t *d1 = &stack[stack_pos - 2]; data_t *d2 = &stack[stack_pos - 1]; /* No need to check if d2 is a list, due to code generation. */ if (d1->type != LIST) { cthrow(type_id, "%D is not a list.", d1); return; } anticipate_assignment(); d1->u.list = list_append(d1->u.list, d2->u.list); pop(1); } /* Effects: If the top two values on the stack are integers, pops them and * pushes their difference. */ void op_subtract(void) { data_t *d1 = &stack[stack_pos - 2]; data_t *d2 = &stack[stack_pos - 1]; if (d1->type == FLOAT && d2->type == INTEGER) { d2->type = FLOAT; d2->u.fval = (float) d2->u.val; } else if (d1->type == INTEGER && d2->type == FLOAT) { d1->type = FLOAT; d1->u.fval = (float) d1->u.val; } if (d1->type != d2->type) { cthrow(type_id, "%D and %D are not of the same type.", d1, d2); } else if (d1->type != INTEGER && d1->type != FLOAT) { cthrow(type_id, "%D and %D are not integers or floats.", d1, d2); } else { if (d1->type == INTEGER) d1->u.val -= d2->u.val; else d1->u.fval -= d2->u.fval; pop(1); } } /* Effects: Pops the top two values on the stack and pushes 1 if they are * equal, 0 if not. */ void op_equal(void) { data_t *d1 = &stack[stack_pos - 2]; data_t *d2 = &stack[stack_pos - 1]; int val = (data_cmp(d1, d2) == 0); pop(2); push_int(val); } /* Effects: Pops the top two values on the stack and returns 1 if they are * unequal, 0 if they are equal. */ void op_not_equal(void) { data_t *d1 = &stack[stack_pos - 2]; data_t *d2 = &stack[stack_pos - 1]; int val = (data_cmp(d1, d2) != 0); pop(2); push_int(val); } /* Definition: Two values are comparable if they are of the same type and that * type is integer or string. */ /* Effects: If the top two values on the stack are comparable, pops them and * pushes 1 if the first is greater than the second, 0 if not. */ void op_greater(void) { data_t *d1 = &stack[stack_pos - 2]; data_t *d2 = &stack[stack_pos - 1]; int val, t = d1->type; if (d1->type == FLOAT && d2->type == INTEGER) { d2->type = FLOAT; d2->u.fval = (float) d2->u.val; } else if (d2->type == FLOAT && d1->type == INTEGER) { d1->type = FLOAT; d1->u.fval = (float) d1->u.val; } if (d1->type != d2->type) { cthrow(type_id, "%D and %D are not of the same type.", d1, d2); } else if (t != INTEGER && t != STRING && t != FLOAT) { cthrow(type_id,"%D and %D are not integers, floats or strings.",d1,d2); } else { /* Discard d1 and d2 and push the appropriate truth value. */ val = (data_cmp(d1, d2) > 0); pop(2); push_int(val); } } /* Effects: If the top two values on the stack are comparable, pops them and * pushes 1 if the first is greater than or equal to the second, 0 if * not. */ void op_greater_or_equal(void) { data_t *d1 = &stack[stack_pos - 2]; data_t *d2 = &stack[stack_pos - 1]; int val, t = d1->type; if (d1->type == FLOAT && d2->type == INTEGER) { d2->type = FLOAT; d2->u.fval = (float) d2->u.val; } else if (d1->type == INTEGER && d2->type == FLOAT) { d1->type = FLOAT; d1->u.fval = (float) d1->u.val; } if (d1->type != d2->type) { cthrow(type_id, "%D and %D are not of the same type.", d1, d2); } else if (t != INTEGER && t != FLOAT && t != STRING) { cthrow(type_id,"%D and %D are not integers, floats or strings.",d1,d2); } else { /* Discard d1 and d2 and push the appropriate truth value. */ val = (data_cmp(d1, d2) >= 0); pop(2); push_int(val); } } /* Effects: If the top two values on the stack are comparable, pops them and * pushes 1 if the first is less than the second, 0 if not. */ void op_less(void) { data_t *d1 = &stack[stack_pos - 2]; data_t *d2 = &stack[stack_pos - 1]; int val, t = d1->type; if (d1->type == FLOAT && d2->type == INTEGER) { d2->type = FLOAT; d2->u.fval = (float) d2->u.val; } else if (d1->type == INTEGER && d2->type == FLOAT) { d1->type = FLOAT; d1->u.fval = (float) d1->u.val; } if (d1->type != d2->type) { cthrow(type_id, "%D and %D are not of the same type.", d1, d2); } else if (t != INTEGER && t != FLOAT && t != STRING) { cthrow(type_id,"%D and %D are not integers, floats or strings.",d1,d2); } else { /* Discard d1 and d2 and push the appropriate truth value. */ val = (data_cmp(d1, d2) < 0); pop(2); push_int(val); } } /* Effects: If the top two values on the stack are comparable, pops them and * pushes 1 if the first is greater than or equal to the second, 0 if * not. */ void op_less_or_equal(void) { data_t *d1 = &stack[stack_pos - 2]; data_t *d2 = &stack[stack_pos - 1]; int val, t = d1->type; if (d1->type == FLOAT && d2->type == INTEGER) { d2->type = FLOAT; d2->u.fval = (float) d2->u.val; } else if (d1->type == INTEGER && d2->type == FLOAT) { d1->type = FLOAT; d1->u.fval = (float) d1->u.val; } if (d1->type != d2->type) { cthrow(type_id, "%D and %D are not of the same type.", d1, d2); } else if (t != INTEGER && t != FLOAT && t != STRING) { cthrow(type_id,"%D and %D are not integers, floats or strings.",d1,d2); } else { /* Discard d1 and d2 and push the appropriate truth value. */ val = (data_cmp(d1, d2) <= 0); pop(2); push_int(val); } } /* Effects: If the top value on the stack is a string or a list, pops the top * two values on the stack and pushes the location of the first value * in the second (where the first element is 1), or 0 if the first * value does not exist in the second. */ void op_in(void) { data_t *d1 = &stack[stack_pos - 2]; data_t *d2 = &stack[stack_pos - 1]; int pos; char *s; if (d2->type == LIST) { pos = list_search(d2->u.list, d1); pop(2); push_int(pos + 1); return; } if (d1->type != STRING || d2->type != STRING) { cthrow(type_id, "Cannot search for %D in %D.", d1, d2); return; } s = strcstr(string_chars(d2->u.str), string_chars(d1->u.str)); if (s) { pos = s - string_chars(d2->u.str); } else { pos = -1; } pop(2); push_int(pos + 1); } /* // ---------------------------------------------------------------- // Bitwise integer operators. // // Added by Jeff Kesselman, March 1995 // ---------------------------------------------------------------- */ /* // Effects: If the top two values on the stack are integers // pops them, bitwise ands them, and pushes // the result. */ void op_bwand(void) { data_t *d1 = &stack[stack_pos - 2]; data_t *d2 = &stack[stack_pos - 1]; /* Make sure we're multiplying two integers. */ if (d1->type != INTEGER) { cthrow(type_id, "Left side (%D) is not an integer.", d1); } else if (d2->type != INTEGER) { cthrow(type_id, "Right side (%D) is not an integer.", d2); } else if (d2->u.val == 0) { cthrow(div_id, "Attempt to divide %D by zero.", d1); } else { /* Replace d1 with d1 / d2, and pop d2. */ d1->u.val &= d2->u.val; pop(1); } } /* // Effects: If the top two values on the stack are integers // pops them, bitwise ors them, and pushes // the result. */ void op_bwor(void) { data_t *d1 = &stack[stack_pos - 2]; data_t *d2 = &stack[stack_pos - 1]; /* Make sure we're multiplying two integers. */ if (d1->type != INTEGER) { cthrow(type_id, "Left side (%D) is not an integer.", d1); } else if (d2->type != INTEGER) { cthrow(type_id, "Right side (%D) is not an integer.", d2); } else if (d2->u.val == 0) { cthrow(div_id, "Attempt to divide %D by zero.", d1); } else { /* Replace d1 with d1 / d2, and pop d2. */ d1->u.val |= d2->u.val; pop(1); } } /* // Effects: If the top two values on the stack are integers // pops them, shifts the left operand to the right // right-operand times, and pushes the result. */ void op_bwshr(void) { data_t *d1 = &stack[stack_pos - 2]; data_t *d2 = &stack[stack_pos - 1]; /* Make sure we're multiplying two integers. */ if (d1->type != INTEGER) { cthrow(type_id, "Left side (%D) is not an integer.", d1); } else if (d2->type != INTEGER) { cthrow(type_id, "Right side (%D) is not an integer.", d2); } else if (d2->u.val == 0) { cthrow(div_id, "Attempt to divide %D by zero.", d1); } else { /* Replace d1 with d1 / d2, and pop d2. */ d1->u.val >>= d2->u.val; pop(1); } } /* // Effects: If the top two values on the stack are integers // pops them, shifts the left operand to the left // right-operand times, and pushes the result. */ void op_bwshl(void) { data_t *d1 = &stack[stack_pos - 2]; data_t *d2 = &stack[stack_pos - 1]; /* Make sure we're multiplying two integers. */ if (d1->type != INTEGER) { cthrow(type_id, "Left side (%D) is not an integer.", d1); } else if (d2->type != INTEGER) { cthrow(type_id, "Right side (%D) is not an integer.", d2); } else if (d2->u.val == 0) { cthrow(div_id, "Attempt to divide %D by zero.", d1); } else { /* Replace d1 with d1 / d2, and pop d2. */ d1->u.val <<= d2->u.val; pop(1); } }