Move shift expression evaluation into a separate module. More checks and
improvements for shift expressions. git-svn-id: svn://svn.cc65.org/cc65/trunk@3138 b7a2c559-68d2-44c3-8de9-860c34a00d81
This commit is contained in:
parent
31f85bc23e
commit
809d1408ce
5 changed files with 329 additions and 192 deletions
193
src/cc65/expr.c
193
src/cc65/expr.c
|
@ -29,6 +29,7 @@
|
||||||
#include "macrotab.h"
|
#include "macrotab.h"
|
||||||
#include "preproc.h"
|
#include "preproc.h"
|
||||||
#include "scanner.h"
|
#include "scanner.h"
|
||||||
|
#include "shiftexpr.h"
|
||||||
#include "stackptr.h"
|
#include "stackptr.h"
|
||||||
#include "stdfunc.h"
|
#include "stdfunc.h"
|
||||||
#include "symtab.h"
|
#include "symtab.h"
|
||||||
|
@ -2281,196 +2282,6 @@ void hie8 (ExprDesc* Expr)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static void hie7 (ExprDesc* Expr)
|
|
||||||
/* Parse the << and >> operators. */
|
|
||||||
{
|
|
||||||
ExprDesc Expr2;
|
|
||||||
CodeMark Mark1;
|
|
||||||
CodeMark Mark2;
|
|
||||||
token_t Tok; /* The operator token */
|
|
||||||
unsigned ltype, rtype, flags;
|
|
||||||
int rconst; /* Operand is a constant */
|
|
||||||
|
|
||||||
|
|
||||||
/* Evaluate the lhs */
|
|
||||||
ExprWithCheck (hie8, Expr);
|
|
||||||
|
|
||||||
while (CurTok.Tok == TOK_SHL || CurTok.Tok == TOK_SHR) {
|
|
||||||
|
|
||||||
/* All operators that call this function expect an int on the lhs */
|
|
||||||
if (!IsClassInt (Expr->Type)) {
|
|
||||||
Error ("Integer expression expected");
|
|
||||||
ED_MakeConstAbsInt (Expr, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Remember the operator token, then skip it */
|
|
||||||
Tok = CurTok.Tok;
|
|
||||||
NextToken ();
|
|
||||||
|
|
||||||
/* Get the lhs on stack */
|
|
||||||
Mark1 = GetCodePos ();
|
|
||||||
ltype = TypeOf (Expr->Type);
|
|
||||||
if (ED_IsConstAbs (Expr)) {
|
|
||||||
/* Constant value */
|
|
||||||
Mark2 = GetCodePos ();
|
|
||||||
g_push (ltype | CF_CONST, Expr->IVal);
|
|
||||||
} else {
|
|
||||||
/* Value not constant */
|
|
||||||
LoadExpr (CF_NONE, Expr);
|
|
||||||
Mark2 = GetCodePos ();
|
|
||||||
g_push (ltype, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get the right hand side */
|
|
||||||
ExprWithCheck (hie8, &Expr2);
|
|
||||||
|
|
||||||
/* Check the type of the rhs */
|
|
||||||
if (!IsClassInt (Expr2.Type)) {
|
|
||||||
Error ("Integer expression expected");
|
|
||||||
ED_MakeConstAbsInt (&Expr2, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check for a constant right side expression */
|
|
||||||
rconst = ED_IsConstAbs (&Expr2);
|
|
||||||
if (!rconst) {
|
|
||||||
|
|
||||||
/* Not constant, load into the primary */
|
|
||||||
LoadExpr (CF_NONE, &Expr2);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
/* If the right hand side is constant, we can check a lot of
|
|
||||||
* things:
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* If the shift count is zero, nothing happens */
|
|
||||||
if (Expr2.IVal == 0) {
|
|
||||||
|
|
||||||
/* Result is already in Expr, remove the generated code */
|
|
||||||
RemoveCode (Mark1);
|
|
||||||
pop (ltype);
|
|
||||||
|
|
||||||
/* Done */
|
|
||||||
goto Next;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If the left hand side is a constant, the result is constant */
|
|
||||||
if (ED_IsConstAbs (Expr)) {
|
|
||||||
|
|
||||||
/* Evaluate the result */
|
|
||||||
Expr->IVal = kcalc (Tok, Expr->IVal, Expr2.IVal);
|
|
||||||
|
|
||||||
/* Both operands are constant, remove the generated code */
|
|
||||||
RemoveCode (Mark1);
|
|
||||||
pop (ltype);
|
|
||||||
|
|
||||||
/* Done */
|
|
||||||
goto Next;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If we're shifting to the left, and the shift count is larger
|
|
||||||
* or equal than the bit count of the integer type, the result
|
|
||||||
* is zero.
|
|
||||||
*/
|
|
||||||
if (Tok == TOK_SHL && Expr2.IVal >= (long) SizeOf (Expr->Type) * 8) {
|
|
||||||
|
|
||||||
/* Set the result */
|
|
||||||
ED_MakeConstAbs (Expr, 0, Expr->Type);
|
|
||||||
|
|
||||||
/* Result is zero, remove the generated code */
|
|
||||||
RemoveCode (Mark1);
|
|
||||||
pop (ltype);
|
|
||||||
|
|
||||||
/* Done */
|
|
||||||
goto Next;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If we're shifting an integer or unsigned to the right, the
|
|
||||||
* lhs has a const address, and the shift count is larger than 8,
|
|
||||||
* we can load just the high byte as a char with the correct
|
|
||||||
* signedness, and reduce the shift count by 8. If the remaining
|
|
||||||
* shift count is zero, we're done.
|
|
||||||
*/
|
|
||||||
if (Tok == TOK_SHR &&
|
|
||||||
IsTypeInt (Expr->Type) &&
|
|
||||||
ED_IsLVal (Expr) &&
|
|
||||||
(ED_IsLocConst (Expr) || ED_IsLocStack (Expr)) &&
|
|
||||||
Expr2.IVal >= 8) {
|
|
||||||
|
|
||||||
type* OldType;
|
|
||||||
|
|
||||||
/* Increase the address by one and decrease the shift count */
|
|
||||||
++Expr->IVal;
|
|
||||||
Expr2.IVal -= 8;
|
|
||||||
|
|
||||||
/* Replace the type of the expression temporarily by the
|
|
||||||
* corresponding char type.
|
|
||||||
*/
|
|
||||||
OldType = Expr->Type;
|
|
||||||
if (IsSignUnsigned (Expr->Type)) {
|
|
||||||
Expr->Type = type_uchar;
|
|
||||||
} else {
|
|
||||||
Expr->Type = type_schar;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Remove the generated load code */
|
|
||||||
RemoveCode (Mark1);
|
|
||||||
pop (ltype);
|
|
||||||
|
|
||||||
/* Generate again code for the load */
|
|
||||||
LoadExpr (CF_NONE, Expr);
|
|
||||||
|
|
||||||
/* Reset the type */
|
|
||||||
Expr->Type = OldType;
|
|
||||||
|
|
||||||
/* If the shift count is now zero, we're done */
|
|
||||||
if (Expr2.IVal == 0) {
|
|
||||||
/* Be sure to mark the value as in the primary */
|
|
||||||
goto Loaded;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Otherwise generate code to push the value */
|
|
||||||
Mark2 = GetCodePos ();
|
|
||||||
g_push (ltype, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If the right hand side is a constant, remove the push of the
|
|
||||||
* primary register.
|
|
||||||
*/
|
|
||||||
rtype = TypeOf (Expr2.Type);
|
|
||||||
flags = 0;
|
|
||||||
if (rconst) {
|
|
||||||
flags |= CF_CONST;
|
|
||||||
rtype |= CF_CONST;
|
|
||||||
RemoveCode (Mark2);
|
|
||||||
pop (ltype);
|
|
||||||
ltype |= CF_REG; /* Value is in register */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Determine the type of the operation result. */
|
|
||||||
flags |= g_typeadjust (ltype, rtype);
|
|
||||||
|
|
||||||
/* Generate code */
|
|
||||||
switch (Tok) {
|
|
||||||
case TOK_SHL: g_asl (flags, Expr2.IVal); break;
|
|
||||||
case TOK_SHR: g_asr (flags, Expr2.IVal); break;
|
|
||||||
default: break;
|
|
||||||
}
|
|
||||||
|
|
||||||
Loaded:
|
|
||||||
/* We have a rvalue in the primary now */
|
|
||||||
ED_MakeRValExpr (Expr);
|
|
||||||
|
|
||||||
Next:
|
|
||||||
/* Get the type of the result */
|
|
||||||
Expr->Type = promoteint (Expr->Type, Expr2.Type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static void hie6 (ExprDesc* Expr)
|
static void hie6 (ExprDesc* Expr)
|
||||||
/* Handle greater-than type comparators */
|
/* Handle greater-than type comparators */
|
||||||
{
|
{
|
||||||
|
@ -2481,7 +2292,7 @@ static void hie6 (ExprDesc* Expr)
|
||||||
{ TOK_GT, GEN_NOPUSH, g_gt },
|
{ TOK_GT, GEN_NOPUSH, g_gt },
|
||||||
{ TOK_INVALID, 0, 0 }
|
{ TOK_INVALID, 0, 0 }
|
||||||
};
|
};
|
||||||
hie_compare (hie6_ops, Expr, hie7);
|
hie_compare (hie6_ops, Expr, ShiftExpr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -78,6 +78,7 @@ OBJS = anonname.o \
|
||||||
scanner.o \
|
scanner.o \
|
||||||
scanstrbuf.o \
|
scanstrbuf.o \
|
||||||
segments.o \
|
segments.o \
|
||||||
|
shiftexpr.o \
|
||||||
stackptr.o \
|
stackptr.o \
|
||||||
standard.o \
|
standard.o \
|
||||||
stdfunc.o \
|
stdfunc.o \
|
||||||
|
|
|
@ -113,6 +113,7 @@ OBJS = anonname.obj \
|
||||||
scanner.obj \
|
scanner.obj \
|
||||||
scanstrbuf.obj \
|
scanstrbuf.obj \
|
||||||
segments.obj \
|
segments.obj \
|
||||||
|
shiftexpr.obj \
|
||||||
stackptr.obj \
|
stackptr.obj \
|
||||||
standard.obj \
|
standard.obj \
|
||||||
stdfunc.obj \
|
stdfunc.obj \
|
||||||
|
|
250
src/cc65/shiftexpr.c
Normal file
250
src/cc65/shiftexpr.c
Normal file
|
@ -0,0 +1,250 @@
|
||||||
|
/*****************************************************************************/
|
||||||
|
/* */
|
||||||
|
/* shiftexpr.c */
|
||||||
|
/* */
|
||||||
|
/* Parse the << and >> operators */
|
||||||
|
/* */
|
||||||
|
/* */
|
||||||
|
/* */
|
||||||
|
/* (C) 2004 Ullrich von Bassewitz */
|
||||||
|
/* Römerstraße 52 */
|
||||||
|
/* D-70794 Filderstadt */
|
||||||
|
/* EMail: uz@cc65.org */
|
||||||
|
/* */
|
||||||
|
/* */
|
||||||
|
/* This software is provided 'as-is', without any expressed or implied */
|
||||||
|
/* warranty. In no event will the authors be held liable for any damages */
|
||||||
|
/* arising from the use of this software. */
|
||||||
|
/* */
|
||||||
|
/* Permission is granted to anyone to use this software for any purpose, */
|
||||||
|
/* including commercial applications, and to alter it and redistribute it */
|
||||||
|
/* freely, subject to the following restrictions: */
|
||||||
|
/* */
|
||||||
|
/* 1. The origin of this software must not be misrepresented; you must not */
|
||||||
|
/* claim that you wrote the original software. If you use this software */
|
||||||
|
/* in a product, an acknowledgment in the product documentation would be */
|
||||||
|
/* appreciated but is not required. */
|
||||||
|
/* 2. Altered source versions must be plainly marked as such, and must not */
|
||||||
|
/* be misrepresented as being the original software. */
|
||||||
|
/* 3. This notice may not be removed or altered from any source */
|
||||||
|
/* distribution. */
|
||||||
|
/* */
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* cc65 */
|
||||||
|
#include "asmcode.h"
|
||||||
|
#include "codegen.h"
|
||||||
|
#include "datatype.h"
|
||||||
|
#include "error.h"
|
||||||
|
#include "expr.h"
|
||||||
|
#include "exprdesc.h"
|
||||||
|
#include "loadexpr.h"
|
||||||
|
#include "scanner.h"
|
||||||
|
#include "shiftexpr.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
/* Data */
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
/* Code */
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void ShiftExpr (struct ExprDesc* Expr)
|
||||||
|
/* Parse the << and >> operators. */
|
||||||
|
{
|
||||||
|
ExprDesc Expr2;
|
||||||
|
CodeMark Mark1;
|
||||||
|
CodeMark Mark2;
|
||||||
|
token_t Tok; /* The operator token */
|
||||||
|
unsigned ExprBits; /* Bits of the lhs operand */
|
||||||
|
unsigned ltype, rtype, flags;
|
||||||
|
int rconst; /* Operand is a constant */
|
||||||
|
|
||||||
|
|
||||||
|
/* Evaluate the lhs */
|
||||||
|
ExprWithCheck (hie8, Expr);
|
||||||
|
|
||||||
|
while (CurTok.Tok == TOK_SHL || CurTok.Tok == TOK_SHR) {
|
||||||
|
|
||||||
|
/* All operators that call this function expect an int on the lhs */
|
||||||
|
if (!IsClassInt (Expr->Type)) {
|
||||||
|
Error ("Integer expression expected");
|
||||||
|
ED_MakeConstAbsInt (Expr, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Remember the operator token, then skip it */
|
||||||
|
Tok = CurTok.Tok;
|
||||||
|
NextToken ();
|
||||||
|
|
||||||
|
/* Calculate the number of bits the lhs operand has */
|
||||||
|
ExprBits = SizeOf (Expr->Type) * 8;
|
||||||
|
|
||||||
|
/* Get the lhs on stack */
|
||||||
|
Mark1 = GetCodePos ();
|
||||||
|
ltype = TypeOf (Expr->Type);
|
||||||
|
if (ED_IsConstAbs (Expr)) {
|
||||||
|
/* Constant value */
|
||||||
|
Mark2 = GetCodePos ();
|
||||||
|
g_push (ltype | CF_CONST, Expr->IVal);
|
||||||
|
} else {
|
||||||
|
/* Value not constant */
|
||||||
|
LoadExpr (CF_NONE, Expr);
|
||||||
|
Mark2 = GetCodePos ();
|
||||||
|
g_push (ltype, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get the right hand side */
|
||||||
|
ExprWithCheck (hie8, &Expr2);
|
||||||
|
|
||||||
|
/* Check the type of the rhs */
|
||||||
|
if (!IsClassInt (Expr2.Type)) {
|
||||||
|
Error ("Integer expression expected");
|
||||||
|
ED_MakeConstAbsInt (&Expr2, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check for a constant right side expression */
|
||||||
|
rconst = ED_IsConstAbs (&Expr2);
|
||||||
|
if (!rconst) {
|
||||||
|
|
||||||
|
/* Not constant, load into the primary */
|
||||||
|
LoadExpr (CF_NONE, &Expr2);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
/* The rhs is a constant numeric value */
|
||||||
|
|
||||||
|
/* If the shift count is greater or equal than the bit count of
|
||||||
|
* the operand, the behaviour is undefined according to the
|
||||||
|
* standard.
|
||||||
|
*/
|
||||||
|
if (Expr2.IVal < 0 || Expr2.IVal >= (long) ExprBits) {
|
||||||
|
|
||||||
|
Warning ("Shift count too large for operand type");
|
||||||
|
Expr2.IVal &= ExprBits - 1;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If the shift count is zero, nothing happens */
|
||||||
|
if (Expr2.IVal == 0) {
|
||||||
|
|
||||||
|
/* Result is already in Expr, remove the generated code */
|
||||||
|
RemoveCode (Mark1);
|
||||||
|
pop (ltype);
|
||||||
|
|
||||||
|
/* Done */
|
||||||
|
goto Next;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If the left hand side is a constant, the result is constant */
|
||||||
|
if (ED_IsConstAbs (Expr)) {
|
||||||
|
|
||||||
|
/* Evaluate the result */
|
||||||
|
switch (Tok) {
|
||||||
|
case TOK_SHL: Expr->IVal <<= Expr2.IVal; break;
|
||||||
|
case TOK_SHR: Expr->IVal >>= Expr2.IVal; break;
|
||||||
|
default: /* Shutup gcc */ break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Both operands are constant, remove the generated code */
|
||||||
|
RemoveCode (Mark1);
|
||||||
|
pop (ltype);
|
||||||
|
|
||||||
|
/* Done */
|
||||||
|
goto Next;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If we're shifting an integer or unsigned to the right, the
|
||||||
|
* lhs has a const address, and the shift count is larger than 8,
|
||||||
|
* we can load just the high byte as a char with the correct
|
||||||
|
* signedness, and reduce the shift count by 8. If the remaining
|
||||||
|
* shift count is zero, we're done.
|
||||||
|
*/
|
||||||
|
if (Tok == TOK_SHR &&
|
||||||
|
IsTypeInt (Expr->Type) &&
|
||||||
|
ED_IsLVal (Expr) &&
|
||||||
|
(ED_IsLocConst (Expr) || ED_IsLocStack (Expr)) &&
|
||||||
|
Expr2.IVal >= 8) {
|
||||||
|
|
||||||
|
type* OldType;
|
||||||
|
|
||||||
|
/* Increase the address by one and decrease the shift count */
|
||||||
|
++Expr->IVal;
|
||||||
|
Expr2.IVal -= 8;
|
||||||
|
|
||||||
|
/* Replace the type of the expression temporarily by the
|
||||||
|
* corresponding char type.
|
||||||
|
*/
|
||||||
|
OldType = Expr->Type;
|
||||||
|
if (IsSignUnsigned (Expr->Type)) {
|
||||||
|
Expr->Type = type_uchar;
|
||||||
|
} else {
|
||||||
|
Expr->Type = type_schar;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Remove the generated load code */
|
||||||
|
RemoveCode (Mark1);
|
||||||
|
pop (ltype);
|
||||||
|
|
||||||
|
/* Generate again code for the load */
|
||||||
|
LoadExpr (CF_NONE, Expr);
|
||||||
|
|
||||||
|
/* Reset the type */
|
||||||
|
Expr->Type = OldType;
|
||||||
|
|
||||||
|
/* If the shift count is now zero, we're done */
|
||||||
|
if (Expr2.IVal == 0) {
|
||||||
|
/* Be sure to mark the value as in the primary */
|
||||||
|
goto Loaded;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Otherwise generate code to push the value */
|
||||||
|
Mark2 = GetCodePos ();
|
||||||
|
g_push (ltype, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If the right hand side is a constant, remove the push of the
|
||||||
|
* primary register.
|
||||||
|
*/
|
||||||
|
rtype = TypeOf (Expr2.Type);
|
||||||
|
flags = 0;
|
||||||
|
if (rconst) {
|
||||||
|
flags |= CF_CONST;
|
||||||
|
rtype |= CF_CONST;
|
||||||
|
RemoveCode (Mark2);
|
||||||
|
pop (ltype);
|
||||||
|
ltype |= CF_REG; /* Value is in register */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Determine the type of the operation result. */
|
||||||
|
flags |= g_typeadjust (ltype, rtype);
|
||||||
|
|
||||||
|
/* Generate code */
|
||||||
|
switch (Tok) {
|
||||||
|
case TOK_SHL: g_asl (flags, Expr2.IVal); break;
|
||||||
|
case TOK_SHR: g_asr (flags, Expr2.IVal); break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Loaded:
|
||||||
|
/* We have a rvalue in the primary now */
|
||||||
|
ED_MakeRValExpr (Expr);
|
||||||
|
|
||||||
|
Next:
|
||||||
|
/* Get the type of the result */
|
||||||
|
Expr->Type = IntPromotion (Expr->Type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
74
src/cc65/shiftexpr.h
Normal file
74
src/cc65/shiftexpr.h
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
/*****************************************************************************/
|
||||||
|
/* */
|
||||||
|
/* shiftexpr.h */
|
||||||
|
/* */
|
||||||
|
/* Parse the << and >> operators */
|
||||||
|
/* */
|
||||||
|
/* */
|
||||||
|
/* */
|
||||||
|
/* (C) 2004 Ullrich von Bassewitz */
|
||||||
|
/* Römerstraße 52 */
|
||||||
|
/* D-70794 Filderstadt */
|
||||||
|
/* EMail: uz@cc65.org */
|
||||||
|
/* */
|
||||||
|
/* */
|
||||||
|
/* This software is provided 'as-is', without any expressed or implied */
|
||||||
|
/* warranty. In no event will the authors be held liable for any damages */
|
||||||
|
/* arising from the use of this software. */
|
||||||
|
/* */
|
||||||
|
/* Permission is granted to anyone to use this software for any purpose, */
|
||||||
|
/* including commercial applications, and to alter it and redistribute it */
|
||||||
|
/* freely, subject to the following restrictions: */
|
||||||
|
/* */
|
||||||
|
/* 1. The origin of this software must not be misrepresented; you must not */
|
||||||
|
/* claim that you wrote the original software. If you use this software */
|
||||||
|
/* in a product, an acknowledgment in the product documentation would be */
|
||||||
|
/* appreciated but is not required. */
|
||||||
|
/* 2. Altered source versions must be plainly marked as such, and must not */
|
||||||
|
/* be misrepresented as being the original software. */
|
||||||
|
/* 3. This notice may not be removed or altered from any source */
|
||||||
|
/* distribution. */
|
||||||
|
/* */
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef SHIFTEXPR_H
|
||||||
|
#define SHIFTEXPT_H
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* common */
|
||||||
|
#include "intstack.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
/* Data */
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Forward */
|
||||||
|
struct ExprDesc;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
/* Code */
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void ShiftExpr (struct ExprDesc* Expr);
|
||||||
|
/* Parse the << and >> operators. */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* End of shiftexpr.h */
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue