/* arithop.c: Arithmetic and relational operators. */ #define _POSIX_SOURCE #include <string.h> #include "x.tab.h" #include "operator.h" #include "execute.h" #include "data.h" #include "ident.h" #include "cmstring.h" #include "util.h" /* All functions in this file 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 *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 *d = &stack[stack_pos - 1]; /* Make sure we're taking the negative of an integer. */ if (d->type != INTEGER) { throw(type_id, "Argument (%D) is not an integer.", d); } else { /* Replace d with -d. */ d->u.val *= -1; } } /* Effects: If the top two values on the stack are integers, pops them and * pushes their product. */ void op_multiply(void) { Data *d1 = &stack[stack_pos - 2]; Data *d2 = &stack[stack_pos - 1]; /* Make sure we're multiplying two integers. */ if (d1->type != INTEGER) { throw(type_id, "Left side (%D) is not an integer.", d1); } else if (d2->type != INTEGER) { throw(type_id, "Right side (%D) is not an integer.", d2); } 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 and the second is * not zero, pops them, divides the first by the second, and pushes * the quotient. */ void op_divide(void) { Data *d1 = &stack[stack_pos - 2]; Data *d2 = &stack[stack_pos - 1]; /* Make sure we're multiplying two integers. */ if (d1->type != INTEGER) { throw(type_id, "Left side (%D) is not an integer.", d1); } else if (d2->type != INTEGER) { throw(type_id, "Right side (%D) is not an integer.", d2); } else if (d2->u.val == 0) { throw(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 and the second is * not zero, pops them, divides the first by the second, and pushes * the remainder. */ void op_modulo(void) { Data *d1 = &stack[stack_pos - 2]; Data *d2 = &stack[stack_pos - 1]; /* Make sure we're multiplying two integers. */ if (d1->type != INTEGER) { throw(type_id, "Left side (%D) is not an integer.", d1); } else if (d2->type != INTEGER) { throw(type_id, "Right side (%D) is not an integer.", d2); } else if (d2->type == 0) { throw(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 *d1 = &stack[stack_pos - 2]; Data *d2 = &stack[stack_pos - 1]; Substring *s1; Sublist *l1; /* 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 == STRING && d2->type == STRING) { anticipate_assignment(); s1 = &d1->u.substr; substring_truncate(s1); s1->str = string_add(s1->str, data_sptr(d2), d2->u.substr.span); s1->span += d2->u.substr.span; } else if (d1->type == LIST && d2->type == LIST) { anticipate_assignment(); l1 = &d1->u.sublist; sublist_truncate(l1); l1->list = list_append(l1->list, data_dptr(d2), d2->u.sublist.span); l1->span += d2->u.sublist.span; } else { throw(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 *d1 = &stack[stack_pos - 2]; Data *d2 = &stack[stack_pos - 1]; Sublist *l1; /* No need to check if d2 is a list, due to code generation. */ if (d1->type != LIST) { throw(type_id, "%D is not a list.", d1); return; } anticipate_assignment(); l1 = &d1->u.sublist; sublist_truncate(l1); l1->list = list_append(l1->list, data_dptr(d2), d2->u.sublist.span); l1->span += d2->u.sublist.span; pop(1); } /* Effects: If the top two values on the stack are integers, pops them and * pushes their difference. */ void op_subtract(void) { Data *d1 = &stack[stack_pos - 2]; Data *d2 = &stack[stack_pos - 1]; /* Make sure we're multiplying two integers. */ if (d1->type != INTEGER) { throw(type_id, "Left side (%D) is not an integer.", d1); } else if (d2->type != INTEGER) { throw(type_id, "Right side (%D) is not an integer.", d2); } else { /* Replace d1 with d1 - d2, and pop d2. */ d1->u.val -= d2->u.val; 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 *d1 = &stack[stack_pos - 2]; Data *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 *d1 = &stack[stack_pos - 2]; Data *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 *d1 = &stack[stack_pos - 2]; Data *d2 = &stack[stack_pos - 1]; int val, t = d1->type; if (d1->type != d2->type) { throw(type_id, "%D and %D are not of the same type.", d1, d2); } else if (t != INTEGER && t != STRING) { throw(type_id, "%D and %D are not integers 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 *d1 = &stack[stack_pos - 2]; Data *d2 = &stack[stack_pos - 1]; int val, t = d1->type; if (d1->type != d2->type) { throw(type_id, "%D and %D are not of the same type.", d1, d2); } else if (t != INTEGER && t != STRING) { throw(type_id, "%D and %D are not integers 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 *d1 = &stack[stack_pos - 2]; Data *d2 = &stack[stack_pos - 1]; int val, t = d1->type; if (d1->type != d2->type) { throw(type_id, "%D and %D are not of the same type.", d1, d2); } else if (t != INTEGER && t != STRING) { throw(type_id, "%D and %D are not integers 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 *d1 = &stack[stack_pos - 2]; Data *d2 = &stack[stack_pos - 1]; int val, t = d1->type; if (d1->type != d2->type) { throw(type_id, "%D and %D are not of the same type.", d1, d2); } else if (t != INTEGER && t != STRING) { throw(type_id, "%D and %D are not integers 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. If the second value is a * string, then the first must be a string of length one. */ void op_in(void) { Data *d1 = &stack[stack_pos - 2]; Data *d2 = &stack[stack_pos - 1]; int i, c; char *s; if (d2->type == LIST) { for (i = 0; i < d2->u.sublist.span; i++) { if (data_cmp(data_dptr(d2) + i, d1) == 0) break; } i = (i == d2->u.sublist.span) ? 0 : i + 1; pop(2); push_int(i); return; } if (d1->type != STRING || d2->type != STRING) { throw(type_id, "Cannot search for %D in %D.", d1, d2); return; } /* Return 1 if d1 is an empty string. */ if (!d1->u.substr.span) { pop(2); push_int(1); return; } c = LCASE(*d1->u.substr.str->s); s = d1->u.substr.str->s + 1; i = 0; while (1) { for (; i <= d2->u.substr.span - d1->u.substr.span; i++) { if (c == LCASE(*(data_sptr(d2) + i))) break; } if (i > d2->u.substr.span - d1->u.substr.span) break; if (strnccmp(data_sptr(d2) + i + 1, s, d1->u.substr.span - 1) == 0) { pop(2); push_int(i + 1); return; } i++; } pop(2); push_int(0); }