xref: /freebsd/bin/expr/expr.y (revision 8c3bbba8241a0ae8804aa9d788f1475ab99e6080)
15b81b6b3SRodney W. Grimes %{
29ddb49cbSWarner Losh /*-
39ddb49cbSWarner Losh  * Written by Pace Willisson (pace@blitz.com)
455c497bfSJ.T. Conklin  * and placed in the public domain.
55b81b6b3SRodney W. Grimes  *
655c497bfSJ.T. Conklin  * Largely rewritten by J.T. Conklin (jtc@wimsey.com)
755c497bfSJ.T. Conklin  *
82a456239SPeter Wemm  * $FreeBSD$
95b81b6b3SRodney W. Grimes  */
104cf61abaSJ.T. Conklin 
1164867286SStefan Eßer #include <sys/types.h>
12f07e4247SGarrett Wollman 
135b81b6b3SRodney W. Grimes #include <ctype.h>
1455c497bfSJ.T. Conklin #include <err.h>
15915198b4SStefan Eßer #include <errno.h>
16f07e4247SGarrett Wollman #include <inttypes.h>
17915198b4SStefan Eßer #include <limits.h>
18f07e4247SGarrett Wollman #include <locale.h>
19f07e4247SGarrett Wollman #include <stdio.h>
20f07e4247SGarrett Wollman #include <stdlib.h>
21f07e4247SGarrett Wollman #include <string.h>
22f07e4247SGarrett Wollman #include <regex.h>
23f07e4247SGarrett Wollman #include <unistd.h>
24f07e4247SGarrett Wollman 
25f07e4247SGarrett Wollman /*
26f07e4247SGarrett Wollman  * POSIX specifies a specific error code for syntax errors.  We exit
27f07e4247SGarrett Wollman  * with this code for all errors.
28f07e4247SGarrett Wollman  */
29f07e4247SGarrett Wollman #define	ERR_EXIT	2
305b81b6b3SRodney W. Grimes 
315b81b6b3SRodney W. Grimes enum valtype {
32717252eaSJoerg Wunsch 	integer, numeric_string, string
335b81b6b3SRodney W. Grimes } ;
345b81b6b3SRodney W. Grimes 
355b81b6b3SRodney W. Grimes struct val {
365b81b6b3SRodney W. Grimes 	enum valtype type;
375b81b6b3SRodney W. Grimes 	union {
385b81b6b3SRodney W. Grimes 		char *s;
39f07e4247SGarrett Wollman 		intmax_t i;
405b81b6b3SRodney W. Grimes 	} u;
415b81b6b3SRodney W. Grimes } ;
425b81b6b3SRodney W. Grimes 
436fbe7bf4SStefan Eßer char		**av;
446fbe7bf4SStefan Eßer int		nonposix;
455b81b6b3SRodney W. Grimes struct val	*result;
463d06e95dSKris Kennaway 
47fa717604SStefan Eßer void		assert_to_integer(struct val *);
486fbe7bf4SStefan Eßer void		assert_div(intmax_t, intmax_t);
496fbe7bf4SStefan Eßer void		assert_minus(intmax_t, intmax_t, intmax_t);
506fbe7bf4SStefan Eßer void		assert_plus(intmax_t, intmax_t, intmax_t);
516fbe7bf4SStefan Eßer void		assert_times(intmax_t, intmax_t, intmax_t);
526fbe7bf4SStefan Eßer int		compare_vals(struct val *, struct val *);
537669d0fcSWarner Losh void		free_value(struct val *);
54fa717604SStefan Eßer int		is_integer(const char *);
5569759f08SStefan Eßer int		is_string(struct val *);
56fa717604SStefan Eßer int		is_zero_or_null(struct val *);
57f07e4247SGarrett Wollman struct val	*make_integer(intmax_t);
587669d0fcSWarner Losh struct val	*make_str(const char *);
597669d0fcSWarner Losh struct val	*op_and(struct val *, struct val *);
607669d0fcSWarner Losh struct val	*op_colon(struct val *, struct val *);
617669d0fcSWarner Losh struct val	*op_div(struct val *, struct val *);
627669d0fcSWarner Losh struct val	*op_eq(struct val *, struct val *);
637669d0fcSWarner Losh struct val	*op_ge(struct val *, struct val *);
647669d0fcSWarner Losh struct val	*op_gt(struct val *, struct val *);
657669d0fcSWarner Losh struct val	*op_le(struct val *, struct val *);
667669d0fcSWarner Losh struct val	*op_lt(struct val *, struct val *);
677669d0fcSWarner Losh struct val	*op_minus(struct val *, struct val *);
687669d0fcSWarner Losh struct val	*op_ne(struct val *, struct val *);
697669d0fcSWarner Losh struct val	*op_or(struct val *, struct val *);
707669d0fcSWarner Losh struct val	*op_plus(struct val *, struct val *);
717669d0fcSWarner Losh struct val	*op_rem(struct val *, struct val *);
727669d0fcSWarner Losh struct val	*op_times(struct val *, struct val *);
73fa717604SStefan Eßer int		to_integer(struct val *);
747669d0fcSWarner Losh void		to_string(struct val *);
757669d0fcSWarner Losh int		yyerror(const char *);
767669d0fcSWarner Losh int		yylex(void);
777669d0fcSWarner Losh int		yyparse(void);
785b81b6b3SRodney W. Grimes 
795b81b6b3SRodney W. Grimes %}
805b81b6b3SRodney W. Grimes 
815b81b6b3SRodney W. Grimes %union
825b81b6b3SRodney W. Grimes {
835b81b6b3SRodney W. Grimes 	struct val *val;
845b81b6b3SRodney W. Grimes }
855b81b6b3SRodney W. Grimes 
865b81b6b3SRodney W. Grimes %left <val> '|'
875b81b6b3SRodney W. Grimes %left <val> '&'
885b81b6b3SRodney W. Grimes %left <val> '=' '>' '<' GE LE NE
895b81b6b3SRodney W. Grimes %left <val> '+' '-'
905b81b6b3SRodney W. Grimes %left <val> '*' '/' '%'
915b81b6b3SRodney W. Grimes %left <val> ':'
925b81b6b3SRodney W. Grimes 
935b81b6b3SRodney W. Grimes %token <val> TOKEN
945b81b6b3SRodney W. Grimes %type <val> start expr
955b81b6b3SRodney W. Grimes 
965b81b6b3SRodney W. Grimes %%
975b81b6b3SRodney W. Grimes 
985b81b6b3SRodney W. Grimes start: expr { result = $$; }
995b81b6b3SRodney W. Grimes 
1005b81b6b3SRodney W. Grimes expr:	TOKEN
1015b81b6b3SRodney W. Grimes 	| '(' expr ')' { $$ = $2; }
1025b81b6b3SRodney W. Grimes 	| expr '|' expr { $$ = op_or($1, $3); }
1035b81b6b3SRodney W. Grimes 	| expr '&' expr { $$ = op_and($1, $3); }
1045b81b6b3SRodney W. Grimes 	| expr '=' expr { $$ = op_eq($1, $3); }
1055b81b6b3SRodney W. Grimes 	| expr '>' expr { $$ = op_gt($1, $3); }
1065b81b6b3SRodney W. Grimes 	| expr '<' expr { $$ = op_lt($1, $3); }
1075b81b6b3SRodney W. Grimes 	| expr GE expr  { $$ = op_ge($1, $3); }
1085b81b6b3SRodney W. Grimes 	| expr LE expr  { $$ = op_le($1, $3); }
1095b81b6b3SRodney W. Grimes 	| expr NE expr  { $$ = op_ne($1, $3); }
1105b81b6b3SRodney W. Grimes 	| expr '+' expr { $$ = op_plus($1, $3); }
1115b81b6b3SRodney W. Grimes 	| expr '-' expr { $$ = op_minus($1, $3); }
1125b81b6b3SRodney W. Grimes 	| expr '*' expr { $$ = op_times($1, $3); }
1135b81b6b3SRodney W. Grimes 	| expr '/' expr { $$ = op_div($1, $3); }
1145b81b6b3SRodney W. Grimes 	| expr '%' expr { $$ = op_rem($1, $3); }
1155b81b6b3SRodney W. Grimes 	| expr ':' expr { $$ = op_colon($1, $3); }
1165b81b6b3SRodney W. Grimes 	;
1175b81b6b3SRodney W. Grimes 
1185b81b6b3SRodney W. Grimes %%
1195b81b6b3SRodney W. Grimes 
1205b81b6b3SRodney W. Grimes struct val *
121f07e4247SGarrett Wollman make_integer(intmax_t i)
1225b81b6b3SRodney W. Grimes {
1235b81b6b3SRodney W. Grimes 	struct val *vp;
1245b81b6b3SRodney W. Grimes 
1255b81b6b3SRodney W. Grimes 	vp = (struct val *)malloc(sizeof(*vp));
12669759f08SStefan Eßer 	if (vp == NULL)
127f07e4247SGarrett Wollman 		errx(ERR_EXIT, "malloc() failed");
1285b81b6b3SRodney W. Grimes 
1295b81b6b3SRodney W. Grimes 	vp->type = integer;
1305b81b6b3SRodney W. Grimes 	vp->u.i  = i;
13169759f08SStefan Eßer 	return (vp);
1325b81b6b3SRodney W. Grimes }
1335b81b6b3SRodney W. Grimes 
1345b81b6b3SRodney W. Grimes struct val *
1357669d0fcSWarner Losh make_str(const char *s)
1365b81b6b3SRodney W. Grimes {
1375b81b6b3SRodney W. Grimes 	struct val *vp;
1385b81b6b3SRodney W. Grimes 
1395b81b6b3SRodney W. Grimes 	vp = (struct val *)malloc(sizeof(*vp));
14069759f08SStefan Eßer 	if (vp == NULL || ((vp->u.s = strdup(s)) == NULL))
141f07e4247SGarrett Wollman 		errx(ERR_EXIT, "malloc() failed");
1425b81b6b3SRodney W. Grimes 
143fa717604SStefan Eßer 	if (is_integer(s))
144f07e4247SGarrett Wollman 		vp->type = numeric_string;
145fa717604SStefan Eßer 	else
146fa717604SStefan Eßer 		vp->type = string;
1472a353a9fSJoerg Wunsch 
14869759f08SStefan Eßer 	return (vp);
1495b81b6b3SRodney W. Grimes }
1505b81b6b3SRodney W. Grimes 
1515b81b6b3SRodney W. Grimes void
1527669d0fcSWarner Losh free_value(struct val *vp)
1535b81b6b3SRodney W. Grimes {
154717252eaSJoerg Wunsch 	if (vp->type == string || vp->type == numeric_string)
1555b81b6b3SRodney W. Grimes 		free(vp->u.s);
1565b81b6b3SRodney W. Grimes }
1575b81b6b3SRodney W. Grimes 
158fa717604SStefan Eßer int
1597669d0fcSWarner Losh to_integer(struct val *vp)
1605b81b6b3SRodney W. Grimes {
161f07e4247SGarrett Wollman 	intmax_t i;
1625b81b6b3SRodney W. Grimes 
163fa717604SStefan Eßer 	/* we can only convert numeric_string to integer, here */
164fa717604SStefan Eßer 	if (vp->type == numeric_string) {
165915198b4SStefan Eßer 		errno = 0;
166f07e4247SGarrett Wollman 		i  = strtoimax(vp->u.s, (char **)NULL, 10);
167fa717604SStefan Eßer 		/* just keep as numeric_string, if the conversion fails */
168fa717604SStefan Eßer 		if (errno != ERANGE) {
1695b81b6b3SRodney W. Grimes 			free(vp->u.s);
1705b81b6b3SRodney W. Grimes 			vp->u.i = i;
171717252eaSJoerg Wunsch 			vp->type = integer;
172fa717604SStefan Eßer 		}
173fa717604SStefan Eßer 	}
174fa717604SStefan Eßer 	return (vp->type == integer);
175fa717604SStefan Eßer }
176fa717604SStefan Eßer 
177fa717604SStefan Eßer void
178fa717604SStefan Eßer assert_to_integer(struct val *vp)
179fa717604SStefan Eßer {
180fa717604SStefan Eßer 	if (vp->type == string)
181fa717604SStefan Eßer 		errx(ERR_EXIT, "not a decimal number: '%s'", vp->u.s);
182fa717604SStefan Eßer 	if (!to_integer(vp))
183fa717604SStefan Eßer 		errx(ERR_EXIT, "operand too large: '%s'", vp->u.s);
1845b81b6b3SRodney W. Grimes }
1855b81b6b3SRodney W. Grimes 
1865b81b6b3SRodney W. Grimes void
1877669d0fcSWarner Losh to_string(struct val *vp)
1885b81b6b3SRodney W. Grimes {
1895b81b6b3SRodney W. Grimes 	char *tmp;
1905b81b6b3SRodney W. Grimes 
191717252eaSJoerg Wunsch 	if (vp->type == string || vp->type == numeric_string)
1925b81b6b3SRodney W. Grimes 		return;
1935b81b6b3SRodney W. Grimes 
194f07e4247SGarrett Wollman 	/*
195f07e4247SGarrett Wollman 	 * log_10(x) ~= 0.3 * log_2(x).  Rounding up gives the number
196f07e4247SGarrett Wollman 	 * of digits; add one each for the sign and terminating null
197f07e4247SGarrett Wollman 	 * character, respectively.
198f07e4247SGarrett Wollman 	 */
199f07e4247SGarrett Wollman #define	NDIGITS(x) (3 * (sizeof(x) * CHAR_BIT) / 10 + 1 + 1 + 1)
200f07e4247SGarrett Wollman 	tmp = malloc(NDIGITS(vp->u.i));
201f07e4247SGarrett Wollman 	if (tmp == NULL)
202f07e4247SGarrett Wollman 		errx(ERR_EXIT, "malloc() failed");
2035b81b6b3SRodney W. Grimes 
204f07e4247SGarrett Wollman 	sprintf(tmp, "%jd", vp->u.i);
2055b81b6b3SRodney W. Grimes 	vp->type = string;
2065b81b6b3SRodney W. Grimes 	vp->u.s  = tmp;
2075b81b6b3SRodney W. Grimes }
2085b81b6b3SRodney W. Grimes 
2095b81b6b3SRodney W. Grimes int
210fa717604SStefan Eßer is_integer(const char *s)
211fa717604SStefan Eßer {
212fa717604SStefan Eßer 	if (nonposix) {
213fa717604SStefan Eßer 		if (*s == '\0')
214fa717604SStefan Eßer 			return (1);
215fa717604SStefan Eßer 		while (isspace((unsigned char)*s))
216fa717604SStefan Eßer 			s++;
217fa717604SStefan Eßer 	}
218fa717604SStefan Eßer 	if (*s == '-' || (nonposix && *s == '+'))
219fa717604SStefan Eßer 		s++;
220fa717604SStefan Eßer 	if (*s == '\0')
221fa717604SStefan Eßer 		return (0);
222fa717604SStefan Eßer 	while (isdigit((unsigned char)*s))
223fa717604SStefan Eßer 		s++;
224fa717604SStefan Eßer 	return (*s == '\0');
225fa717604SStefan Eßer }
226fa717604SStefan Eßer 
227fa717604SStefan Eßer int
22869759f08SStefan Eßer is_string(struct val *vp)
2295b81b6b3SRodney W. Grimes {
230717252eaSJoerg Wunsch 	/* only TRUE if this string is not a valid integer */
2315b81b6b3SRodney W. Grimes 	return (vp->type == string);
2325b81b6b3SRodney W. Grimes }
2335b81b6b3SRodney W. Grimes 
2345b81b6b3SRodney W. Grimes int
2357669d0fcSWarner Losh yylex(void)
2365b81b6b3SRodney W. Grimes {
2375b81b6b3SRodney W. Grimes 	char *p;
2385b81b6b3SRodney W. Grimes 
2395b81b6b3SRodney W. Grimes 	if (*av == NULL)
2405b81b6b3SRodney W. Grimes 		return (0);
2415b81b6b3SRodney W. Grimes 
2425b81b6b3SRodney W. Grimes 	p = *av++;
2435b81b6b3SRodney W. Grimes 
2445b81b6b3SRodney W. Grimes 	if (strlen(p) == 1) {
2455b81b6b3SRodney W. Grimes 		if (strchr("|&=<>+-*/%:()", *p))
2465b81b6b3SRodney W. Grimes 			return (*p);
2475b81b6b3SRodney W. Grimes 	} else if (strlen(p) == 2 && p[1] == '=') {
2485b81b6b3SRodney W. Grimes 		switch (*p) {
2495b81b6b3SRodney W. Grimes 		case '>': return (GE);
2505b81b6b3SRodney W. Grimes 		case '<': return (LE);
2515b81b6b3SRodney W. Grimes 		case '!': return (NE);
2525b81b6b3SRodney W. Grimes 		}
2535b81b6b3SRodney W. Grimes 	}
2545b81b6b3SRodney W. Grimes 
2555b81b6b3SRodney W. Grimes 	yylval.val = make_str(p);
2565b81b6b3SRodney W. Grimes 	return (TOKEN);
2575b81b6b3SRodney W. Grimes }
2585b81b6b3SRodney W. Grimes 
2595b81b6b3SRodney W. Grimes int
2607669d0fcSWarner Losh is_zero_or_null(struct val *vp)
2615b81b6b3SRodney W. Grimes {
26269759f08SStefan Eßer 	if (vp->type == integer)
2635b81b6b3SRodney W. Grimes 		return (vp->u.i == 0);
26469759f08SStefan Eßer 
265c9fe00dcSJ.T. Conklin 	return (*vp->u.s == 0 || (to_integer(vp) && vp->u.i == 0));
2665b81b6b3SRodney W. Grimes }
2675b81b6b3SRodney W. Grimes 
268717252eaSJoerg Wunsch int
269f07e4247SGarrett Wollman main(int argc, char *argv[])
2705b81b6b3SRodney W. Grimes {
271f07e4247SGarrett Wollman 	int c;
2724cf61abaSJ.T. Conklin 
273f07e4247SGarrett Wollman 	setlocale(LC_ALL, "");
274c9885518SGarrett Wollman 	if (getenv("EXPR_COMPAT") != NULL
275c9885518SGarrett Wollman 	    || check_utility_compat("expr")) {
27696ab7da3SGarrett Wollman 		av = argv + 1;
277fa717604SStefan Eßer 		nonposix = 1;
27896ab7da3SGarrett Wollman 	} else {
27969759f08SStefan Eßer 		while ((c = getopt(argc, argv, "e")) != -1) {
280f07e4247SGarrett Wollman 			switch (c) {
2811393277eSGarrett Wollman 			case 'e':
282fa717604SStefan Eßer 				nonposix = 1;
2831393277eSGarrett Wollman 				break;
284f07e4247SGarrett Wollman 			default:
28569759f08SStefan Eßer 				errx(ERR_EXIT,
2861393277eSGarrett Wollman 				    "usage: expr [-e] expression\n");
28769759f08SStefan Eßer 			}
288f07e4247SGarrett Wollman 		}
289f07e4247SGarrett Wollman 		av = argv + optind;
29096ab7da3SGarrett Wollman 	}
2915b81b6b3SRodney W. Grimes 
2925b81b6b3SRodney W. Grimes 	yyparse();
2935b81b6b3SRodney W. Grimes 
2945b81b6b3SRodney W. Grimes 	if (result->type == integer)
295f07e4247SGarrett Wollman 		printf("%jd\n", result->u.i);
2965b81b6b3SRodney W. Grimes 	else
2975b81b6b3SRodney W. Grimes 		printf("%s\n", result->u.s);
2985b81b6b3SRodney W. Grimes 
299717252eaSJoerg Wunsch 	return (is_zero_or_null(result));
3005b81b6b3SRodney W. Grimes }
3015b81b6b3SRodney W. Grimes 
3025b81b6b3SRodney W. Grimes int
3037669d0fcSWarner Losh yyerror(const char *s __unused)
3045b81b6b3SRodney W. Grimes {
305f07e4247SGarrett Wollman 	errx(ERR_EXIT, "syntax error");
3065b81b6b3SRodney W. Grimes }
3075b81b6b3SRodney W. Grimes 
3085b81b6b3SRodney W. Grimes struct val *
3097669d0fcSWarner Losh op_or(struct val *a, struct val *b)
3105b81b6b3SRodney W. Grimes {
311fa717604SStefan Eßer 	if (!is_zero_or_null(a)) {
3125b81b6b3SRodney W. Grimes 		free_value(b);
3135b81b6b3SRodney W. Grimes 		return (a);
3145b81b6b3SRodney W. Grimes 	}
315fa717604SStefan Eßer 	free_value(a);
316fa717604SStefan Eßer 	if (!is_zero_or_null(b))
317fa717604SStefan Eßer 		return (b);
318fa717604SStefan Eßer 	free_value(b);
319fa717604SStefan Eßer 	return (make_integer((intmax_t)0));
3205b81b6b3SRodney W. Grimes }
3215b81b6b3SRodney W. Grimes 
3225b81b6b3SRodney W. Grimes struct val *
3237669d0fcSWarner Losh op_and(struct val *a, struct val *b)
3245b81b6b3SRodney W. Grimes {
3255b81b6b3SRodney W. Grimes 	if (is_zero_or_null(a) || is_zero_or_null(b)) {
3265b81b6b3SRodney W. Grimes 		free_value(a);
3275b81b6b3SRodney W. Grimes 		free_value(b);
328f07e4247SGarrett Wollman 		return (make_integer((intmax_t)0));
3295b81b6b3SRodney W. Grimes 	} else {
3305b81b6b3SRodney W. Grimes 		free_value(b);
3315b81b6b3SRodney W. Grimes 		return (a);
3325b81b6b3SRodney W. Grimes 	}
3335b81b6b3SRodney W. Grimes }
3345b81b6b3SRodney W. Grimes 
3356fbe7bf4SStefan Eßer int
3366fbe7bf4SStefan Eßer compare_vals(struct val *a, struct val *b)
3375b81b6b3SRodney W. Grimes {
3386fbe7bf4SStefan Eßer 	int r;
3395b81b6b3SRodney W. Grimes 
34069759f08SStefan Eßer 	if (is_string(a) || is_string(b)) {
3415b81b6b3SRodney W. Grimes 		to_string(a);
3425b81b6b3SRodney W. Grimes 		to_string(b);
3436fbe7bf4SStefan Eßer 		r = strcoll(a->u.s, b->u.s);
3445b81b6b3SRodney W. Grimes 	} else {
345fa717604SStefan Eßer 		assert_to_integer(a);
346fa717604SStefan Eßer 		assert_to_integer(b);
3476fbe7bf4SStefan Eßer 		if (a->u.i > b->u.i)
3486fbe7bf4SStefan Eßer 			r = 1;
3496fbe7bf4SStefan Eßer 		else if (a->u.i < b->u.i)
3506fbe7bf4SStefan Eßer 			r = -1;
3516fbe7bf4SStefan Eßer 		else
3526fbe7bf4SStefan Eßer 			r = 0;
3535b81b6b3SRodney W. Grimes 	}
3545b81b6b3SRodney W. Grimes 
3555b81b6b3SRodney W. Grimes 	free_value(a);
3565b81b6b3SRodney W. Grimes 	free_value(b);
3576fbe7bf4SStefan Eßer 	return (r);
3586fbe7bf4SStefan Eßer }
3596fbe7bf4SStefan Eßer 
3606fbe7bf4SStefan Eßer struct val *
3616fbe7bf4SStefan Eßer op_eq(struct val *a, struct val *b)
3626fbe7bf4SStefan Eßer {
3636fbe7bf4SStefan Eßer 	return (make_integer((intmax_t)(compare_vals(a, b) == 0)));
3645b81b6b3SRodney W. Grimes }
3655b81b6b3SRodney W. Grimes 
3665b81b6b3SRodney W. Grimes struct val *
3677669d0fcSWarner Losh op_gt(struct val *a, struct val *b)
3685b81b6b3SRodney W. Grimes {
3696fbe7bf4SStefan Eßer 	return (make_integer((intmax_t)(compare_vals(a, b) > 0)));
3705b81b6b3SRodney W. Grimes }
3715b81b6b3SRodney W. Grimes 
3725b81b6b3SRodney W. Grimes struct val *
3737669d0fcSWarner Losh op_lt(struct val *a, struct val *b)
3745b81b6b3SRodney W. Grimes {
3756fbe7bf4SStefan Eßer 	return (make_integer((intmax_t)(compare_vals(a, b) < 0)));
3765b81b6b3SRodney W. Grimes }
3775b81b6b3SRodney W. Grimes 
3785b81b6b3SRodney W. Grimes struct val *
3797669d0fcSWarner Losh op_ge(struct val *a, struct val *b)
3805b81b6b3SRodney W. Grimes {
3816fbe7bf4SStefan Eßer 	return (make_integer((intmax_t)(compare_vals(a, b) >= 0)));
3825b81b6b3SRodney W. Grimes }
3835b81b6b3SRodney W. Grimes 
3845b81b6b3SRodney W. Grimes struct val *
3857669d0fcSWarner Losh op_le(struct val *a, struct val *b)
3865b81b6b3SRodney W. Grimes {
3876fbe7bf4SStefan Eßer 	return (make_integer((intmax_t)(compare_vals(a, b) <= 0)));
3885b81b6b3SRodney W. Grimes }
3895b81b6b3SRodney W. Grimes 
3905b81b6b3SRodney W. Grimes struct val *
3917669d0fcSWarner Losh op_ne(struct val *a, struct val *b)
3925b81b6b3SRodney W. Grimes {
3936fbe7bf4SStefan Eßer 	return (make_integer((intmax_t)(compare_vals(a, b) != 0)));
3945b81b6b3SRodney W. Grimes }
3955b81b6b3SRodney W. Grimes 
3966fbe7bf4SStefan Eßer void
3976fbe7bf4SStefan Eßer assert_plus(intmax_t a, intmax_t b, intmax_t r)
398915198b4SStefan Eßer {
3996fbe7bf4SStefan Eßer 	/*
4006fbe7bf4SStefan Eßer 	 * sum of two positive numbers must be positive,
4016fbe7bf4SStefan Eßer 	 * sum of two negative numbers must be negative
4026fbe7bf4SStefan Eßer 	 */
4036fbe7bf4SStefan Eßer 	if ((a > 0 && b > 0 && r <= 0) ||
4046fbe7bf4SStefan Eßer 	    (a < 0 && b < 0 && r >= 0))
4056fbe7bf4SStefan Eßer 		errx(ERR_EXIT, "overflow");
406915198b4SStefan Eßer }
407915198b4SStefan Eßer 
4085b81b6b3SRodney W. Grimes struct val *
4097669d0fcSWarner Losh op_plus(struct val *a, struct val *b)
4105b81b6b3SRodney W. Grimes {
4115b81b6b3SRodney W. Grimes 	struct val *r;
4125b81b6b3SRodney W. Grimes 
413fa717604SStefan Eßer 	assert_to_integer(a);
414fa717604SStefan Eßer 	assert_to_integer(b);
4151393277eSGarrett Wollman 	r = make_integer(a->u.i + b->u.i);
4166fbe7bf4SStefan Eßer 	assert_plus(a->u.i, b->u.i, r->u.i);
4171393277eSGarrett Wollman 
4185b81b6b3SRodney W. Grimes 	free_value(a);
4195b81b6b3SRodney W. Grimes 	free_value(b);
42069759f08SStefan Eßer 	return (r);
4215b81b6b3SRodney W. Grimes }
4225b81b6b3SRodney W. Grimes 
4236fbe7bf4SStefan Eßer void
4246fbe7bf4SStefan Eßer assert_minus(intmax_t a, intmax_t b, intmax_t r)
425915198b4SStefan Eßer {
426f07e4247SGarrett Wollman 	/* special case subtraction of INTMAX_MIN */
4276fbe7bf4SStefan Eßer 	if (b == INTMAX_MIN && a < 0)
4286fbe7bf4SStefan Eßer 		errx(ERR_EXIT, "overflow");
4296fbe7bf4SStefan Eßer 	/* check addition of negative subtrahend */
4306fbe7bf4SStefan Eßer 	assert_plus(a, -b, r);
431915198b4SStefan Eßer }
432915198b4SStefan Eßer 
4335b81b6b3SRodney W. Grimes struct val *
4347669d0fcSWarner Losh op_minus(struct val *a, struct val *b)
4355b81b6b3SRodney W. Grimes {
4365b81b6b3SRodney W. Grimes 	struct val *r;
4375b81b6b3SRodney W. Grimes 
438fa717604SStefan Eßer 	assert_to_integer(a);
439fa717604SStefan Eßer 	assert_to_integer(b);
4401393277eSGarrett Wollman 	r = make_integer(a->u.i - b->u.i);
4416fbe7bf4SStefan Eßer 	assert_minus(a->u.i, b->u.i, r->u.i);
4421393277eSGarrett Wollman 
4435b81b6b3SRodney W. Grimes 	free_value(a);
4445b81b6b3SRodney W. Grimes 	free_value(b);
44569759f08SStefan Eßer 	return (r);
4465b81b6b3SRodney W. Grimes }
4475b81b6b3SRodney W. Grimes 
4486fbe7bf4SStefan Eßer void
4496fbe7bf4SStefan Eßer assert_times(intmax_t a, intmax_t b, intmax_t r)
450915198b4SStefan Eßer {
4516fbe7bf4SStefan Eßer 	/*
4526fbe7bf4SStefan Eßer 	 * if first operand is 0, no overflow is possible,
4536fbe7bf4SStefan Eßer 	 * else result of division test must match second operand
4546fbe7bf4SStefan Eßer 	 */
4556fbe7bf4SStefan Eßer 	if (a != 0 && r / a != b)
4566fbe7bf4SStefan Eßer 		errx(ERR_EXIT, "overflow");
457915198b4SStefan Eßer }
458915198b4SStefan Eßer 
4595b81b6b3SRodney W. Grimes struct val *
4607669d0fcSWarner Losh op_times(struct val *a, struct val *b)
4615b81b6b3SRodney W. Grimes {
4625b81b6b3SRodney W. Grimes 	struct val *r;
4635b81b6b3SRodney W. Grimes 
464fa717604SStefan Eßer 	assert_to_integer(a);
465fa717604SStefan Eßer 	assert_to_integer(b);
4661393277eSGarrett Wollman 	r = make_integer(a->u.i * b->u.i);
4676fbe7bf4SStefan Eßer 	assert_times(a->u.i, b->u.i, r->u.i);
4681393277eSGarrett Wollman 
4695b81b6b3SRodney W. Grimes 	free_value(a);
4705b81b6b3SRodney W. Grimes 	free_value(b);
4715b81b6b3SRodney W. Grimes 	return (r);
4725b81b6b3SRodney W. Grimes }
4735b81b6b3SRodney W. Grimes 
4746fbe7bf4SStefan Eßer void
4756fbe7bf4SStefan Eßer assert_div(intmax_t a, intmax_t b)
476915198b4SStefan Eßer {
4776fbe7bf4SStefan Eßer 	if (b == 0)
4786fbe7bf4SStefan Eßer 		errx(ERR_EXIT, "division by zero");
479f07e4247SGarrett Wollman 	/* only INTMAX_MIN / -1 causes overflow */
480f07e4247SGarrett Wollman 	if (a == INTMAX_MIN && b == -1)
4816fbe7bf4SStefan Eßer 		errx(ERR_EXIT, "overflow");
482915198b4SStefan Eßer }
483915198b4SStefan Eßer 
4845b81b6b3SRodney W. Grimes struct val *
4857669d0fcSWarner Losh op_div(struct val *a, struct val *b)
4865b81b6b3SRodney W. Grimes {
4875b81b6b3SRodney W. Grimes 	struct val *r;
4885b81b6b3SRodney W. Grimes 
489fa717604SStefan Eßer 	assert_to_integer(a);
490fa717604SStefan Eßer 	assert_to_integer(b);
4916fbe7bf4SStefan Eßer 	/* assert based on operands only, not on result */
4926fbe7bf4SStefan Eßer 	assert_div(a->u.i, b->u.i);
493fa717604SStefan Eßer 	r = make_integer(a->u.i / b->u.i);
4941393277eSGarrett Wollman 
4955b81b6b3SRodney W. Grimes 	free_value(a);
4965b81b6b3SRodney W. Grimes 	free_value(b);
49769759f08SStefan Eßer 	return (r);
4985b81b6b3SRodney W. Grimes }
4995b81b6b3SRodney W. Grimes 
5005b81b6b3SRodney W. Grimes struct val *
5017669d0fcSWarner Losh op_rem(struct val *a, struct val *b)
5025b81b6b3SRodney W. Grimes {
5035b81b6b3SRodney W. Grimes 	struct val *r;
5045b81b6b3SRodney W. Grimes 
505fa717604SStefan Eßer 	assert_to_integer(a);
506fa717604SStefan Eßer 	assert_to_integer(b);
5076fbe7bf4SStefan Eßer 	/* pass a=1 to only check for div by zero */
5086fbe7bf4SStefan Eßer 	assert_div(1, b->u.i);
5091393277eSGarrett Wollman 	r = make_integer(a->u.i % b->u.i);
5101393277eSGarrett Wollman 
5115b81b6b3SRodney W. Grimes 	free_value(a);
5125b81b6b3SRodney W. Grimes 	free_value(b);
51369759f08SStefan Eßer 	return (r);
5145b81b6b3SRodney W. Grimes }
5155b81b6b3SRodney W. Grimes 
5165b81b6b3SRodney W. Grimes struct val *
5177669d0fcSWarner Losh op_colon(struct val *a, struct val *b)
5185b81b6b3SRodney W. Grimes {
5194ba5f298SAndrew Moore 	regex_t rp;
5204cf61abaSJ.T. Conklin 	regmatch_t rm[2];
5214ba5f298SAndrew Moore 	char errbuf[256];
5224ba5f298SAndrew Moore 	int eval;
5234ba5f298SAndrew Moore 	struct val *v;
524c9fe00dcSJ.T. Conklin 
525e1854a84SCeri Davies 	/* coerce both arguments to strings */
526c9fe00dcSJ.T. Conklin 	to_string(a);
527c9fe00dcSJ.T. Conklin 	to_string(b);
5285b81b6b3SRodney W. Grimes 
5294ba5f298SAndrew Moore 	/* compile regular expression */
5304a13ab7cSJ.T. Conklin 	if ((eval = regcomp(&rp, b->u.s, 0)) != 0) {
5314ba5f298SAndrew Moore 		regerror(eval, &rp, errbuf, sizeof(errbuf));
532f07e4247SGarrett Wollman 		errx(ERR_EXIT, "%s", errbuf);
5335b81b6b3SRodney W. Grimes 	}
5344ba5f298SAndrew Moore 
5354ba5f298SAndrew Moore 	/* compare string against pattern */
5364a13ab7cSJ.T. Conklin 	/* remember that patterns are anchored to the beginning of the line */
53769759f08SStefan Eßer 	if (regexec(&rp, a->u.s, (size_t)2, rm, 0) == 0 && rm[0].rm_so == 0)
5384ba5f298SAndrew Moore 		if (rm[1].rm_so >= 0) {
53955c497bfSJ.T. Conklin 			*(a->u.s + rm[1].rm_eo) = '\0';
5404ba5f298SAndrew Moore 			v = make_str(a->u.s + rm[1].rm_so);
5414ba5f298SAndrew Moore 
54269759f08SStefan Eßer 		} else
543*8c3bbba8SEitan Adler 			v = make_integer((intmax_t)(rm[0].rm_eo));
54469759f08SStefan Eßer 	else
54569759f08SStefan Eßer 		if (rp.re_nsub == 0)
546f07e4247SGarrett Wollman 			v = make_integer((intmax_t)0);
54769759f08SStefan Eßer 		else
54855c497bfSJ.T. Conklin 			v = make_str("");
5494ba5f298SAndrew Moore 
5504ba5f298SAndrew Moore 	/* free arguments and pattern buffer */
5514ba5f298SAndrew Moore 	free_value(a);
5524ba5f298SAndrew Moore 	free_value(b);
5534ba5f298SAndrew Moore 	regfree(&rp);
5544ba5f298SAndrew Moore 
55569759f08SStefan Eßer 	return (v);
5564ba5f298SAndrew Moore }
557