implement unsigned arithmetics as an mksh extension
This commit is contained in:
parent
06626b0de2
commit
858d8e8b5a
41
check.t
41
check.t
@ -1,4 +1,4 @@
|
|||||||
# $MirOS: src/bin/mksh/check.t,v 1.252 2008/12/13 17:02:11 tg Exp $
|
# $MirOS: src/bin/mksh/check.t,v 1.253 2008/12/17 19:39:21 tg Exp $
|
||||||
# $OpenBSD: bksl-nl.t,v 1.2 2001/01/28 23:04:56 niklas Exp $
|
# $OpenBSD: bksl-nl.t,v 1.2 2001/01/28 23:04:56 niklas Exp $
|
||||||
# $OpenBSD: history.t,v 1.5 2001/01/28 23:04:56 niklas Exp $
|
# $OpenBSD: history.t,v 1.5 2001/01/28 23:04:56 niklas Exp $
|
||||||
# $OpenBSD: read.t,v 1.3 2003/03/10 03:48:16 david Exp $
|
# $OpenBSD: read.t,v 1.3 2003/03/10 03:48:16 david Exp $
|
||||||
@ -7,7 +7,7 @@
|
|||||||
# http://www.research.att.com/~gsf/public/ifs.sh
|
# http://www.research.att.com/~gsf/public/ifs.sh
|
||||||
|
|
||||||
expected-stdout:
|
expected-stdout:
|
||||||
@(#)MIRBSD KSH R36 2008/12/13
|
@(#)MIRBSD KSH R36 2008/12/17
|
||||||
description:
|
description:
|
||||||
Check version of shell.
|
Check version of shell.
|
||||||
stdin:
|
stdin:
|
||||||
@ -240,6 +240,43 @@ expected-stdout:
|
|||||||
6
|
6
|
||||||
6,5,3
|
6,5,3
|
||||||
---
|
---
|
||||||
|
name: arith-unsigned-1
|
||||||
|
description:
|
||||||
|
Check if unsigned arithmetics work
|
||||||
|
stdin:
|
||||||
|
# signed vs unsigned
|
||||||
|
print x1 $((-1)) $((#-1))
|
||||||
|
# calculating
|
||||||
|
typeset -i vs
|
||||||
|
typeset -Ui vu
|
||||||
|
vs=4123456789; vu=4123456789
|
||||||
|
print x2 $vs $vu
|
||||||
|
(( vs %= 2147483647 ))
|
||||||
|
(( vu %= 2147483647 ))
|
||||||
|
print x3 $vs $vu
|
||||||
|
vs=4123456789; vu=4123456789
|
||||||
|
(( # vs %= 2147483647 ))
|
||||||
|
(( # vu %= 2147483647 ))
|
||||||
|
print x4 $vs $vu
|
||||||
|
# make sure the calculation does not change unsigned flag
|
||||||
|
vs=4123456789; vu=4123456789
|
||||||
|
print x5 $vs $vu
|
||||||
|
# short form
|
||||||
|
print x6 $((# vs % 2147483647)) $((# vu % 2147483647))
|
||||||
|
# array refs
|
||||||
|
set -A va
|
||||||
|
va[1975973142]=right
|
||||||
|
va[4123456789]=wrong
|
||||||
|
print x7 ${va[#4123456789%2147483647]}
|
||||||
|
expected-stdout:
|
||||||
|
x1 -1 4294967295
|
||||||
|
x2 -171510507 4123456789
|
||||||
|
x3 -171510507 4123456789
|
||||||
|
x4 1975973142 1975973142
|
||||||
|
x5 -171510507 4123456789
|
||||||
|
x6 1975973142 1975973142
|
||||||
|
x7 right
|
||||||
|
---
|
||||||
name: bksl-nl-ign-1
|
name: bksl-nl-ign-1
|
||||||
description:
|
description:
|
||||||
Check that \newline is not collasped after #
|
Check that \newline is not collasped after #
|
||||||
|
109
expr.c
109
expr.c
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
#include "sh.h"
|
#include "sh.h"
|
||||||
|
|
||||||
__RCSID("$MirOS: src/bin/mksh/expr.c,v 1.21 2008/12/13 17:02:13 tg Exp $");
|
__RCSID("$MirOS: src/bin/mksh/expr.c,v 1.22 2008/12/17 19:39:21 tg Exp $");
|
||||||
|
|
||||||
/* The order of these enums is constrained by the order of opinfo[] */
|
/* The order of these enums is constrained by the order of opinfo[] */
|
||||||
enum token {
|
enum token {
|
||||||
@ -114,8 +114,26 @@ struct expr_state {
|
|||||||
struct tbl *evaling; /* variable that is being recursively
|
struct tbl *evaling; /* variable that is being recursively
|
||||||
* expanded (EXPRINEVAL flag set) */
|
* expanded (EXPRINEVAL flag set) */
|
||||||
bool arith; /* evaluating an $(()) expression? */
|
bool arith; /* evaluating an $(()) expression? */
|
||||||
|
bool natural; /* unsigned arithmetic calculation */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define bivui(x, op, y) (es->natural ? \
|
||||||
|
(long)((x)->val.u op (y)->val.u) : \
|
||||||
|
(long)((x)->val.i op (y)->val.i) \
|
||||||
|
)
|
||||||
|
#define chvui(x, op) do { \
|
||||||
|
if (es->natural) \
|
||||||
|
(x)->val.u = op (x)->val.u; \
|
||||||
|
else \
|
||||||
|
(x)->val.i = op (x)->val.i; \
|
||||||
|
} while (/* CONSTCOND */ 0)
|
||||||
|
#define stvui(x, n) do { \
|
||||||
|
if (es->natural) \
|
||||||
|
(x)->val.u = (n); \
|
||||||
|
else \
|
||||||
|
(x)->val.i = (n); \
|
||||||
|
} while (/* CONSTCOND */ 0)
|
||||||
|
|
||||||
enum error_type {
|
enum error_type {
|
||||||
ET_UNEXPECTED, ET_BADLIT, ET_RECURSIVE,
|
ET_UNEXPECTED, ET_BADLIT, ET_RECURSIVE,
|
||||||
ET_LVALUE, ET_RDONLY, ET_STR
|
ET_LVALUE, ET_RDONLY, ET_STR
|
||||||
@ -163,6 +181,7 @@ v_evaluate(struct tbl *vp, const char *expr, volatile int error_ok,
|
|||||||
curstate.noassign = 0;
|
curstate.noassign = 0;
|
||||||
curstate.arith = arith;
|
curstate.arith = arith;
|
||||||
curstate.evaling = NULL;
|
curstate.evaling = NULL;
|
||||||
|
curstate.natural = false;
|
||||||
|
|
||||||
newenv(E_ERRH);
|
newenv(E_ERRH);
|
||||||
i = sigsetjmp(e->jbuf, 0);
|
i = sigsetjmp(e->jbuf, 0);
|
||||||
@ -190,6 +209,8 @@ v_evaluate(struct tbl *vp, const char *expr, volatile int error_ok,
|
|||||||
if (es->tok != END)
|
if (es->tok != END)
|
||||||
evalerr(es, ET_UNEXPECTED, NULL);
|
evalerr(es, ET_UNEXPECTED, NULL);
|
||||||
|
|
||||||
|
if (es->arith && es->natural)
|
||||||
|
vp->flag |= INT_U;
|
||||||
if (vp->flag & INTEGER)
|
if (vp->flag & INTEGER)
|
||||||
setint_v(vp, v, es->arith);
|
setint_v(vp, v, es->arith);
|
||||||
else
|
else
|
||||||
@ -272,11 +293,11 @@ evalexpr(Expr_state *es, int prec)
|
|||||||
exprtoken(es);
|
exprtoken(es);
|
||||||
vl = intvar(es, evalexpr(es, P_PRIMARY));
|
vl = intvar(es, evalexpr(es, P_PRIMARY));
|
||||||
if (op == O_BNOT)
|
if (op == O_BNOT)
|
||||||
vl->val.i = ~vl->val.i;
|
chvui(vl, ~);
|
||||||
else if (op == O_LNOT)
|
else if (op == O_LNOT)
|
||||||
vl->val.i = !vl->val.i;
|
chvui(vl, !);
|
||||||
else if (op == O_MINUS)
|
else if (op == O_MINUS)
|
||||||
vl->val.i = -vl->val.i;
|
chvui(vl, -);
|
||||||
/* op == O_PLUS is a no-op */
|
/* op == O_PLUS is a no-op */
|
||||||
} else if (op == OPEN_PAREN) {
|
} else if (op == OPEN_PAREN) {
|
||||||
exprtoken(es);
|
exprtoken(es);
|
||||||
@ -302,7 +323,7 @@ evalexpr(Expr_state *es, int prec)
|
|||||||
return (vl);
|
return (vl);
|
||||||
}
|
}
|
||||||
vl = evalexpr(es, prec - 1);
|
vl = evalexpr(es, prec - 1);
|
||||||
for (op = es->tok; IS_BINOP(op) && opinfo[(int) op].prec == prec;
|
for (op = es->tok; IS_BINOP(op) && opinfo[(int)op].prec == prec;
|
||||||
op = es->tok) {
|
op = es->tok) {
|
||||||
exprtoken(es);
|
exprtoken(es);
|
||||||
vasn = vl;
|
vasn = vl;
|
||||||
@ -320,70 +341,70 @@ evalexpr(Expr_state *es, int prec)
|
|||||||
else
|
else
|
||||||
evalerr(es, ET_STR, "zero divisor");
|
evalerr(es, ET_STR, "zero divisor");
|
||||||
}
|
}
|
||||||
switch ((int) op) {
|
switch ((int)op) {
|
||||||
case O_TIMES:
|
case O_TIMES:
|
||||||
case O_TIMESASN:
|
case O_TIMESASN:
|
||||||
res = vl->val.i * vr->val.i;
|
res = bivui(vl, *, vr);
|
||||||
break;
|
break;
|
||||||
case O_DIV:
|
case O_DIV:
|
||||||
case O_DIVASN:
|
case O_DIVASN:
|
||||||
res = vl->val.i / vr->val.i;
|
res = bivui(vl, /, vr);
|
||||||
break;
|
break;
|
||||||
case O_MOD:
|
case O_MOD:
|
||||||
case O_MODASN:
|
case O_MODASN:
|
||||||
res = vl->val.i % vr->val.i;
|
res = bivui(vl, %, vr);
|
||||||
break;
|
break;
|
||||||
case O_PLUS:
|
case O_PLUS:
|
||||||
case O_PLUSASN:
|
case O_PLUSASN:
|
||||||
res = vl->val.i + vr->val.i;
|
res = bivui(vl, +, vr);
|
||||||
break;
|
break;
|
||||||
case O_MINUS:
|
case O_MINUS:
|
||||||
case O_MINUSASN:
|
case O_MINUSASN:
|
||||||
res = vl->val.i - vr->val.i;
|
res = bivui(vl, -, vr);
|
||||||
break;
|
break;
|
||||||
case O_LSHIFT:
|
case O_LSHIFT:
|
||||||
case O_LSHIFTASN:
|
case O_LSHIFTASN:
|
||||||
res = vl->val.i << vr->val.i;
|
res = bivui(vl, <<, vr);
|
||||||
break;
|
break;
|
||||||
case O_RSHIFT:
|
case O_RSHIFT:
|
||||||
case O_RSHIFTASN:
|
case O_RSHIFTASN:
|
||||||
res = vl->val.i >> vr->val.i;
|
res = bivui(vl, >>, vr);
|
||||||
break;
|
break;
|
||||||
case O_LT:
|
case O_LT:
|
||||||
res = vl->val.i < vr->val.i;
|
res = bivui(vl, <, vr);
|
||||||
break;
|
break;
|
||||||
case O_LE:
|
case O_LE:
|
||||||
res = vl->val.i <= vr->val.i;
|
res = bivui(vl, <=, vr);
|
||||||
break;
|
break;
|
||||||
case O_GT:
|
case O_GT:
|
||||||
res = vl->val.i > vr->val.i;
|
res = bivui(vl, >, vr);
|
||||||
break;
|
break;
|
||||||
case O_GE:
|
case O_GE:
|
||||||
res = vl->val.i >= vr->val.i;
|
res = bivui(vl, >=, vr);
|
||||||
break;
|
break;
|
||||||
case O_EQ:
|
case O_EQ:
|
||||||
res = vl->val.i == vr->val.i;
|
res = bivui(vl, ==, vr);
|
||||||
break;
|
break;
|
||||||
case O_NE:
|
case O_NE:
|
||||||
res = vl->val.i != vr->val.i;
|
res = bivui(vl, !=, vr);
|
||||||
break;
|
break;
|
||||||
case O_BAND:
|
case O_BAND:
|
||||||
case O_BANDASN:
|
case O_BANDASN:
|
||||||
res = vl->val.i & vr->val.i;
|
res = bivui(vl, &, vr);
|
||||||
break;
|
break;
|
||||||
case O_BXOR:
|
case O_BXOR:
|
||||||
case O_BXORASN:
|
case O_BXORASN:
|
||||||
res = vl->val.i ^ vr->val.i;
|
res = bivui(vl, ^, vr);
|
||||||
break;
|
break;
|
||||||
case O_BOR:
|
case O_BOR:
|
||||||
case O_BORASN:
|
case O_BORASN:
|
||||||
res = vl->val.i | vr->val.i;
|
res = bivui(vl, |, vr);
|
||||||
break;
|
break;
|
||||||
case O_LAND:
|
case O_LAND:
|
||||||
if (!vl->val.i)
|
if (!vl->val.i)
|
||||||
es->noassign++;
|
es->noassign++;
|
||||||
vr = intvar(es, evalexpr(es, prec - 1));
|
vr = intvar(es, evalexpr(es, prec - 1));
|
||||||
res = vl->val.i && vr->val.i;
|
res = bivui(vl, &&, vr);
|
||||||
if (!vl->val.i)
|
if (!vl->val.i)
|
||||||
es->noassign--;
|
es->noassign--;
|
||||||
break;
|
break;
|
||||||
@ -391,13 +412,13 @@ evalexpr(Expr_state *es, int prec)
|
|||||||
if (vl->val.i)
|
if (vl->val.i)
|
||||||
es->noassign++;
|
es->noassign++;
|
||||||
vr = intvar(es, evalexpr(es, prec - 1));
|
vr = intvar(es, evalexpr(es, prec - 1));
|
||||||
res = vl->val.i || vr->val.i;
|
res = bivui(vl, ||, vr);
|
||||||
if (vl->val.i)
|
if (vl->val.i)
|
||||||
es->noassign--;
|
es->noassign--;
|
||||||
break;
|
break;
|
||||||
case O_TERN:
|
case O_TERN:
|
||||||
{
|
{
|
||||||
int ev = vl->val.i != 0;
|
bool ev = vl->val.i != 0;
|
||||||
|
|
||||||
if (!ev)
|
if (!ev)
|
||||||
es->noassign++;
|
es->noassign++;
|
||||||
@ -423,14 +444,14 @@ evalexpr(Expr_state *es, int prec)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (IS_ASSIGNOP(op)) {
|
if (IS_ASSIGNOP(op)) {
|
||||||
vr->val.i = res;
|
stvui(vr, res);
|
||||||
if (vasn->flag & INTEGER)
|
if (vasn->flag & INTEGER)
|
||||||
setint_v(vasn, vr, es->arith);
|
setint_v(vasn, vr, es->arith);
|
||||||
else
|
else
|
||||||
setint(vasn, res);
|
setint(vasn, res);
|
||||||
vl = vr;
|
vl = vr;
|
||||||
} else if (op != O_TERN)
|
} else if (op != O_TERN)
|
||||||
vl->val.i = res;
|
stvui(vl, res);
|
||||||
}
|
}
|
||||||
return (vl);
|
return (vl);
|
||||||
}
|
}
|
||||||
@ -438,13 +459,20 @@ evalexpr(Expr_state *es, int prec)
|
|||||||
static void
|
static void
|
||||||
exprtoken(Expr_state *es)
|
exprtoken(Expr_state *es)
|
||||||
{
|
{
|
||||||
const char *cp;
|
const char *cp = es->tokp;
|
||||||
int c;
|
int c;
|
||||||
char *tvar;
|
char *tvar;
|
||||||
|
|
||||||
/* skip white space */
|
/* skip white space */
|
||||||
for (cp = es->tokp; (c = *cp), ksh_isspace(c); cp++)
|
skip_spaces:
|
||||||
;
|
while ((c = *cp), ksh_isspace(c))
|
||||||
|
++cp;
|
||||||
|
if (es->tokp == es->expression && c == '#') {
|
||||||
|
/* expression begins with # */
|
||||||
|
es->natural = true; /* switch to unsigned */
|
||||||
|
++cp;
|
||||||
|
goto skip_spaces;
|
||||||
|
}
|
||||||
es->tokp = cp;
|
es->tokp = cp;
|
||||||
|
|
||||||
if (c == '\0')
|
if (c == '\0')
|
||||||
@ -499,7 +527,7 @@ exprtoken(Expr_state *es)
|
|||||||
for (i = 0; (n0 = opinfo[i].name[0]); i++)
|
for (i = 0; (n0 = opinfo[i].name[0]); i++)
|
||||||
if (c == n0 &&
|
if (c == n0 &&
|
||||||
strncmp(cp, opinfo[i].name, opinfo[i].len) == 0) {
|
strncmp(cp, opinfo[i].name, opinfo[i].len) == 0) {
|
||||||
es->tok = (enum token) i;
|
es->tok = (enum token)i;
|
||||||
cp += opinfo[i].len;
|
cp += opinfo[i].len;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -514,12 +542,23 @@ static struct tbl *
|
|||||||
do_ppmm(Expr_state *es, enum token op, struct tbl *vasn, bool is_prefix)
|
do_ppmm(Expr_state *es, enum token op, struct tbl *vasn, bool is_prefix)
|
||||||
{
|
{
|
||||||
struct tbl *vl;
|
struct tbl *vl;
|
||||||
int oval;
|
long oval;
|
||||||
|
|
||||||
assign_check(es, op, vasn);
|
assign_check(es, op, vasn);
|
||||||
|
|
||||||
vl = intvar(es, vasn);
|
vl = intvar(es, vasn);
|
||||||
oval = op == O_PLUSPLUS ? vl->val.i++ : vl->val.i--;
|
oval = vl->val.i;
|
||||||
|
if (op == O_PLUSPLUS) {
|
||||||
|
if (es->natural)
|
||||||
|
++vl->val.u;
|
||||||
|
else
|
||||||
|
++vl->val.i;
|
||||||
|
} else {
|
||||||
|
if (es->natural)
|
||||||
|
--vl->val.u;
|
||||||
|
else
|
||||||
|
--vl->val.i;
|
||||||
|
}
|
||||||
if (vasn->flag & INTEGER)
|
if (vasn->flag & INTEGER)
|
||||||
setint_v(vasn, vl, es->arith);
|
setint_v(vasn, vl, es->arith);
|
||||||
else
|
else
|
||||||
@ -534,9 +573,9 @@ static void
|
|||||||
assign_check(Expr_state *es, enum token op, struct tbl *vasn)
|
assign_check(Expr_state *es, enum token op, struct tbl *vasn)
|
||||||
{
|
{
|
||||||
if (vasn->name[0] == '\0' && !(vasn->flag & EXPRLVALUE))
|
if (vasn->name[0] == '\0' && !(vasn->flag & EXPRLVALUE))
|
||||||
evalerr(es, ET_LVALUE, opinfo[(int) op].name);
|
evalerr(es, ET_LVALUE, opinfo[(int)op].name);
|
||||||
else if (vasn->flag & RDONLY)
|
else if (vasn->flag & RDONLY)
|
||||||
evalerr(es, ET_RDONLY, opinfo[(int) op].name);
|
evalerr(es, ET_RDONLY, opinfo[(int)op].name);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct tbl *
|
static struct tbl *
|
||||||
|
9
mksh.1
9
mksh.1
@ -1,4 +1,4 @@
|
|||||||
.\" $MirOS: src/bin/mksh/mksh.1,v 1.147 2008/12/17 19:32:47 tg Exp $
|
.\" $MirOS: src/bin/mksh/mksh.1,v 1.148 2008/12/17 19:39:22 tg Exp $
|
||||||
.\" $OpenBSD: ksh.1,v 1.122 2008/05/17 23:31:52 sobrado Exp $
|
.\" $OpenBSD: ksh.1,v 1.122 2008/05/17 23:31:52 sobrado Exp $
|
||||||
.\"-
|
.\"-
|
||||||
.\" Try to make GNU groff and AT&T nroff more compatible
|
.\" Try to make GNU groff and AT&T nroff more compatible
|
||||||
@ -2096,6 +2096,13 @@ as numeric arguments to the
|
|||||||
.Ic test
|
.Ic test
|
||||||
command, and as the value of an assignment to an integer parameter.
|
command, and as the value of an assignment to an integer parameter.
|
||||||
.Pp
|
.Pp
|
||||||
|
Expressions are calculated using signed arithmetic and the
|
||||||
|
.Vt long
|
||||||
|
host type, unless they begin with a sole
|
||||||
|
.Sq #
|
||||||
|
character, in which case they use
|
||||||
|
.Vt unsigned long .
|
||||||
|
.Pp
|
||||||
Expressions may contain alpha-numeric parameter identifiers, array references,
|
Expressions may contain alpha-numeric parameter identifiers, array references,
|
||||||
and integer constants and may be combined with the following C operators
|
and integer constants and may be combined with the following C operators
|
||||||
(listed and grouped in increasing order of precedence):
|
(listed and grouped in increasing order of precedence):
|
||||||
|
5
sh.h
5
sh.h
@ -103,9 +103,9 @@
|
|||||||
#define __SCCSID(x) __IDSTRING(sccsid,x)
|
#define __SCCSID(x) __IDSTRING(sccsid,x)
|
||||||
|
|
||||||
#ifdef EXTERN
|
#ifdef EXTERN
|
||||||
__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.267 2008/12/13 18:32:27 tg Exp $");
|
__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.268 2008/12/17 19:39:23 tg Exp $");
|
||||||
#endif
|
#endif
|
||||||
#define MKSH_VERSION "R36 2008/12/13"
|
#define MKSH_VERSION "R36 2008/12/17"
|
||||||
|
|
||||||
#ifndef MKSH_INCLUDES_ONLY
|
#ifndef MKSH_INCLUDES_ONLY
|
||||||
|
|
||||||
@ -775,6 +775,7 @@ struct tbl { /* table item */
|
|||||||
union {
|
union {
|
||||||
char *s; /* string */
|
char *s; /* string */
|
||||||
long i; /* integer */
|
long i; /* integer */
|
||||||
|
unsigned long u; /* unsigned integer */
|
||||||
int (*f)(const char **);/* int function */
|
int (*f)(const char **);/* int function */
|
||||||
struct op *t; /* "function" tree */
|
struct op *t; /* "function" tree */
|
||||||
} val; /* value */
|
} val; /* value */
|
||||||
|
Loading…
x
Reference in New Issue
Block a user