OpenSolaris_b135/ucbcmd/expr/expr.y

%{
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
%}
/*
 * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
/*	  All Rights Reserved	*/

%{
#pragma ident	"%Z%%M%	%I%	%E% SMI"
%}

/* Yacc productions for "expr" command: */

%{
typedef	char *yystype;
#define	YYSTYPE	yystype
%}

%token OR AND ADD SUBT MULT DIV REM EQ GT GEQ LT LEQ NEQ
%token A_STRING SUBSTR LENGTH INDEX NOARG MATCH

/* operators listed below in increasing precedence: */
%left OR
%left AND
%left EQ LT GT GEQ LEQ NEQ
%left ADD SUBT
%left MULT DIV REM
%left MCH
%left MATCH
%left SUBSTR
%left LENGTH INDEX
%%

/* a single `expression' is evaluated and printed: */

expression:	expr NOARG = {
			printf("%s\n", $1);
			exit((!strcmp($1,"0")||!strcmp($1,"\0"))? 1: 0);
			}
	;


expr:	'(' expr ')' = { $$ = $2; }
	| expr OR expr   = { $$ = conj(OR, $1, $3); }
	| expr AND expr   = { $$ = conj(AND, $1, $3); }
	| expr EQ expr   = { $$ = rel(EQ, $1, $3); }
	| expr GT expr   = { $$ = rel(GT, $1, $3); }
	| expr GEQ expr   = { $$ = rel(GEQ, $1, $3); }
	| expr LT expr   = { $$ = rel(LT, $1, $3); }
	| expr LEQ expr   = { $$ = rel(LEQ, $1, $3); }
	| expr NEQ expr   = { $$ = rel(NEQ, $1, $3); }
	| expr ADD expr   = { $$ = arith(ADD, $1, $3); }
	| expr SUBT expr   = { $$ = arith(SUBT, $1, $3); }
	| expr MULT expr   = { $$ = arith(MULT, $1, $3); }
	| expr DIV expr   = { $$ = arith(DIV, $1, $3); }
	| expr REM expr   = { $$ = arith(REM, $1, $3); }
	| expr MCH expr	 = { $$ = match($1, $3); }
	| MATCH expr expr = { $$ = match($2, $3); }
	| SUBSTR expr expr expr = { $$ = substr($2, $3, $4); }
	| LENGTH expr       = { $$ = length($2); }
	| INDEX expr expr = { $$ = index($2, $3); }
	| A_STRING
	;
%%

#define ESIZE	256
#define EQL(x,y) !strcmp(x,y)

#define INIT	char *sp = instring;
#define GETC()		(*sp++)
#define PEEKC()		(*sp)
#define UNGETC(c)	(--sp)
#define RETURN(c)	return(c)
#define ERROR(c)	errxx(c)
#include  <regexp.h>
#include  <malloc.h>
#include  <stdlib.h>

char	**Av;
int	Ac;
int	Argi;

char *ltoa(long l);

char Mstring[1][128];


char *operator[] = {
	"|", "&", "+", "-", "*", "/", "%", ":",
	"=", "==", "<", "<=", ">", ">=", "!=",
	"match", "substr", "length", "index", "\0" };
int op[] = {
	OR, AND, ADD,  SUBT, MULT, DIV, REM, MCH,
	EQ, EQ, LT, LEQ, GT, GEQ, NEQ,
	MATCH, SUBSTR, LENGTH, INDEX };
int
yylex(void)
{
	char *p;
	int i;

	if(Argi >= Ac) return NOARG;

	p = Av[Argi++];

	if((*p == '(' || *p == ')') && p[1] == '\0' )
		return (int)*p;
	for(i = 0; *operator[i]; ++i)
		if(EQL(operator[i], p))
			return op[i];

	yylval = p;
	return A_STRING;
}

char *
rel(int oper, char *r1, char *r2)
{
	long i;

	if(ematch(r1, "-\\{0,1\\}[0-9]*$") && ematch(r2, "-\\{0,1\\}[0-9]*$"))
		i = atol(r1) - atol(r2);
	else
		i = strcmp(r1, r2);
	switch(oper) {
	case EQ:
		i = i==0;
		break;
	case GT:
		i = i>0;
		break;
	case GEQ:
		i = i>=0;
		break;
	case LT:
		i = i<0;
		break;
	case LEQ:
		i = i<=0;
		break;
	case NEQ:
		i = i!=0;
		break;
	}
	return i? "1": "0";
}

char *arith(oper, r1, r2) char *r1, *r2;
{
	long i1, i2;
	char *rv;

	if(!(ematch(r1, "-\\{0,1\\}[0-9]*$") && ematch(r2, "-\\{0,1\\}[0-9]*$")))
		yyerror("non-numeric argument");
	i1 = atol(r1);
	i2 = atol(r2);

	switch(oper) {
	case ADD:
		i1 = i1 + i2;
		break;
	case SUBT:
		i1 = i1 - i2;
		break;
	case MULT:
		i1 = i1 * i2;
		break;
	case DIV:
		if (i2 == 0)
			yyerror("division by zero");
		i1 = i1 / i2;
		break;
	case REM:
		if (i2 == 0)
			yyerror("division by zero");
		i1 = i1 % i2;
		break;
	}
	rv = malloc(16);
	(void) strcpy(rv, ltoa(i1));
	return rv;
}
char *conj(oper, r1, r2) char *r1, *r2;
{
	char *rv;

	switch(oper) {

	case OR:
		if(EQL(r1, "0")
		    || EQL(r1, ""))
			if(EQL(r2, "0")
			    || EQL(r2, ""))
				rv = "0";
			else
				rv = r2;
		else
			rv = r1;
		break;
	case AND:
		if(EQL(r1, "0")
		    || EQL(r1, ""))
			rv = "0";
		else if(EQL(r2, "0")
		    || EQL(r2, ""))
			rv = "0";
		else
			rv = r1;
		break;
	}
	return rv;
}

char *
substr(char *v, char *s, char *w)
{
	int si, wi;
	char *res;

	si = atol(s);
	wi = atol(w);
	while(--si) if(*v) ++v;

	res = v;

	while(wi--) if(*v) ++v;

	*v = '\0';
	return res;
}

char *
index(char *s, char *t)
{
	long i, j;
	char *rv;

	for(i = 0; s[i] ; ++i)
		for(j = 0; t[j] ; ++j)
			if(s[i]==t[j]) {
				(void) strcpy(rv = malloc(8), ltoa(++i));
				return rv;
			}
	return "0";
}

char *
length(char *s)
{
	long i = 0;
	char *rv;

	while(*s++) ++i;

	rv = malloc(8);
	(void) strcpy(rv, ltoa(i));
	return rv;
}

char *
match(char *s, char *p)
{
	char *rv;

	(void) strcpy(rv=malloc(8), ltoa((long)ematch(s, p)));
	if(nbra) {
		rv = malloc((unsigned) strlen(Mstring[0]) + 1);
		(void) strcpy(rv, Mstring[0]);
	}
	return rv;
}

int
ematch(char *s, char *p)
{
	static char expbuf[ESIZE];
	char *compile();
	int num;
	extern char *braslist[], *braelist[], *loc2;

	compile(p, expbuf, &expbuf[ESIZE], 0);
	if(nbra > 1)
		yyerror("Too many '\\('s");
	if(advance(s, expbuf)) {
		if(nbra == 1) {
			p = braslist[0];
			num = braelist[0] - p;
			if ((num > 127) || (num < 0)) yyerror("Paren problem");
			(void) strncpy(Mstring[0], p, num);
			Mstring[0][num] = '\0';
		}
		return(loc2-s);
	}
	return(0);
}

int
errxx(int err)
{
	char *message;

	switch(err) {
		case 11:
			message = "Range endpoint too large";
			break;
		case 16:
			message = "Bad number";
			break;
		case 25:
			message = "``\\digit'' out of range";
			break;
		case 36:
			message = "Illegal or missing delimiter";
			break;
		case 41:
			message = "No remembered search string";
			break;
		case 42:
			message = "\\( \\) imbalance";
			break;
		case 43:
			message = "Too many \\(";
			break;
		case 44:
			message = "More than 2 numbers given in \\{ \\}";
			break;
		case 45:
			message = "} expected after \\";
			break;
		case 46:
			message = "First number exceeds second in \\{ \\}";
			break;
		case 49:
			message = "[ ] imbalance";
			break;
		case 50:
			message = "Regular expression too long";
			break;
		default:
			message = "Unknown regexp error code!!";
			break;
	}
	yyerror(message);
	/* NOTREACHED */
	return (0);
}

int
yyerror(char *s)
{
	(void) write(2, "expr: ", 6);
	(void) write(2, s, (unsigned) strlen(s));
	(void) write(2, "\n", 1);
	exit(2);
	/* NOTREACHED */
	return (0);
}

char *
ltoa(long l)
{
	static char str[20];
	char *sp;
	int i;
	int neg;

	if(l == 0x80000000L)
		return "-2147483648";
	neg = 0;
	if(l < 0)
		++neg, l = -l;
	sp = &str[20];
	*--sp = '\0';
	do {
		i = l % 10;
		*--sp = '0' + i;
		l /= 10;
	} 
	while(l);
	if(neg)
		*--sp = '-';
	return sp;
}

int
main(int argc, char **argv)
{
	Ac = argc;
	Argi = 1;
	Av = argv;
	yyparse();
	return (0);
}