- properly sign-/zero-extend returned values, e.g. the following
function must return 255 and not -1:
unsigned char f(void) { return -1; }
- properly decay arrays on the left side of ->, e.g. the following must
compile:
{
struct {char c;} s[1];
s[0].c = 'a'; putchar(s[0].c);
(s+0)->c = 'b'; putchar(s[0].c);
(*s).c = 'c'; putchar(s[0].c);
s->c = 'd'; putchar(s[0].c);
putchar('\n');
}
Also:
- support structure passing/returning by value (x86 only)
8443 lines
219 KiB
C
8443 lines
219 KiB
C
/*
|
|
Copyright (c) 2012-2015, Alexey Frunze
|
|
All rights reserved.
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions are met:
|
|
|
|
1. Redistributions of source code must retain the above copyright notice, this
|
|
list of conditions and the following disclaimer.
|
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
this list of conditions and the following disclaimer in the documentation
|
|
and/or other materials provided with the distribution.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
|
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
The views and conclusions contained in the software and documentation are those
|
|
of the authors and should not be interpreted as representing official policies,
|
|
either expressed or implied, of the FreeBSD Project.
|
|
*/
|
|
|
|
/*****************************************************************************/
|
|
/* */
|
|
/* Smaller C */
|
|
/* */
|
|
/* A simple and small single-pass C compiler */
|
|
/* */
|
|
/* Produces 16/32-bit 80386 assembly output for NASM. */
|
|
/* Produces 32-bit MIPS assembly output for gcc/as. */
|
|
/* Produces 32-bit TR3200 assembly output for vasm. */
|
|
/* */
|
|
/* Main file */
|
|
/* */
|
|
/*****************************************************************************/
|
|
|
|
// Making most functions static helps with code optimization,
|
|
// use that to further reduce compiler's code size on RetroBSD.
|
|
#ifndef STATIC
|
|
#define STATIC
|
|
#else
|
|
#undef STATIC
|
|
#define STATIC static
|
|
#endif
|
|
|
|
#ifdef NO_EXTRAS
|
|
#define NO_PPACK
|
|
#define NO_TYPEDEF_ENUM
|
|
#define NO_FUNC_
|
|
#define NO_EXTRA_WARNS
|
|
#define NO_FOR_DECL
|
|
#define NO_STRUCT_BY_VAL
|
|
#endif
|
|
|
|
// Passing and returning structures by value is currenly supported
|
|
// on x86 only
|
|
#ifdef MIPS
|
|
#ifndef NO_STRUCT_BY_VAL
|
|
#define NO_STRUCT_BY_VAL
|
|
#endif
|
|
#endif
|
|
#ifdef TR3200
|
|
#ifndef NO_STRUCT_BY_VAL
|
|
#define NO_STRUCT_BY_VAL
|
|
#endif
|
|
#endif
|
|
|
|
#ifndef __SMALLER_C__
|
|
|
|
#include <limits.h>
|
|
#include <stdarg.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <stdio.h>
|
|
|
|
#if UINT_MAX >= 0xFFFFFFFF
|
|
#define CAN_COMPILE_32BIT
|
|
#endif
|
|
|
|
#else // #ifndef __SMALLER_C__
|
|
|
|
#define NULL 0
|
|
#define size_t unsigned int
|
|
|
|
#define CHAR_BIT (8)
|
|
|
|
#ifdef __SMALLER_C_SCHAR__
|
|
#define CHAR_MIN (-128)
|
|
#define CHAR_MAX (127)
|
|
#endif
|
|
#ifdef __SMALLER_C_UCHAR__
|
|
#define CHAR_MIN (0)
|
|
#define CHAR_MAX (255)
|
|
#endif
|
|
|
|
#ifndef __SMALLER_C_SCHAR__
|
|
#ifndef __SMALLER_C_UCHAR__
|
|
#error __SMALLER_C_SCHAR__ or __SMALLER_C_UCHAR__ must be defined
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef __SMALLER_C_16__
|
|
#define INT_MAX (32767)
|
|
#define INT_MIN (-32767-1)
|
|
#define UINT_MAX (65535u)
|
|
#define UINT_MIN (0u)
|
|
#endif
|
|
#ifdef __SMALLER_C_32__
|
|
#define INT_MAX (2147483647)
|
|
#define INT_MIN (-2147483647-1)
|
|
#define UINT_MAX (4294967295u)
|
|
#define UINT_MIN (0u)
|
|
#define CAN_COMPILE_32BIT
|
|
#endif
|
|
|
|
#ifndef __SMALLER_C_16__
|
|
#ifndef __SMALLER_C_32__
|
|
#error __SMALLER_C_16__ or __SMALLER_C_32__ must be defined
|
|
#endif
|
|
#endif
|
|
|
|
void exit(int);
|
|
int atoi(char*);
|
|
|
|
size_t strlen(char*);
|
|
char* strcpy(char*, char*);
|
|
char* strchr(char*, int);
|
|
int strcmp(char*, char*);
|
|
int strncmp(char*, char*, size_t);
|
|
void* memmove(void*, void*, size_t);
|
|
void* memcpy(void*, void*, size_t);
|
|
void* memset(void*, int, size_t);
|
|
|
|
int isspace(int);
|
|
int isdigit(int);
|
|
int isalpha(int);
|
|
int isalnum(int);
|
|
|
|
#define FILE void
|
|
#define EOF (-1)
|
|
FILE* fopen(char*, char*);
|
|
int fclose(FILE*);
|
|
int putchar(int);
|
|
int fputc(int, FILE*);
|
|
int fgetc(FILE*);
|
|
int puts(char*);
|
|
int fputs(char*, FILE*);
|
|
int sprintf(char*, char*, ...);
|
|
//int vsprintf(char*, char*, va_list);
|
|
int vsprintf(char*, char*, void*);
|
|
int printf(char*, ...);
|
|
int fprintf(FILE*, char*, ...);
|
|
//int vprintf(char*, va_list);
|
|
int vprintf(char*, void*);
|
|
//int vfprintf(FILE*, char*, va_list);
|
|
int vfprintf(FILE*, char*, void*);
|
|
|
|
#endif // #ifndef __SMALLER_C__
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// all public macros
|
|
|
|
#ifndef MAX_IDENT_LEN
|
|
#define MAX_IDENT_LEN 63
|
|
#endif
|
|
#define MAX_STRING_LEN 255
|
|
#define MAX_CHAR_QUEUE_LEN 256
|
|
|
|
#ifndef MAX_MACRO_TABLE_LEN
|
|
#define MAX_MACRO_TABLE_LEN 4096
|
|
#endif
|
|
|
|
#ifndef MAX_STRING_TABLE_LEN
|
|
#define MAX_STRING_TABLE_LEN (512+128)
|
|
#endif
|
|
|
|
#ifndef MAX_IDENT_TABLE_LEN
|
|
#define MAX_IDENT_TABLE_LEN (4096+656)
|
|
#endif
|
|
|
|
#ifndef SYNTAX_STACK_MAX
|
|
#define SYNTAX_STACK_MAX (2048+512+64)
|
|
#endif
|
|
|
|
#ifndef MAX_FILE_NAME_LEN
|
|
#define MAX_FILE_NAME_LEN 95
|
|
#endif
|
|
|
|
#ifndef NO_PREPROCESSOR
|
|
#define MAX_INCLUDES 8
|
|
#define PREP_STACK_SIZE 8
|
|
#define MAX_SEARCH_PATH 256
|
|
#else
|
|
#define MAX_INCLUDES 1
|
|
#define PREP_STACK_SIZE 1
|
|
#define MAX_SEARCH_PATH 1
|
|
#endif
|
|
|
|
/* +-~* /% &|^! << >> && || < <= > >= == != () *[] ++ -- = += -= ~= *= /= %= &= |= ^= <<= >>= {} ,;: -> ... */
|
|
|
|
#define tokEof 0
|
|
#define tokNumInt 1
|
|
#define tokNumUint 2
|
|
#define tokLitStr 3
|
|
|
|
#define tokLShift 4
|
|
#define tokRShift 5
|
|
#define tokLogAnd 6
|
|
#define tokLogOr 7
|
|
#define tokEQ 8
|
|
#define tokNEQ 9
|
|
#define tokLEQ 10
|
|
#define tokGEQ 11
|
|
#define tokInc 12
|
|
#define tokDec 13
|
|
#define tokArrow 14
|
|
#define tokEllipsis 15
|
|
|
|
#define tokIdent 16
|
|
#define tokVoid 17
|
|
#define tokChar 18
|
|
#define tokInt 19
|
|
#define tokReturn 20
|
|
#define tokGoto 21
|
|
#define tokIf 22
|
|
#define tokElse 23
|
|
#define tokWhile 24
|
|
#define tokCont 25
|
|
#define tokBreak 26
|
|
#define tokSizeof 27
|
|
|
|
#define tokAssignMul 'A'
|
|
#define tokAssignDiv 'B'
|
|
#define tokAssignMod 'C'
|
|
#define tokAssignAdd 'D'
|
|
#define tokAssignSub 'E'
|
|
#define tokAssignLSh 'F'
|
|
#define tokAssignRSh 'G'
|
|
#define tokAssignAnd 'H'
|
|
#define tokAssignXor 'I'
|
|
#define tokAssignOr 'J'
|
|
|
|
#define tokFloat 'a'
|
|
#define tokDouble 'b'
|
|
#define tokLong 'c'
|
|
#define tokShort 'd'
|
|
#define tokUnsigned 'e'
|
|
#define tokSigned 'f'
|
|
#define tokConst 'g'
|
|
#define tokVolatile 'h'
|
|
#define tokRestrict 'i'
|
|
#define tokStatic 'j'
|
|
#define tokInline 'k'
|
|
#define tokExtern 'l'
|
|
#define tokAuto 'm'
|
|
#define tokRegister 'n'
|
|
#define tokTypedef 'o'
|
|
#define tokEnum 'p'
|
|
#define tokStruct 'q'
|
|
#define tokUnion 'r'
|
|
#define tokDo 's'
|
|
#define tokFor 't'
|
|
#define tokSwitch 'u'
|
|
#define tokCase 'v'
|
|
#define tokDefault 'w'
|
|
#define tok_Bool 'x'
|
|
#define tok_Complex 'y'
|
|
#define tok_Imagin 'z'
|
|
|
|
#define tok_Asm '`'
|
|
|
|
/* Pseudo-tokens (converted from others or generated) */
|
|
#define tokURShift 28
|
|
#define tokUDiv 29
|
|
#define tokUMod 30
|
|
#define tokAssignURSh 31
|
|
#define tokAssignUDiv '@'
|
|
#define tokAssignUMod 'K'
|
|
#define tokComma '0'
|
|
|
|
#define tokIfNot 'L'
|
|
|
|
#define tokUnaryAnd 'M'
|
|
#define tokUnaryStar 'N'
|
|
#define tokUnaryPlus 'O'
|
|
#define tokUnaryMinus 'P'
|
|
|
|
#define tokPostInc 'Q'
|
|
#define tokPostDec 'R'
|
|
#define tokPostAdd 'S'
|
|
#define tokPostSub 'T'
|
|
|
|
#define tokULess 'U'
|
|
#define tokUGreater 'V'
|
|
#define tokULEQ 'W'
|
|
#define tokUGEQ 'X'
|
|
|
|
#define tokLocalOfs 'Y'
|
|
#define tokShortCirc 'Z'
|
|
|
|
#define tokSChar 0x80
|
|
#define tokUChar 0x81
|
|
#define tokUShort 0x82
|
|
#define tokULong 0x83
|
|
//#define tokLongLong 0x84
|
|
//#define tokULongLong 0x85
|
|
//#define tokLongDbl 0x86
|
|
#define tokGotoLabel 0x8F
|
|
#define tokStructPtr 0x90
|
|
#define tokTag 0x91
|
|
#define tokMemberIdent 0x92
|
|
#define tokEnumPtr 0x93
|
|
#define tokIntr 0x94
|
|
|
|
#define FormatFlat 0
|
|
#define FormatSegmented 1
|
|
#define FormatSegTurbo 2
|
|
#define FormatSegHuge 3
|
|
|
|
#define SymVoidSynPtr 0
|
|
#define SymIntSynPtr 1
|
|
#define SymUintSynPtr 2
|
|
#define SymFuncPtr 3
|
|
|
|
#ifndef STACK_SIZE
|
|
#define STACK_SIZE 128
|
|
#endif
|
|
|
|
#define SymFxn 1
|
|
#define SymGlobalVar 2
|
|
#define SymGlobalArr 3
|
|
#define SymLocalVar 4
|
|
#define SymLocalArr 5
|
|
|
|
// all public prototypes
|
|
STATIC
|
|
int uint2int(unsigned);
|
|
STATIC
|
|
unsigned truncUint(unsigned);
|
|
STATIC
|
|
int truncInt(int);
|
|
|
|
STATIC
|
|
int GetToken(void);
|
|
STATIC
|
|
char* GetTokenName(int token);
|
|
|
|
#ifndef NO_PREPROCESSOR
|
|
#ifndef NO_ANNOTATIONS
|
|
STATIC
|
|
void DumpMacroTable(void);
|
|
#endif
|
|
#endif
|
|
|
|
STATIC
|
|
void PurgeStringTable(void);
|
|
STATIC
|
|
void AddString(int label, char* str, int len);
|
|
STATIC
|
|
char* FindString(int label);
|
|
|
|
STATIC
|
|
int AddIdent(char* name);
|
|
STATIC
|
|
int FindIdent(char* name);
|
|
#ifndef NO_ANNOTATIONS
|
|
STATIC
|
|
void DumpIdentTable(void);
|
|
#endif
|
|
STATIC
|
|
char* lab2str(char* p, int n);
|
|
|
|
STATIC
|
|
void GenInit(void);
|
|
STATIC
|
|
void GenFin(void);
|
|
STATIC
|
|
int GenInitParams(int argc, char** argv, int* idx);
|
|
STATIC
|
|
void GenInitFinalize(void);
|
|
STATIC
|
|
void GenStartCommentLine(void);
|
|
STATIC
|
|
void GenWordAlignment(void);
|
|
STATIC
|
|
void GenLabel(char* Label, int Static);
|
|
STATIC
|
|
void GenNumLabel(int Label);
|
|
STATIC
|
|
void GenZeroData(unsigned Size);
|
|
STATIC
|
|
void GenIntData(int Size, int Val);
|
|
STATIC
|
|
void GenStartAsciiString(void);
|
|
STATIC
|
|
void GenAddrData(int Size, char* Label, int ofs);
|
|
|
|
STATIC
|
|
void GenJumpUncond(int Label);
|
|
STATIC
|
|
void GenJumpIfZero(int Label);
|
|
STATIC
|
|
void GenJumpIfNotZero(int Label);
|
|
#ifndef USE_SWITCH_TAB
|
|
STATIC
|
|
void GenJumpIfEqual(int val, int Label);
|
|
#endif
|
|
|
|
STATIC
|
|
void GenFxnProlog(void);
|
|
STATIC
|
|
void GenFxnProlog2(void);
|
|
STATIC
|
|
void GenFxnEpilog(void);
|
|
void GenIsrProlog(void);
|
|
void GenIsrEpilog(void);
|
|
|
|
STATIC
|
|
int GenMaxLocalsSize(void);
|
|
|
|
STATIC
|
|
unsigned GenStrData(int generatingCode, unsigned requiredLen);
|
|
STATIC
|
|
void GenExpr(void);
|
|
|
|
STATIC
|
|
void PushSyntax(int t);
|
|
STATIC
|
|
void PushSyntax2(int t, int v);
|
|
|
|
#ifndef NO_ANNOTATIONS
|
|
STATIC
|
|
void DumpSynDecls(void);
|
|
#endif
|
|
|
|
STATIC
|
|
void push2(int v, int v2);
|
|
STATIC
|
|
void ins2(int pos, int v, int v2);
|
|
STATIC
|
|
void ins(int pos, int v);
|
|
STATIC
|
|
void del(int pos, int cnt);
|
|
|
|
STATIC
|
|
int TokenStartsDeclaration(int t, int params);
|
|
STATIC
|
|
int ParseDecl(int tok, unsigned structInfo[4], int cast, int label);
|
|
|
|
STATIC
|
|
void ShiftChar(void);
|
|
STATIC
|
|
int puts2(char*);
|
|
STATIC
|
|
int printf2(char*, ...);
|
|
|
|
STATIC
|
|
void error(char* format, ...);
|
|
STATIC
|
|
void warning(char* format, ...);
|
|
STATIC
|
|
void errorFile(char* n);
|
|
STATIC
|
|
void errorFileName(void);
|
|
STATIC
|
|
void errorInternal(int n);
|
|
STATIC
|
|
void errorChrStr(void);
|
|
STATIC
|
|
void errorUnexpectedToken(int tok);
|
|
STATIC
|
|
void errorDirective(void);
|
|
STATIC
|
|
void errorCtrlOutOfScope(void);
|
|
STATIC
|
|
void errorDecl(void);
|
|
STATIC
|
|
void errorVarSize(void);
|
|
STATIC
|
|
void errorInit(void);
|
|
STATIC
|
|
void errorUnexpectedVoid(void);
|
|
STATIC
|
|
void errorOpType(void);
|
|
STATIC
|
|
void errorNotLvalue(void);
|
|
STATIC
|
|
void errorNotConst(void);
|
|
STATIC
|
|
void errorLongExpr(void);
|
|
|
|
STATIC
|
|
int FindSymbol(char* s);
|
|
STATIC
|
|
int SymType(int SynPtr);
|
|
STATIC
|
|
int FindTaggedDecl(char* s, int start, int* CurScope);
|
|
#ifndef NO_TYPEDEF_ENUM
|
|
STATIC
|
|
int FindTypedef(char* s, int* CurScope, int forUse);
|
|
#endif
|
|
STATIC
|
|
int GetDeclSize(int SyntaxPtr, int SizeForDeref);
|
|
|
|
STATIC
|
|
int ParseExpr(int tok, int* GotUnary, int* ExprTypeSynPtr, int* ConstExpr, int* ConstVal, int option, int option2);
|
|
STATIC
|
|
int GetFxnInfo(int ExprTypeSynPtr, int* MinParams, int* MaxParams, int* ReturnExprTypeSynPtr, int* FirstParamSynPtr);
|
|
|
|
// all data
|
|
|
|
int verbose = 0;
|
|
int warnings = 0;
|
|
int warnCnt = 0;
|
|
|
|
// prep.c data
|
|
|
|
int TokenValueInt = 0;
|
|
char TokenIdentName[MAX_IDENT_LEN + 1];
|
|
int TokenIdentNameLen = 0;
|
|
char TokenValueString[MAX_STRING_LEN + 1];
|
|
int TokenStringLen = 0;
|
|
int LineNo = 1;
|
|
int LinePos = 1;
|
|
char CharQueue[MAX_CHAR_QUEUE_LEN];
|
|
int CharQueueLen = 0;
|
|
|
|
#ifndef NO_PREPROCESSOR
|
|
/*
|
|
Macro table entry format:
|
|
idlen char: identifier length (<= 127)
|
|
id char[idlen]: identifier (ASCIIZ)
|
|
exlen char: length of what the identifier expands into (<= 127)
|
|
ex char[exlen]: what the identifier expands into (ASCII)
|
|
*/
|
|
char MacroTable[MAX_MACRO_TABLE_LEN];
|
|
int MacroTableLen = 0;
|
|
#endif
|
|
|
|
/*
|
|
String table entry format:
|
|
labell uchar: temporary identifier's (char*) label number low 8 bits
|
|
labelh uchar: temporary identifier's (char*) label number high 8 bits
|
|
len uchar: string length (<= 255)
|
|
str char[len]: string (ASCII)
|
|
*/
|
|
char StringTable[MAX_STRING_TABLE_LEN];
|
|
int StringTableLen = 0;
|
|
|
|
/*
|
|
Identifier table entry format:
|
|
id char[idlen]: string (ASCIIZ)
|
|
idlen char: string length (<= 127)
|
|
*/
|
|
char IdentTable[MAX_IDENT_TABLE_LEN];
|
|
int IdentTableLen = 0;
|
|
|
|
#ifndef MAX_GOTO_LABELS
|
|
#define MAX_GOTO_LABELS 16
|
|
#endif
|
|
int gotoLabels[MAX_GOTO_LABELS][2];
|
|
// gotoLabStat[]: bit 1 = used (by "goto label;"), bit 0 = defined (with "label:")
|
|
char gotoLabStat[MAX_GOTO_LABELS];
|
|
int gotoLabCnt = 0;
|
|
|
|
#ifndef MAX_CASES
|
|
#define MAX_CASES 128
|
|
#endif
|
|
int Cases[MAX_CASES][2]; // [0] is case constant, [1] is case label number
|
|
int CasesCnt = 0;
|
|
#ifdef USE_SWITCH_TAB
|
|
int SwitchJmpLabel; // label of the function to do table-based switch()
|
|
#endif
|
|
|
|
// Data structures to support #include
|
|
int FileCnt = 0;
|
|
char FileNames[MAX_INCLUDES][MAX_FILE_NAME_LEN + 1];
|
|
FILE* Files[MAX_INCLUDES];
|
|
FILE* OutFile;
|
|
char CharQueues[MAX_INCLUDES][3];
|
|
int LineNos[MAX_INCLUDES];
|
|
int LinePoss[MAX_INCLUDES];
|
|
char SysSearchPaths[MAX_SEARCH_PATH];
|
|
int SysSearchPathsLen = 0;
|
|
char SearchPaths[MAX_SEARCH_PATH];
|
|
int SearchPathsLen = 0;
|
|
|
|
// Data structures to support #ifdef/#ifndef,#else,#endif
|
|
int PrepDontSkipTokens = 1;
|
|
int PrepStack[PREP_STACK_SIZE][2];
|
|
int PrepSp = 0;
|
|
|
|
// Data structures to support #pragma pack(...)
|
|
#ifndef NO_PPACK
|
|
#define PPACK_STACK_SIZE 16
|
|
int PragmaPackValue;
|
|
int PragmaPackValues[PPACK_STACK_SIZE];
|
|
int PragmaPackSp = 0;
|
|
#endif
|
|
|
|
// expr.c data
|
|
|
|
int ExprLevel = 0;
|
|
|
|
// TBD??? merge expression and operator stacks into one
|
|
int stack[STACK_SIZE][2];
|
|
int sp = 0;
|
|
|
|
#define OPERATOR_STACK_SIZE STACK_SIZE
|
|
int opstack[OPERATOR_STACK_SIZE][2];
|
|
int opsp = 0;
|
|
|
|
// smc.c data
|
|
|
|
int OutputFormat = FormatSegmented;
|
|
int GenExterns = 1;
|
|
|
|
#ifdef CAN_COMPILE_32BIT
|
|
// Name of the function to call in main()'s prolog to construct C++ objects/init data.
|
|
// gcc calls __main().
|
|
char* MainPrologCtorFxn = NULL;
|
|
#endif
|
|
|
|
// Names of C functions and variables are usually prefixed with an underscore.
|
|
// One notable exception is the ELF format used by gcc in Linux.
|
|
// Global C identifiers in the ELF format should not be predixed with an underscore.
|
|
int UseLeadingUnderscores = 1;
|
|
|
|
char* FileHeader = "";
|
|
char* CodeHeader = "";
|
|
char* CodeFooter = "";
|
|
char* DataHeader = "";
|
|
char* DataFooter = "";
|
|
|
|
int CharIsSigned = 1;
|
|
int SizeOfWord = 2; // in chars (char can be a multiple of octets); ints and pointers are of word size
|
|
|
|
// TBD??? implement a function to allocate N labels with overflow checks
|
|
int LabelCnt = 1; // label counter for jumps
|
|
int StructCpyLabel = 0; // label of the function to copy structures/unions
|
|
int StructPushLabel = 0; // label of the function to push structures/unions onto the stack
|
|
|
|
// call stack (from higher to lower addresses):
|
|
// arg n
|
|
// ...
|
|
// arg 1
|
|
// return address
|
|
// saved xbp register
|
|
// local var 1
|
|
// ...
|
|
// local var n
|
|
int CurFxnSyntaxPtr = 0;
|
|
int CurFxnParamCntMin = 0;
|
|
int CurFxnParamCntMax = 0;
|
|
int CurFxnLocalOfs = 0; // negative
|
|
int CurFxnMinLocalOfs = 0; // negative
|
|
|
|
int CurFxnReturnExprTypeSynPtr = 0;
|
|
int CurFxnEpilogLabel = 0;
|
|
|
|
char* CurFxnName = NULL;
|
|
#ifndef NO_FUNC_
|
|
int CurFxnNameLabel = 0;
|
|
#endif
|
|
|
|
int ParseLevel = 0; // Parse level/scope (file:0, fxn:1+)
|
|
int ParamLevel = 0; // 1+ if parsing params, 0 otherwise
|
|
|
|
int SyntaxStack[SYNTAX_STACK_MAX][2] =
|
|
{
|
|
{ tokVoid }, // SymVoidSynPtr
|
|
{ tokInt }, // SymIntSynPtr
|
|
{ tokUnsigned }, // SymUintSynPtr
|
|
{ tokIdent }, // SymFuncPtr
|
|
{ '[' },
|
|
{ tokNumUint },
|
|
{ ']' },
|
|
{ tokChar }
|
|
};
|
|
|
|
int SyntaxStackCnt = 8; // number of explicitly initialized elements in SyntaxStack[][2]
|
|
|
|
// all code
|
|
|
|
STATIC
|
|
int uint2int(unsigned n)
|
|
{
|
|
int r;
|
|
// Convert n to (int)n in such a way that (unsigned)(int)n == n,
|
|
// IOW, avoid signed overflows in (int)n that result in an implementation-defined value/signal.
|
|
// We're assuming ints are 2's complement.
|
|
|
|
// "n < INT_MAX + 1u" is equivalent to "n <= INT_MAX" without the
|
|
// possible warning about comparing signed and unsigned types
|
|
if (n < INT_MAX + 1u)
|
|
{
|
|
r = n;
|
|
}
|
|
else
|
|
{
|
|
n = n - INT_MAX - 1; // Now, 0 <= n <= INT_MAX, n is representable in int
|
|
r = n;
|
|
r = r - INT_MAX - 1; // Now, INT_MIN <= r <= -1
|
|
}
|
|
|
|
return r;
|
|
}
|
|
|
|
STATIC
|
|
unsigned truncUint(unsigned n)
|
|
{
|
|
// Truncate n to SizeOfWord * 8 bits
|
|
if (SizeOfWord == 2)
|
|
n &= ~(~0u << 8 << 8);
|
|
#ifdef CAN_COMPILE_32BIT
|
|
else if (SizeOfWord == 4)
|
|
n &= ~(~0u << 8 << 12 << 12);
|
|
#endif
|
|
return n;
|
|
}
|
|
|
|
STATIC
|
|
int truncInt(int n)
|
|
{
|
|
// Truncate n to SizeOfWord * 8 bits and then sign-extend it
|
|
unsigned un = n;
|
|
if (SizeOfWord == 2)
|
|
{
|
|
un &= ~(~0u << 8 << 8);
|
|
un |= (((un >> 8 >> 7) & 1) * ~0u) << 8 << 8;
|
|
}
|
|
#ifdef CAN_COMPILE_32BIT
|
|
else if (SizeOfWord == 4)
|
|
{
|
|
un &= ~(~0u << 8 << 12 << 12);
|
|
un |= (((un >> 8 >> 12 >> 11) & 1) * ~0u) << 8 << 12 << 12;
|
|
}
|
|
#endif
|
|
return uint2int(un);
|
|
}
|
|
|
|
// prep.c code
|
|
|
|
#ifndef NO_PREPROCESSOR
|
|
STATIC
|
|
int FindMacro(char* name)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < MacroTableLen; )
|
|
{
|
|
if (!strcmp(MacroTable + i + 1, name))
|
|
return i + 1 + MacroTable[i];
|
|
|
|
i = i + 1 + MacroTable[i]; // skip id
|
|
i = i + 1 + MacroTable[i]; // skip ex
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
STATIC
|
|
int UndefineMacro(char* name)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < MacroTableLen; )
|
|
{
|
|
if (!strcmp(MacroTable + i + 1, name))
|
|
{
|
|
int len = 1 + MacroTable[i]; // id part len
|
|
len = len + 1 + MacroTable[i + len]; // + ex part len
|
|
|
|
memmove(MacroTable + i,
|
|
MacroTable + i + len,
|
|
MacroTableLen - i - len);
|
|
MacroTableLen -= len;
|
|
|
|
return 1;
|
|
}
|
|
|
|
i = i + 1 + MacroTable[i]; // skip id
|
|
i = i + 1 + MacroTable[i]; // skip ex
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
STATIC
|
|
void AddMacroIdent(char* name)
|
|
{
|
|
int l = strlen(name);
|
|
|
|
if (l >= 127)
|
|
error("Macro identifier too long '%s'\n", name);
|
|
|
|
if (MAX_MACRO_TABLE_LEN - MacroTableLen < l + 3)
|
|
error("Macro table exhausted\n");
|
|
|
|
MacroTable[MacroTableLen++] = l + 1; // idlen
|
|
strcpy(MacroTable + MacroTableLen, name);
|
|
MacroTableLen += l + 1;
|
|
|
|
MacroTable[MacroTableLen] = 0; // exlen
|
|
}
|
|
|
|
STATIC
|
|
void AddMacroExpansionChar(char e)
|
|
{
|
|
if (e == '\0')
|
|
{
|
|
// finalize macro definition entry
|
|
// remove trailing space first
|
|
while (MacroTable[MacroTableLen] &&
|
|
strchr(" \t", MacroTable[MacroTableLen + MacroTable[MacroTableLen]]))
|
|
MacroTable[MacroTableLen]--;
|
|
MacroTableLen += 1 + MacroTable[MacroTableLen];
|
|
return;
|
|
}
|
|
|
|
if (MacroTableLen + 1 + MacroTable[MacroTableLen] >= MAX_MACRO_TABLE_LEN)
|
|
error("Macro table exhausted\n");
|
|
|
|
if (MacroTable[MacroTableLen] >= 127)
|
|
error("Macro definition too long\n");
|
|
|
|
MacroTable[MacroTableLen + 1 + MacroTable[MacroTableLen]] = e;
|
|
MacroTable[MacroTableLen]++;
|
|
}
|
|
|
|
STATIC
|
|
void DefineMacro(char* name, char* expansion)
|
|
{
|
|
AddMacroIdent(name);
|
|
do
|
|
{
|
|
AddMacroExpansionChar(*expansion);
|
|
} while (*expansion++ != '\0');
|
|
}
|
|
|
|
#ifndef NO_ANNOTATIONS
|
|
STATIC
|
|
void DumpMacroTable(void)
|
|
{
|
|
int i, j;
|
|
|
|
puts2("");
|
|
GenStartCommentLine(); printf2("Macro table:\n");
|
|
for (i = 0; i < MacroTableLen; )
|
|
{
|
|
GenStartCommentLine(); printf2("Macro %s = ", MacroTable + i + 1);
|
|
i = i + 1 + MacroTable[i]; // skip id
|
|
printf2("`");
|
|
j = MacroTable[i++];
|
|
while (j--)
|
|
printf2("%c", MacroTable[i++]);
|
|
printf2("`\n");
|
|
}
|
|
GenStartCommentLine(); printf2("Bytes used: %d/%d\n\n", MacroTableLen, MAX_MACRO_TABLE_LEN);
|
|
}
|
|
#endif
|
|
#endif // #ifndef NO_PREPROCESSOR
|
|
|
|
int KeepStringTable = 0;
|
|
|
|
STATIC
|
|
void PurgeStringTable(void)
|
|
{
|
|
if (!KeepStringTable)
|
|
StringTableLen = 0;
|
|
}
|
|
|
|
STATIC
|
|
void AddString(int label, char* str, int len)
|
|
{
|
|
if (len > MAX_STRING_LEN)
|
|
error("String literal too long\n");
|
|
|
|
if (MAX_STRING_TABLE_LEN - StringTableLen < 2 + 1 + len)
|
|
error("String table exhausted\n");
|
|
|
|
StringTable[StringTableLen++] = label & 0xFF;
|
|
StringTable[StringTableLen++] = (label >> 8) & 0xFF;
|
|
|
|
StringTable[StringTableLen++] = len;
|
|
memcpy(StringTable + StringTableLen, str, len);
|
|
StringTableLen += len;
|
|
}
|
|
|
|
STATIC
|
|
char* FindString(int label)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < StringTableLen; )
|
|
{
|
|
int lab;
|
|
|
|
lab = StringTable[i] & 0xFF;
|
|
lab += (StringTable[i + 1] & 0xFFu) << 8;
|
|
|
|
if (lab == label)
|
|
return StringTable + i + 2;
|
|
|
|
i += 2;
|
|
i += 1 + (StringTable[i] & 0xFF);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
STATIC
|
|
int FindIdent(char* name)
|
|
{
|
|
int i;
|
|
for (i = IdentTableLen; i > 0; )
|
|
{
|
|
i -= 1 + IdentTable[i - 1];
|
|
if (!strcmp(IdentTable + i, name))
|
|
return i;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
STATIC
|
|
int AddIdent(char* name)
|
|
{
|
|
int i, len;
|
|
|
|
if ((i = FindIdent(name)) >= 0)
|
|
return i;
|
|
|
|
i = IdentTableLen;
|
|
len = strlen(name);
|
|
|
|
if (len >= 127)
|
|
error("Identifier too long\n");
|
|
|
|
if (MAX_IDENT_TABLE_LEN - IdentTableLen < len + 2)
|
|
error("Identifier table exhausted\n");
|
|
|
|
strcpy(IdentTable + IdentTableLen, name);
|
|
IdentTableLen += len + 1;
|
|
IdentTable[IdentTableLen++] = len + 1;
|
|
|
|
return i;
|
|
}
|
|
|
|
STATIC
|
|
int AddNumericIdent__(int n)
|
|
{
|
|
char s[1 + 2 + (2 + CHAR_BIT * sizeof n) / 3];
|
|
char *p = s + sizeof s;
|
|
*--p = '\0';
|
|
p = lab2str(p, n);
|
|
*--p = '_';
|
|
*--p = '_';
|
|
return AddIdent(p);
|
|
}
|
|
|
|
STATIC
|
|
int AddGotoLabel(char* name, int label)
|
|
{
|
|
int i;
|
|
for (i = 0; i < gotoLabCnt; i++)
|
|
{
|
|
if (!strcmp(IdentTable + gotoLabels[i][0], name))
|
|
{
|
|
if (gotoLabStat[i] & label)
|
|
error("Redefinition of label '%s'\n", name);
|
|
gotoLabStat[i] |= 2*!label + label;
|
|
return gotoLabels[i][1];
|
|
}
|
|
}
|
|
if (gotoLabCnt >= MAX_GOTO_LABELS)
|
|
error("Goto table exhausted\n");
|
|
gotoLabels[gotoLabCnt][0] = AddIdent(name);
|
|
gotoLabels[gotoLabCnt][1] = LabelCnt++;
|
|
gotoLabStat[gotoLabCnt] = 2*!label + label;
|
|
return gotoLabels[gotoLabCnt++][1];
|
|
}
|
|
|
|
STATIC
|
|
void UndoNonLabelIdents(int len)
|
|
{
|
|
int i;
|
|
IdentTableLen = len;
|
|
for (i = 0; i < gotoLabCnt; i++)
|
|
if (gotoLabels[i][0] >= len)
|
|
{
|
|
char* pfrom = IdentTable + gotoLabels[i][0];
|
|
char* pto = IdentTable + IdentTableLen;
|
|
int l = strlen(pfrom) + 2;
|
|
memmove(pto, pfrom, l);
|
|
IdentTableLen += l;
|
|
gotoLabels[i][0] = pto - IdentTable;
|
|
}
|
|
}
|
|
|
|
STATIC
|
|
void AddCase(int val, int label)
|
|
{
|
|
if (CasesCnt >= MAX_CASES)
|
|
error("Case table exhausted\n");
|
|
|
|
Cases[CasesCnt][0] = val;
|
|
Cases[CasesCnt++][1] = label;
|
|
}
|
|
|
|
#ifndef NO_ANNOTATIONS
|
|
STATIC
|
|
void DumpIdentTable(void)
|
|
{
|
|
int i;
|
|
puts2("");
|
|
GenStartCommentLine(); printf2("Identifier table:\n");
|
|
for (i = 0; i < IdentTableLen; )
|
|
{
|
|
GenStartCommentLine(); printf2("Ident %s\n", IdentTable + i);
|
|
i += strlen(IdentTable + i) + 2;
|
|
}
|
|
GenStartCommentLine(); printf2("Bytes used: %d/%d\n\n", IdentTableLen, MAX_IDENT_TABLE_LEN);
|
|
}
|
|
#endif
|
|
|
|
char* rws[] =
|
|
{
|
|
"break", "case", "char", "continue", "default", "do", "else",
|
|
"extern", "for", "if", "int", "return", "signed", "sizeof",
|
|
"static", "switch", "unsigned", "void", "while", "asm", "auto",
|
|
"const", "double", "enum", "float", "goto", "inline", "long",
|
|
"register", "restrict", "short", "struct", "typedef", "union",
|
|
"volatile", "_Bool", "_Complex", "_Imaginary",
|
|
"__interrupt"
|
|
};
|
|
|
|
unsigned char rwtk[] =
|
|
{
|
|
tokBreak, tokCase, tokChar, tokCont, tokDefault, tokDo, tokElse,
|
|
tokExtern, tokFor, tokIf, tokInt, tokReturn, tokSigned, tokSizeof,
|
|
tokStatic, tokSwitch, tokUnsigned, tokVoid, tokWhile, tok_Asm, tokAuto,
|
|
tokConst, tokDouble, tokEnum, tokFloat, tokGoto, tokInline, tokLong,
|
|
tokRegister, tokRestrict, tokShort, tokStruct, tokTypedef, tokUnion,
|
|
tokVolatile, tok_Bool, tok_Complex, tok_Imagin,
|
|
tokIntr
|
|
};
|
|
|
|
STATIC
|
|
int GetTokenByWord(char* word)
|
|
{
|
|
unsigned i;
|
|
|
|
for (i = 0; i < sizeof rws / sizeof rws[0]; i++)
|
|
if (!strcmp(rws[i], word))
|
|
return rwtk[i];
|
|
|
|
return tokIdent;
|
|
}
|
|
|
|
unsigned char tktk[] =
|
|
{
|
|
tokEof,
|
|
// Single-character operators and punctuators:
|
|
'+', '-', '~', '*', '/', '%', '&', '|', '^', '!',
|
|
'<', '>', '(', ')', '[', ']',
|
|
'{', '}', '=', ',', ';', ':', '.', '?',
|
|
// Multi-character operators and punctuators:
|
|
tokLShift, tokLogAnd, tokEQ, tokLEQ, tokInc, tokArrow, tokAssignMul,
|
|
tokAssignMod, tokAssignSub, tokAssignRSh, tokAssignXor,
|
|
tokRShift, tokLogOr, tokNEQ, tokGEQ, tokDec, tokEllipsis,
|
|
tokAssignDiv, tokAssignAdd, tokAssignLSh, tokAssignAnd, tokAssignOr,
|
|
// Some of the above tokens get converted into these in the process:
|
|
tokUnaryAnd, tokUnaryPlus, tokPostInc, tokPostAdd,
|
|
tokULess, tokULEQ, tokURShift, tokUDiv, tokUMod, tokComma,
|
|
tokUnaryStar, tokUnaryMinus, tokPostDec, tokPostSub,
|
|
tokUGreater, tokUGEQ, tokAssignURSh, tokAssignUDiv, tokAssignUMod,
|
|
// Helper (pseudo-)tokens:
|
|
tokNumInt, tokLitStr, tokLocalOfs, tokNumUint, tokIdent, tokShortCirc,
|
|
tokSChar, tokShort, tokLong, tokUChar, tokUShort, tokULong,
|
|
};
|
|
|
|
char* tks[] =
|
|
{
|
|
"<EOF>",
|
|
// Single-character operators and punctuators:
|
|
"+", "-", "~", "*", "/", "%", "&", "|", "^", "!",
|
|
"<", ">", "(", ")", "[", "]",
|
|
"{", "}", "=", ",", ";", ":", ".", "?",
|
|
// Multi-character operators and punctuators:
|
|
"<<", "&&", "==", "<=", "++", "->", "*=",
|
|
"%=", "-=", ">>=", "^=",
|
|
">>", "||", "!=", ">=", "--", "...",
|
|
"/=", "+=", "<<=", "&=", "|=",
|
|
// Some of the above tokens get converted into these in the process:
|
|
"&u", "+u", "++p", "+=p",
|
|
"<u", "<=u", ">>u", "/u", "%u", ",b",
|
|
"*u", "-u", "--p", "-=p",
|
|
">u", ">=u", ">>=u", "/=u", "%=u",
|
|
// Helper (pseudo-)tokens:
|
|
"<NumInt>", "<LitStr>", "<LocalOfs>", "<NumUint>", "<Ident>", "<ShortCirc>",
|
|
"signed char", "short", "long", "unsigned char", "unsigned short", "unsigned long",
|
|
};
|
|
|
|
STATIC
|
|
char* GetTokenName(int token)
|
|
{
|
|
unsigned i;
|
|
|
|
/* +-~* /% &|^! << >> && || < <= > >= == != () *[] ++ -- = += -= ~= *= /= %= &= |= ^= <<= >>= {} ,;: -> ... */
|
|
|
|
// Tokens other than reserved keywords:
|
|
for (i = 0; i < sizeof tktk / sizeof tktk[0]; i++)
|
|
if (tktk[i] == token)
|
|
return tks[i];
|
|
|
|
// Reserved keywords:
|
|
for (i = 0; i < sizeof rws / sizeof rws[0]; i++)
|
|
if (rwtk[i] == token)
|
|
return rws[i];
|
|
|
|
//error("Internal Error: GetTokenName(): Invalid token %d\n", token);
|
|
errorInternal(1);
|
|
return "";
|
|
}
|
|
|
|
STATIC
|
|
int GetNextChar(void)
|
|
{
|
|
int ch = EOF;
|
|
|
|
if (FileCnt && Files[FileCnt - 1])
|
|
{
|
|
if ((ch = fgetc(Files[FileCnt - 1])) == EOF)
|
|
{
|
|
fclose(Files[FileCnt - 1]);
|
|
Files[FileCnt - 1] = NULL;
|
|
|
|
// store the last line/pos, they may still be needed later
|
|
LineNos[FileCnt - 1] = LineNo;
|
|
LinePoss[FileCnt - 1] = LinePos;
|
|
|
|
// don't drop the file record just yet
|
|
}
|
|
}
|
|
|
|
return ch;
|
|
}
|
|
|
|
STATIC
|
|
void ShiftChar(void)
|
|
{
|
|
if (CharQueueLen)
|
|
memmove(CharQueue, CharQueue + 1, --CharQueueLen);
|
|
|
|
// make sure there always are at least 3 chars in the queue
|
|
while (CharQueueLen < 3)
|
|
{
|
|
int ch = GetNextChar();
|
|
if (ch == EOF)
|
|
ch = '\0';
|
|
CharQueue[CharQueueLen++] = ch;
|
|
}
|
|
}
|
|
|
|
STATIC
|
|
void ShiftCharN(int n)
|
|
{
|
|
while (n-- > 0)
|
|
{
|
|
ShiftChar();
|
|
LinePos++;
|
|
}
|
|
}
|
|
|
|
#ifndef NO_PREPROCESSOR
|
|
STATIC
|
|
void IncludeFile(int quot)
|
|
{
|
|
int nlen = strlen(TokenValueString);
|
|
|
|
if (CharQueueLen != 3)
|
|
//error("#include parsing error\n");
|
|
errorInternal(2);
|
|
|
|
if (FileCnt >= MAX_INCLUDES)
|
|
error("Too many include files\n");
|
|
|
|
// store the including file's position and buffered chars
|
|
LineNos[FileCnt - 1] = LineNo;
|
|
LinePoss[FileCnt - 1] = LinePos;
|
|
memcpy(CharQueues[FileCnt - 1], CharQueue, CharQueueLen);
|
|
|
|
// open the included file
|
|
|
|
if (nlen > MAX_FILE_NAME_LEN)
|
|
//error("File name too long\n");
|
|
errorFileName();
|
|
|
|
// DONE: differentiate between quot == '\"' and quot == '<'
|
|
|
|
// First, try opening "file" in the current directory
|
|
// (Open Watcom C/C++ 1.9, Turbo C++ 1.01 use the current directory,
|
|
// unlike gcc, which uses the same directory as the current file)
|
|
if (quot == '\"')
|
|
{
|
|
strcpy(FileNames[FileCnt], TokenValueString);
|
|
Files[FileCnt] = fopen(FileNames[FileCnt], "r");
|
|
}
|
|
|
|
// Next, iterate the search paths trying to open "file" or <file>.
|
|
// "file" is first searched using the list provided by the -I option.
|
|
// "file" is then searched using the list provided by the -SI option.
|
|
// <file> is searched using the list provided by the -SI option.
|
|
if (Files[FileCnt] == NULL)
|
|
{
|
|
int i;
|
|
char *paths = SearchPaths;
|
|
int pl = SearchPathsLen;
|
|
for (;;)
|
|
{
|
|
if (quot == '<')
|
|
{
|
|
paths = SysSearchPaths;
|
|
pl = SysSearchPathsLen;
|
|
}
|
|
for (i = 0; i < pl; )
|
|
{
|
|
int plen = strlen(paths + i);
|
|
if (plen + 1 + nlen < MAX_FILE_NAME_LEN)
|
|
{
|
|
strcpy(FileNames[FileCnt], paths + i);
|
|
strcpy(FileNames[FileCnt] + plen + 1, TokenValueString);
|
|
// Use '/' as a separator, typical for Linux/Unix,
|
|
// but also supported by file APIs in DOS/Windows just as '\\'
|
|
FileNames[FileCnt][plen] = '/';
|
|
if ((Files[FileCnt] = fopen(FileNames[FileCnt], "r")) != NULL)
|
|
break;
|
|
}
|
|
i += plen + 1;
|
|
}
|
|
if (Files[FileCnt] || quot == '<')
|
|
break;
|
|
quot = '<';
|
|
}
|
|
}
|
|
|
|
if (Files[FileCnt] == NULL)
|
|
{
|
|
//error("Cannot open file \"%s\"\n", TokenValueString);
|
|
errorFile(TokenValueString);
|
|
}
|
|
|
|
// reset line/pos and empty the char queue
|
|
CharQueueLen = 0;
|
|
LineNo = LinePos = 1;
|
|
FileCnt++;
|
|
|
|
// fill the char queue with file data
|
|
ShiftChar();
|
|
}
|
|
#endif // #ifndef NO_PREPROCESSOR
|
|
|
|
STATIC
|
|
int EndOfFiles(void)
|
|
{
|
|
// if there are no including files, we're done
|
|
if (!--FileCnt)
|
|
return 1;
|
|
|
|
// restore the including file's position and buffered chars
|
|
LineNo = LineNos[FileCnt - 1];
|
|
LinePos = LinePoss[FileCnt - 1];
|
|
CharQueueLen = 3;
|
|
memcpy(CharQueue, CharQueues[FileCnt - 1], CharQueueLen);
|
|
|
|
return 0;
|
|
}
|
|
|
|
STATIC
|
|
void SkipSpace(int SkipNewLines)
|
|
{
|
|
char* p = CharQueue;
|
|
|
|
while (*p != '\0')
|
|
{
|
|
if (strchr(" \t\f\v", *p))
|
|
{
|
|
ShiftCharN(1);
|
|
continue;
|
|
}
|
|
|
|
if (strchr("\r\n", *p))
|
|
{
|
|
if (!SkipNewLines)
|
|
return;
|
|
|
|
if (*p == '\r' && p[1] == '\n')
|
|
ShiftChar();
|
|
|
|
ShiftChar();
|
|
LineNo++;
|
|
LinePos = 1;
|
|
continue;
|
|
}
|
|
|
|
#ifndef NO_PREPROCESSOR
|
|
if (*p == '/')
|
|
{
|
|
if (p[1] == '/')
|
|
{
|
|
// // comment
|
|
ShiftCharN(2);
|
|
while (!strchr("\r\n", *p))
|
|
ShiftCharN(1);
|
|
continue;
|
|
}
|
|
else if (p[1] == '*')
|
|
{
|
|
// /**/ comment
|
|
ShiftCharN(2);
|
|
while (*p != '\0' && !(*p == '*' && p[1] == '/'))
|
|
{
|
|
if (strchr("\r\n", *p))
|
|
{
|
|
if (!SkipNewLines)
|
|
error("Invalid comment\n");
|
|
|
|
if (*p == '\r' && p[1] == '\n')
|
|
ShiftChar();
|
|
|
|
ShiftChar();
|
|
LineNo++;
|
|
LinePos = 1;
|
|
}
|
|
else
|
|
{
|
|
ShiftCharN(1);
|
|
}
|
|
}
|
|
if (*p == '\0')
|
|
error("Invalid comment\n");
|
|
ShiftCharN(2);
|
|
continue;
|
|
}
|
|
} // endof if (*p == '/')
|
|
#endif
|
|
|
|
break;
|
|
} // endof while (*p != '\0')
|
|
}
|
|
|
|
#ifndef NO_PREPROCESSOR
|
|
STATIC
|
|
void SkipLine(void)
|
|
{
|
|
char* p = CharQueue;
|
|
|
|
while (*p != '\0')
|
|
{
|
|
if (strchr("\r\n", *p))
|
|
{
|
|
if (*p == '\r' && p[1] == '\n')
|
|
ShiftChar();
|
|
|
|
ShiftChar();
|
|
LineNo++;
|
|
LinePos = 1;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
ShiftCharN(1);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
STATIC
|
|
void GetIdent(void)
|
|
{
|
|
char* p = CharQueue;
|
|
|
|
if (*p != '_' && !isalpha(*p & 0xFFu))
|
|
error("Identifier expected\n");
|
|
|
|
if (*p == 'L' &&
|
|
(p[1] == '\'' || p[1] == '\"'))
|
|
//error("Wide characters and strings not supported\n");
|
|
errorChrStr();
|
|
|
|
TokenIdentNameLen = 0;
|
|
TokenIdentName[TokenIdentNameLen++] = *p;
|
|
TokenIdentName[TokenIdentNameLen] = '\0';
|
|
ShiftCharN(1);
|
|
|
|
while (*p == '_' || isalnum(*p & 0xFFu))
|
|
{
|
|
if (TokenIdentNameLen == MAX_IDENT_LEN)
|
|
error("Identifier too long '%s'\n", TokenIdentName);
|
|
TokenIdentName[TokenIdentNameLen++] = *p;
|
|
TokenIdentName[TokenIdentNameLen] = '\0';
|
|
ShiftCharN(1);
|
|
}
|
|
}
|
|
|
|
STATIC
|
|
void GetString(char terminator, int SkipNewLines)
|
|
{
|
|
char* p = CharQueue;
|
|
char ch;
|
|
|
|
TokenStringLen = 0;
|
|
TokenValueString[TokenStringLen] = '\0';
|
|
|
|
for (;;)
|
|
{
|
|
ShiftCharN(1);
|
|
while (!(*p == terminator || strchr("\n\r", *p)))
|
|
{
|
|
ch = *p;
|
|
if (ch == '\\')
|
|
{
|
|
ShiftCharN(1);
|
|
ch = *p;
|
|
if (strchr("\n\r", ch))
|
|
break;
|
|
switch (ch)
|
|
{
|
|
case 'a': ch = '\a'; ShiftCharN(1); break;
|
|
case 'b': ch = '\b'; ShiftCharN(1); break;
|
|
case 'f': ch = '\f'; ShiftCharN(1); break;
|
|
case 'n': ch = '\n'; ShiftCharN(1); break;
|
|
case 'r': ch = '\r'; ShiftCharN(1); break;
|
|
case 't': ch = '\t'; ShiftCharN(1); break;
|
|
case 'v': ch = '\v'; ShiftCharN(1); break;
|
|
// DONE: \nnn, \xnn
|
|
case 'x':
|
|
{
|
|
// hexadecimal character codes \xN+
|
|
int cnt = 0;
|
|
int c = 0;
|
|
ShiftCharN(1);
|
|
while (*p != '\0' && (isdigit(*p & 0xFFu) || strchr("abcdefABCDEF", *p)))
|
|
{
|
|
c = (c * 16) & 0xFF;
|
|
if (*p >= 'a') c += *p - 'a' + 10;
|
|
else if (*p >= 'A') c += *p - 'A' + 10;
|
|
else c += *p - '0';
|
|
ShiftCharN(1);
|
|
cnt++;
|
|
}
|
|
if (!cnt)
|
|
//error("Unsupported or invalid character/string constant\n");
|
|
errorChrStr();
|
|
c -= (c >= 0x80 && CHAR_MIN < 0) * 0x100;
|
|
ch = c;
|
|
}
|
|
break;
|
|
default:
|
|
if (*p >= '0' && *p <= '7')
|
|
{
|
|
// octal character codes \N+
|
|
int cnt = 0;
|
|
int c = 0;
|
|
while (*p >= '0' && *p <= '7')
|
|
{
|
|
c = (c * 8) & 0xFF;
|
|
c += *p - '0';
|
|
ShiftCharN(1);
|
|
// octal escape sequence is terminated after three octal digits
|
|
if (++cnt == 3)
|
|
break;
|
|
}
|
|
c -= (c >= 0x80 && CHAR_MIN < 0) * 0x100;
|
|
ch = c;
|
|
}
|
|
else
|
|
{
|
|
ShiftCharN(1);
|
|
}
|
|
break;
|
|
} // endof switch (ch)
|
|
} // endof if (ch == '\\')
|
|
else
|
|
{
|
|
ShiftCharN(1);
|
|
}
|
|
|
|
if (terminator == '\'')
|
|
{
|
|
if (TokenStringLen != 0)
|
|
//error("Character constant too long\n");
|
|
errorChrStr();
|
|
}
|
|
else if (TokenStringLen == MAX_STRING_LEN)
|
|
error("String literal too long\n");
|
|
|
|
TokenValueString[TokenStringLen++] = ch;
|
|
TokenValueString[TokenStringLen] = '\0';
|
|
} // endof while (!(*p == '\0' || *p == terminator || strchr("\n\r", *p)))
|
|
|
|
if (*p != terminator)
|
|
//error("Unsupported or invalid character/string constant\n");
|
|
errorChrStr();
|
|
|
|
ShiftCharN(1);
|
|
|
|
if (terminator != '\"')
|
|
break; // done with character constants
|
|
|
|
// Concatenate this string literal with all following ones, if any
|
|
SkipSpace(SkipNewLines);
|
|
if (*p != '\"')
|
|
break; // nothing to concatenate with
|
|
// Continue consuming string characters
|
|
} // endof for (;;)
|
|
}
|
|
|
|
#ifndef NO_PREPROCESSOR
|
|
STATIC
|
|
void pushPrep(int NoSkip)
|
|
{
|
|
if (PrepSp >= PREP_STACK_SIZE)
|
|
error("Too many #if(n)def's\n");
|
|
PrepStack[PrepSp][0] = PrepDontSkipTokens;
|
|
PrepStack[PrepSp++][1] = NoSkip;
|
|
PrepDontSkipTokens &= NoSkip;
|
|
}
|
|
|
|
STATIC
|
|
int popPrep(void)
|
|
{
|
|
if (PrepSp <= 0)
|
|
error("#else or #endif without #if(n)def\n");
|
|
PrepDontSkipTokens = PrepStack[--PrepSp][0];
|
|
return PrepStack[PrepSp][1];
|
|
}
|
|
#endif
|
|
|
|
STATIC
|
|
int GetNumber(void)
|
|
{
|
|
char* p = CharQueue;
|
|
int ch = *p;
|
|
unsigned n = 0;
|
|
int type = 0;
|
|
int uSuffix = 0;
|
|
#ifdef CAN_COMPILE_32BIT
|
|
int lSuffix = 0;
|
|
#endif
|
|
char* eTooBig = "Constant too big\n";
|
|
|
|
if (ch == '0')
|
|
{
|
|
// this is either an octal or a hex constant
|
|
type = 'o';
|
|
ShiftCharN(1);
|
|
if ((ch = *p) == 'x' || ch == 'X')
|
|
{
|
|
// this is a hex constant
|
|
int cnt = 0;
|
|
ShiftCharN(1);
|
|
while ((ch = *p) != '\0' && (isdigit(ch & 0xFFu) || strchr("abcdefABCDEF", ch)))
|
|
{
|
|
if (ch >= 'a') ch -= 'a' - 10;
|
|
else if (ch >= 'A') ch -= 'A' - 10;
|
|
else ch -= '0';
|
|
if (PrepDontSkipTokens && (n * 16 / 16 != n || n * 16 + ch < n * 16))
|
|
error(eTooBig);
|
|
n = n * 16 + ch;
|
|
ShiftCharN(1);
|
|
cnt++;
|
|
}
|
|
if (!cnt)
|
|
error("Invalid hexadecimal constant\n");
|
|
type = 'h';
|
|
}
|
|
// this is an octal constant
|
|
else while ((ch = *p) >= '0' && ch <= '7')
|
|
{
|
|
ch -= '0';
|
|
if (PrepDontSkipTokens && (n * 8 / 8 != n || n * 8 + ch < n * 8))
|
|
error(eTooBig);
|
|
n = n * 8 + ch;
|
|
ShiftCharN(1);
|
|
}
|
|
}
|
|
// this is a decimal constant
|
|
else
|
|
{
|
|
type = 'd';
|
|
while ((ch = *p) >= '0' && ch <= '9')
|
|
{
|
|
ch -= '0';
|
|
if (PrepDontSkipTokens && (n * 10 / 10 != n || n * 10 + ch < n * 10))
|
|
error(eTooBig);
|
|
n = n * 10 + ch;
|
|
ShiftCharN(1);
|
|
}
|
|
}
|
|
|
|
// possible combinations of suffixes:
|
|
// none
|
|
// U
|
|
// UL
|
|
// L
|
|
// LU
|
|
if ((ch = *p) == 'u' || ch == 'U')
|
|
{
|
|
uSuffix = 1;
|
|
ShiftCharN(1);
|
|
}
|
|
#ifdef CAN_COMPILE_32BIT
|
|
if ((ch = *p) == 'l' || ch == 'L')
|
|
{
|
|
lSuffix = 1;
|
|
ShiftCharN(1);
|
|
if (!uSuffix && ((ch = *p) == 'u' || ch == 'U'))
|
|
{
|
|
uSuffix = 1;
|
|
ShiftCharN(1);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (!PrepDontSkipTokens)
|
|
{
|
|
// Don't fail on big constants when skipping tokens under #if
|
|
TokenValueInt = 0;
|
|
return tokNumInt;
|
|
}
|
|
|
|
// Ensure the constant fits into 16(32) bits
|
|
if (
|
|
(SizeOfWord == 2 && n >> 8 >> 8) // equiv. to SizeOfWord == 2 && n > 0xFFFF
|
|
#ifdef CAN_COMPILE_32BIT
|
|
|| (SizeOfWord == 2 && lSuffix) // long (which must have at least 32 bits) isn't supported in 16-bit models
|
|
|| (SizeOfWord == 4 && n >> 8 >> 12 >> 12) // equiv. to SizeOfWord == 4 && n > 0xFFFFFFFF
|
|
#endif
|
|
)
|
|
error("Constant too big for %d-bit type\n", SizeOfWord * 8);
|
|
|
|
TokenValueInt = uint2int(n);
|
|
|
|
// Unsuffixed (with 'u') integer constants (octal, decimal, hex)
|
|
// fitting into 15(31) out of 16(32) bits are signed ints
|
|
if (!uSuffix &&
|
|
(
|
|
(SizeOfWord == 2 && !(n >> 15)) // equiv. to SizeOfWord == 2 && n <= 0x7FFF
|
|
#ifdef CAN_COMPILE_32BIT
|
|
|| (SizeOfWord == 4 && !(n >> 8 >> 12 >> 11)) // equiv. to SizeOfWord == 4 && n <= 0x7FFFFFFF
|
|
#endif
|
|
)
|
|
)
|
|
return tokNumInt;
|
|
|
|
// Unlike octal and hex constants, decimal constants are always
|
|
// a signed type. Error out when a decimal constant doesn't fit
|
|
// into an int since currently there's no next bigger signed type
|
|
// (e.g. long) to use instead of int.
|
|
if (!uSuffix && type == 'd')
|
|
error("Constant too big for %d-bit signed type\n", SizeOfWord * 8);
|
|
|
|
return tokNumUint;
|
|
}
|
|
|
|
STATIC
|
|
int GetTokenInner(void)
|
|
{
|
|
char* p = CharQueue;
|
|
int ch = *p;
|
|
|
|
// these single-character tokens/operators need no further processing
|
|
if (strchr(",;:()[]{}~?", ch))
|
|
{
|
|
ShiftCharN(1);
|
|
return ch;
|
|
}
|
|
|
|
// parse multi-character tokens/operators
|
|
|
|
// DONE: other assignment operators
|
|
switch (ch)
|
|
{
|
|
case '+':
|
|
if (p[1] == '+') { ShiftCharN(2); return tokInc; }
|
|
if (p[1] == '=') { ShiftCharN(2); return tokAssignAdd; }
|
|
ShiftCharN(1); return ch;
|
|
case '-':
|
|
if (p[1] == '-') { ShiftCharN(2); return tokDec; }
|
|
if (p[1] == '=') { ShiftCharN(2); return tokAssignSub; }
|
|
if (p[1] == '>') { ShiftCharN(2); return tokArrow; }
|
|
ShiftCharN(1); return ch;
|
|
case '!':
|
|
if (p[1] == '=') { ShiftCharN(2); return tokNEQ; }
|
|
ShiftCharN(1); return ch;
|
|
case '=':
|
|
if (p[1] == '=') { ShiftCharN(2); return tokEQ; }
|
|
ShiftCharN(1); return ch;
|
|
case '<':
|
|
if (p[1] == '=') { ShiftCharN(2); return tokLEQ; }
|
|
if (p[1] == '<') { ShiftCharN(2); if (p[0] != '=') return tokLShift; ShiftCharN(1); return tokAssignLSh; }
|
|
ShiftCharN(1); return ch;
|
|
case '>':
|
|
if (p[1] == '=') { ShiftCharN(2); return tokGEQ; }
|
|
if (p[1] == '>') { ShiftCharN(2); if (p[0] != '=') return tokRShift; ShiftCharN(1); return tokAssignRSh; }
|
|
ShiftCharN(1); return ch;
|
|
case '&':
|
|
if (p[1] == '&') { ShiftCharN(2); return tokLogAnd; }
|
|
if (p[1] == '=') { ShiftCharN(2); return tokAssignAnd; }
|
|
ShiftCharN(1); return ch;
|
|
case '|':
|
|
if (p[1] == '|') { ShiftCharN(2); return tokLogOr; }
|
|
if (p[1] == '=') { ShiftCharN(2); return tokAssignOr; }
|
|
ShiftCharN(1); return ch;
|
|
case '^':
|
|
if (p[1] == '=') { ShiftCharN(2); return tokAssignXor; }
|
|
ShiftCharN(1); return ch;
|
|
case '.':
|
|
if (p[1] == '.' && p[2] == '.') { ShiftCharN(3); return tokEllipsis; }
|
|
ShiftCharN(1); return ch;
|
|
case '*':
|
|
if (p[1] == '=') { ShiftCharN(2); return tokAssignMul; }
|
|
ShiftCharN(1); return ch;
|
|
case '%':
|
|
if (p[1] == '=') { ShiftCharN(2); return tokAssignMod; }
|
|
ShiftCharN(1); return ch;
|
|
case '/':
|
|
if (p[1] == '=') { ShiftCharN(2); return tokAssignDiv; }
|
|
// if (p[1] == '/' || p[1] == '*') { SkipSpace(1); continue; } // already taken care of
|
|
ShiftCharN(1); return ch;
|
|
}
|
|
|
|
// DONE: hex and octal constants
|
|
if (isdigit(ch & 0xFFu))
|
|
return GetNumber();
|
|
|
|
// parse character and string constants
|
|
if (ch == '\'' || ch == '\"')
|
|
{
|
|
GetString(ch, 1);
|
|
|
|
if (ch == '\'')
|
|
{
|
|
if (TokenStringLen != 1)
|
|
//error("Character constant too short\n");
|
|
errorChrStr();
|
|
|
|
TokenValueInt = TokenValueString[0] & 0xFF;
|
|
TokenValueInt -= (CharIsSigned && TokenValueInt >= 0x80) * 0x100;
|
|
return tokNumInt;
|
|
}
|
|
|
|
return tokLitStr;
|
|
} // endof if (ch == '\'' || ch == '\"')
|
|
|
|
return tokEof;
|
|
}
|
|
|
|
#ifndef NO_PREPROCESSOR
|
|
STATIC
|
|
void Reserve4Expansion(char* name, int len)
|
|
{
|
|
if (MAX_CHAR_QUEUE_LEN - CharQueueLen < len + 1)
|
|
error("Too long expansion of macro '%s'\n", name);
|
|
|
|
memmove(CharQueue + len + 1, CharQueue, CharQueueLen);
|
|
|
|
CharQueue[len] = ' '; // space to avoid concatenation
|
|
|
|
CharQueueLen += len + 1;
|
|
}
|
|
#endif
|
|
|
|
// TBD??? implement file I/O for input source code and output code (use fxn ptrs/wrappers to make librarization possible)
|
|
// DONE: support string literals
|
|
STATIC
|
|
int GetToken(void)
|
|
{
|
|
char* p = CharQueue;
|
|
int ch;
|
|
int tok;
|
|
|
|
for (;;)
|
|
{
|
|
/* +-~* /% &|^! << >> && || < <= > >= == != () *[] ++ -- = += -= ~= *= /= %= &= |= ^= <<= >>= {} ,;: -> ... */
|
|
|
|
// skip white space and comments
|
|
SkipSpace(1);
|
|
|
|
if ((ch = *p) == '\0')
|
|
{
|
|
// done with the current file, drop its record,
|
|
// pick up the including files (if any) or terminate
|
|
if (EndOfFiles())
|
|
break;
|
|
continue;
|
|
}
|
|
|
|
if ((tok = GetTokenInner()) != tokEof)
|
|
{
|
|
if (PrepDontSkipTokens)
|
|
return tok;
|
|
continue;
|
|
}
|
|
|
|
// parse identifiers and reserved keywords
|
|
if (ch == '_' || isalpha(ch & 0xFFu))
|
|
{
|
|
#ifndef NO_PREPROCESSOR
|
|
int midx;
|
|
#endif
|
|
|
|
GetIdent();
|
|
|
|
if (!PrepDontSkipTokens)
|
|
continue;
|
|
|
|
tok = GetTokenByWord(TokenIdentName);
|
|
|
|
#ifndef NO_PREPROCESSOR
|
|
// TBD!!! think of expanding macros in the context of concatenating string literals,
|
|
// maybe factor out this piece of code
|
|
if (!strcmp(TokenIdentName, "__FILE__"))
|
|
{
|
|
char* p = FileNames[FileCnt - 1];
|
|
int len = strlen(p);
|
|
Reserve4Expansion(TokenIdentName, len + 2);
|
|
*CharQueue = '"';
|
|
memcpy(CharQueue + 1, p, len);
|
|
CharQueue[len + 1] = '"';
|
|
continue;
|
|
}
|
|
else if (!strcmp(TokenIdentName, "__LINE__"))
|
|
{
|
|
char s[(2 + CHAR_BIT * sizeof LineNo) / 3];
|
|
char *p = lab2str(s + sizeof s, LineNo);
|
|
int len = s + sizeof s - p;
|
|
Reserve4Expansion(TokenIdentName, len);
|
|
memcpy(CharQueue, p, len);
|
|
continue;
|
|
}
|
|
else if ((midx = FindMacro(TokenIdentName)) >= 0)
|
|
{
|
|
// this is a macro identifier, need to expand it
|
|
int len = MacroTable[midx];
|
|
Reserve4Expansion(TokenIdentName, len);
|
|
memcpy(CharQueue, MacroTable + midx + 1, len);
|
|
continue;
|
|
}
|
|
#endif
|
|
|
|
// treat keywords auto, const, register, restrict and volatile as white space for now
|
|
if ((tok == tokConst) | (tok == tokVolatile) |
|
|
(tok == tokAuto) | (tok == tokRegister) |
|
|
(tok == tokRestrict))
|
|
continue;
|
|
|
|
return tok;
|
|
} // endof if (ch == '_' || isalpha(ch))
|
|
|
|
// parse preprocessor directives
|
|
if (ch == '#')
|
|
{
|
|
int line = 0;
|
|
|
|
ShiftCharN(1);
|
|
|
|
// Skip space
|
|
SkipSpace(0);
|
|
|
|
// Allow # not followed by a directive
|
|
if (strchr("\r\n", *p))
|
|
continue;
|
|
|
|
// Get preprocessor directive
|
|
if (isdigit(*p & 0xFFu))
|
|
{
|
|
// gcc-style #line directive without "line"
|
|
line = 1;
|
|
}
|
|
else
|
|
{
|
|
GetIdent();
|
|
if (!strcmp(TokenIdentName, "line"))
|
|
{
|
|
// C89-style #line directive
|
|
SkipSpace(0);
|
|
if (!isdigit(*p & 0xFFu))
|
|
errorDirective();
|
|
line = 1;
|
|
}
|
|
}
|
|
|
|
if (line)
|
|
{
|
|
// Support for external, gcc-like, preprocessor output:
|
|
// # linenum filename flags
|
|
//
|
|
// no flags, flag = 1 -- start of a file
|
|
// flag = 2 -- return to a file after #include
|
|
// other flags -- uninteresting
|
|
|
|
// DONE: should also support the following C89 form:
|
|
// # line linenum filename-opt
|
|
|
|
if (GetNumber() != tokNumInt)
|
|
//error("Invalid line number in preprocessor output\n");
|
|
errorDirective();
|
|
line = TokenValueInt;
|
|
|
|
SkipSpace(0);
|
|
|
|
if (*p == '\"' || *p == '<')
|
|
{
|
|
if (*p == '\"')
|
|
GetString('\"', 0);
|
|
else
|
|
GetString('>', 0);
|
|
|
|
if (strlen(TokenValueString) > MAX_FILE_NAME_LEN)
|
|
//error("File name too long in preprocessor output\n");
|
|
errorFileName();
|
|
strcpy(FileNames[FileCnt - 1], TokenValueString);
|
|
}
|
|
|
|
// Ignore gcc-style #line's flags, if any
|
|
while (!strchr("\r\n", *p))
|
|
ShiftCharN(1);
|
|
|
|
LineNo = line - 1; // "line" is the number of the next line
|
|
LinePos = 1;
|
|
|
|
continue;
|
|
} // endof if (line)
|
|
|
|
#ifndef NO_PPACK
|
|
if (!strcmp(TokenIdentName, "pragma"))
|
|
{
|
|
int canHaveNumber = 1, hadNumber = 0;
|
|
|
|
if (!PrepDontSkipTokens)
|
|
{
|
|
while (!strchr("\r\n", *p))
|
|
ShiftCharN(1);
|
|
continue;
|
|
}
|
|
|
|
SkipSpace(0);
|
|
GetIdent();
|
|
if (strcmp(TokenIdentName, "pack"))
|
|
errorDirective();
|
|
// TBD??? fail if inside a structure declaration
|
|
SkipSpace(0);
|
|
if (*p == '(')
|
|
ShiftCharN(1);
|
|
SkipSpace(0);
|
|
|
|
if (*p == 'p')
|
|
{
|
|
GetIdent();
|
|
if (!strcmp(TokenIdentName, "push"))
|
|
{
|
|
SkipSpace(0);
|
|
if (*p == ',')
|
|
{
|
|
ShiftCharN(1);
|
|
SkipSpace(0);
|
|
if (!isdigit(*p & 0xFFu) || GetNumber() != tokNumInt)
|
|
errorDirective();
|
|
hadNumber = 1;
|
|
}
|
|
if (PragmaPackSp >= PPACK_STACK_SIZE)
|
|
error("#pragma pack stack overflow\n");
|
|
PragmaPackValues[PragmaPackSp++] = PragmaPackValue;
|
|
}
|
|
else if (!strcmp(TokenIdentName, "pop"))
|
|
{
|
|
if (PragmaPackSp <= 0)
|
|
error("#pragma pack stack underflow\n");
|
|
PragmaPackValue = PragmaPackValues[--PragmaPackSp];
|
|
}
|
|
else
|
|
errorDirective();
|
|
SkipSpace(0);
|
|
canHaveNumber = 0;
|
|
}
|
|
|
|
if (canHaveNumber && isdigit(*p & 0xFFu))
|
|
{
|
|
if (GetNumber() != tokNumInt)
|
|
errorDirective();
|
|
hadNumber = 1;
|
|
SkipSpace(0);
|
|
}
|
|
|
|
if (hadNumber)
|
|
{
|
|
PragmaPackValue = TokenValueInt;
|
|
if (PragmaPackValue <= 0 ||
|
|
PragmaPackValue > SizeOfWord ||
|
|
PragmaPackValue & (PragmaPackValue - 1))
|
|
error("Invalid alignment value\n");
|
|
}
|
|
else if (canHaveNumber)
|
|
{
|
|
PragmaPackValue = SizeOfWord;
|
|
}
|
|
|
|
if (*p != ')')
|
|
errorDirective();
|
|
ShiftCharN(1);
|
|
|
|
SkipSpace(0);
|
|
if (!strchr("\r\n", *p))
|
|
errorDirective();
|
|
continue;
|
|
}
|
|
#endif
|
|
|
|
#ifndef NO_PREPROCESSOR
|
|
if (!strcmp(TokenIdentName, "define"))
|
|
{
|
|
// Skip space and get macro name
|
|
SkipSpace(0);
|
|
GetIdent();
|
|
|
|
if (!PrepDontSkipTokens)
|
|
{
|
|
SkipSpace(0);
|
|
while (!strchr("\r\n", *p))
|
|
ShiftCharN(1);
|
|
continue;
|
|
}
|
|
|
|
if (FindMacro(TokenIdentName) >= 0)
|
|
error("Redefinition of macro '%s'\n", TokenIdentName);
|
|
if (*p == '(')
|
|
//error("Unsupported type of macro '%s'\n", TokenIdentName);
|
|
errorDirective();
|
|
|
|
AddMacroIdent(TokenIdentName);
|
|
|
|
SkipSpace(0);
|
|
|
|
// accumulate the macro expansion text
|
|
while (!strchr("\r\n", *p))
|
|
{
|
|
AddMacroExpansionChar(*p);
|
|
ShiftCharN(1);
|
|
if (*p != '\0' && (strchr(" \t", *p) || (*p == '/' && (p[1] == '/' || p[1] == '*'))))
|
|
{
|
|
SkipSpace(0);
|
|
AddMacroExpansionChar(' ');
|
|
}
|
|
}
|
|
AddMacroExpansionChar('\0');
|
|
|
|
continue;
|
|
}
|
|
else if (!strcmp(TokenIdentName, "undef"))
|
|
{
|
|
// Skip space and get macro name
|
|
SkipSpace(0);
|
|
GetIdent();
|
|
|
|
if (PrepDontSkipTokens)
|
|
UndefineMacro(TokenIdentName);
|
|
|
|
SkipSpace(0);
|
|
if (!strchr("\r\n", *p))
|
|
//error("Invalid preprocessor directive\n");
|
|
errorDirective();
|
|
continue;
|
|
}
|
|
else if (!strcmp(TokenIdentName, "include"))
|
|
{
|
|
int quot;
|
|
|
|
// Skip space and get file name
|
|
SkipSpace(0);
|
|
|
|
quot = *p;
|
|
if (*p == '\"')
|
|
GetString('\"', 0);
|
|
else if (*p == '<')
|
|
GetString('>', 0);
|
|
else
|
|
//error("Invalid file name\n");
|
|
errorFileName();
|
|
|
|
SkipSpace(0);
|
|
if (!strchr("\r\n", *p))
|
|
//error("Unsupported or invalid preprocessor directive\n");
|
|
errorDirective();
|
|
|
|
if (PrepDontSkipTokens)
|
|
IncludeFile(quot);
|
|
|
|
continue;
|
|
}
|
|
else if (!strcmp(TokenIdentName, "ifdef"))
|
|
{
|
|
int def;
|
|
// Skip space and get macro name
|
|
SkipSpace(0);
|
|
GetIdent();
|
|
def = FindMacro(TokenIdentName) >= 0;
|
|
SkipSpace(0);
|
|
if (!strchr("\r\n", *p))
|
|
//error("Invalid preprocessor directive\n");
|
|
errorDirective();
|
|
pushPrep(def);
|
|
continue;
|
|
}
|
|
else if (!strcmp(TokenIdentName, "ifndef"))
|
|
{
|
|
int def;
|
|
// Skip space and get macro name
|
|
SkipSpace(0);
|
|
GetIdent();
|
|
def = FindMacro(TokenIdentName) >= 0;
|
|
SkipSpace(0);
|
|
if (!strchr("\r\n", *p))
|
|
//error("Invalid preprocessor directive\n");
|
|
errorDirective();
|
|
pushPrep(!def);
|
|
continue;
|
|
}
|
|
else if (!strcmp(TokenIdentName, "else"))
|
|
{
|
|
int def;
|
|
SkipSpace(0);
|
|
if (!strchr("\r\n", *p))
|
|
//error("Invalid preprocessor directive\n");
|
|
errorDirective();
|
|
def = popPrep();
|
|
if (def >= 2)
|
|
error("#else or #endif without #if(n)def\n");
|
|
pushPrep(2 + !def); // #else works in opposite way to its preceding #if(n)def
|
|
continue;
|
|
}
|
|
else if (!strcmp(TokenIdentName, "endif"))
|
|
{
|
|
SkipSpace(0);
|
|
if (!strchr("\r\n", *p))
|
|
//error("Invalid preprocessor directive\n");
|
|
errorDirective();
|
|
popPrep();
|
|
continue;
|
|
}
|
|
|
|
if (!PrepDontSkipTokens)
|
|
{
|
|
// If skipping code and directives under #ifdef/#ifndef/#else,
|
|
// ignore unsupported directives #if, #elif, #error (no error checking)
|
|
if (!strcmp(TokenIdentName, "if"))
|
|
pushPrep(0);
|
|
else if (!strcmp(TokenIdentName, "elif"))
|
|
popPrep(), pushPrep(0);
|
|
SkipLine();
|
|
continue;
|
|
}
|
|
#endif // #ifndef NO_PREPROCESSOR
|
|
|
|
//error("Unsupported or invalid preprocessor directive\n");
|
|
errorDirective();
|
|
} // endof if (ch == '#')
|
|
|
|
error("Invalid or unsupported character with code 0x%02X\n", *p & 0xFFu);
|
|
} // endof for (;;)
|
|
|
|
return tokEof;
|
|
}
|
|
|
|
#ifdef MIPS
|
|
#ifndef CAN_COMPILE_32BIT
|
|
#error MIPS target requires a 32-bit compiler
|
|
#endif
|
|
#include "cgmips.c"
|
|
#else
|
|
#ifdef TR3200
|
|
#include "cgtr3k2.c"
|
|
#else
|
|
#include "cgx86.c"
|
|
#endif // #ifdef TR3200
|
|
#endif // #ifdef MIPS
|
|
|
|
// expr.c code
|
|
|
|
STATIC
|
|
void push2(int v, int v2)
|
|
{
|
|
if (sp >= STACK_SIZE)
|
|
//error("expression stack overflow!\n");
|
|
errorLongExpr();
|
|
stack[sp][0] = v;
|
|
stack[sp++][1] = v2;
|
|
}
|
|
|
|
STATIC
|
|
void push(int v)
|
|
{
|
|
push2(v, 0);
|
|
}
|
|
|
|
STATIC
|
|
int stacktop()
|
|
{
|
|
if (sp == 0)
|
|
//error("expression stack underflow!\n");
|
|
errorInternal(3);
|
|
return stack[sp - 1][0];
|
|
}
|
|
|
|
STATIC
|
|
int pop2(int* v2)
|
|
{
|
|
int v = stacktop();
|
|
*v2 = stack[sp - 1][1];
|
|
sp--;
|
|
return v;
|
|
}
|
|
|
|
int pop()
|
|
{
|
|
int v2;
|
|
return pop2(&v2);
|
|
}
|
|
|
|
STATIC
|
|
void ins2(int pos, int v, int v2)
|
|
{
|
|
if (sp >= STACK_SIZE)
|
|
//error("expression stack overflow!\n");
|
|
errorLongExpr();
|
|
memmove(&stack[pos + 1], &stack[pos], sizeof(stack[0]) * (sp - pos));
|
|
stack[pos][0] = v;
|
|
stack[pos][1] = v2;
|
|
sp++;
|
|
}
|
|
|
|
STATIC
|
|
void ins(int pos, int v)
|
|
{
|
|
ins2(pos, v, 0);
|
|
}
|
|
|
|
STATIC
|
|
void del(int pos, int cnt)
|
|
{
|
|
memmove(stack[pos],
|
|
stack[pos + cnt],
|
|
sizeof(stack[0]) * (sp - (pos + cnt)));
|
|
sp -= cnt;
|
|
}
|
|
|
|
STATIC
|
|
void pushop2(int v, int v2)
|
|
{
|
|
if (opsp >= OPERATOR_STACK_SIZE)
|
|
//error("operator stack overflow!\n");
|
|
errorLongExpr();
|
|
opstack[opsp][0] = v;
|
|
opstack[opsp++][1] = v2;
|
|
}
|
|
|
|
STATIC
|
|
void pushop(int v)
|
|
{
|
|
pushop2(v, 0);
|
|
}
|
|
|
|
STATIC
|
|
int opstacktop()
|
|
{
|
|
if (opsp == 0)
|
|
//error("operator stack underflow!\n");
|
|
errorInternal(4);
|
|
return opstack[opsp - 1][0];
|
|
}
|
|
|
|
STATIC
|
|
int popop2(int* v2)
|
|
{
|
|
int v = opstacktop();
|
|
*v2 = opstack[opsp - 1][1];
|
|
opsp--;
|
|
return v;
|
|
}
|
|
|
|
STATIC
|
|
int popop()
|
|
{
|
|
int v2;
|
|
return popop2(&v2);
|
|
}
|
|
|
|
STATIC
|
|
int isop(int tok)
|
|
{
|
|
static unsigned char toks[] =
|
|
{
|
|
'!',
|
|
'~',
|
|
'&',
|
|
'*',
|
|
'/', '%',
|
|
'+', '-',
|
|
'|', '^',
|
|
'<', '>',
|
|
'=',
|
|
tokLogOr, tokLogAnd,
|
|
tokEQ, tokNEQ,
|
|
tokLEQ, tokGEQ,
|
|
tokLShift, tokRShift,
|
|
tokInc, tokDec,
|
|
tokSizeof,
|
|
tokAssignMul, tokAssignDiv, tokAssignMod,
|
|
tokAssignAdd, tokAssignSub,
|
|
tokAssignLSh, tokAssignRSh,
|
|
tokAssignAnd, tokAssignXor, tokAssignOr,
|
|
tokComma,
|
|
'?'
|
|
};
|
|
unsigned i;
|
|
for (i = 0; i < sizeof toks / sizeof toks[0]; i++)
|
|
if (toks[i] == tok)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
STATIC
|
|
int isunary(int tok)
|
|
{
|
|
return (tok == '!') | (tok == '~') | (tok == tokInc) | (tok == tokDec) | (tok == tokSizeof);
|
|
}
|
|
|
|
STATIC
|
|
int preced(int tok)
|
|
{
|
|
switch (tok)
|
|
{
|
|
case '*': case '/': case '%': return 13;
|
|
case '+': case '-': return 12;
|
|
case tokLShift: case tokRShift: return 11;
|
|
case '<': case '>': case tokLEQ: case tokGEQ: return 10;
|
|
case tokEQ: case tokNEQ: return 9;
|
|
case '&': return 8;
|
|
case '^': return 7;
|
|
case '|': return 6;
|
|
case tokLogAnd: return 5;
|
|
case tokLogOr: return 4;
|
|
case '?': case ':': return 3;
|
|
case '=':
|
|
case tokAssignMul: case tokAssignDiv: case tokAssignMod:
|
|
case tokAssignAdd: case tokAssignSub:
|
|
case tokAssignLSh: case tokAssignRSh:
|
|
case tokAssignAnd: case tokAssignXor: case tokAssignOr:
|
|
return 2;
|
|
case tokComma:
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
STATIC
|
|
int precedGEQ(int lfttok, int rhttok)
|
|
{
|
|
// DONE: rethink the comma operator as it could be implemented similarly
|
|
// DONE: is this correct:???
|
|
int pl = preced(lfttok);
|
|
int pr = preced(rhttok);
|
|
// ternary/conditional operator ?: is right-associative
|
|
if (pl == 3 && pr >= 3)
|
|
pl = 0;
|
|
// assignment is right-associative
|
|
if (pl == 2 && pr >= 2)
|
|
pl = 0;
|
|
return pl >= pr;
|
|
}
|
|
|
|
STATIC
|
|
int expr(int tok, int* gotUnary, int commaSeparator);
|
|
|
|
STATIC
|
|
char* lab2str(char* p, int n)
|
|
{
|
|
do
|
|
{
|
|
*--p = '0' + n % 10;
|
|
n /= 10;
|
|
} while (n);
|
|
|
|
return p;
|
|
}
|
|
|
|
STATIC
|
|
int exprUnary(int tok, int* gotUnary, int commaSeparator, int argOfSizeOf)
|
|
{
|
|
int decl = 0;
|
|
*gotUnary = 0;
|
|
|
|
if (isop(tok) && (isunary(tok) || strchr("&*+-", tok)))
|
|
{
|
|
int lastTok = tok;
|
|
tok = exprUnary(GetToken(), gotUnary, commaSeparator, lastTok == tokSizeof);
|
|
if (!*gotUnary)
|
|
//error("exprUnary(): primary expression expected after token %s\n", GetTokenName(lastTok));
|
|
errorUnexpectedToken(tok);
|
|
switch (lastTok)
|
|
{
|
|
// DONE: remove all collapsing of all unary operators.
|
|
// It's wrong because type checking must occur before any optimizations.
|
|
// WRONG: DONE: collapse alternating & and * (e.g. "*&*&x" "&*&*x")
|
|
// WRONGISH: DONE: replace prefix ++/-- with +=1/-=1
|
|
case '&':
|
|
push(tokUnaryAnd);
|
|
break;
|
|
case '*':
|
|
push(tokUnaryStar);
|
|
break;
|
|
case '+':
|
|
push(tokUnaryPlus);
|
|
break;
|
|
case '-':
|
|
push(tokUnaryMinus);
|
|
break;
|
|
case '!':
|
|
// replace "!" with "== 0"
|
|
push(tokNumInt);
|
|
push(tokEQ);
|
|
break;
|
|
default:
|
|
push(lastTok);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int inspos = sp;
|
|
|
|
if (tok == tokNumInt || tok == tokNumUint)
|
|
{
|
|
push2(tok, TokenValueInt);
|
|
*gotUnary = 1;
|
|
tok = GetToken();
|
|
}
|
|
else if (tok == tokLitStr)
|
|
{
|
|
int lbl = (LabelCnt += 2) - 2; // 1 extra label for the jump over the string
|
|
int len, id;
|
|
char s[1 + (2 + CHAR_BIT * sizeof lbl) / 3];
|
|
char *p = s + sizeof s;
|
|
|
|
// imitate definition: char #[len] = "...";
|
|
|
|
AddString(lbl, TokenValueString, len = 1 + TokenStringLen);
|
|
|
|
*--p = '\0';
|
|
p = lab2str(p, lbl);
|
|
|
|
// DONE: can this break incomplete yet declarations???, e.g.: int x[sizeof("az")][5];
|
|
PushSyntax2(tokIdent, id = AddIdent(p));
|
|
PushSyntax('[');
|
|
PushSyntax2(tokNumUint, len);
|
|
PushSyntax(']');
|
|
PushSyntax(tokChar);
|
|
|
|
push2(tokIdent, id);
|
|
*gotUnary = 1;
|
|
tok = GetToken();
|
|
}
|
|
else if (tok == tokIdent)
|
|
{
|
|
push2(tok, AddIdent(TokenIdentName));
|
|
*gotUnary = 1;
|
|
tok = GetToken();
|
|
}
|
|
else if (tok == '(')
|
|
{
|
|
tok = GetToken();
|
|
decl = TokenStartsDeclaration(tok, 1);
|
|
|
|
if (decl)
|
|
{
|
|
int synPtr;
|
|
int lbl = LabelCnt++;
|
|
char s[1 + (2 + CHAR_BIT * sizeof lbl) / 3 + sizeof "<something>" - 1];
|
|
char *p = s + sizeof s;
|
|
|
|
tok = ParseDecl(tok, NULL, !argOfSizeOf, 0);
|
|
if (tok != ')')
|
|
//error("exprUnary(): ')' expected, unexpected token %s\n", GetTokenName(tok));
|
|
errorUnexpectedToken(tok);
|
|
synPtr = FindSymbol("<something>");
|
|
|
|
// Rename "<something>" to "<something#>", where # is lbl.
|
|
// This makes the nameless declaration uniquely identifiable by name.
|
|
|
|
*--p = '\0';
|
|
*--p = ")>"[argOfSizeOf]; // differentiate casts (something#) from not casts <something#>
|
|
|
|
p = lab2str(p, lbl);
|
|
|
|
p -= sizeof "<something>" - 2 - 1;
|
|
memcpy(p, "something", sizeof "something" - 1);
|
|
|
|
*--p = "(<"[argOfSizeOf]; // differentiate casts (something#) from not casts <something#>
|
|
|
|
SyntaxStack[synPtr][1] = AddIdent(p);
|
|
tok = GetToken();
|
|
if (argOfSizeOf)
|
|
{
|
|
// expression: sizeof(type)
|
|
*gotUnary = 1;
|
|
}
|
|
else
|
|
{
|
|
// unary type cast operator: (type)
|
|
decl = 0;
|
|
tok = exprUnary(tok, gotUnary, commaSeparator, 0);
|
|
if (!*gotUnary)
|
|
//error("exprUnary(): primary expression expected after '(type)'\n");
|
|
errorUnexpectedToken(tok);
|
|
}
|
|
push2(tokIdent, SyntaxStack[synPtr][1]);
|
|
}
|
|
else
|
|
{
|
|
tok = expr(tok, gotUnary, 0);
|
|
if (tok != ')')
|
|
//error("exprUnary(): ')' expected, unexpected token %s\n", GetTokenName(tok));
|
|
errorUnexpectedToken(tok);
|
|
if (!*gotUnary)
|
|
//error("exprUnary(): primary expression expected in '()'\n");
|
|
errorUnexpectedToken(tok);
|
|
tok = GetToken();
|
|
}
|
|
}
|
|
|
|
while (*gotUnary && !decl)
|
|
{
|
|
// DONE: f(args1)(args2) and the like: need stack order: args2, args1, f, (), ()
|
|
// DONE: reverse the order of evaluation of groups of args in
|
|
// f(args1)(args2)(args3)
|
|
// DONE: reverse the order of function argument evaluation for variadic functions
|
|
// we want 1st arg to be the closest to the stack top.
|
|
// DONE: (args)[index] can be repeated interchangeably indefinitely
|
|
// DONE: (expr)() & (expr)[]
|
|
// DONE: [index] can be followed by ++/--, which can be followed by [index] and so on...
|
|
// DONE: postfix ++/-- & differentiate from prefix ++/--
|
|
|
|
if (tok == '(')
|
|
{
|
|
int acnt = 0;
|
|
ins(inspos, '(');
|
|
for (;;)
|
|
{
|
|
int pos2 = sp;
|
|
|
|
tok = GetToken();
|
|
tok = expr(tok, gotUnary, 1);
|
|
|
|
// Reverse the order of argument evaluation, which is important for
|
|
// variadic functions like printf():
|
|
// we want 1st arg to be the closest to the stack top.
|
|
// This also reverses the order of evaluation of all groups of
|
|
// arguments.
|
|
while (pos2 < sp)
|
|
{
|
|
// TBD??? not quite efficient
|
|
int v, v2;
|
|
v = pop2(&v2);
|
|
ins2(inspos + 1, v, v2);
|
|
pos2++;
|
|
}
|
|
|
|
if (tok == ',')
|
|
{
|
|
if (!*gotUnary)
|
|
//error("exprUnary(): primary expression (fxn argument) expected before ','\n");
|
|
errorUnexpectedToken(tok);
|
|
acnt++;
|
|
ins(inspos + 1, ','); // helper argument separator (hint for expression evaluator)
|
|
continue; // off to next arg
|
|
}
|
|
if (tok == ')')
|
|
{
|
|
if (acnt && !*gotUnary)
|
|
//error("exprUnary(): primary expression (fxn argument) expected between ',' and ')'\n");
|
|
errorUnexpectedToken(tok);
|
|
*gotUnary = 1; // don't fail for 0 args in ()
|
|
break; // end of args
|
|
}
|
|
// DONE: think of inserting special arg pseudo tokens for verification purposes
|
|
//error("exprUnary(): ',' or ')' expected, unexpected token %s\n", GetTokenName(tok));
|
|
errorUnexpectedToken(tok);
|
|
} // endof for(;;) for fxn args
|
|
push(')');
|
|
}
|
|
else if (tok == '[')
|
|
{
|
|
tok = GetToken();
|
|
tok = expr(tok, gotUnary, 0);
|
|
if (!*gotUnary)
|
|
//error("exprUnary(): primary expression expected in '[]'\n");
|
|
errorUnexpectedToken(tok);
|
|
if (tok != ']')
|
|
//error("exprUnary(): ']' expected, unexpected token %s\n", GetTokenName(tok));
|
|
errorUnexpectedToken(tok);
|
|
// TBD??? add implicit casts to size_t of array indicies.
|
|
// E1[E2] -> *(E1 + E2)
|
|
// push('[');
|
|
push('+');
|
|
push(tokUnaryStar);
|
|
}
|
|
// WRONG: DONE: replace postfix ++/-- with (+=1)-1/(-=1)+1
|
|
else if (tok == tokInc)
|
|
{
|
|
push(tokPostInc);
|
|
}
|
|
else if (tok == tokDec)
|
|
{
|
|
push(tokPostDec);
|
|
}
|
|
else if (tok == '.' || tok == tokArrow)
|
|
{
|
|
// transform a.b into (&a)->b
|
|
if (tok == '.')
|
|
push(tokUnaryAnd);
|
|
tok = GetToken();
|
|
if (tok != tokIdent)
|
|
errorUnexpectedToken(tok);
|
|
push2(tok, AddIdent(TokenIdentName));
|
|
// "->" in "a->b" will function as "+" in "*(type_of_b*)((char*)a + offset_of_b_in_a)"
|
|
push(tokArrow);
|
|
push(tokUnaryStar);
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
tok = GetToken();
|
|
} // endof while (*gotUnary)
|
|
}
|
|
|
|
if (tok == ',' && !commaSeparator)
|
|
tok = tokComma;
|
|
|
|
return tok;
|
|
}
|
|
|
|
STATIC
|
|
int expr(int tok, int* gotUnary, int commaSeparator)
|
|
{
|
|
*gotUnary = 0;
|
|
|
|
pushop(tokEof);
|
|
|
|
tok = exprUnary(tok, gotUnary, commaSeparator, 0);
|
|
|
|
while (tok != tokEof && strchr(",;:)]}", tok) == NULL && *gotUnary)
|
|
{
|
|
if (isop(tok) && !isunary(tok))
|
|
{
|
|
//int lastTok = tok;
|
|
|
|
while (precedGEQ(opstacktop(), tok))
|
|
{
|
|
int v, v2;
|
|
int c = 0;
|
|
// move ?expr: as a whole to the expression stack as "expr?"
|
|
do
|
|
{
|
|
v = popop2(&v2);
|
|
if (v != ':')
|
|
push2(v, v2);
|
|
c += (v == ':') - (v == '?');
|
|
} while (c);
|
|
}
|
|
|
|
// here: preced(postacktop()) < preced(tok)
|
|
pushop(tok);
|
|
|
|
// treat the ternary/conditional operator ?expr: as a pseudo binary operator
|
|
if (tok == '?')
|
|
{
|
|
int ssp = sp;
|
|
|
|
tok = expr(GetToken(), gotUnary, 0);
|
|
if (!*gotUnary || tok != ':')
|
|
errorUnexpectedToken(tok);
|
|
|
|
// move ?expr: as a whole to the operator stack
|
|
// this is beautiful and ugly at the same time
|
|
while (sp > ssp)
|
|
{
|
|
int v, v2;
|
|
v = pop2(&v2);
|
|
pushop2(v, v2);
|
|
}
|
|
|
|
pushop(tok);
|
|
}
|
|
|
|
tok = exprUnary(GetToken(), gotUnary, commaSeparator, 0);
|
|
// DONE: figure out a check to see if exprUnary() fails to add a rhs operand
|
|
if (!*gotUnary)
|
|
//error("expr(): primary expression expected after token %s\n", GetTokenName(lastTok));
|
|
errorUnexpectedToken(tok);
|
|
|
|
continue;
|
|
}
|
|
|
|
//error("expr(): Unexpected token %s\n", GetTokenName(tok));
|
|
errorUnexpectedToken(tok);
|
|
}
|
|
|
|
while (opstacktop() != tokEof)
|
|
{
|
|
int v, v2;
|
|
v = popop2(&v2);
|
|
if (v != ':')
|
|
push2(v, v2);
|
|
}
|
|
|
|
popop();
|
|
|
|
return tok;
|
|
}
|
|
|
|
STATIC
|
|
void decayArray(int* ExprTypeSynPtr, int arithmetic)
|
|
{
|
|
// Dacay arrays to pointers to their first elements in
|
|
// binary + and - operators
|
|
if (*ExprTypeSynPtr >= 0 && SyntaxStack[*ExprTypeSynPtr][0] == '[')
|
|
{
|
|
while (SyntaxStack[*ExprTypeSynPtr][0] != ']')
|
|
++*ExprTypeSynPtr;
|
|
++*ExprTypeSynPtr;
|
|
*ExprTypeSynPtr = -*ExprTypeSynPtr;
|
|
}
|
|
// Also, to simplify code, return all other pointers as
|
|
// negative expression stack syntax indices/pointers
|
|
else if (*ExprTypeSynPtr >= 0 && SyntaxStack[*ExprTypeSynPtr][0] == '*')
|
|
{
|
|
++*ExprTypeSynPtr;
|
|
*ExprTypeSynPtr = -*ExprTypeSynPtr;
|
|
}
|
|
|
|
// DONE: disallow arithmetic on pointers to void
|
|
// DONE: disallow function pointers
|
|
if (arithmetic)
|
|
{
|
|
if (*ExprTypeSynPtr < 0)
|
|
{
|
|
if (SyntaxStack[-*ExprTypeSynPtr][0] == tokVoid)
|
|
//error("decayArray(): cannot do pointer arithmetic on a pointer to 'void'\n");
|
|
errorUnexpectedVoid();
|
|
if (SyntaxStack[-*ExprTypeSynPtr][0] == '(' ||
|
|
!GetDeclSize(-*ExprTypeSynPtr, 0))
|
|
//error("decayArray(): cannot do pointer arithmetic on a pointer to a function\n");
|
|
errorOpType();
|
|
}
|
|
else
|
|
{
|
|
if (SyntaxStack[*ExprTypeSynPtr][0] == '(')
|
|
//error("decayArray(): cannot do arithmetic on a function\n");
|
|
errorOpType();
|
|
}
|
|
}
|
|
}
|
|
|
|
STATIC
|
|
void nonVoidTypeCheck(int ExprTypeSynPtr)
|
|
{
|
|
if (ExprTypeSynPtr >= 0 && SyntaxStack[ExprTypeSynPtr][0] == tokVoid)
|
|
//error("nonVoidTypeCheck(): unexpected operand type 'void' for operator '%s'\n", GetTokenName(tok));
|
|
errorUnexpectedVoid();
|
|
}
|
|
|
|
STATIC
|
|
void scalarTypeCheck(int ExprTypeSynPtr)
|
|
{
|
|
nonVoidTypeCheck(ExprTypeSynPtr);
|
|
|
|
if (ExprTypeSynPtr >= 0 && SyntaxStack[ExprTypeSynPtr][0] == tokStructPtr)
|
|
errorOpType();
|
|
}
|
|
|
|
STATIC
|
|
void numericTypeCheck(int ExprTypeSynPtr)
|
|
{
|
|
if (ExprTypeSynPtr >= 0 &&
|
|
(SyntaxStack[ExprTypeSynPtr][0] == tokChar ||
|
|
SyntaxStack[ExprTypeSynPtr][0] == tokSChar ||
|
|
SyntaxStack[ExprTypeSynPtr][0] == tokUChar ||
|
|
#ifdef CAN_COMPILE_32BIT
|
|
SyntaxStack[ExprTypeSynPtr][0] == tokShort ||
|
|
SyntaxStack[ExprTypeSynPtr][0] == tokUShort ||
|
|
#endif
|
|
SyntaxStack[ExprTypeSynPtr][0] == tokInt ||
|
|
SyntaxStack[ExprTypeSynPtr][0] == tokUnsigned))
|
|
return;
|
|
//error("numericTypeCheck(): unexpected operand type for operator '%s', numeric type expected\n", GetTokenName(tok));
|
|
errorOpType();
|
|
}
|
|
|
|
STATIC
|
|
void compatCheck(int* ExprTypeSynPtr, int TheOtherExprTypeSynPtr, int ConstExpr[2], int lidx, int ridx)
|
|
{
|
|
int exprTypeSynPtr = *ExprTypeSynPtr;
|
|
int c = 0;
|
|
int lptr, rptr, lnum, rnum;
|
|
|
|
// convert functions to pointers to functions
|
|
if (exprTypeSynPtr >= 0 && SyntaxStack[exprTypeSynPtr][0] == '(')
|
|
*ExprTypeSynPtr = exprTypeSynPtr = -exprTypeSynPtr;
|
|
if (TheOtherExprTypeSynPtr >= 0 && SyntaxStack[TheOtherExprTypeSynPtr][0] == '(')
|
|
TheOtherExprTypeSynPtr = -TheOtherExprTypeSynPtr;
|
|
|
|
lptr = exprTypeSynPtr < 0;
|
|
rptr = TheOtherExprTypeSynPtr < 0;
|
|
lnum = !lptr && (SyntaxStack[exprTypeSynPtr][0] == tokInt ||
|
|
SyntaxStack[exprTypeSynPtr][0] == tokUnsigned);
|
|
rnum = !rptr && (SyntaxStack[TheOtherExprTypeSynPtr][0] == tokInt ||
|
|
SyntaxStack[TheOtherExprTypeSynPtr][0] == tokUnsigned);
|
|
|
|
// both operands have arithmetic type
|
|
// (arithmetic operands have been already promoted):
|
|
if (lnum && rnum)
|
|
return;
|
|
|
|
// both operands have void type:
|
|
if (!lptr && SyntaxStack[exprTypeSynPtr][0] == tokVoid &&
|
|
!rptr && SyntaxStack[TheOtherExprTypeSynPtr][0] == tokVoid)
|
|
return;
|
|
|
|
// TBD??? check for exact 0?
|
|
// one operand is a pointer and the other is NULL constant
|
|
// ((void*)0 is also a valid null pointer constant),
|
|
// the type of the expression is that of the pointer:
|
|
if (lptr &&
|
|
((rnum && ConstExpr[1]) ||
|
|
(rptr && SyntaxStack[-TheOtherExprTypeSynPtr][0] == tokVoid &&
|
|
stack[ridx][0] == tokUnaryPlus && // "(type*)constant" appears as "constant +(unary)"
|
|
(stack[ridx - 1][0] == tokNumInt || stack[ridx - 1][0] == tokNumUint))))
|
|
return;
|
|
if (rptr &&
|
|
((lnum && ConstExpr[0]) ||
|
|
(lptr && SyntaxStack[-exprTypeSynPtr][0] == tokVoid &&
|
|
stack[lidx][0] == tokUnaryPlus && // "(type*)constant" appears as "constant +(unary)"
|
|
(stack[lidx - 1][0] == tokNumInt || stack[lidx - 1][0] == tokNumUint))))
|
|
{
|
|
*ExprTypeSynPtr = TheOtherExprTypeSynPtr;
|
|
return;
|
|
}
|
|
|
|
// not expecting non-pointers beyond this point
|
|
if (!(lptr && rptr))
|
|
errorOpType();
|
|
|
|
// one operand is a pointer and the other is a pointer to void
|
|
// (except (void*)0, which is different from other pointers to void),
|
|
// the type of the expression is pointer to void:
|
|
if (SyntaxStack[-exprTypeSynPtr][0] == tokVoid)
|
|
return;
|
|
if (SyntaxStack[-TheOtherExprTypeSynPtr][0] == tokVoid)
|
|
{
|
|
*ExprTypeSynPtr = TheOtherExprTypeSynPtr;
|
|
return;
|
|
}
|
|
|
|
// both operands are pointers to compatible types:
|
|
|
|
if (exprTypeSynPtr == TheOtherExprTypeSynPtr)
|
|
return;
|
|
|
|
exprTypeSynPtr = -exprTypeSynPtr;
|
|
TheOtherExprTypeSynPtr = -TheOtherExprTypeSynPtr;
|
|
|
|
for (;;)
|
|
{
|
|
int tok = SyntaxStack[exprTypeSynPtr][0];
|
|
if (tok != SyntaxStack[TheOtherExprTypeSynPtr][0])
|
|
errorOpType();
|
|
|
|
if (tok != tokIdent &&
|
|
SyntaxStack[exprTypeSynPtr][1] != SyntaxStack[TheOtherExprTypeSynPtr][1])
|
|
errorOpType();
|
|
|
|
c += (tok == '(') - (tok == ')') + (tok == '[') - (tok == ']');
|
|
|
|
if (!c)
|
|
{
|
|
switch (tok)
|
|
{
|
|
case tokVoid:
|
|
case tokChar: case tokSChar: case tokUChar:
|
|
#ifdef CAN_COMPILE_32BIT
|
|
case tokShort: case tokUShort:
|
|
#endif
|
|
case tokInt: case tokUnsigned:
|
|
case tokStructPtr:
|
|
return;
|
|
}
|
|
}
|
|
|
|
exprTypeSynPtr++;
|
|
TheOtherExprTypeSynPtr++;
|
|
}
|
|
}
|
|
|
|
STATIC
|
|
void shiftCountCheck(int *psr, int idx, int ExprTypeSynPtr)
|
|
{
|
|
int sr = *psr;
|
|
// can't shift by a negative count and by a count exceeding
|
|
// the number of bits in int
|
|
if ((SyntaxStack[ExprTypeSynPtr][0] != tokUnsigned && sr < 0) ||
|
|
(sr + 0u) >= CHAR_BIT * sizeof(int) ||
|
|
(sr + 0u) >= 8u * SizeOfWord)
|
|
{
|
|
//error("exprval(): Invalid shift count\n");
|
|
warning("Shift count out of range\n");
|
|
// truncate the count, so the assembler doesn't get an invalid count
|
|
sr &= SizeOfWord * 8 - 1;
|
|
*psr = sr;
|
|
stack[idx][1] = sr;
|
|
}
|
|
}
|
|
|
|
STATIC
|
|
int divCheckAndCalc(int tok, int* psl, int sr, int Unsigned, int ConstExpr[2])
|
|
{
|
|
int div0 = 0;
|
|
int sl = *psl;
|
|
|
|
if (!ConstExpr[1])
|
|
return !div0;
|
|
|
|
if (Unsigned)
|
|
{
|
|
sl = uint2int(truncUint(sl));
|
|
sr = uint2int(truncUint(sr));
|
|
}
|
|
else
|
|
{
|
|
sl = truncInt(sl);
|
|
sr = truncInt(sr);
|
|
}
|
|
|
|
if (sr == 0)
|
|
{
|
|
div0 = 1;
|
|
}
|
|
else if (!ConstExpr[0])
|
|
{
|
|
return !div0;
|
|
}
|
|
else if (!Unsigned && ((sl == INT_MIN && sr == -1) || sl / sr != truncInt(sl / sr)))
|
|
{
|
|
div0 = 1;
|
|
}
|
|
else
|
|
{
|
|
if (Unsigned)
|
|
{
|
|
if (tok == '/')
|
|
sl = uint2int((sl + 0u) / sr);
|
|
else
|
|
sl = uint2int((sl + 0u) % sr);
|
|
}
|
|
else
|
|
{
|
|
// TBD!!! C89 gives freedom in how exactly division of negative integers
|
|
// can be implemented w.r.t. rounding and w.r.t. the sign of the remainder.
|
|
// A stricter, C99-conforming implementation, non-dependent on the
|
|
// compiler used to compile Smaller C is needed.
|
|
if (tok == '/')
|
|
sl /= sr;
|
|
else
|
|
sl %= sr;
|
|
}
|
|
*psl = sl;
|
|
}
|
|
|
|
if (div0)
|
|
warning("Division by 0 or division overflow\n");
|
|
|
|
return !div0;
|
|
}
|
|
|
|
STATIC
|
|
void promoteType(int* ExprTypeSynPtr, int* TheOtherExprTypeSynPtr)
|
|
{
|
|
// chars must be promoted to ints in expressions as the very first thing
|
|
if (*ExprTypeSynPtr >= 0 &&
|
|
(SyntaxStack[*ExprTypeSynPtr][0] == tokChar ||
|
|
#ifdef CAN_COMPILE_32BIT
|
|
SyntaxStack[*ExprTypeSynPtr][0] == tokShort ||
|
|
SyntaxStack[*ExprTypeSynPtr][0] == tokUShort ||
|
|
#endif
|
|
SyntaxStack[*ExprTypeSynPtr][0] == tokSChar ||
|
|
SyntaxStack[*ExprTypeSynPtr][0] == tokUChar))
|
|
*ExprTypeSynPtr = SymIntSynPtr;
|
|
|
|
// ints must be converted to unsigned ints if they are used in binary
|
|
// operators whose other operand is unsigned int (except <<,>>,<<=,>>=)
|
|
if (*ExprTypeSynPtr >= 0 && SyntaxStack[*ExprTypeSynPtr][0] == tokInt &&
|
|
*TheOtherExprTypeSynPtr >= 0 && SyntaxStack[*TheOtherExprTypeSynPtr][0] == tokUnsigned)
|
|
*ExprTypeSynPtr = SymUintSynPtr;
|
|
}
|
|
|
|
STATIC
|
|
int GetFxnInfo(int ExprTypeSynPtr, int* MinParams, int* MaxParams, int* ReturnExprTypeSynPtr, int* FirstParamSynPtr)
|
|
{
|
|
int ptr = 0;
|
|
|
|
*MaxParams = *MinParams = 0;
|
|
|
|
if (ExprTypeSynPtr < 0)
|
|
{
|
|
ptr = 1;
|
|
ExprTypeSynPtr = -ExprTypeSynPtr;
|
|
}
|
|
|
|
while (SyntaxStack[ExprTypeSynPtr][0] == tokIdent || SyntaxStack[ExprTypeSynPtr][0] == tokLocalOfs)
|
|
ExprTypeSynPtr++;
|
|
|
|
if (!(SyntaxStack[ExprTypeSynPtr][0] == '(' ||
|
|
(!ptr && SyntaxStack[ExprTypeSynPtr][0] == '*' && SyntaxStack[ExprTypeSynPtr + 1][0] == '(')))
|
|
return 0;
|
|
|
|
// DONE: return syntax pointer to the function's return type
|
|
|
|
// Count params
|
|
|
|
while (SyntaxStack[ExprTypeSynPtr][0] != '(')
|
|
ExprTypeSynPtr++;
|
|
ExprTypeSynPtr++;
|
|
|
|
if (FirstParamSynPtr)
|
|
*FirstParamSynPtr = ExprTypeSynPtr;
|
|
|
|
if (SyntaxStack[ExprTypeSynPtr][0] == ')')
|
|
{
|
|
// "fxn()": unspecified parameters, so, there can be any number of them
|
|
*MaxParams = 32767; // INT_MAX;
|
|
*ReturnExprTypeSynPtr = ExprTypeSynPtr + 1;
|
|
return 1;
|
|
}
|
|
|
|
if (SyntaxStack[ExprTypeSynPtr + 1][0] == tokVoid)
|
|
{
|
|
// "fxn(void)": 0 parameters
|
|
*ReturnExprTypeSynPtr = ExprTypeSynPtr + 3;
|
|
return 1;
|
|
}
|
|
|
|
for (;;)
|
|
{
|
|
int tok = SyntaxStack[ExprTypeSynPtr][0];
|
|
|
|
if (tok == tokIdent)
|
|
{
|
|
if (SyntaxStack[ExprTypeSynPtr + 1][0] != tokEllipsis)
|
|
{
|
|
++*MinParams;
|
|
++*MaxParams;
|
|
}
|
|
else
|
|
{
|
|
*MaxParams = 32767; // INT_MAX;
|
|
}
|
|
}
|
|
else if (tok == '(')
|
|
{
|
|
// skip parameters in parameters
|
|
int c = 1;
|
|
while (c && ExprTypeSynPtr < SyntaxStackCnt)
|
|
{
|
|
tok = SyntaxStack[++ExprTypeSynPtr][0];
|
|
c += (tok == '(') - (tok == ')');
|
|
}
|
|
}
|
|
else if (tok == ')')
|
|
{
|
|
ExprTypeSynPtr++;
|
|
break;
|
|
}
|
|
|
|
ExprTypeSynPtr++;
|
|
}
|
|
|
|
// get the function's return type
|
|
*ReturnExprTypeSynPtr = ExprTypeSynPtr;
|
|
|
|
return 1;
|
|
}
|
|
|
|
STATIC
|
|
void simplifyConstExpr(int val, int isConst, int* ExprTypeSynPtr, int top, int bottom)
|
|
{
|
|
if (!isConst || stack[top][0] == tokNumInt || stack[top][0] == tokNumUint)
|
|
return;
|
|
|
|
if (SyntaxStack[*ExprTypeSynPtr][0] == tokUnsigned)
|
|
stack[top][0] = tokNumUint;
|
|
else
|
|
stack[top][0] = tokNumInt;
|
|
stack[top][1] = val;
|
|
|
|
del(bottom, top - bottom);
|
|
}
|
|
|
|
STATIC
|
|
int AllocLocal(unsigned size)
|
|
{
|
|
// Let's calculate variable's relative on-stack location
|
|
int oldOfs = CurFxnLocalOfs;
|
|
|
|
// Note: local vars are word-aligned on the stack
|
|
CurFxnLocalOfs = uint2int((CurFxnLocalOfs - size) & ~(SizeOfWord - 1u));
|
|
if (CurFxnLocalOfs >= oldOfs ||
|
|
CurFxnLocalOfs != truncInt(CurFxnLocalOfs) ||
|
|
CurFxnLocalOfs < -GenMaxLocalsSize())
|
|
//error("AllocLocal(): Local variables take too much space\n");
|
|
errorVarSize();
|
|
|
|
if (CurFxnMinLocalOfs > CurFxnLocalOfs)
|
|
CurFxnMinLocalOfs = CurFxnLocalOfs;
|
|
|
|
return CurFxnLocalOfs;
|
|
}
|
|
|
|
// DONE: sizeof(type)
|
|
// DONE: "sizeof expr"
|
|
// DONE: constant expressions
|
|
// DONE: collapse constant subexpressions into constants
|
|
STATIC
|
|
int exprval(int* idx, int* ExprTypeSynPtr, int* ConstExpr)
|
|
{
|
|
int tok;
|
|
int s;
|
|
int RightExprTypeSynPtr;
|
|
int oldIdxRight;
|
|
int oldSpRight;
|
|
int constExpr[3];
|
|
|
|
if (*idx < 0)
|
|
//error("exprval(): idx < 0\n");
|
|
errorInternal(5);
|
|
|
|
tok = stack[*idx][0];
|
|
s = stack[*idx][1];
|
|
|
|
--*idx;
|
|
|
|
oldIdxRight = *idx;
|
|
oldSpRight = sp;
|
|
|
|
switch (tok)
|
|
{
|
|
// Constants
|
|
case tokNumInt:
|
|
// return the constant's type: int
|
|
*ExprTypeSynPtr = SymIntSynPtr;
|
|
*ConstExpr = 1;
|
|
break;
|
|
case tokNumUint:
|
|
// return the constant's type: unsigned int
|
|
*ExprTypeSynPtr = SymUintSynPtr;
|
|
*ConstExpr = 1;
|
|
break;
|
|
|
|
// Identifiers
|
|
case tokIdent:
|
|
{
|
|
// DONE: support __func__
|
|
char* ident = IdentTable + s;
|
|
int synPtr, type;
|
|
#ifndef NO_FUNC_
|
|
if (CurFxnName && !strcmp(ident, "__func__"))
|
|
{
|
|
if (CurFxnNameLabel >= 0)
|
|
CurFxnNameLabel = -CurFxnNameLabel;
|
|
stack[*idx + 1][1] = SyntaxStack[SymFuncPtr][1];
|
|
synPtr = SymFuncPtr;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
synPtr = FindSymbol(ident);
|
|
// "Rename" static vars in function scope
|
|
if (synPtr >= 0 && synPtr + 1 < SyntaxStackCnt && SyntaxStack[synPtr + 1][0] == tokIdent)
|
|
{
|
|
s = stack[*idx + 1][1] = SyntaxStack[++synPtr][1];
|
|
ident = IdentTable + s;
|
|
}
|
|
}
|
|
|
|
if (synPtr < 0)
|
|
{
|
|
if ((*idx + 2 >= sp) || stack[*idx + 2][0] != ')')
|
|
error("Undeclared identifier '%s'\n", ident);
|
|
else
|
|
{
|
|
warning("Call to undeclared function '%s()'\n", ident);
|
|
// Implicitly declare "extern int ident();"
|
|
PushSyntax2(tokIdent, s);
|
|
PushSyntax('(');
|
|
PushSyntax(')');
|
|
PushSyntax(tokInt);
|
|
synPtr = FindSymbol(ident);
|
|
}
|
|
}
|
|
|
|
#ifndef NO_TYPEDEF_ENUM
|
|
if (synPtr + 1 < SyntaxStackCnt &&
|
|
SyntaxStack[synPtr + 1][0] == tokNumInt)
|
|
{
|
|
// this is an enum constant
|
|
stack[*idx + 1][0] = tokNumInt;
|
|
s = stack[*idx + 1][1] = SyntaxStack[synPtr + 1][1];
|
|
*ExprTypeSynPtr = SymIntSynPtr;
|
|
*ConstExpr = 1;
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
// DONE: this declaration is actually a type cast
|
|
if (!strncmp(IdentTable + SyntaxStack[synPtr][1], "(something", sizeof "(something)" - 1 - 1))
|
|
{
|
|
int castSize;
|
|
|
|
if (SyntaxStack[++synPtr][0] == tokLocalOfs) // TBD!!! is this really needed???
|
|
synPtr++;
|
|
|
|
s = exprval(idx, ExprTypeSynPtr, ConstExpr);
|
|
|
|
// can't cast void or structure/union to anything (except void)
|
|
if (*ExprTypeSynPtr >= 0 &&
|
|
(SyntaxStack[*ExprTypeSynPtr][0] == tokVoid ||
|
|
SyntaxStack[*ExprTypeSynPtr][0] == tokStructPtr) &&
|
|
SyntaxStack[synPtr][0] != tokVoid)
|
|
errorOpType();
|
|
|
|
// can't cast to function, array or structure/union
|
|
if (SyntaxStack[synPtr][0] == '(' ||
|
|
SyntaxStack[synPtr][0] == '[' ||
|
|
SyntaxStack[synPtr][0] == tokStructPtr)
|
|
errorOpType();
|
|
|
|
// will try to propagate constants through casts
|
|
if (!*ConstExpr &&
|
|
(stack[oldIdxRight - (oldSpRight - sp)][0] == tokNumInt ||
|
|
stack[oldIdxRight - (oldSpRight - sp)][0] == tokNumUint))
|
|
{
|
|
s = stack[oldIdxRight - (oldSpRight - sp)][1];
|
|
*ConstExpr = 1;
|
|
}
|
|
|
|
castSize = GetDeclSize(synPtr, 1);
|
|
|
|
// insertion of tokUChar, tokSChar and tokUnaryPlus transforms
|
|
// lvalues (values formed by dereferences) into rvalues
|
|
// (by hiding the dereferences), just as casts should do
|
|
switch (castSize)
|
|
{
|
|
case 1:
|
|
// cast to unsigned char
|
|
stack[oldIdxRight + 1 - (oldSpRight - sp)][0] = tokUChar;
|
|
s &= 0xFFu;
|
|
break;
|
|
case -1:
|
|
// cast to signed char
|
|
stack[oldIdxRight + 1 - (oldSpRight - sp)][0] = tokSChar;
|
|
if ((s &= 0xFFu) >= 0x80)
|
|
s -= 0x100;
|
|
break;
|
|
default:
|
|
#ifdef CAN_COMPILE_32BIT
|
|
if (castSize && castSize != SizeOfWord)
|
|
{
|
|
if (castSize == 2)
|
|
{
|
|
// cast to unsigned short
|
|
stack[oldIdxRight + 1 - (oldSpRight - sp)][0] = tokUShort;
|
|
s &= 0xFFFFu;
|
|
}
|
|
else
|
|
{
|
|
// cast to signed short
|
|
stack[oldIdxRight + 1 - (oldSpRight - sp)][0] = tokShort;
|
|
if ((s &= 0xFFFFu) >= 0x8000)
|
|
s -= 0x10000;
|
|
}
|
|
}
|
|
else // fallthrough
|
|
#endif
|
|
{
|
|
// cast to int/unsigned/pointer
|
|
if (stack[oldIdxRight - (oldSpRight - sp)][0] == tokUnaryStar)
|
|
// hide the dereference
|
|
stack[oldIdxRight + 1 - (oldSpRight - sp)][0] = tokUnaryPlus;
|
|
else
|
|
// nothing to hide, remove the cast
|
|
del(oldIdxRight + 1 - (oldSpRight - sp), 1);
|
|
}
|
|
break;
|
|
}
|
|
|
|
switch (SyntaxStack[synPtr][0])
|
|
{
|
|
case tokChar:
|
|
case tokSChar:
|
|
case tokUChar:
|
|
#ifdef CAN_COMPILE_32BIT
|
|
case tokShort:
|
|
case tokUShort:
|
|
#endif
|
|
case tokInt:
|
|
case tokUnsigned:
|
|
break;
|
|
default:
|
|
*ConstExpr = 0;
|
|
break;
|
|
}
|
|
|
|
*ExprTypeSynPtr = synPtr;
|
|
simplifyConstExpr(s, *ConstExpr, ExprTypeSynPtr, oldIdxRight + 1 - (oldSpRight - sp), *idx + 1);
|
|
break;
|
|
}
|
|
|
|
type = SymType(synPtr);
|
|
|
|
if (type == SymLocalVar || type == SymLocalArr)
|
|
{
|
|
// replace local variables/arrays with their local addresses
|
|
// (global variables/arrays' addresses are their names)
|
|
stack[*idx + 1][0] = tokLocalOfs;
|
|
stack[*idx + 1][1] = SyntaxStack[synPtr + 1][1];
|
|
}
|
|
if (type == SymLocalVar || type == SymGlobalVar)
|
|
{
|
|
// add implicit dereferences for local/global variables
|
|
ins2(*idx + 2, tokUnaryStar, GetDeclSize(synPtr, 1));
|
|
}
|
|
|
|
// return the identifier's type
|
|
while (SyntaxStack[synPtr][0] == tokIdent || SyntaxStack[synPtr][0] == tokLocalOfs)
|
|
synPtr++;
|
|
*ExprTypeSynPtr = synPtr;
|
|
}
|
|
*ConstExpr = 0;
|
|
break;
|
|
|
|
// sizeof operator
|
|
case tokSizeof:
|
|
s = exprval(idx, ExprTypeSynPtr, ConstExpr);
|
|
|
|
if (*ExprTypeSynPtr >= 0)
|
|
s = GetDeclSize(*ExprTypeSynPtr, 0);
|
|
else
|
|
s = SizeOfWord;
|
|
if (s == 0)
|
|
error("sizeof of incomplete type\n");
|
|
|
|
// replace sizeof with its numeric value
|
|
stack[oldIdxRight + 1 - (oldSpRight - sp)][0] = tokNumUint;
|
|
stack[oldIdxRight + 1 - (oldSpRight - sp)][1] = s;
|
|
|
|
// remove the sizeof's subexpression
|
|
del(*idx + 1, oldIdxRight - (oldSpRight - sp) - *idx);
|
|
|
|
*ExprTypeSynPtr = SymUintSynPtr;
|
|
*ConstExpr = 1;
|
|
break;
|
|
|
|
// Address unary operator
|
|
case tokUnaryAnd:
|
|
exprval(idx, ExprTypeSynPtr, ConstExpr);
|
|
|
|
if (*ExprTypeSynPtr >= 0 && SyntaxStack[*ExprTypeSynPtr][0] == '[')
|
|
{
|
|
// convert an array into a pointer to the array,
|
|
// remove the reference
|
|
*ExprTypeSynPtr = -*ExprTypeSynPtr;
|
|
del(oldIdxRight + 1 - (oldSpRight - sp), 1);
|
|
}
|
|
else if (*ExprTypeSynPtr >= 0 && SyntaxStack[*ExprTypeSynPtr][0] == '(')
|
|
{
|
|
// convert a function into a pointer to the function,
|
|
// remove the reference
|
|
*ExprTypeSynPtr = -*ExprTypeSynPtr;
|
|
del(oldIdxRight + 1 - (oldSpRight - sp), 1);
|
|
}
|
|
else if (*ExprTypeSynPtr >= 0 &&
|
|
oldIdxRight - (oldSpRight - sp) >= 0 &&
|
|
stack[oldIdxRight - (oldSpRight - sp)][0] == tokUnaryStar)
|
|
{
|
|
// it's an lvalue (with implicit or explicit dereference),
|
|
// convert it into its address,
|
|
// collapse/remove the reference and the dereference
|
|
*ExprTypeSynPtr = -*ExprTypeSynPtr;
|
|
del(oldIdxRight - (oldSpRight - sp), 2);
|
|
}
|
|
else
|
|
//error("exprval(): lvalue expected after '&'\n");
|
|
errorNotLvalue();
|
|
|
|
*ConstExpr = 0;
|
|
break;
|
|
|
|
// Indirection unary operator
|
|
case tokUnaryStar:
|
|
exprval(idx, ExprTypeSynPtr, ConstExpr);
|
|
|
|
if (*ExprTypeSynPtr < 0 || SyntaxStack[*ExprTypeSynPtr][0] == '*')
|
|
{
|
|
// type is a pointer to something,
|
|
// transform it into that something
|
|
if (*ExprTypeSynPtr < 0)
|
|
*ExprTypeSynPtr = -*ExprTypeSynPtr;
|
|
else
|
|
++*ExprTypeSynPtr;
|
|
nonVoidTypeCheck(*ExprTypeSynPtr);
|
|
if (SyntaxStack[*ExprTypeSynPtr][0] == tokStructPtr && !GetDeclSize(*ExprTypeSynPtr, 0))
|
|
// incomplete structure/union type
|
|
errorOpType();
|
|
// remove the dereference if that something is an array or a function
|
|
if (SyntaxStack[*ExprTypeSynPtr][0] == '[' ||
|
|
SyntaxStack[*ExprTypeSynPtr][0] == '(')
|
|
del(oldIdxRight + 1 - (oldSpRight - sp), 1);
|
|
// else add dereference size in bytes
|
|
else
|
|
stack[oldIdxRight + 1 - (oldSpRight - sp)][1] = GetDeclSize(*ExprTypeSynPtr, 1);
|
|
}
|
|
else if (SyntaxStack[*ExprTypeSynPtr][0] == '[')
|
|
{
|
|
// type is an array,
|
|
// transform it into the array's first element
|
|
// (a subarray, if type is a multidimensional array)
|
|
while (SyntaxStack[*ExprTypeSynPtr][0] != ']')
|
|
++*ExprTypeSynPtr;
|
|
++*ExprTypeSynPtr;
|
|
// remove the dereference if that element is an array
|
|
if (SyntaxStack[*ExprTypeSynPtr][0] == '[')
|
|
del(oldIdxRight + 1 - (oldSpRight - sp), 1);
|
|
// else add dereference size in bytes
|
|
else
|
|
stack[oldIdxRight + 1 - (oldSpRight - sp)][1] = GetDeclSize(*ExprTypeSynPtr, 1);
|
|
}
|
|
else
|
|
//error("exprval(): pointer/array expected after '*' / before '[]'\n");
|
|
errorOpType();
|
|
|
|
*ConstExpr = 0;
|
|
break;
|
|
|
|
// Additive binary operators
|
|
case '+':
|
|
case '-':
|
|
// WRONGISH: DONE: replace prefix ++/-- with +=1/-=1
|
|
// WRONG: DONE: replace postfix ++/-- with (+=1)-1/(-=1)+1
|
|
{
|
|
int ptrmask;
|
|
int oldIdxLeft, oldSpLeft;
|
|
int sl, sr;
|
|
int incSize;
|
|
sr = exprval(idx, &RightExprTypeSynPtr, &constExpr[1]);
|
|
oldIdxLeft = *idx;
|
|
oldSpLeft = sp;
|
|
sl = exprval(idx, ExprTypeSynPtr, &constExpr[0]);
|
|
|
|
if (tok == '+')
|
|
s = uint2int(sl + 0u + sr);
|
|
else
|
|
s = uint2int(sl + 0u - sr);
|
|
|
|
scalarTypeCheck(RightExprTypeSynPtr);
|
|
scalarTypeCheck(*ExprTypeSynPtr);
|
|
|
|
// Decay arrays to pointers to their first elements
|
|
decayArray(&RightExprTypeSynPtr, 1);
|
|
decayArray(ExprTypeSynPtr, 1);
|
|
|
|
ptrmask = (RightExprTypeSynPtr < 0) + (*ExprTypeSynPtr < 0) * 2;
|
|
|
|
// DONE: index/subscript scaling
|
|
if (ptrmask == 1 && tok == '+') // pointer in right-hand expression
|
|
{
|
|
incSize = GetDeclSize(-RightExprTypeSynPtr, 0);
|
|
|
|
if (constExpr[0]) // integer constant in left-hand expression
|
|
{
|
|
s = uint2int((sl + 0u) * incSize);
|
|
stack[oldIdxLeft - (oldSpLeft - sp)][1] = s;
|
|
// optimize a little if possible
|
|
{
|
|
int i = oldIdxRight - (oldSpRight - sp);
|
|
// Skip any type cast markers
|
|
while (stack[i][0] == tokUnaryPlus || stack[i][0] == '+')
|
|
i--;
|
|
// See if the pointer is an integer constant or a local variable offset
|
|
// and if it is, adjust it here instead of generating code for
|
|
// addition/subtraction
|
|
if (stack[i][0] == tokNumInt || stack[i][0] == tokNumUint || stack[i][0] == tokLocalOfs)
|
|
{
|
|
s = uint2int(stack[i][1] + 0u + s);
|
|
stack[i][1] = s; // TBD!!! need extra truncation?
|
|
del(oldIdxLeft - (oldSpLeft - sp), 1);
|
|
del(oldIdxRight - (oldSpRight - sp) + 1, 1);
|
|
}
|
|
}
|
|
}
|
|
else if (incSize != 1)
|
|
{
|
|
ins2(oldIdxLeft + 1 - (oldSpLeft - sp), tokNumInt, incSize);
|
|
ins(oldIdxLeft + 1 - (oldSpLeft - sp), '*');
|
|
}
|
|
|
|
*ExprTypeSynPtr = RightExprTypeSynPtr;
|
|
}
|
|
else if (ptrmask == 2) // pointer in left-hand expression
|
|
{
|
|
incSize = GetDeclSize(-*ExprTypeSynPtr, 0);
|
|
if (constExpr[1]) // integer constant in right-hand expression
|
|
{
|
|
s = uint2int((sr + 0u) * incSize);
|
|
stack[oldIdxRight - (oldSpRight - sp)][1] = s;
|
|
// optimize a little if possible
|
|
{
|
|
int i = oldIdxLeft - (oldSpLeft - sp);
|
|
// Skip any type cast markers
|
|
while (stack[i][0] == tokUnaryPlus || stack[i][0] == '+')
|
|
i--;
|
|
// See if the pointer is an integer constant or a local variable offset
|
|
// and if it is, adjust it here instead of generating code for
|
|
// addition/subtraction
|
|
if (stack[i][0] == tokNumInt || stack[i][0] == tokNumUint || stack[i][0] == tokLocalOfs)
|
|
{
|
|
if (tok == '-')
|
|
s = uint2int(-(s + 0u));
|
|
s = uint2int(stack[i][1] + 0u + s);
|
|
stack[i][1] = s; // TBD!!! need extra truncation?
|
|
del(oldIdxRight - (oldSpRight - sp), 2);
|
|
}
|
|
}
|
|
}
|
|
else if (incSize != 1)
|
|
{
|
|
ins2(oldIdxRight + 1 - (oldSpRight - sp), tokNumInt, incSize);
|
|
ins(oldIdxRight + 1 - (oldSpRight - sp), '*');
|
|
}
|
|
}
|
|
else if (ptrmask == 3 && tok == '-') // pointers in both expressions
|
|
{
|
|
incSize = GetDeclSize(-*ExprTypeSynPtr, 0);
|
|
// TBD!!! "ptr1-ptr2": better pointer type compatibility test needed, like compatCheck()?
|
|
if (incSize != GetDeclSize(-RightExprTypeSynPtr, 0))
|
|
//error("exprval(): incompatible pointers\n");
|
|
errorOpType();
|
|
if (incSize != 1)
|
|
{
|
|
ins2(oldIdxRight + 2 - (oldSpRight - sp), tokNumInt, incSize);
|
|
ins(oldIdxRight + 2 - (oldSpRight - sp), '/');
|
|
}
|
|
*ExprTypeSynPtr = SymIntSynPtr;
|
|
}
|
|
else if (ptrmask)
|
|
//error("exprval(): invalid combination of operands for '+' or '-'\n");
|
|
errorOpType();
|
|
|
|
// Promote the result from char to int (and from int to unsigned) if necessary
|
|
promoteType(ExprTypeSynPtr, &RightExprTypeSynPtr);
|
|
|
|
*ConstExpr = constExpr[0] && constExpr[1];
|
|
simplifyConstExpr(s, *ConstExpr, ExprTypeSynPtr, oldIdxRight + 1 - (oldSpRight - sp), *idx + 1);
|
|
}
|
|
break;
|
|
|
|
// Prefix/postfix increment/decrement unary operators
|
|
case tokInc:
|
|
case tokDec:
|
|
case tokPostInc:
|
|
case tokPostDec:
|
|
{
|
|
int incSize = 1;
|
|
int inc = tok == tokInc || tok == tokPostInc;
|
|
int post = tok == tokPostInc || tok == tokPostDec;
|
|
int opSize;
|
|
|
|
exprval(idx, ExprTypeSynPtr, ConstExpr);
|
|
|
|
scalarTypeCheck(*ExprTypeSynPtr);
|
|
|
|
decayArray(ExprTypeSynPtr, 1);
|
|
|
|
// lvalue check for ++, --
|
|
if (!(oldIdxRight - (oldSpRight - sp) >= 0 &&
|
|
stack[oldIdxRight - (oldSpRight - sp)][0] == tokUnaryStar))
|
|
//error("exprval(): lvalue expected for '++' or '--'\n");
|
|
errorNotLvalue();
|
|
|
|
// "remove" the lvalue dereference as we don't need
|
|
// to read the value and forget its location. We need to
|
|
// keep the lvalue location.
|
|
// Remember the operand size.
|
|
opSize = stack[oldIdxRight - (oldSpRight - sp)][1];
|
|
del(oldIdxRight - (oldSpRight - sp), 1);
|
|
|
|
if (*ExprTypeSynPtr < 0)
|
|
incSize = GetDeclSize(-*ExprTypeSynPtr, 0);
|
|
|
|
if (incSize == 1)
|
|
{
|
|
// store the operand size in the operator
|
|
stack[oldIdxRight + 1 - (oldSpRight - sp)][1] = opSize;
|
|
}
|
|
else
|
|
{
|
|
// replace ++/-- with "postfix" +=/-= incSize when incSize != 1
|
|
if (inc)
|
|
{
|
|
if (post)
|
|
stack[oldIdxRight + 1 - (oldSpRight - sp)][0] = tokPostAdd;
|
|
else
|
|
stack[oldIdxRight + 1 - (oldSpRight - sp)][0] = tokAssignAdd;
|
|
}
|
|
else
|
|
{
|
|
if (post)
|
|
stack[oldIdxRight + 1 - (oldSpRight - sp)][0] = tokPostSub;
|
|
else
|
|
stack[oldIdxRight + 1 - (oldSpRight - sp)][0] = tokAssignSub;
|
|
}
|
|
// store the operand size in the operator
|
|
stack[oldIdxRight + 1 - (oldSpRight - sp)][1] = opSize;
|
|
|
|
ins2(oldIdxRight + 1 - (oldSpRight - sp), tokNumInt, incSize);
|
|
}
|
|
|
|
*ConstExpr = 0;
|
|
}
|
|
break;
|
|
|
|
// Simple assignment binary operator
|
|
case '=':
|
|
{
|
|
int oldIdxLeft, oldSpLeft;
|
|
int opSize;
|
|
int structs;
|
|
|
|
exprval(idx, &RightExprTypeSynPtr, ConstExpr);
|
|
oldIdxLeft = *idx;
|
|
oldSpLeft = sp;
|
|
exprval(idx, ExprTypeSynPtr, ConstExpr);
|
|
|
|
nonVoidTypeCheck(RightExprTypeSynPtr);
|
|
nonVoidTypeCheck(*ExprTypeSynPtr);
|
|
|
|
decayArray(&RightExprTypeSynPtr, 0);
|
|
decayArray(ExprTypeSynPtr, 0);
|
|
|
|
if (!(oldIdxLeft - (oldSpLeft - sp) >= 0 &&
|
|
stack[oldIdxLeft - (oldSpLeft - sp)][0] == tokUnaryStar))
|
|
//error("exprval(): lvalue expected before '='\n");
|
|
errorNotLvalue();
|
|
|
|
structs = (RightExprTypeSynPtr >= 0 && SyntaxStack[RightExprTypeSynPtr][0] == tokStructPtr) +
|
|
(*ExprTypeSynPtr >= 0 && SyntaxStack[*ExprTypeSynPtr][0] == tokStructPtr) * 2;
|
|
if (structs)
|
|
{
|
|
int sz;
|
|
|
|
if (structs != 3 ||
|
|
SyntaxStack[RightExprTypeSynPtr][1] != SyntaxStack[*ExprTypeSynPtr][1])
|
|
errorOpType();
|
|
|
|
// TBD??? (a = b) should be an rvalue and so &(a = b) and (&(a = b))->c shouldn't be
|
|
// allowed, while (a = b).c should be allowed.
|
|
|
|
// transform "*psleft = *psright" into "*fxn(sizeof *psright, psright, psleft)"
|
|
/*
|
|
if (stack[oldIdxLeft - (oldSpLeft - sp)][0] != tokUnaryStar ||
|
|
stack[oldIdxRight - (oldSpRight - sp)][0] != tokUnaryStar)
|
|
errorInternal(18);
|
|
*/
|
|
stack[oldIdxLeft - (oldSpLeft - sp)][0] = ','; // replace '*' with ','
|
|
stack[oldIdxRight - (oldSpRight - sp)][0] = ','; // replace '*' with ','
|
|
|
|
sz = GetDeclSize(RightExprTypeSynPtr, 0);
|
|
|
|
stack[oldIdxRight + 1 - (oldSpRight - sp)][0] = tokNumUint; // replace '=' with "sizeof *psright"
|
|
stack[oldIdxRight + 1 - (oldSpRight - sp)][1] = sz;
|
|
|
|
ins(oldIdxRight + 2 - (oldSpRight - sp), ',');
|
|
|
|
if (!StructCpyLabel)
|
|
StructCpyLabel = LabelCnt++;
|
|
ins2(oldIdxRight + 2 - (oldSpRight - sp), tokIdent, AddNumericIdent__(StructCpyLabel));
|
|
|
|
ins2(oldIdxRight + 2 - (oldSpRight - sp), ')', SizeOfWord * 3);
|
|
ins2(oldIdxRight + 2 - (oldSpRight - sp), tokUnaryStar, 0); // use 0 deref size to drop meaningless dereferences
|
|
|
|
ins2(*idx + 1, '(', SizeOfWord * 3);
|
|
}
|
|
else
|
|
{
|
|
// "remove" the lvalue dereference as we don't need
|
|
// to read the value and forget its location. We need to
|
|
// keep the lvalue location.
|
|
opSize = stack[oldIdxLeft - (oldSpLeft - sp)][1];
|
|
// store the operand size in the operator
|
|
stack[oldIdxRight + 1 - (oldSpRight - sp)][1] = opSize;
|
|
del(oldIdxLeft - (oldSpLeft - sp), 1);
|
|
}
|
|
|
|
*ConstExpr = 0;
|
|
}
|
|
break;
|
|
|
|
// DONE: other assignment operators
|
|
|
|
// Arithmetic and bitwise unary operators
|
|
case '~':
|
|
case tokUnaryPlus:
|
|
case tokUnaryMinus:
|
|
s = exprval(idx, ExprTypeSynPtr, ConstExpr);
|
|
scalarTypeCheck(*ExprTypeSynPtr);
|
|
numericTypeCheck(*ExprTypeSynPtr);
|
|
switch (tok)
|
|
{
|
|
case '~': s = ~s; break;
|
|
case tokUnaryPlus: s = +s; break;
|
|
case tokUnaryMinus: s = uint2int(~(s - 1u)); break;
|
|
}
|
|
promoteType(ExprTypeSynPtr, ExprTypeSynPtr);
|
|
simplifyConstExpr(s, *ConstExpr, ExprTypeSynPtr, oldIdxRight + 1 - (oldSpRight - sp), *idx + 1);
|
|
break;
|
|
|
|
// Arithmetic and bitwise binary operators
|
|
case '*':
|
|
case '/':
|
|
case '%':
|
|
case tokLShift:
|
|
case tokRShift:
|
|
case '&':
|
|
case '^':
|
|
case '|':
|
|
{
|
|
// int oldIdxLeft, oldSpLeft;
|
|
int sr, sl;
|
|
int Unsigned;
|
|
sr = exprval(idx, &RightExprTypeSynPtr, &constExpr[1]);
|
|
// oldIdxLeft = *idx;
|
|
// oldSpLeft = sp;
|
|
sl = exprval(idx, ExprTypeSynPtr, &constExpr[0]);
|
|
|
|
scalarTypeCheck(RightExprTypeSynPtr);
|
|
scalarTypeCheck(*ExprTypeSynPtr);
|
|
|
|
numericTypeCheck(RightExprTypeSynPtr);
|
|
numericTypeCheck(*ExprTypeSynPtr);
|
|
|
|
*ConstExpr = constExpr[0] && constExpr[1];
|
|
|
|
Unsigned = SyntaxStack[*ExprTypeSynPtr][0] == tokUnsigned || SyntaxStack[RightExprTypeSynPtr][0] == tokUnsigned;
|
|
|
|
switch (tok)
|
|
{
|
|
// DONE: check for division overflows
|
|
case '/':
|
|
case '%':
|
|
*ConstExpr &= divCheckAndCalc(tok, &sl, sr, Unsigned, constExpr);
|
|
|
|
if (Unsigned)
|
|
{
|
|
if (tok == '/')
|
|
stack[oldIdxRight + 1 - (oldSpRight - sp)][0] = tokUDiv;
|
|
else
|
|
stack[oldIdxRight + 1 - (oldSpRight - sp)][0] = tokUMod;
|
|
}
|
|
break;
|
|
|
|
case '*':
|
|
sl = uint2int((sl + 0u) * sr);
|
|
break;
|
|
|
|
case tokLShift:
|
|
case tokRShift:
|
|
if (constExpr[1])
|
|
{
|
|
if (SyntaxStack[RightExprTypeSynPtr][0] != tokUnsigned)
|
|
sr = truncInt(sr);
|
|
else
|
|
sr = uint2int(truncUint(sr));
|
|
shiftCountCheck(&sr, oldIdxRight - (oldSpRight - sp), RightExprTypeSynPtr);
|
|
}
|
|
if (*ConstExpr)
|
|
{
|
|
if (tok == tokLShift)
|
|
{
|
|
// left shift is the same for signed and unsigned ints
|
|
sl = uint2int((sl + 0u) << sr);
|
|
}
|
|
else
|
|
{
|
|
if (SyntaxStack[*ExprTypeSynPtr][0] == tokUnsigned)
|
|
{
|
|
// right shift for unsigned ints
|
|
sl = uint2int(truncUint(sl) >> sr);
|
|
}
|
|
else if (sr)
|
|
{
|
|
// right shift for signed ints is arithmetic, sign-bit-preserving
|
|
// don't depend on the compiler's implementation, do it "manually"
|
|
sl = truncInt(sl);
|
|
sl = uint2int((truncUint(sl) >> sr) |
|
|
((sl < 0) * (~0u << (8 * SizeOfWord - sr))));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (SyntaxStack[*ExprTypeSynPtr][0] == tokUnsigned && tok == tokRShift)
|
|
stack[oldIdxRight + 1 - (oldSpRight - sp)][0] = tokURShift;
|
|
|
|
// ignore RightExprTypeSynPtr for the purpose of promotion/conversion of the result of <</>>
|
|
RightExprTypeSynPtr = SymIntSynPtr;
|
|
break;
|
|
|
|
case '&': sl &= sr; break;
|
|
case '^': sl ^= sr; break;
|
|
case '|': sl |= sr; break;
|
|
}
|
|
|
|
s = sl;
|
|
promoteType(ExprTypeSynPtr, &RightExprTypeSynPtr);
|
|
simplifyConstExpr(s, *ConstExpr, ExprTypeSynPtr, oldIdxRight + 1 - (oldSpRight - sp), *idx + 1);
|
|
}
|
|
break;
|
|
|
|
// Relational binary operators
|
|
// DONE: add (sub)tokens for unsigned >, >=, <, <= for pointers
|
|
case '<':
|
|
case '>':
|
|
case tokLEQ:
|
|
case tokGEQ:
|
|
case tokEQ:
|
|
case tokNEQ:
|
|
{
|
|
int ptrmask;
|
|
int sr, sl;
|
|
int Unsigned;
|
|
sr = exprval(idx, &RightExprTypeSynPtr, &constExpr[1]);
|
|
sl = exprval(idx, ExprTypeSynPtr, &constExpr[0]);
|
|
|
|
scalarTypeCheck(RightExprTypeSynPtr);
|
|
scalarTypeCheck(*ExprTypeSynPtr);
|
|
|
|
decayArray(&RightExprTypeSynPtr, 0);
|
|
decayArray(ExprTypeSynPtr, 0);
|
|
|
|
ptrmask = (RightExprTypeSynPtr < 0) + (*ExprTypeSynPtr < 0) * 2;
|
|
|
|
// Disallow >, <, >=, <= between a pointer and a number
|
|
if (ptrmask >= 1 && ptrmask <= 2 &&
|
|
tok != tokEQ && tok != tokNEQ)
|
|
//error("exprval(): Invalid/unsupported combination of compared operands\n");
|
|
errorOpType();
|
|
|
|
*ConstExpr = constExpr[0] && constExpr[1];
|
|
|
|
Unsigned = !ptrmask &&
|
|
(SyntaxStack[*ExprTypeSynPtr][0] == tokUnsigned || SyntaxStack[RightExprTypeSynPtr][0] == tokUnsigned);
|
|
|
|
if (*ConstExpr)
|
|
{
|
|
if (!Unsigned)
|
|
{
|
|
sl = truncInt(sl);
|
|
sr = truncInt(sr);
|
|
switch (tok)
|
|
{
|
|
case '<': sl = sl < sr; break;
|
|
case '>': sl = sl > sr; break;
|
|
case tokLEQ: sl = sl <= sr; break;
|
|
case tokGEQ: sl = sl >= sr; break;
|
|
case tokEQ: sl = sl == sr; break;
|
|
case tokNEQ: sl = sl != sr; break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sl = uint2int(truncUint(sl));
|
|
sr = uint2int(truncUint(sr));
|
|
switch (tok)
|
|
{
|
|
case '<': sl = sl + 0u < sr + 0u; break;
|
|
case '>': sl = sl + 0u > sr + 0u; break;
|
|
case tokLEQ: sl = sl + 0u <= sr + 0u; break;
|
|
case tokGEQ: sl = sl + 0u >= sr + 0u; break;
|
|
case tokEQ: sl = sl == sr; break;
|
|
case tokNEQ: sl = sl != sr; break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ptrmask || Unsigned)
|
|
{
|
|
// Pointer comparison should be unsigned
|
|
int t = tok;
|
|
switch (tok)
|
|
{
|
|
case '<': t = tokULess; break;
|
|
case '>': t = tokUGreater; break;
|
|
case tokLEQ: t = tokULEQ; break;
|
|
case tokGEQ: t = tokUGEQ; break;
|
|
}
|
|
if (t != tok)
|
|
stack[oldIdxRight + 1 - (oldSpRight - sp)][0] = t;
|
|
}
|
|
|
|
s = sl;
|
|
*ExprTypeSynPtr = SymIntSynPtr;
|
|
simplifyConstExpr(s, *ConstExpr, ExprTypeSynPtr, oldIdxRight + 1 - (oldSpRight - sp), *idx + 1);
|
|
}
|
|
break;
|
|
|
|
// implicit pseudo-conversion to _Bool of operands of && and ||
|
|
case tok_Bool:
|
|
s = exprval(idx, ExprTypeSynPtr, ConstExpr);
|
|
s = truncInt(s) != 0;
|
|
scalarTypeCheck(*ExprTypeSynPtr);
|
|
decayArray(ExprTypeSynPtr, 0);
|
|
*ExprTypeSynPtr = SymIntSynPtr;
|
|
simplifyConstExpr(s, *ConstExpr, ExprTypeSynPtr, oldIdxRight + 1 - (oldSpRight - sp), *idx + 1);
|
|
break;
|
|
|
|
// Logical binary operators
|
|
case tokLogAnd: // DONE: short-circuit
|
|
case tokLogOr: // DONE: short-circuit
|
|
{
|
|
int sr, sl;
|
|
|
|
// DONE: think of pushing a special short-circuit (jump-to) token
|
|
// to skip the rhs operand evaluation in && and ||
|
|
// DONE: add implicit "casts to _Bool" of && and || operands,
|
|
// do the same for control statements of if() while() and for(;;).
|
|
|
|
int sc = LabelCnt++;
|
|
// tag the logical operator as a numbered short-circuit jump target
|
|
stack[*idx + 1][1] = sc;
|
|
|
|
// insert "!= 0" for right-hand operand
|
|
switch (stack[*idx][0])
|
|
{
|
|
case '<':
|
|
case tokULess:
|
|
case '>':
|
|
case tokUGreater:
|
|
case tokLEQ:
|
|
case tokULEQ:
|
|
case tokGEQ:
|
|
case tokUGEQ:
|
|
case tokEQ:
|
|
case tokNEQ:
|
|
break;
|
|
default:
|
|
ins(++*idx, tok_Bool);
|
|
break;
|
|
}
|
|
|
|
sr = exprval(idx, &RightExprTypeSynPtr, &constExpr[1]);
|
|
|
|
// insert a reference to the short-circuit jump target
|
|
if (tok == tokLogAnd)
|
|
ins2(++*idx, tokShortCirc, sc);
|
|
else
|
|
ins2(++*idx, tokShortCirc, -sc);
|
|
// insert "!= 0" for left-hand operand
|
|
switch (stack[*idx - 1][0])
|
|
{
|
|
case '<':
|
|
case tokULess:
|
|
case '>':
|
|
case tokUGreater:
|
|
case tokLEQ:
|
|
case tokULEQ:
|
|
case tokGEQ:
|
|
case tokUGEQ:
|
|
case tokEQ:
|
|
case tokNEQ:
|
|
--*idx;
|
|
break;
|
|
default:
|
|
ins(*idx, tok_Bool);
|
|
break;
|
|
}
|
|
|
|
sl = exprval(idx, ExprTypeSynPtr, &constExpr[0]);
|
|
|
|
if (tok == tokLogAnd)
|
|
s = sl && sr;
|
|
else
|
|
s = sl || sr;
|
|
|
|
*ExprTypeSynPtr = SymIntSynPtr;
|
|
*ConstExpr = constExpr[0] && constExpr[1];
|
|
if (constExpr[0])
|
|
{
|
|
if (tok == tokLogAnd)
|
|
{
|
|
if (!sl)
|
|
*ConstExpr = 1, s = 0;
|
|
// TBD??? else can drop LHS expression
|
|
}
|
|
else
|
|
{
|
|
if (sl)
|
|
*ConstExpr = s = 1;
|
|
// TBD??? else can drop LHS expression
|
|
}
|
|
}
|
|
simplifyConstExpr(s, *ConstExpr, ExprTypeSynPtr, oldIdxRight + 1 - (oldSpRight - sp), *idx + 1);
|
|
}
|
|
break;
|
|
|
|
// Function call
|
|
case ')':
|
|
{
|
|
int tmpSynPtr, c;
|
|
int minParams, maxParams;
|
|
int firstParamSynPtr;
|
|
#ifndef NO_STRUCT_BY_VAL
|
|
int oldIdx, oldSp;
|
|
unsigned structSize = 0;
|
|
int retStruct = 0;
|
|
int retOfs = 0;
|
|
#endif
|
|
exprval(idx, ExprTypeSynPtr, ConstExpr);
|
|
|
|
if (!GetFxnInfo(*ExprTypeSynPtr, &minParams, &maxParams, ExprTypeSynPtr, &firstParamSynPtr))
|
|
//error("exprval(): function or function pointer expected\n");
|
|
errorOpType();
|
|
|
|
// DONE: validate the number of function arguments
|
|
// DONE: warnings on int<->pointer substitution in params/args
|
|
|
|
#ifndef NO_STRUCT_BY_VAL
|
|
// If a structure is returned, allocate space for it on the stack
|
|
// and pass its location as the first (implicit) argument.
|
|
if (ParseLevel &&
|
|
*ExprTypeSynPtr >= 0 &&
|
|
SyntaxStack[*ExprTypeSynPtr][0] == tokStructPtr)
|
|
{
|
|
unsigned sz = GetDeclSize(*ExprTypeSynPtr, 0);
|
|
// Make sure the return structure type is complete
|
|
if (!sz)
|
|
errorOpType();
|
|
retOfs = AllocLocal(sz);
|
|
// Transform fxn(args) into fxn(pretval, args)
|
|
ins(*idx + 1, ',');
|
|
ins2(*idx + 1, tokLocalOfs, retOfs);
|
|
retStruct = 1;
|
|
}
|
|
#endif
|
|
|
|
// evaluate function arguments
|
|
c = 0;
|
|
while (stack[*idx][0] != '(')
|
|
{
|
|
#ifndef NO_STRUCT_BY_VAL
|
|
int gotStructs;
|
|
#endif
|
|
// add a comma after the first (last to be pushed) argument,
|
|
// so all arguments can be pushed whenever a comma is encountered
|
|
if (!c)
|
|
ins(*idx + 1, ',');
|
|
|
|
#ifndef NO_STRUCT_BY_VAL
|
|
oldIdx = *idx;
|
|
oldSp = sp;
|
|
#endif
|
|
exprval(idx, &tmpSynPtr, ConstExpr);
|
|
//error("exprval(): function arguments cannot be of type 'void'\n");
|
|
|
|
#ifdef NO_STRUCT_BY_VAL
|
|
scalarTypeCheck(tmpSynPtr);
|
|
#else
|
|
nonVoidTypeCheck(tmpSynPtr);
|
|
|
|
// If the argument is a structure, push it by calling a dedicated function
|
|
gotStructs = tmpSynPtr >= 0 && SyntaxStack[tmpSynPtr][0] == tokStructPtr;
|
|
if (gotStructs)
|
|
{
|
|
unsigned sz = GetDeclSize(tmpSynPtr, 0);
|
|
int i = oldIdx - (oldSp - sp);
|
|
stack[i][0] = ')';
|
|
stack[i][1] = SizeOfWord * 2;
|
|
|
|
if (!StructPushLabel)
|
|
StructPushLabel = LabelCnt++;
|
|
|
|
// The code generator expects functions to return values.
|
|
// If a function argument is a value produced by another function,
|
|
// as is the case here, the code generator will naturally
|
|
// want/need to push something of the size of the machine word.
|
|
// This works perfectly with non-structures.
|
|
// But we only want to push the structure without pushing any other words.
|
|
// In order to avoid involving changes in the code generator,
|
|
// we make the function that pushes structures onto the stack
|
|
// push all words but the first one. The dedicated function will
|
|
// return this word and the code generator will push it.
|
|
// This is ugly.
|
|
|
|
ins2(i, tokIdent, AddNumericIdent__(StructPushLabel));
|
|
ins(i, ',');
|
|
i = *idx + 1;
|
|
ins(i, ',');
|
|
ins2(i, tokNumUint, uint2int(sz));
|
|
ins2(i, '(', SizeOfWord * 2);
|
|
|
|
if (sz > (unsigned)GenMaxLocalsSize())
|
|
errorVarSize();
|
|
// Structures will be padded to machine word boundary when pushed
|
|
sz = (sz + SizeOfWord - 1) & ~(SizeOfWord - 1u);
|
|
// Count the cumulative size of the pushed structures, excluding
|
|
// the first words that will be pushed by the code generator
|
|
if (structSize + sz < structSize)
|
|
errorVarSize();
|
|
structSize += sz - SizeOfWord;
|
|
if (structSize > (unsigned)GenMaxLocalsSize())
|
|
errorVarSize();
|
|
// TBD??? complete overflow checks (an expression may contain more than one call)?
|
|
}
|
|
#endif
|
|
|
|
if (++c > maxParams)
|
|
error("Too many function arguments\n");
|
|
|
|
#ifndef NO_STRUCT_BY_VAL
|
|
#ifndef CHECK_FXN_ARGS
|
|
#define CHECK_FXN_ARGS
|
|
#endif
|
|
#endif
|
|
|
|
#ifndef NO_EXTRA_WARNS
|
|
#ifndef CHECK_FXN_ARGS
|
|
#define CHECK_FXN_ARGS
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef CHECK_FXN_ARGS
|
|
// Issue a warning if the argument has to be a pointer but isn't and vice versa.
|
|
// DONE: struct type compat checks
|
|
// TBD??? Compare pointer types deeply as in compatCheck()???
|
|
// TBD??? Issue a similar warning for return values and initializers???
|
|
if (c <= minParams)
|
|
{
|
|
int t;
|
|
#ifndef NO_EXTRA_WARNS
|
|
int gotPtr = tmpSynPtr < 0;
|
|
int needPtr;
|
|
if (!gotPtr)
|
|
{
|
|
t = SyntaxStack[tmpSynPtr][0];
|
|
gotPtr = (t == '*') | (t == '[') | (t == '('); // arrays and functions decay to pointers
|
|
}
|
|
#endif
|
|
// Find the type of the formal parameter in the function declaration
|
|
while ((t = SyntaxStack[firstParamSynPtr][0]) != tokIdent)
|
|
{
|
|
if (t == '(')
|
|
{
|
|
// skip parameters in parameters
|
|
int c = 1;
|
|
while (c)
|
|
{
|
|
t = SyntaxStack[++firstParamSynPtr][0];
|
|
c += (t == '(') - (t == ')');
|
|
}
|
|
}
|
|
firstParamSynPtr++;
|
|
}
|
|
firstParamSynPtr++;
|
|
#ifndef NO_STRUCT_BY_VAL
|
|
gotStructs += (SyntaxStack[firstParamSynPtr][0] == tokStructPtr) * 2;
|
|
if (gotStructs)
|
|
{
|
|
// Structures must be of the same type
|
|
if (gotStructs != 3 ||
|
|
SyntaxStack[tmpSynPtr][1] != SyntaxStack[firstParamSynPtr][1])
|
|
errorOpType();
|
|
}
|
|
#endif
|
|
#ifndef NO_EXTRA_WARNS
|
|
needPtr = SyntaxStack[firstParamSynPtr][0] == '*';
|
|
if (needPtr != gotPtr &&
|
|
// Make an exception for integer constants equal to 0, treat them as NULL pointers
|
|
!(
|
|
needPtr &&
|
|
*ConstExpr &&
|
|
!stack[*idx + 1][1]
|
|
)
|
|
)
|
|
warning("Expected %spointer in argument %d\n", needPtr ? "" : "non-", c);
|
|
#endif
|
|
}
|
|
#endif // CHECK_FXN_ARGS
|
|
|
|
if (stack[*idx][0] == ',')
|
|
--*idx;
|
|
}
|
|
--*idx;
|
|
|
|
if (c < minParams)
|
|
error("Too few function arguments\n");
|
|
|
|
// store the cumulative argument size in the function call operators
|
|
{
|
|
int i = oldIdxRight + 1 - (oldSpRight - sp);
|
|
#ifndef NO_STRUCT_BY_VAL
|
|
// Count the implicit param/arg for returned structure
|
|
c += retStruct;
|
|
#endif
|
|
stack[1 + *idx][1] = stack[i][1] = c * SizeOfWord;
|
|
#ifndef NO_STRUCT_BY_VAL
|
|
// Correct the value by which the stack pointer
|
|
// will be incremented after the call
|
|
stack[i][1] += structSize;
|
|
// If a structure is returned, transform
|
|
// fxn(pretval, args) into *(fxn(pretval, args), pretval)
|
|
if (retStruct)
|
|
{
|
|
ins(i + 1, tokUnaryStar);
|
|
ins(i + 1, tokComma);
|
|
ins2(i + 1, tokLocalOfs, retOfs);
|
|
ins(i + 1, tokVoid);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
*ConstExpr = 0;
|
|
}
|
|
break;
|
|
|
|
// Binary comma operator
|
|
case tokComma:
|
|
{
|
|
int oldIdxLeft, oldSpLeft;
|
|
int retStruct = 0;
|
|
s = exprval(idx, &RightExprTypeSynPtr, &constExpr[1]);
|
|
oldIdxLeft = *idx;
|
|
oldSpLeft = sp;
|
|
|
|
// Signify uselessness of the result of the left operand's value
|
|
ins(*idx + 1, tokVoid);
|
|
|
|
exprval(idx, ExprTypeSynPtr, &constExpr[0]);
|
|
*ConstExpr = constExpr[0] && constExpr[1];
|
|
*ExprTypeSynPtr = RightExprTypeSynPtr;
|
|
retStruct = RightExprTypeSynPtr >= 0 && SyntaxStack[RightExprTypeSynPtr][0] == tokStructPtr;
|
|
|
|
if (*ConstExpr)
|
|
{
|
|
// both subexprs are const, remove both and comma
|
|
simplifyConstExpr(s, *ConstExpr, ExprTypeSynPtr, oldIdxRight + 1 - (oldSpRight - sp), *idx + 1);
|
|
}
|
|
else if (constExpr[0])
|
|
{
|
|
// only left subexpr is const, remove it
|
|
del(*idx + 1, oldIdxLeft - (oldSpLeft - sp) - *idx);
|
|
|
|
if (!retStruct)
|
|
// Ensure non-lvalue-ness of the result by changing comma to unary plus
|
|
// and thus hiding dereference, if any
|
|
stack[oldIdxRight + 1 - (oldSpRight - sp)][0] = tokUnaryPlus;
|
|
else
|
|
// However, (something, struct).member should still be allowed,
|
|
// so, comma needs to produce lvalue
|
|
del(oldIdxRight + 1 - (oldSpRight - sp), 1);
|
|
}
|
|
else if (retStruct)
|
|
{
|
|
// However, (something, struct).member should still be allowed,
|
|
// so, comma needs to produce lvalue. Swap comma and structure dereference.
|
|
int i = oldIdxRight + 1 - (oldSpRight - sp);
|
|
stack[i][0] = tokUnaryStar;
|
|
stack[i][1] = stack[i - 1][1];
|
|
stack[i - 1][0] = tokComma;
|
|
}
|
|
}
|
|
break;
|
|
|
|
// Compound assignment operators
|
|
case tokAssignMul: case tokAssignDiv: case tokAssignMod:
|
|
case tokAssignAdd: case tokAssignSub:
|
|
case tokAssignLSh: case tokAssignRSh:
|
|
case tokAssignAnd: case tokAssignXor: case tokAssignOr:
|
|
{
|
|
int ptrmask;
|
|
int oldIdxLeft, oldSpLeft;
|
|
int incSize;
|
|
int opSize;
|
|
int Unsigned;
|
|
int sr = exprval(idx, &RightExprTypeSynPtr, &constExpr[1]);
|
|
oldIdxLeft = *idx;
|
|
oldSpLeft = sp;
|
|
exprval(idx, ExprTypeSynPtr, &constExpr[0]);
|
|
|
|
scalarTypeCheck(RightExprTypeSynPtr);
|
|
scalarTypeCheck(*ExprTypeSynPtr);
|
|
|
|
decayArray(&RightExprTypeSynPtr, 1);
|
|
decayArray(ExprTypeSynPtr, 1);
|
|
|
|
if (!(oldIdxLeft - (oldSpLeft - sp) >= 0 &&
|
|
stack[oldIdxLeft - (oldSpLeft - sp)][0] == tokUnaryStar))
|
|
//error("exprval(): lvalue expected before %s\n", GetTokenName(tok));
|
|
errorNotLvalue();
|
|
|
|
// "remove" the lvalue dereference as we don't need
|
|
// to read the value and forget its location. We need to
|
|
// keep the lvalue location.
|
|
opSize = stack[oldIdxLeft - (oldSpLeft - sp)][1];
|
|
// store the operand size in the operator
|
|
stack[oldIdxRight + 1 - (oldSpRight - sp)][1] = opSize;
|
|
del(oldIdxLeft - (oldSpLeft - sp), 1);
|
|
|
|
ptrmask = (RightExprTypeSynPtr < 0) + (*ExprTypeSynPtr < 0) * 2;
|
|
|
|
Unsigned = !ptrmask &&
|
|
(SyntaxStack[*ExprTypeSynPtr][0] == tokUnsigned || SyntaxStack[RightExprTypeSynPtr][0] == tokUnsigned);
|
|
|
|
if (tok != tokAssignAdd && tok != tokAssignSub)
|
|
{
|
|
if (ptrmask)
|
|
//error("exprval(): invalid combination of operands for %s\n", GetTokenName(tok));
|
|
errorOpType();
|
|
}
|
|
else
|
|
{
|
|
// No pointer to the right of += and -=
|
|
if (ptrmask & 1)
|
|
//error("exprval(): invalid combination of operands for %s\n", GetTokenName(tok));
|
|
errorOpType();
|
|
}
|
|
|
|
if (tok == tokAssignLSh || tok == tokAssignRSh)
|
|
{
|
|
if (constExpr[1])
|
|
{
|
|
if (SyntaxStack[RightExprTypeSynPtr][0] != tokUnsigned)
|
|
sr = truncInt(sr);
|
|
else
|
|
sr = uint2int(truncUint(sr));
|
|
shiftCountCheck(&sr, oldIdxRight - (oldSpRight - sp), RightExprTypeSynPtr);
|
|
}
|
|
}
|
|
|
|
if (tok == tokAssignDiv || tok == tokAssignMod)
|
|
{
|
|
int t, sl = 0;
|
|
if (tok == tokAssignDiv)
|
|
t = '/';
|
|
else
|
|
t = '%';
|
|
divCheckAndCalc(t, &sl, sr, 1, constExpr);
|
|
}
|
|
|
|
// TBD??? replace +=/-= with prefix ++/-- if incSize == 1
|
|
if (ptrmask == 2) // left-hand expression
|
|
{
|
|
incSize = GetDeclSize(-*ExprTypeSynPtr, 0);
|
|
if (constExpr[1])
|
|
{
|
|
int t = uint2int(stack[oldIdxRight - (oldSpRight - sp)][1] * (incSize + 0u));
|
|
stack[oldIdxRight - (oldSpRight - sp)][1] = t;
|
|
}
|
|
else if (incSize != 1)
|
|
{
|
|
ins2(oldIdxRight + 1 - (oldSpRight - sp), tokNumInt, incSize);
|
|
ins(oldIdxRight + 1 - (oldSpRight - sp), '*');
|
|
}
|
|
}
|
|
else if (Unsigned)
|
|
{
|
|
int t = tok;
|
|
switch (tok)
|
|
{
|
|
case tokAssignDiv: t = tokAssignUDiv; break;
|
|
case tokAssignMod: t = tokAssignUMod; break;
|
|
case tokAssignRSh:
|
|
if (SyntaxStack[*ExprTypeSynPtr][0] == tokUnsigned)
|
|
t = tokAssignURSh;
|
|
break;
|
|
}
|
|
stack[oldIdxRight + 1 - (oldSpRight - sp)][0] = t;
|
|
}
|
|
|
|
*ConstExpr = 0;
|
|
}
|
|
break;
|
|
|
|
// Ternary/conditional operator
|
|
case '?':
|
|
{
|
|
int oldIdxLeft, oldSpLeft;
|
|
int sr, sl, smid;
|
|
int condTypeSynPtr;
|
|
int sc = (LabelCnt += 2) - 2;
|
|
int structs;
|
|
|
|
// "exprL ? exprMID : exprR" appears on the stack as
|
|
// "exprL exprR exprMID ?"
|
|
|
|
stack[*idx + 1][0] = tokLogAnd; // piggyback on && for CG (ugly, but simple)
|
|
stack[*idx + 1][1] = sc + 1;
|
|
smid = exprval(idx, ExprTypeSynPtr, &constExpr[1]);
|
|
|
|
ins2(*idx + 1, tokLogAnd, sc); // piggyback on && for CG (ugly, but simple)
|
|
|
|
ins2(*idx + 1, tokGoto, sc + 1); // jump to end of ?:
|
|
oldIdxLeft = *idx;
|
|
oldSpLeft = sp;
|
|
sr = exprval(idx, &RightExprTypeSynPtr, &constExpr[2]);
|
|
|
|
ins2(*idx + 1, tokShortCirc, -sc); // jump to mid if left is non-zero
|
|
sl = exprval(idx, &condTypeSynPtr, &constExpr[0]);
|
|
|
|
scalarTypeCheck(condTypeSynPtr);
|
|
|
|
decayArray(&RightExprTypeSynPtr, 0);
|
|
decayArray(ExprTypeSynPtr, 0);
|
|
promoteType(&RightExprTypeSynPtr, ExprTypeSynPtr);
|
|
promoteType(ExprTypeSynPtr, &RightExprTypeSynPtr);
|
|
|
|
// TBD??? move struct/union-related checks into compatChecks()
|
|
|
|
structs = (RightExprTypeSynPtr >= 0 && SyntaxStack[RightExprTypeSynPtr][0] == tokStructPtr) +
|
|
(*ExprTypeSynPtr >= 0 && SyntaxStack[*ExprTypeSynPtr][0] == tokStructPtr) * 2;
|
|
if (structs)
|
|
{
|
|
if (structs != 3 ||
|
|
SyntaxStack[RightExprTypeSynPtr][1] != SyntaxStack[*ExprTypeSynPtr][1])
|
|
errorOpType();
|
|
|
|
// transform "cond ? a : b" into "*(cond ? &a : &b)"
|
|
/*
|
|
if (stack[oldIdxLeft - (oldSpLeft - sp)][0] != tokUnaryStar ||
|
|
stack[oldIdxRight - (oldSpRight - sp)][0] != tokUnaryStar)
|
|
errorInternal(19);
|
|
*/
|
|
del(oldIdxLeft - (oldSpLeft - sp), 1); // delete '*'
|
|
del(oldIdxRight - (oldSpRight - sp), 1); // delete '*'
|
|
ins2(oldIdxRight + 2 - (oldSpRight - sp), tokUnaryStar, 0); // use 0 deref size to drop meaningless dereferences
|
|
}
|
|
else
|
|
{
|
|
compatCheck(ExprTypeSynPtr,
|
|
RightExprTypeSynPtr,
|
|
&constExpr[1],
|
|
oldIdxRight - (oldSpRight - sp),
|
|
oldIdxLeft - (oldSpLeft - sp));
|
|
}
|
|
|
|
*ConstExpr = s = 0;
|
|
|
|
if (constExpr[0])
|
|
{
|
|
if (truncUint(sl))
|
|
{
|
|
if (constExpr[1])
|
|
*ConstExpr = 1, s = smid;
|
|
// TBD??? else can drop LHS and RHS expressions
|
|
}
|
|
else
|
|
{
|
|
if (constExpr[2])
|
|
*ConstExpr = 1, s = sr;
|
|
// TBD??? else can drop LHS and MID expressions
|
|
}
|
|
}
|
|
simplifyConstExpr(s, *ConstExpr, ExprTypeSynPtr, oldIdxRight + 1 - (oldSpRight - sp), *idx + 1);
|
|
}
|
|
break;
|
|
|
|
// Postfix indirect structure/union member selection operator
|
|
case tokArrow:
|
|
{
|
|
int member, i, j = 0, c = 1, ofs = 0;
|
|
|
|
stack[*idx + 1][0] = '+'; // replace -> with +
|
|
member = stack[*idx][1]; // keep the member name, it will be replaced with member offset
|
|
stack[*idx][0] = tokNumInt;
|
|
|
|
--*idx;
|
|
exprval(idx, ExprTypeSynPtr, ConstExpr);
|
|
|
|
decayArray(ExprTypeSynPtr, 0);
|
|
|
|
if (*ExprTypeSynPtr >= 0 ||
|
|
SyntaxStack[-*ExprTypeSynPtr][0] != tokStructPtr)
|
|
error("Pointer to or structure or union expected\n");
|
|
|
|
i = SyntaxStack[-*ExprTypeSynPtr][1];
|
|
if (i + 2 > SyntaxStackCnt ||
|
|
(SyntaxStack[i][0] != tokStruct && SyntaxStack[i][0] != tokUnion) ||
|
|
SyntaxStack[i + 1][0] != tokTag)
|
|
errorInternal(20);
|
|
|
|
if (!GetDeclSize(i, 0))
|
|
// incomplete structure/union type
|
|
errorOpType();
|
|
|
|
i += 5; // step inside the {} body of the struct/union
|
|
while (c)
|
|
{
|
|
int t = SyntaxStack[i][0];
|
|
c += (t == '(') - (t == ')') + (t == '{') - (t == '}');
|
|
if (c == 1 &&
|
|
t == tokMemberIdent && SyntaxStack[i][1] == member &&
|
|
SyntaxStack[i + 1][0] == tokLocalOfs)
|
|
{
|
|
j = i;
|
|
ofs = SyntaxStack[i + 1][1];
|
|
}
|
|
i++;
|
|
}
|
|
if (!j)
|
|
error("Undefined structure or union member '%s'\n", IdentTable + member);
|
|
|
|
j += 2;
|
|
*ExprTypeSynPtr = -j; // type: pointer to member's type
|
|
|
|
stack[oldIdxRight - (oldSpRight - sp)][1] = ofs; // member offset within structure/union
|
|
|
|
// optimize a little, if possible
|
|
{
|
|
int i = oldIdxRight - (oldSpRight - sp) - 1;
|
|
// Skip any type cast markers
|
|
while (stack[i][0] == tokUnaryPlus)
|
|
i--;
|
|
// See if the pointer is an integer constant or a local variable offset
|
|
// and if it is, adjust it here instead of generating code for
|
|
// addition/subtraction
|
|
if (stack[i][0] == tokNumInt || stack[i][0] == tokNumUint || stack[i][0] == tokLocalOfs)
|
|
{
|
|
stack[i][1] = uint2int(stack[i][1] + 0u + ofs); // TBD!!! need extra truncation?
|
|
del(oldIdxRight - (oldSpRight - sp), 2);
|
|
}
|
|
}
|
|
|
|
*ConstExpr = 0;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
//error("exprval(): Unexpected token %s\n", GetTokenName(tok));
|
|
errorInternal(21);
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
STATIC
|
|
int ParseExpr(int tok, int* GotUnary, int* ExprTypeSynPtr, int* ConstExpr, int* ConstVal, int option, int option2)
|
|
{
|
|
int identFirst = tok == tokIdent;
|
|
#ifndef NO_STRUCT_BY_VAL
|
|
int oldOfs = CurFxnLocalOfs;
|
|
#endif
|
|
*ConstVal = *ConstExpr = 0;
|
|
*ExprTypeSynPtr = SymVoidSynPtr;
|
|
|
|
if (!ExprLevel++)
|
|
{
|
|
opsp = sp = 0;
|
|
PurgeStringTable();
|
|
}
|
|
|
|
if (option == '=')
|
|
push2(tokIdent, option2);
|
|
|
|
tok = expr(tok, GotUnary, option == ',' || option == '=');
|
|
|
|
if (tok == tokEof || strchr(",;:)]}", tok) == NULL)
|
|
//error("ParseExpr(): Unexpected token %s\n", GetTokenName(tok));
|
|
errorUnexpectedToken(tok);
|
|
|
|
if (option == '=')
|
|
{
|
|
push('=');
|
|
}
|
|
else if (option == tokGotoLabel && identFirst && tok == ':' && *GotUnary && sp == 1 && stack[sp - 1][0] == tokIdent)
|
|
{
|
|
// This is a label.
|
|
ExprLevel--;
|
|
return tokGotoLabel;
|
|
}
|
|
|
|
if (*GotUnary)
|
|
{
|
|
int j;
|
|
// Do this twice so we can see the stack before
|
|
// and after manipulations
|
|
for (j = 0; j < 2; j++)
|
|
{
|
|
#ifndef NO_ANNOTATIONS
|
|
int i;
|
|
GenStartCommentLine();
|
|
if (j) printf2("Expanded");
|
|
else printf2("RPN'ized");
|
|
printf2(" expression: \"");
|
|
for (i = 0; i < sp; i++)
|
|
{
|
|
int tok = stack[i][0];
|
|
switch (tok)
|
|
{
|
|
case tokNumInt:
|
|
printf2("%d", truncInt(stack[i][1]));
|
|
break;
|
|
case tokNumUint:
|
|
printf2("%uu", truncUint(stack[i][1]));
|
|
break;
|
|
case tokIdent:
|
|
{
|
|
char* p = IdentTable + stack[i][1];
|
|
if (isdigit(*p))
|
|
printf2("L");
|
|
printf2("%s", p);
|
|
}
|
|
break;
|
|
case tokShortCirc:
|
|
if (stack[i][1] >= 0)
|
|
printf2("[sh&&->%d]", stack[i][1]);
|
|
else
|
|
printf2("[sh||->%d]", -stack[i][1]);
|
|
break;
|
|
case tokLocalOfs:
|
|
printf2("(@%d)", truncInt(stack[i][1]));
|
|
break;
|
|
case tokUnaryStar:
|
|
if (j) printf2("*(%d)", stack[i][1]);
|
|
else printf2("*u");
|
|
break;
|
|
case '(': case ',':
|
|
if (!j) printf2("%c", tok);
|
|
// else printf2("\b");
|
|
break;
|
|
case ')':
|
|
if (j) printf2("(");
|
|
printf2("%c", tok);
|
|
if (j) printf2("%d", stack[i][1]);
|
|
break;
|
|
default:
|
|
printf2("%s", GetTokenName(tok));
|
|
if (j)
|
|
{
|
|
switch (tok)
|
|
{
|
|
case tokLogOr: case tokLogAnd:
|
|
printf2("[%d]", stack[i][1]);
|
|
break;
|
|
case '=':
|
|
case tokInc: case tokDec:
|
|
case tokPostInc: case tokPostDec:
|
|
case tokAssignAdd: case tokAssignSub:
|
|
case tokPostAdd: case tokPostSub:
|
|
case tokAssignMul:
|
|
case tokAssignDiv: case tokAssignMod:
|
|
case tokAssignUDiv: case tokAssignUMod:
|
|
case tokAssignLSh: case tokAssignRSh: case tokAssignURSh:
|
|
case tokAssignAnd: case tokAssignXor: case tokAssignOr:
|
|
printf2("(%d)", stack[i][1]);
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
printf2(" ");
|
|
}
|
|
printf2("\"\n");
|
|
#endif
|
|
if (!j)
|
|
{
|
|
int idx = sp - 1;
|
|
*ConstVal = exprval(&idx, ExprTypeSynPtr, ConstExpr);
|
|
// remove the unneeded unary +'s that have served their cast-substitute purpose
|
|
// also remove dereferences of size 0 (dereferences of pointers to structures)
|
|
for (idx = sp - 1; idx >= 0; idx--)
|
|
if (stack[idx][0] == tokUnaryPlus ||
|
|
(stack[idx][0] == tokUnaryStar && !stack[idx][1]))
|
|
del(idx, 1);
|
|
}
|
|
#ifndef NO_ANNOTATIONS
|
|
else if (*ConstExpr)
|
|
{
|
|
GenStartCommentLine();
|
|
|
|
switch (SyntaxStack[*ExprTypeSynPtr][0])
|
|
{
|
|
case tokChar:
|
|
case tokSChar:
|
|
case tokUChar:
|
|
#ifdef CAN_COMPILE_32BIT
|
|
case tokShort:
|
|
case tokUShort:
|
|
#endif
|
|
case tokInt:
|
|
printf2("Expression value: %d\n", truncInt(*ConstVal));
|
|
break;
|
|
default:
|
|
case tokUnsigned:
|
|
printf2("Expression value: %uu\n", truncUint(*ConstVal));
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
ExprLevel--;
|
|
#ifndef NO_STRUCT_BY_VAL
|
|
// Reclaim stack space used by temporary structure/union objects
|
|
// returned by functions
|
|
CurFxnLocalOfs = oldOfs;
|
|
#endif
|
|
|
|
return tok;
|
|
}
|
|
|
|
// smc.c code
|
|
|
|
#ifdef __SMALLER_C__
|
|
#ifdef DETERMINE_VA_LIST
|
|
// 2 if va_list is a one-element array containing a pointer
|
|
// (typical for x86 Open Watcom C/C++)
|
|
// 1 if va_list is a pointer
|
|
// (typical for Turbo C++, x86 gcc)
|
|
// 0 if va_list is something else, and
|
|
// the code may have long crashed by now
|
|
int VaListType = 0;
|
|
|
|
// Attempts to determine the type of va_list as
|
|
// expected by the standard library
|
|
STATIC
|
|
void DetermineVaListType(void)
|
|
{
|
|
void* testptr[2];
|
|
// hopefully enough space to sprintf() 3 pointers using "%p"
|
|
char testbuf[3][CHAR_BIT * sizeof(void*) + 1];
|
|
|
|
// TBD!!! This is not good. Really need the va_something macros.
|
|
// Test whether va_list is a pointer to the first optional argument or
|
|
// an array of one element containing said pointer
|
|
testptr[0] = &testptr[1];
|
|
testptr[1] = &testptr[0];
|
|
memset(testbuf, '\0', sizeof(testbuf));
|
|
sprintf(testbuf[0], "%p", testptr[0]);
|
|
sprintf(testbuf[1], "%p", testptr[1]);
|
|
vsprintf(testbuf[2], "%p", &testptr[0]);
|
|
if (!strcmp(testbuf[2], testbuf[0]))
|
|
{
|
|
// va_list is a pointer
|
|
VaListType = 1;
|
|
}
|
|
else if (!strcmp(testbuf[2], testbuf[1]))
|
|
{
|
|
// va_list is a one-element array containing a pointer
|
|
VaListType = 2;
|
|
}
|
|
else
|
|
{
|
|
// va_list is something else, and
|
|
// the code may have long crashed by now
|
|
printf("Internal error: Indeterminate underlying type of va_list\n");
|
|
exit(-1);
|
|
}
|
|
}
|
|
#endif // DETERMINE_VA_LIST
|
|
#endif // __SMALLER_C__
|
|
|
|
// Equivalent to puts() but outputs to OutFile
|
|
// if it's not NULL.
|
|
STATIC
|
|
int puts2(char* s)
|
|
{
|
|
int res;
|
|
if (OutFile)
|
|
{
|
|
// Turbo C++ 1.01's fputs() returns EOF if s is empty, which is wrong.
|
|
// Hence the workaround.
|
|
if (*s == '\0' || (res = fputs(s, OutFile)) >= 0)
|
|
{
|
|
// unlike puts(), fputs() doesn't append '\n', append it manually
|
|
res = fputc('\n', OutFile);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
res = puts(s);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
// Equivalent to printf() but outputs to OutFile
|
|
// if it's not NULL.
|
|
STATIC
|
|
int printf2(char* format, ...)
|
|
{
|
|
int res;
|
|
|
|
#ifndef __SMALLER_C__
|
|
va_list vl;
|
|
va_start(vl, format);
|
|
#else
|
|
void* vl = &format + 1;
|
|
#endif
|
|
|
|
#ifndef __SMALLER_C__
|
|
if (OutFile)
|
|
res = vfprintf(OutFile, format, vl);
|
|
else
|
|
res = vprintf(format, vl);
|
|
#else
|
|
// TBD!!! This is not good. Really need the va_something macros.
|
|
#ifdef DETERMINE_VA_LIST
|
|
if (VaListType == 2)
|
|
{
|
|
// va_list is a one-element array containing a pointer
|
|
if (OutFile)
|
|
res = vfprintf(OutFile, format, &vl);
|
|
else
|
|
res = vprintf(format, &vl);
|
|
}
|
|
else // if (VaListType == 1)
|
|
// fallthrough
|
|
#endif // DETERMINE_VA_LIST
|
|
{
|
|
// va_list is a pointer
|
|
if (OutFile)
|
|
res = vfprintf(OutFile, format, vl);
|
|
else
|
|
res = vprintf(format, vl);
|
|
}
|
|
#endif // __SMALLER_C__
|
|
|
|
#ifndef __SMALLER_C__
|
|
va_end(vl);
|
|
#endif
|
|
|
|
return res;
|
|
}
|
|
|
|
STATIC
|
|
void error(char* format, ...)
|
|
{
|
|
int i, fidx = FileCnt - 1 + !FileCnt;
|
|
#ifndef __SMALLER_C__
|
|
va_list vl;
|
|
va_start(vl, format);
|
|
#else
|
|
void* vl = &format + 1;
|
|
#endif
|
|
|
|
for (i = 0; i < FileCnt; i++)
|
|
if (Files[i])
|
|
fclose(Files[i]);
|
|
|
|
puts2("");
|
|
|
|
#ifndef NO_ANNOTATIONS
|
|
DumpSynDecls();
|
|
#endif
|
|
#ifndef NO_PREPROCESSOR
|
|
#ifndef NO_ANNOTATIONS
|
|
DumpMacroTable();
|
|
#endif
|
|
#endif
|
|
#ifndef NO_ANNOTATIONS
|
|
DumpIdentTable();
|
|
#endif
|
|
|
|
// using stdout implicitly instead of stderr explicitly because:
|
|
// - stderr can be a macro and it's unknown if standard headers
|
|
// aren't included (which is the case when SmallerC is compiled
|
|
// with itself and linked with some other compiler's standard
|
|
// libraries)
|
|
// - output to stderr can interfere/overlap with buffered
|
|
// output to stdout and the result may literally look ugly
|
|
|
|
GenStartCommentLine(); printf2("Compilation failed.\n");
|
|
|
|
if (OutFile)
|
|
fclose(OutFile);
|
|
|
|
printf("Error in \"%s\" (%d:%d)\n", FileNames[fidx], LineNo, LinePos);
|
|
|
|
#ifndef __SMALLER_C__
|
|
vprintf(format, vl);
|
|
#else
|
|
// TBD!!! This is not good. Really need the va_something macros.
|
|
#ifdef DETERMINE_VA_LIST
|
|
if (VaListType == 2)
|
|
{
|
|
// va_list is a one-element array containing a pointer
|
|
vprintf(format, &vl);
|
|
}
|
|
else // if (VaListType == 1)
|
|
// fallthrough
|
|
#endif // DETERMINE_VA_LIST
|
|
{
|
|
// va_list is a pointer
|
|
vprintf(format, vl);
|
|
}
|
|
#endif // __SMALLER_C__
|
|
|
|
#ifndef __SMALLER_C__
|
|
va_end(vl);
|
|
#endif
|
|
|
|
exit(-1);
|
|
}
|
|
|
|
STATIC
|
|
void warning(char* format, ...)
|
|
{
|
|
int fidx = FileCnt - 1 + !FileCnt;
|
|
#ifndef __SMALLER_C__
|
|
va_list vl;
|
|
va_start(vl, format);
|
|
#else
|
|
void* vl = &format + 1;
|
|
#endif
|
|
|
|
warnCnt++;
|
|
|
|
if (!(warnings && OutFile))
|
|
return;
|
|
|
|
printf("Warning in \"%s\" (%d:%d)\n", FileNames[fidx], LineNo, LinePos);
|
|
|
|
#ifndef __SMALLER_C__
|
|
vprintf(format, vl);
|
|
#else
|
|
// TBD!!! This is not good. Really need the va_something macros.
|
|
#ifdef DETERMINE_VA_LIST
|
|
if (VaListType == 2)
|
|
{
|
|
// va_list is a one-element array containing a pointer
|
|
vprintf(format, &vl);
|
|
}
|
|
else // if (VaListType == 1)
|
|
// fallthrough
|
|
#endif // DETERMINE_VA_LIST
|
|
{
|
|
// va_list is a pointer
|
|
vprintf(format, vl);
|
|
}
|
|
#endif // __SMALLER_C__
|
|
|
|
#ifndef __SMALLER_C__
|
|
va_end(vl);
|
|
#endif
|
|
}
|
|
|
|
STATIC
|
|
void errorFile(char* n)
|
|
{
|
|
error("Unable to open, read, write or close file \"%s\"\n", n);
|
|
}
|
|
|
|
STATIC
|
|
void errorFileName(void)
|
|
{
|
|
error("Invalid or too long file name or path name\n");
|
|
}
|
|
|
|
STATIC
|
|
void errorInternal(int n)
|
|
{
|
|
error("%d (internal)\n", n);
|
|
}
|
|
|
|
STATIC
|
|
void errorChrStr(void)
|
|
{
|
|
error("Invalid or unsupported character constant or string literal\n");
|
|
}
|
|
|
|
STATIC
|
|
void errorUnexpectedToken(int tok)
|
|
{
|
|
error("Unexpected token %s\n", (tok == tokIdent) ? TokenIdentName : GetTokenName(tok));
|
|
}
|
|
|
|
STATIC
|
|
void errorDirective(void)
|
|
{
|
|
error("Invalid or unsupported preprocessor directive\n");
|
|
}
|
|
|
|
STATIC
|
|
void errorCtrlOutOfScope(void)
|
|
{
|
|
error("break, continue, case or default in wrong scope\n");
|
|
}
|
|
|
|
STATIC
|
|
void errorDecl(void)
|
|
{
|
|
error("Invalid or unsupported declaration\n");
|
|
}
|
|
|
|
STATIC
|
|
void errorTagRedef(int ident)
|
|
{
|
|
error("Redefinition of type tagged '%s'\n", IdentTable + ident);
|
|
}
|
|
|
|
STATIC
|
|
void errorRedef(char* s)
|
|
{
|
|
error("Redefinition of identifier '%s'\n", s);
|
|
}
|
|
|
|
STATIC
|
|
void errorVarSize(void)
|
|
{
|
|
error("Variable(s) take(s) too much space\n");
|
|
}
|
|
|
|
STATIC
|
|
void errorInit(void)
|
|
{
|
|
error("Invalid or unsupported initialization\n");
|
|
}
|
|
|
|
STATIC
|
|
void errorUnexpectedVoid(void)
|
|
{
|
|
error("Unexpected declaration or expression of type void\n");
|
|
}
|
|
|
|
STATIC
|
|
void errorOpType(void)
|
|
{
|
|
error("Unexpected operand type\n");
|
|
}
|
|
|
|
STATIC
|
|
void errorNotLvalue(void)
|
|
{
|
|
error("lvalue expected\n");
|
|
}
|
|
|
|
STATIC
|
|
void errorNotConst(void)
|
|
{
|
|
error("Non-constant expression\n");
|
|
}
|
|
|
|
STATIC
|
|
void errorLongExpr(void)
|
|
{
|
|
error("Too long expression\n");
|
|
}
|
|
|
|
int tsd[] =
|
|
{
|
|
tokVoid, tokChar, tokInt,
|
|
tokSigned, tokUnsigned, tokShort,
|
|
tokStruct, tokUnion,
|
|
};
|
|
|
|
STATIC
|
|
int TokenStartsDeclaration(int t, int params)
|
|
{
|
|
#ifndef NO_TYPEDEF_ENUM
|
|
int CurScope;
|
|
#endif
|
|
unsigned i;
|
|
|
|
for (i = 0; i < sizeof tsd / sizeof tsd[0]; i++)
|
|
if (tsd[i] == t)
|
|
return 1;
|
|
|
|
return
|
|
#ifdef CAN_COMPILE_32BIT
|
|
(SizeOfWord != 2 && t == tokLong) ||
|
|
#endif
|
|
#ifndef NO_TYPEDEF_ENUM
|
|
t == tokEnum ||
|
|
(t == tokIdent &&
|
|
FindTypedef(TokenIdentName, &CurScope, 1) >= 0) ||
|
|
#endif
|
|
(!params && (t == tokExtern ||
|
|
#ifndef NO_TYPEDEF_ENUM
|
|
t == tokTypedef ||
|
|
#endif
|
|
t == tokStatic));
|
|
}
|
|
|
|
STATIC
|
|
void PushSyntax2(int t, int v)
|
|
{
|
|
if (SyntaxStackCnt >= SYNTAX_STACK_MAX)
|
|
error("Symbol table exhausted\n");
|
|
SyntaxStack[SyntaxStackCnt][0] = t;
|
|
SyntaxStack[SyntaxStackCnt++][1] = v;
|
|
}
|
|
|
|
STATIC
|
|
void PushSyntax(int t)
|
|
{
|
|
PushSyntax2(t, 0);
|
|
}
|
|
|
|
STATIC
|
|
void InsertSyntax2(int pos, int t, int v)
|
|
{
|
|
if (SyntaxStackCnt >= SYNTAX_STACK_MAX)
|
|
error("Symbol table exhausted\n");
|
|
memmove(SyntaxStack[pos + 1],
|
|
SyntaxStack[pos],
|
|
sizeof(SyntaxStack[0]) * (SyntaxStackCnt - pos));
|
|
SyntaxStack[pos][0] = t;
|
|
SyntaxStack[pos][1] = v;
|
|
SyntaxStackCnt++;
|
|
}
|
|
|
|
STATIC
|
|
void InsertSyntax(int pos, int t)
|
|
{
|
|
InsertSyntax2(pos, t, 0);
|
|
}
|
|
|
|
STATIC
|
|
void DeleteSyntax(int pos, int cnt)
|
|
{
|
|
memmove(SyntaxStack[pos],
|
|
SyntaxStack[pos + cnt],
|
|
sizeof(SyntaxStack[0]) * (SyntaxStackCnt - (pos + cnt)));
|
|
SyntaxStackCnt -= cnt;
|
|
}
|
|
|
|
STATIC
|
|
int FindSymbol(char* s)
|
|
{
|
|
int i;
|
|
|
|
// TBD!!! return declaration scope number so
|
|
// redeclarations can be reported if occur in the same scope.
|
|
|
|
// TBD??? Also, I could first use FindIdent() and then just look for the
|
|
// index into IdentTable[] instead of doing strcmp()
|
|
|
|
for (i = SyntaxStackCnt - 1; i >= 0; i--)
|
|
{
|
|
int t = SyntaxStack[i][0];
|
|
if (t == tokIdent &&
|
|
!strcmp(IdentTable + SyntaxStack[i][1], s))
|
|
{
|
|
return i;
|
|
}
|
|
|
|
if (t == ')')
|
|
{
|
|
// Skip over the function params
|
|
int c = -1;
|
|
while (c)
|
|
{
|
|
t = SyntaxStack[--i][0];
|
|
c += (t == '(') - (t == ')');
|
|
}
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
STATIC
|
|
int SymType(int SynPtr)
|
|
{
|
|
int local = 0;
|
|
|
|
if (SyntaxStack[SynPtr][0] == tokIdent)
|
|
SynPtr++;
|
|
|
|
if ((local = SyntaxStack[SynPtr][0] == tokLocalOfs) != 0)
|
|
SynPtr++;
|
|
|
|
switch (SyntaxStack[SynPtr][0])
|
|
{
|
|
case '(':
|
|
return SymFxn;
|
|
|
|
case '[':
|
|
if (local)
|
|
return SymLocalArr;
|
|
return SymGlobalArr;
|
|
|
|
default:
|
|
if (local)
|
|
return SymLocalVar;
|
|
return SymGlobalVar;
|
|
}
|
|
}
|
|
|
|
STATIC
|
|
int FindTaggedDecl(char* s, int start, int* CurScope)
|
|
{
|
|
int i;
|
|
|
|
*CurScope = 1;
|
|
|
|
for (i = start; i >= 0; i--)
|
|
{
|
|
int t = SyntaxStack[i][0];
|
|
if (t == tokTag &&
|
|
!strcmp(IdentTable + SyntaxStack[i][1], s))
|
|
{
|
|
return i - 1;
|
|
}
|
|
else if (t == ')')
|
|
{
|
|
// Skip over the function params
|
|
int c = -1;
|
|
while (c)
|
|
{
|
|
t = SyntaxStack[--i][0];
|
|
c += (t == '(') - (t == ')');
|
|
}
|
|
}
|
|
else if (t == '#')
|
|
{
|
|
// the scope has changed to the outer scope
|
|
*CurScope = 0;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
#ifndef NO_TYPEDEF_ENUM
|
|
// TBD??? rename this fxn? Cleanup/unify search functions?
|
|
STATIC
|
|
int FindTypedef(char* s, int* CurScope, int forUse)
|
|
{
|
|
int i;
|
|
|
|
*CurScope = 1;
|
|
|
|
for (i = SyntaxStackCnt - 1; i >= 0; i--)
|
|
{
|
|
int t = SyntaxStack[i][0];
|
|
if ((t == tokTypedef || t == tokIdent) &&
|
|
!strcmp(IdentTable + SyntaxStack[i][1], s))
|
|
{
|
|
// if the closest declaration isn't from typedef,
|
|
// (i.e. if it's a variable/function declaration),
|
|
// then the type is unknown for the purpose of
|
|
// declaring a variable of this type
|
|
if (forUse && t == tokIdent)
|
|
return -1;
|
|
return i;
|
|
}
|
|
|
|
if (t == ')')
|
|
{
|
|
// Skip over the function params
|
|
int c = -1;
|
|
while (c)
|
|
{
|
|
t = SyntaxStack[--i][0];
|
|
c += (t == '(') - (t == ')');
|
|
}
|
|
}
|
|
else if (t == '#')
|
|
{
|
|
// the scope has changed to the outer scope
|
|
*CurScope = 0;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
#endif
|
|
|
|
STATIC
|
|
int GetDeclSize(int SyntaxPtr, int SizeForDeref)
|
|
{
|
|
int i;
|
|
unsigned size = 1;
|
|
int arr = 0;
|
|
|
|
if (SyntaxPtr < 0)
|
|
errorInternal(10);
|
|
|
|
for (i = SyntaxPtr; i < SyntaxStackCnt; i++)
|
|
{
|
|
int tok = SyntaxStack[i][0];
|
|
switch (tok)
|
|
{
|
|
case tokIdent: // skip leading identifiers, if any
|
|
case tokLocalOfs: // skip local var offset, too
|
|
break;
|
|
case tokChar:
|
|
case tokSChar:
|
|
if (!arr && ((tok == tokSChar) || CharIsSigned) && SizeForDeref)
|
|
return -1; // 1 byte, needing sign extension when converted to int/unsigned int
|
|
// fallthrough
|
|
case tokUChar:
|
|
return uint2int(size);
|
|
#ifdef CAN_COMPILE_32BIT
|
|
case tokShort:
|
|
if (!arr && SizeForDeref)
|
|
return -2; // 2 bytes, needing sign extension when converted to int/unsigned int
|
|
// fallthrough
|
|
case tokUShort:
|
|
if (size * 2 / 2 != size)
|
|
//error("Variable too big\n");
|
|
errorVarSize();
|
|
size *= 2;
|
|
if (size != truncUint(size))
|
|
//error("Variable too big\n");
|
|
errorVarSize();
|
|
return uint2int(size);
|
|
#endif
|
|
case tokInt:
|
|
case tokUnsigned:
|
|
case '*':
|
|
case '(': // size of fxn = size of ptr for now
|
|
if (size * SizeOfWord / SizeOfWord != size)
|
|
//error("Variable too big\n");
|
|
errorVarSize();
|
|
size *= SizeOfWord;
|
|
if (size != truncUint(size))
|
|
//error("Variable too big\n");
|
|
errorVarSize();
|
|
return uint2int(size);
|
|
case '[':
|
|
if (SyntaxStack[i + 1][0] != tokNumInt && SyntaxStack[i + 1][0] != tokNumUint)
|
|
errorInternal(11);
|
|
if (SyntaxStack[i + 1][1] &&
|
|
size * SyntaxStack[i + 1][1] / SyntaxStack[i + 1][1] != size)
|
|
//error("Variable too big\n");
|
|
errorVarSize();
|
|
size *= SyntaxStack[i + 1][1];
|
|
if (size != truncUint(size))
|
|
//error("Variable too big\n");
|
|
errorVarSize();
|
|
i += 2;
|
|
arr = 1;
|
|
break;
|
|
case tokStructPtr:
|
|
// follow the "type pointer"
|
|
i = SyntaxStack[i][1] - 1;
|
|
break;
|
|
case tokStruct:
|
|
case tokUnion:
|
|
if (i + 2 < SyntaxStackCnt && SyntaxStack[i + 2][0] == tokSizeof && !SizeForDeref)
|
|
{
|
|
unsigned s = SyntaxStack[i + 2][1];
|
|
if (s && size * s / s != size)
|
|
errorVarSize();
|
|
size *= s;
|
|
if (size != truncUint(size))
|
|
errorVarSize();
|
|
return uint2int(size);
|
|
}
|
|
return 0;
|
|
case tokVoid:
|
|
return 0;
|
|
default:
|
|
errorInternal(12);
|
|
}
|
|
}
|
|
|
|
errorInternal(13);
|
|
return 0;
|
|
}
|
|
|
|
STATIC
|
|
int GetDeclAlignment(int SyntaxPtr)
|
|
{
|
|
int i;
|
|
|
|
if (SyntaxPtr < 0)
|
|
errorInternal(14);
|
|
|
|
for (i = SyntaxPtr; i < SyntaxStackCnt; i++)
|
|
{
|
|
int tok = SyntaxStack[i][0];
|
|
switch (tok)
|
|
{
|
|
case tokIdent: // skip leading identifiers, if any
|
|
case tokLocalOfs: // skip local var offset, too
|
|
break;
|
|
case tokChar:
|
|
case tokSChar:
|
|
case tokUChar:
|
|
return 1;
|
|
#ifdef CAN_COMPILE_32BIT
|
|
case tokShort:
|
|
case tokUShort:
|
|
return 2;
|
|
#endif
|
|
case tokInt:
|
|
case tokUnsigned:
|
|
case '*':
|
|
case '(':
|
|
return SizeOfWord;
|
|
case '[':
|
|
if (SyntaxStack[i + 1][0] != tokNumInt && SyntaxStack[i + 1][0] != tokNumUint)
|
|
errorInternal(15);
|
|
i += 2;
|
|
break;
|
|
case tokStructPtr:
|
|
// follow the "type pointer"
|
|
i = SyntaxStack[i][1] - 1;
|
|
break;
|
|
case tokStruct:
|
|
case tokUnion:
|
|
if (i + 3 < SyntaxStackCnt && SyntaxStack[i + 2][0] == tokSizeof)
|
|
{
|
|
return SyntaxStack[i + 3][1];
|
|
}
|
|
return 1;
|
|
case tokVoid:
|
|
return 1;
|
|
default:
|
|
errorInternal(16);
|
|
}
|
|
}
|
|
|
|
errorInternal(17);
|
|
return 0;
|
|
}
|
|
|
|
#ifndef NO_ANNOTATIONS
|
|
STATIC
|
|
void DumpDecl(int SyntaxPtr, int IsParam)
|
|
{
|
|
int i;
|
|
int icnt = 0;
|
|
|
|
if (SyntaxPtr < 0)
|
|
return;
|
|
|
|
for (i = SyntaxPtr; i < SyntaxStackCnt; i++)
|
|
{
|
|
int tok = SyntaxStack[i][0];
|
|
int v = SyntaxStack[i][1];
|
|
switch (tok)
|
|
{
|
|
case tokLocalOfs:
|
|
printf2("(@%d): ", truncInt(v));
|
|
break;
|
|
|
|
case tokIdent:
|
|
if (++icnt > 1 && !IsParam) // show at most one declaration, except params
|
|
return;
|
|
|
|
GenStartCommentLine();
|
|
|
|
if (ParseLevel == 0)
|
|
printf2("glb ");
|
|
else if (IsParam)
|
|
printf2("prm ");
|
|
else
|
|
printf2("loc ");
|
|
|
|
{
|
|
int j;
|
|
for (j = 0; j < ParseLevel * 4; j++)
|
|
printf2(" ");
|
|
}
|
|
|
|
if (IsParam && !strcmp(IdentTable + v, "<something>") && (i + 1 < SyntaxStackCnt))
|
|
{
|
|
if (SyntaxStack[i + 1][0] == tokEllipsis)
|
|
continue;
|
|
}
|
|
|
|
printf2("%s : ", IdentTable + v);
|
|
break;
|
|
|
|
case '[':
|
|
printf2("[");
|
|
break;
|
|
|
|
case tokNumInt:
|
|
printf2("%d", truncInt(v));
|
|
break;
|
|
case tokNumUint:
|
|
printf2("%uu", truncUint(v));
|
|
break;
|
|
|
|
case ']':
|
|
printf2("] ");
|
|
break;
|
|
|
|
case '(':
|
|
{
|
|
int noparams;
|
|
// Skip over the params to the base type
|
|
int j = ++i, c = 1;
|
|
while (c)
|
|
{
|
|
int t = SyntaxStack[j++][0];
|
|
c += (t == '(') - (t == ')');
|
|
}
|
|
|
|
noparams = (i + 1 == j) || (SyntaxStack[i + 1][0] == tokVoid);
|
|
|
|
printf2("(");
|
|
|
|
// Print the params (recursively)
|
|
if (noparams)
|
|
{
|
|
// Don't recurse if it's "fxn()" or "fxn(void)"
|
|
if (i + 1 != j)
|
|
printf2("void");
|
|
}
|
|
else
|
|
{
|
|
puts2("");
|
|
ParseLevel++;
|
|
DumpDecl(i, 1);
|
|
ParseLevel--;
|
|
}
|
|
|
|
// Continue normally
|
|
i = j - 1;
|
|
if (!noparams)
|
|
{
|
|
GenStartCommentLine();
|
|
printf2(" ");
|
|
{
|
|
int j;
|
|
for (j = 0; j < ParseLevel * 4; j++)
|
|
printf2(" ");
|
|
}
|
|
}
|
|
|
|
printf2(") ");
|
|
}
|
|
break;
|
|
|
|
case ')': // end of param list
|
|
return;
|
|
|
|
case tokStructPtr:
|
|
DumpDecl(v, 0);
|
|
break;
|
|
|
|
default:
|
|
switch (tok)
|
|
{
|
|
case tokVoid:
|
|
case tokChar:
|
|
case tokSChar:
|
|
case tokUChar:
|
|
#ifdef CAN_COMPILE_32BIT
|
|
case tokShort:
|
|
case tokUShort:
|
|
#endif
|
|
case tokInt:
|
|
case tokUnsigned:
|
|
case tokEllipsis:
|
|
printf2("%s\n", GetTokenName(tok));
|
|
break;
|
|
default:
|
|
printf2("%s ", GetTokenName(tok));
|
|
break;
|
|
case tokTag:
|
|
printf2("%s\n", IdentTable + v);
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifndef NO_ANNOTATIONS
|
|
STATIC
|
|
void DumpSynDecls(void)
|
|
{
|
|
int used = SyntaxStackCnt * sizeof SyntaxStack[0];
|
|
int total = SYNTAX_STACK_MAX * sizeof SyntaxStack[0];
|
|
puts2("");
|
|
GenStartCommentLine(); printf2("Syntax/declaration table/stack:\n");
|
|
GenStartCommentLine(); printf2("Bytes used: %d/%d\n\n", used, total);
|
|
}
|
|
#endif
|
|
|
|
STATIC
|
|
int ParseArrayDimension(int AllowEmptyDimension)
|
|
{
|
|
int tok;
|
|
int gotUnary, synPtr, constExpr, exprVal;
|
|
unsigned exprValU;
|
|
int oldssp, oldesp, undoIdents;
|
|
|
|
tok = GetToken();
|
|
// DONE: support arbitrary constant expressions
|
|
oldssp = SyntaxStackCnt;
|
|
oldesp = sp;
|
|
undoIdents = IdentTableLen;
|
|
tok = ParseExpr(tok, &gotUnary, &synPtr, &constExpr, &exprVal, 0, 0);
|
|
IdentTableLen = undoIdents; // remove all temporary identifier names from e.g. "sizeof"
|
|
SyntaxStackCnt = oldssp; // undo any temporary declarations from e.g. "sizeof" in the expression
|
|
sp = oldesp;
|
|
|
|
if (tok != ']')
|
|
//error("ParseArrayDimension(): Unsupported or invalid array dimension (token %s)\n", GetTokenName(tok));
|
|
errorUnexpectedToken(tok);
|
|
|
|
if (!gotUnary)
|
|
{
|
|
if (!AllowEmptyDimension)
|
|
//error("ParseArrayDimension(): missing array dimension\n");
|
|
errorUnexpectedToken(tok);
|
|
// Empty dimension is dimension of 0
|
|
exprVal = 0;
|
|
}
|
|
else
|
|
{
|
|
if (!constExpr)
|
|
//error("ParseArrayDimension(): non-constant array dimension\n");
|
|
errorNotConst();
|
|
|
|
exprValU = truncUint(exprVal);
|
|
exprVal = truncInt(exprVal);
|
|
|
|
promoteType(&synPtr, &synPtr);
|
|
if ((SyntaxStack[synPtr][0] == tokInt && exprVal < 1) || (SyntaxStack[synPtr][0] == tokUnsigned && exprValU < 1))
|
|
error("Array dimension less than 1\n");
|
|
|
|
exprVal = uint2int(exprValU);
|
|
}
|
|
|
|
PushSyntax2(tokNumUint, exprVal);
|
|
return tok;
|
|
}
|
|
|
|
STATIC
|
|
void ParseFxnParams(int tok);
|
|
STATIC
|
|
int ParseBlock(int BrkCntTarget[2], int casesIdx);
|
|
STATIC
|
|
void AddFxnParamSymbols(int SyntaxPtr);
|
|
|
|
STATIC
|
|
int ParseBase(int tok, int base[2])
|
|
{
|
|
#ifndef NO_TYPEDEF_ENUM
|
|
int CurScope;
|
|
#endif
|
|
int valid = 1;
|
|
base[1] = 0;
|
|
|
|
if ((tok == tokVoid) |
|
|
(tok == tokChar) |
|
|
(tok == tokInt))
|
|
{
|
|
*base = tok;
|
|
tok = GetToken();
|
|
}
|
|
else if (tok == tokShort
|
|
#ifdef CAN_COMPILE_32BIT
|
|
|| tok == tokLong
|
|
#endif
|
|
)
|
|
{
|
|
*base = tok;
|
|
tok = GetToken();
|
|
if (tok == tokInt)
|
|
tok = GetToken();
|
|
}
|
|
else if ((tok == tokSigned) |
|
|
(tok == tokUnsigned))
|
|
{
|
|
int sign = tok;
|
|
tok = GetToken();
|
|
|
|
if (tok == tokChar)
|
|
{
|
|
if (sign == tokUnsigned)
|
|
*base = tokUChar;
|
|
else
|
|
*base = tokSChar;
|
|
tok = GetToken();
|
|
}
|
|
else if (tok == tokShort)
|
|
{
|
|
if (sign == tokUnsigned)
|
|
*base = tokUShort;
|
|
else
|
|
*base = tokShort;
|
|
tok = GetToken();
|
|
if (tok == tokInt)
|
|
tok = GetToken();
|
|
}
|
|
#ifdef CAN_COMPILE_32BIT
|
|
else if (tok == tokLong)
|
|
{
|
|
if (sign == tokUnsigned)
|
|
*base = tokULong;
|
|
else
|
|
*base = tokLong;
|
|
tok = GetToken();
|
|
if (tok == tokInt)
|
|
tok = GetToken();
|
|
}
|
|
#endif
|
|
else
|
|
{
|
|
if (sign == tokUnsigned)
|
|
*base = tokUnsigned;
|
|
else
|
|
*base = tokInt;
|
|
if (tok == tokInt)
|
|
tok = GetToken();
|
|
}
|
|
}
|
|
else if ((tok == tokStruct) |
|
|
(tok == tokUnion)
|
|
#ifndef NO_TYPEDEF_ENUM
|
|
| (tok == tokEnum)
|
|
#endif
|
|
)
|
|
{
|
|
int structType = tok;
|
|
int empty = 1;
|
|
int typePtr = SyntaxStackCnt;
|
|
int gotTag = 0, tagIdent = 0, declPtr = -1, curScope = 0;
|
|
|
|
tok = GetToken();
|
|
|
|
if (tok == tokIdent)
|
|
{
|
|
// this is a structure/union/enum tag
|
|
gotTag = 1;
|
|
declPtr = FindTaggedDecl(TokenIdentName, SyntaxStackCnt - 1, &curScope);
|
|
tagIdent = AddIdent(TokenIdentName);
|
|
|
|
if (declPtr >= 0)
|
|
{
|
|
// Within the same scope we can't declare more than one union, structure or enum
|
|
// with the same tag.
|
|
// There's one common tag namespace for structures, unions and enumerations.
|
|
if (curScope && SyntaxStack[declPtr][0] != structType)
|
|
errorTagRedef(tagIdent);
|
|
}
|
|
else if (ParamLevel)
|
|
{
|
|
// new structure/union/enum declarations aren't supported in function parameters
|
|
errorDecl();
|
|
}
|
|
|
|
tok = GetToken();
|
|
}
|
|
else
|
|
{
|
|
// structure/union/enum declarations aren't supported in expressions
|
|
if (ExprLevel)
|
|
errorDecl();
|
|
PushSyntax(structType);
|
|
PushSyntax2(tokTag, AddIdent("<something>"));
|
|
}
|
|
|
|
if (tok == '{')
|
|
{
|
|
unsigned structInfo[4], sz, alignment, tmp;
|
|
|
|
// new structure/union/enum declarations aren't supported in expressions and function parameters
|
|
if (ExprLevel || ParamLevel)
|
|
errorDecl();
|
|
|
|
if (gotTag)
|
|
{
|
|
// Cannot redefine a tagged structure/union/enum within the same scope
|
|
if (declPtr >= 0 &&
|
|
curScope &&
|
|
((declPtr + 2 < SyntaxStackCnt && SyntaxStack[declPtr + 2][0] == tokSizeof)
|
|
#ifndef NO_TYPEDEF_ENUM
|
|
|| structType == tokEnum
|
|
#endif
|
|
))
|
|
errorTagRedef(tagIdent);
|
|
|
|
PushSyntax(structType);
|
|
PushSyntax2(tokTag, tagIdent);
|
|
}
|
|
|
|
#ifndef NO_TYPEDEF_ENUM
|
|
if (structType == tokEnum)
|
|
{
|
|
int val = 0;
|
|
int CurScope;
|
|
|
|
tok = GetToken();
|
|
while (tok != '}')
|
|
{
|
|
char* s;
|
|
int ident;
|
|
|
|
if (tok != tokIdent)
|
|
errorUnexpectedToken(tok);
|
|
|
|
s = TokenIdentName;
|
|
if (FindTypedef(s, &CurScope, 0) >= 0 && CurScope)
|
|
errorRedef(s);
|
|
|
|
ident = AddIdent(s);
|
|
|
|
empty = 0;
|
|
|
|
tok = GetToken();
|
|
if (tok == '=')
|
|
{
|
|
int gotUnary, synPtr, constExpr;
|
|
int oldssp, oldesp, undoIdents;
|
|
|
|
oldssp = SyntaxStackCnt;
|
|
oldesp = sp;
|
|
undoIdents = IdentTableLen;
|
|
|
|
tok = ParseExpr(GetToken(), &gotUnary, &synPtr, &constExpr, &val, ',', 0);
|
|
|
|
IdentTableLen = undoIdents; // remove all temporary identifier names from e.g. "sizeof"
|
|
SyntaxStackCnt = oldssp; // undo any temporary declarations from e.g. "sizeof" in the expression
|
|
sp = oldesp;
|
|
|
|
if (!gotUnary)
|
|
errorUnexpectedToken(tok);
|
|
if (!constExpr)
|
|
errorNotConst();
|
|
}
|
|
|
|
PushSyntax2(tokIdent, ident);
|
|
PushSyntax2(tokNumInt, val);
|
|
val = uint2int(val + 1u);
|
|
|
|
if (tok == ',')
|
|
tok = GetToken();
|
|
else if (tok != '}')
|
|
errorUnexpectedToken(tok);
|
|
}
|
|
|
|
if (empty)
|
|
errorUnexpectedToken('}');
|
|
|
|
base[0] = tokEnumPtr;
|
|
base[1] = typePtr;
|
|
|
|
tok = GetToken();
|
|
return tok;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
structInfo[0] = structType;
|
|
structInfo[1] = 1; // initial member alignment
|
|
structInfo[2] = 0; // initial member offset
|
|
structInfo[3] = 0; // initial max member size (for unions)
|
|
|
|
PushSyntax(tokSizeof); // 0 = initial structure/union size, to be updated
|
|
PushSyntax2(tokSizeof, 1); // 1 = initial structure/union alignment, to be updated
|
|
|
|
PushSyntax('{');
|
|
|
|
tok = GetToken();
|
|
while (tok != '}')
|
|
{
|
|
if (!TokenStartsDeclaration(tok, 1))
|
|
errorUnexpectedToken(tok);
|
|
tok = ParseDecl(tok, structInfo, 0, 0);
|
|
empty = 0;
|
|
}
|
|
|
|
if (empty)
|
|
errorUnexpectedToken('}');
|
|
|
|
PushSyntax('}');
|
|
|
|
// Update structure/union alignment
|
|
alignment = structInfo[1];
|
|
SyntaxStack[typePtr + 3][1] = alignment;
|
|
|
|
// Update structure/union size and include trailing padding if needed
|
|
sz = structInfo[2] + structInfo[3];
|
|
tmp = sz;
|
|
sz = (sz + alignment - 1) & ~(alignment - 1);
|
|
if (sz < tmp || sz != truncUint(sz))
|
|
errorVarSize();
|
|
SyntaxStack[typePtr + 2][1] = uint2int(sz);
|
|
|
|
tok = GetToken();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
#ifndef NO_TYPEDEF_ENUM
|
|
if (structType == tokEnum)
|
|
{
|
|
if (!gotTag || declPtr < 0)
|
|
errorDecl(); // TBD!!! different error when enum tag is not found
|
|
|
|
base[0] = tokEnumPtr;
|
|
base[1] = declPtr;
|
|
return tok;
|
|
}
|
|
#endif
|
|
|
|
if (gotTag)
|
|
{
|
|
if (declPtr >= 0 &&
|
|
SyntaxStack[declPtr][0] == structType)
|
|
{
|
|
base[0] = tokStructPtr;
|
|
base[1] = declPtr;
|
|
return tok;
|
|
}
|
|
|
|
PushSyntax(structType);
|
|
PushSyntax2(tokTag, tagIdent);
|
|
|
|
empty = 0;
|
|
}
|
|
}
|
|
|
|
if (empty)
|
|
errorDecl();
|
|
|
|
base[0] = tokStructPtr;
|
|
base[1] = typePtr;
|
|
|
|
// If we've just defined a structure/union and there are
|
|
// preceding references to this tag within this scope,
|
|
// IOW references to an incomplete type, complete the
|
|
// type in the references
|
|
if (gotTag && SyntaxStack[SyntaxStackCnt - 1][0] == '}')
|
|
{
|
|
int i;
|
|
for (i = SyntaxStackCnt - 1; i >= 0; i--)
|
|
if (SyntaxStack[i][0] == tokStructPtr)
|
|
{
|
|
int j = SyntaxStack[i][1];
|
|
if (SyntaxStack[j + 1][1] == tagIdent)
|
|
SyntaxStack[i][1] = typePtr;
|
|
}
|
|
else if (SyntaxStack[i][0] == '#')
|
|
{
|
|
// reached the beginning of the current scope
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
#ifndef NO_TYPEDEF_ENUM
|
|
else if (tok == tokIdent &&
|
|
(base[1] = FindTypedef(TokenIdentName, &CurScope, 1)) >= 0)
|
|
{
|
|
base[0] = tokTypedef;
|
|
tok = GetToken();
|
|
}
|
|
#endif
|
|
else
|
|
{
|
|
valid = 0;
|
|
}
|
|
|
|
#ifdef CAN_COMPILE_32BIT
|
|
if (SizeOfWord == 2 &&
|
|
(*base == tokLong || *base == tokULong))
|
|
valid = 0;
|
|
// to simplify matters, treat long and unsigned long as aliases for int and unsigned int
|
|
// in 32-bit and huge mode(l)s
|
|
if (*base == tokLong)
|
|
*base = tokInt;
|
|
if (*base == tokULong)
|
|
*base = tokUnsigned;
|
|
#endif
|
|
|
|
if (SizeOfWord == 2)
|
|
{
|
|
// to simplify matters, treat short and unsigned short as aliases for int and unsigned int
|
|
// in 16-bit mode
|
|
if (*base == tokShort)
|
|
*base = tokInt;
|
|
if (*base == tokUShort)
|
|
*base = tokUnsigned;
|
|
}
|
|
|
|
// TBD!!! review/test this fxn
|
|
// if (!valid || !tok || !(strchr("*([,)", tok) || tok == tokIdent))
|
|
if (!valid | !tok)
|
|
//error("ParseBase(): Invalid or unsupported type\n");
|
|
error("Invalid or unsupported type\n");
|
|
|
|
return tok;
|
|
}
|
|
|
|
/*
|
|
base * name [] -> name : [] * base
|
|
base *2 (*1 name []1) []2 -> name : []1 *1 []2 *2 base
|
|
base *3 (*2 (*1 name []1) []2) []3 -> name : []1 *1 []2 *2 []3 *3 base
|
|
*/
|
|
|
|
STATIC
|
|
int ParseDerived(int tok)
|
|
{
|
|
int stars = 0;
|
|
int params = 0;
|
|
#ifndef MIPS
|
|
#ifdef CAN_COMPILE_32BIT
|
|
int isInterrupt = 0;
|
|
#endif
|
|
#endif
|
|
|
|
while (tok == '*')
|
|
{
|
|
stars++;
|
|
tok = GetToken();
|
|
}
|
|
|
|
#ifndef MIPS
|
|
#ifdef CAN_COMPILE_32BIT
|
|
if (tok == tokIntr)
|
|
{
|
|
// __interrupt is supported in the huge mode(l) only
|
|
if (OutputFormat != FormatSegHuge)
|
|
errorDecl();
|
|
isInterrupt = 1;
|
|
tok = GetToken();
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
if (tok == '(')
|
|
{
|
|
tok = GetToken();
|
|
if (tok != ')' && !TokenStartsDeclaration(tok, 1))
|
|
{
|
|
tok = ParseDerived(tok);
|
|
if (tok != ')')
|
|
//error("ParseDerived(): ')' expected\n");
|
|
errorUnexpectedToken(tok);
|
|
tok = GetToken();
|
|
}
|
|
else
|
|
{
|
|
params = 1;
|
|
}
|
|
}
|
|
else if (tok == tokIdent)
|
|
{
|
|
PushSyntax2(tok, AddIdent(TokenIdentName));
|
|
tok = GetToken();
|
|
}
|
|
else
|
|
{
|
|
PushSyntax2(tokIdent, AddIdent("<something>"));
|
|
}
|
|
|
|
if (params | (tok == '('))
|
|
{
|
|
int t = SyntaxStack[SyntaxStackCnt - 1][0];
|
|
if ((t == ')') | (t == ']'))
|
|
errorUnexpectedToken('('); // array of functions or function returning function
|
|
if (!params)
|
|
tok = GetToken();
|
|
else
|
|
PushSyntax2(tokIdent, AddIdent("<something>"));
|
|
#ifndef MIPS
|
|
#ifdef CAN_COMPILE_32BIT
|
|
if (isInterrupt)
|
|
PushSyntax2('(', 1);
|
|
else // fallthrough
|
|
#endif
|
|
#endif
|
|
PushSyntax('(');
|
|
|
|
ParseLevel++;
|
|
ParamLevel++;
|
|
ParseFxnParams(tok);
|
|
ParamLevel--;
|
|
ParseLevel--;
|
|
PushSyntax(')');
|
|
tok = GetToken();
|
|
}
|
|
else if (tok == '[')
|
|
{
|
|
// DONE!!! allow the first [] without the dimension in function parameters
|
|
int allowEmptyDimension = 1;
|
|
if (SyntaxStack[SyntaxStackCnt - 1][0] == ')')
|
|
errorUnexpectedToken('['); // function returning array
|
|
while (tok == '[')
|
|
{
|
|
int oldsp = SyntaxStackCnt;
|
|
PushSyntax(tokVoid); // prevent cases like "int arr[arr];" and "int arr[arr[0]];"
|
|
PushSyntax(tok);
|
|
tok = ParseArrayDimension(allowEmptyDimension);
|
|
if (tok != ']')
|
|
//error("ParseDerived(): ']' expected\n");
|
|
errorUnexpectedToken(tok);
|
|
PushSyntax(']');
|
|
tok = GetToken();
|
|
DeleteSyntax(oldsp, 1);
|
|
allowEmptyDimension = 0;
|
|
}
|
|
}
|
|
|
|
while (stars--)
|
|
PushSyntax('*');
|
|
|
|
if (!tok || !strchr(",;{=)", tok))
|
|
//error("ParseDerived(): unexpected token %s\n", GetTokenName(tok));
|
|
errorUnexpectedToken(tok);
|
|
|
|
return tok;
|
|
}
|
|
|
|
STATIC
|
|
void PushBase(int base[2])
|
|
{
|
|
#ifndef NO_TYPEDEF_ENUM
|
|
if (base[0] == tokTypedef)
|
|
{
|
|
int ptr = base[1];
|
|
int c = 0, copying = 1;
|
|
|
|
while (copying)
|
|
{
|
|
int tok = SyntaxStack[++ptr][0];
|
|
int t = SyntaxStack[SyntaxStackCnt - 1][0];
|
|
|
|
// Cannot have:
|
|
// function returning function
|
|
// array of functions
|
|
// function returning array
|
|
if (((t == ')' || t == ']') && tok == '(') ||
|
|
(t == ')' && tok == '['))
|
|
errorDecl();
|
|
|
|
PushSyntax2(tok, SyntaxStack[ptr][1]);
|
|
|
|
c += (tok == '(') - (tok == ')') + (tok == '[') - (tok == ']');
|
|
|
|
if (!c)
|
|
{
|
|
switch (tok)
|
|
{
|
|
case tokVoid:
|
|
case tokChar: case tokSChar: case tokUChar:
|
|
#ifdef CAN_COMPILE_32BIT
|
|
case tokShort: case tokUShort:
|
|
#endif
|
|
case tokInt: case tokUnsigned:
|
|
case tokStructPtr:
|
|
copying = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
PushSyntax2(base[0], base[1]);
|
|
}
|
|
|
|
// Cannot have array of void
|
|
if (SyntaxStack[SyntaxStackCnt - 1][0] == tokVoid &&
|
|
SyntaxStack[SyntaxStackCnt - 2][0] == ']')
|
|
errorUnexpectedVoid();
|
|
}
|
|
|
|
STATIC
|
|
int InitScalar(int synPtr, int tok);
|
|
STATIC
|
|
int InitArray(int synPtr, int tok);
|
|
STATIC
|
|
int InitStruct(int synPtr, int tok);
|
|
|
|
STATIC
|
|
int InitVar(int synPtr, int tok)
|
|
{
|
|
int p = synPtr, t;
|
|
int undoIdents = IdentTableLen;
|
|
|
|
while ((SyntaxStack[p][0] == tokIdent) | (SyntaxStack[p][0] == tokLocalOfs))
|
|
p++;
|
|
|
|
PurgeStringTable();
|
|
KeepStringTable = 1;
|
|
|
|
t = SyntaxStack[p][0];
|
|
if (t == '[')
|
|
tok = InitArray(p, tok);
|
|
else if (t == tokStructPtr)
|
|
tok = InitStruct(p, tok);
|
|
else
|
|
tok = InitScalar(p, tok);
|
|
|
|
if (!strchr(",;", tok))
|
|
errorUnexpectedToken(tok);
|
|
|
|
{
|
|
int lab;
|
|
char s[1 + (2 + CHAR_BIT * sizeof lab) / 3];
|
|
int i = 0;
|
|
|
|
// Construct an expression for each buffered string for GenStrData()
|
|
sp = 1;
|
|
stack[0][0] = tokIdent;
|
|
|
|
// Dump all buffered strings, one by one, the ugly way
|
|
while (i < StringTableLen)
|
|
{
|
|
char *p = s + sizeof s;
|
|
|
|
lab = StringTable[i] & 0xFF;
|
|
lab += (StringTable[i + 1] & 0xFFu) << 8;
|
|
|
|
// Reconstruct the identifier for the definition: char #[len] = "...";
|
|
*--p = '\0';
|
|
p = lab2str(p, lab);
|
|
stack[0][1] = AddIdent(p);
|
|
|
|
GenStrData(0, 0);
|
|
|
|
// Drop the identifier from the identifier table so as not to
|
|
// potentially overflow it when there are many initializing
|
|
// string literals and the table is nearly full.
|
|
IdentTableLen = undoIdents; // remove all temporary identifier names from e.g. "sizeof" or "str"
|
|
|
|
i += 2;
|
|
i += 1 + (StringTable[i] & 0xFF);
|
|
}
|
|
}
|
|
|
|
PurgeStringTable();
|
|
KeepStringTable = 0;
|
|
|
|
return tok;
|
|
}
|
|
|
|
STATIC
|
|
int InitScalar(int synPtr, int tok)
|
|
{
|
|
unsigned elementSz = GetDeclSize(synPtr, 0);
|
|
int gotUnary, synPtr2, constExpr, exprVal;
|
|
int oldssp = SyntaxStackCnt;
|
|
int undoIdents = IdentTableLen;
|
|
int ttop;
|
|
|
|
tok = ParseExpr(tok, &gotUnary, &synPtr2, &constExpr, &exprVal, ',', 0);
|
|
|
|
if (!gotUnary)
|
|
errorUnexpectedToken(tok);
|
|
|
|
scalarTypeCheck(synPtr2);
|
|
|
|
ttop = stack[sp - 1][0];
|
|
if (ttop == tokNumInt || ttop == tokNumUint)
|
|
{
|
|
// TBD??? truncate values for types smaller than int (e.g. char and short),
|
|
// so they are always in range?
|
|
GenIntData(elementSz, stack[0][1]);
|
|
}
|
|
else if (elementSz == SizeOfWord + 0u)
|
|
{
|
|
if (ttop == tokIdent)
|
|
{
|
|
GenAddrData(elementSz, IdentTable + stack[sp - 1][1], 0);
|
|
}
|
|
else if (ttop == '+' || ttop == '-')
|
|
{
|
|
int tleft = stack[sp - 3][0];
|
|
int tright = stack[sp - 2][0];
|
|
if (tleft == tokIdent &&
|
|
(tright == tokNumInt || tright == tokNumUint))
|
|
{
|
|
GenAddrData(elementSz, IdentTable + stack[sp - 3][1], (ttop == '+') ? stack[sp - 2][1] : -stack[sp - 2][1]);
|
|
}
|
|
else if (ttop == '+' &&
|
|
tright == tokIdent &&
|
|
(tleft == tokNumInt || tleft == tokNumUint))
|
|
{
|
|
GenAddrData(elementSz, IdentTable + stack[sp - 2][1], stack[sp - 3][1]);
|
|
}
|
|
else
|
|
errorNotConst();
|
|
}
|
|
else
|
|
errorNotConst();
|
|
// Defer storage of string literal data (if any) until the end.
|
|
// This will let us generate the contiguous array of pointers to
|
|
// string literals unperturbed by the string literal data
|
|
// (e.g. "char* colors[] = { "red", "green", "blue" };").
|
|
}
|
|
else
|
|
//error("ParseDecl(): cannot initialize a global variable with a non-constant expression\n");
|
|
errorNotConst();
|
|
|
|
IdentTableLen = undoIdents; // remove all temporary identifier names from e.g. "sizeof" or "str"
|
|
SyntaxStackCnt = oldssp; // undo any temporary declarations from e.g. "sizeof" or "str" in the expression
|
|
return tok;
|
|
}
|
|
|
|
STATIC
|
|
int InitArray(int synPtr, int tok)
|
|
{
|
|
int elementTypePtr = synPtr + 3;
|
|
int elementType = SyntaxStack[elementTypePtr][0];
|
|
unsigned elementSz = GetDeclSize(elementTypePtr, 0);
|
|
int braces = 0;
|
|
unsigned elementCnt = 0;
|
|
unsigned elementsRequired = SyntaxStack[synPtr + 1][1];
|
|
int arrOfChar = (elementType == tokChar) | (elementType == tokUChar) | (elementType == tokSChar);
|
|
|
|
if (tok == '{')
|
|
{
|
|
braces = 1;
|
|
tok = GetToken();
|
|
}
|
|
else
|
|
{
|
|
// Only fully-bracketed (sic) initialization of arrays is supported,
|
|
// except for arrays of char initialized with string literals
|
|
// (the string literals may be optionally enclosed in braces)
|
|
if (!(arrOfChar & (tok == tokLitStr)))
|
|
errorUnexpectedToken(tok);
|
|
}
|
|
|
|
if (arrOfChar & (tok == tokLitStr))
|
|
{
|
|
// this is 'someArray[someCountIfAny] = "some string"' or
|
|
// 'someArray[someCountIfAny] = { "some string" }'
|
|
int gotUnary, synPtr2, constExpr, exprVal;
|
|
int oldssp = SyntaxStackCnt;
|
|
int undoIdents = IdentTableLen;
|
|
int slen = StringTableLen;
|
|
|
|
if (elementsRequired * ((unsigned)TokenStringLen > elementsRequired))
|
|
warning("String literal truncated\n");
|
|
|
|
tok = ParseExpr(tok, &gotUnary, &synPtr2, &constExpr, &exprVal, ',', 0);
|
|
|
|
if (!gotUnary ||
|
|
stack[sp - 1][0] != tokIdent ||
|
|
!isdigit(IdentTable[stack[sp - 1][1]]))
|
|
errorInit();
|
|
|
|
elementCnt = GenStrData(0, elementsRequired);
|
|
|
|
StringTableLen = slen; // don't accumulate strings initializing arrays of char
|
|
IdentTableLen = undoIdents; // remove all temporary identifier names from e.g. "sizeof" or "str"
|
|
SyntaxStackCnt = oldssp; // undo any temporary declarations from e.g. "sizeof" or "str" in the expression
|
|
|
|
if (braces)
|
|
{
|
|
if (tok != '}')
|
|
errorUnexpectedToken(tok);
|
|
tok = GetToken();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
while (tok != '}')
|
|
{
|
|
if (elementType == '[')
|
|
{
|
|
tok = InitArray(elementTypePtr, tok);
|
|
}
|
|
else if (elementType == tokStructPtr)
|
|
{
|
|
tok = InitStruct(elementTypePtr, tok);
|
|
}
|
|
else
|
|
{
|
|
tok = InitScalar(elementTypePtr, tok);
|
|
}
|
|
|
|
if (++elementCnt > elementsRequired && elementsRequired)
|
|
error("Too many array initializers\n");
|
|
|
|
if (tok == ',')
|
|
tok = GetToken();
|
|
else if (tok != '}')
|
|
errorUnexpectedToken(tok);
|
|
}
|
|
|
|
if (!elementCnt)
|
|
errorUnexpectedToken('}');
|
|
|
|
if (elementCnt < elementsRequired)
|
|
GenZeroData((elementsRequired - elementCnt) * elementSz);
|
|
|
|
tok = GetToken();
|
|
}
|
|
|
|
// Store the element count if it's an incomplete array
|
|
if (!elementsRequired)
|
|
SyntaxStack[synPtr + 1][1] = elementCnt;
|
|
|
|
return tok;
|
|
}
|
|
|
|
STATIC
|
|
int InitStruct(int synPtr, int tok)
|
|
{
|
|
int isUnion;
|
|
unsigned size, ofs = 0;
|
|
|
|
synPtr = SyntaxStack[synPtr][1];
|
|
isUnion = SyntaxStack[synPtr++][0] == tokUnion;
|
|
size = SyntaxStack[++synPtr][1];
|
|
synPtr += 3; // step inside the {} body of the struct/union
|
|
|
|
if (tok != '{')
|
|
errorUnexpectedToken(tok);
|
|
tok = GetToken();
|
|
|
|
while (tok != '}')
|
|
{
|
|
int c = 1;
|
|
int elementTypePtr, elementType;
|
|
unsigned elementOfs, elementSz;
|
|
|
|
// Find the next member or the closing brace
|
|
while (c)
|
|
{
|
|
int t = SyntaxStack[synPtr][0];
|
|
c += (t == '(') - (t == ')') + (t == '{') - (t == '}');
|
|
if (c == 1 && t == tokMemberIdent)
|
|
break;
|
|
synPtr++;
|
|
}
|
|
if (!c)
|
|
errorUnexpectedToken(tok); // too many initializers
|
|
|
|
elementOfs = SyntaxStack[++synPtr][1];
|
|
elementTypePtr = ++synPtr;
|
|
elementType = SyntaxStack[elementTypePtr][0];
|
|
elementSz = GetDeclSize(elementTypePtr, 0);
|
|
|
|
// Alignment
|
|
if (ofs < elementOfs)
|
|
GenZeroData(elementOfs - ofs);
|
|
|
|
if (elementType == '[')
|
|
{
|
|
tok = InitArray(elementTypePtr, tok);
|
|
}
|
|
else if (elementType == tokStructPtr)
|
|
{
|
|
tok = InitStruct(elementTypePtr, tok);
|
|
}
|
|
else
|
|
{
|
|
tok = InitScalar(elementTypePtr, tok);
|
|
}
|
|
|
|
ofs = elementOfs + elementSz;
|
|
|
|
if (tok == ',')
|
|
tok = GetToken();
|
|
else if (tok != '}')
|
|
errorUnexpectedToken(tok);
|
|
|
|
// Only one member (first) is initialized in unions explicitly
|
|
if (isUnion && tok != '}')
|
|
errorUnexpectedToken(tok);
|
|
}
|
|
|
|
if (!ofs)
|
|
errorUnexpectedToken('}');
|
|
|
|
// Implicit initialization of the rest and trailing padding
|
|
if (ofs < size)
|
|
GenZeroData(size - ofs);
|
|
|
|
tok = GetToken();
|
|
|
|
return tok;
|
|
}
|
|
|
|
// DONE: support extern
|
|
// DONE: support static
|
|
// DONE: support basic initialization
|
|
// DONE: support simple non-array initializations with string literals
|
|
// DONE: support basic 1-d array initialization
|
|
// DONE: global/static data allocations
|
|
STATIC
|
|
int ParseDecl(int tok, unsigned structInfo[4], int cast, int label)
|
|
{
|
|
int base[2];
|
|
int lastSyntaxPtr;
|
|
int external = tok == tokExtern;
|
|
int Static = tok == tokStatic;
|
|
#ifndef NO_TYPEDEF_ENUM
|
|
int typeDef = tok == tokTypedef;
|
|
#endif
|
|
|
|
if (external |
|
|
#ifndef NO_TYPEDEF_ENUM
|
|
typeDef |
|
|
#endif
|
|
Static)
|
|
{
|
|
tok = GetToken();
|
|
if (!TokenStartsDeclaration(tok, 1))
|
|
//error("ParseDecl(): unexpected token %s\n", GetTokenName(tok));
|
|
// Implicit int (as in "extern x; static y;") isn't supported
|
|
errorUnexpectedToken(tok);
|
|
}
|
|
tok = ParseBase(tok, base);
|
|
|
|
#ifndef NO_TYPEDEF_ENUM
|
|
if (label && tok == ':' && base[0] == tokTypedef &&
|
|
!(external | Static | typeDef) && ParseLevel)
|
|
{
|
|
// This is a label.
|
|
return tokGotoLabel;
|
|
}
|
|
#endif
|
|
|
|
for (;;)
|
|
{
|
|
lastSyntaxPtr = SyntaxStackCnt;
|
|
|
|
/* derived type */
|
|
tok = ParseDerived(tok);
|
|
|
|
/* base type */
|
|
PushBase(base);
|
|
|
|
if ((tok && strchr(",;{=", tok)) || (tok == ')' && ExprLevel))
|
|
{
|
|
int isLocal = 0, isGlobal = 0, isFxn, isStruct, isArray, isIncompleteArr;
|
|
unsigned alignment = 0;
|
|
|
|
// Disallow void variables
|
|
if (SyntaxStack[SyntaxStackCnt - 1][0] == tokVoid)
|
|
{
|
|
if (SyntaxStack[SyntaxStackCnt - 2][0] == tokIdent &&
|
|
!(cast
|
|
#ifndef NO_TYPEDEF_ENUM
|
|
| typeDef
|
|
#endif
|
|
))
|
|
//error("ParseDecl(): Cannot declare a variable ('%s') of type 'void'\n", IdentTable + SyntaxStack[lastSyntaxPtr][1]);
|
|
errorUnexpectedVoid();
|
|
}
|
|
|
|
isFxn = SyntaxStack[lastSyntaxPtr + 1][0] == '(';
|
|
|
|
#ifdef NO_STRUCT_BY_VAL
|
|
if (isFxn &&
|
|
SyntaxStack[SyntaxStackCnt - 1][0] == tokStructPtr &&
|
|
SyntaxStack[SyntaxStackCnt - 2][0] == ')')
|
|
// structure returning isn't supported currently
|
|
errorDecl();
|
|
#endif
|
|
|
|
isArray = SyntaxStack[lastSyntaxPtr + 1][0] == '[';
|
|
isIncompleteArr = isArray && SyntaxStack[lastSyntaxPtr + 2][1] == 0;
|
|
|
|
isStruct = SyntaxStack[lastSyntaxPtr + 1][0] == tokStructPtr;
|
|
|
|
if (!(ExprLevel || structInfo) &&
|
|
!(external |
|
|
#ifndef NO_TYPEDEF_ENUM
|
|
typeDef |
|
|
#endif
|
|
Static) &&
|
|
!strcmp(IdentTable + SyntaxStack[lastSyntaxPtr][1], "<something>") &&
|
|
tok == ';')
|
|
{
|
|
if (isStruct)
|
|
{
|
|
// This is either an incomplete tagged structure/union declaration, e.g. "struct sometag;",
|
|
// or a tagged complete structure/union declaration, e.g. "struct sometag { ... };", without an instance variable,
|
|
// or an untagged complete structure/union declaration, e.g. "struct { ... };", without an instance variable
|
|
int declPtr, curScope;
|
|
int j = SyntaxStack[lastSyntaxPtr + 1][1];
|
|
|
|
if (j + 2 < SyntaxStackCnt &&
|
|
IdentTable[SyntaxStack[j + 1][1]] == '<' && // without tag
|
|
SyntaxStack[j + 2][0] == tokSizeof) // but with the {} "body"
|
|
errorDecl();
|
|
|
|
// If a structure/union with this tag has been declared in an outer scope,
|
|
// this new declaration should override it
|
|
declPtr = FindTaggedDecl(IdentTable + SyntaxStack[j + 1][1], lastSyntaxPtr - 1, &curScope);
|
|
if (declPtr >= 0 && !curScope)
|
|
{
|
|
// If that's the case, unbind this declaration from the old declaration
|
|
// and make it a new incomplete declaration
|
|
PushSyntax(SyntaxStack[j][0]); // tokStruct or tokUnion
|
|
PushSyntax2(tokTag, SyntaxStack[j + 1][1]);
|
|
SyntaxStack[lastSyntaxPtr + 1][1] = SyntaxStackCnt - 2;
|
|
}
|
|
return GetToken();
|
|
}
|
|
#ifndef NO_TYPEDEF_ENUM
|
|
else if (SyntaxStack[lastSyntaxPtr + 1][0] == tokEnumPtr)
|
|
{
|
|
return GetToken();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#ifndef NO_TYPEDEF_ENUM
|
|
// Convert enums into ints
|
|
if (SyntaxStack[SyntaxStackCnt - 1][0] == tokEnumPtr)
|
|
{
|
|
SyntaxStack[SyntaxStackCnt - 1][0] = tokInt;
|
|
SyntaxStack[SyntaxStackCnt - 1][1] = 0;
|
|
}
|
|
#endif
|
|
|
|
// Structure/union members can't be initialized nor be functions nor
|
|
// be incompletely typed arrays inside structure/union declarations
|
|
if (structInfo && ((tok == '=') | isFxn | (tok == '{') | isIncompleteArr))
|
|
errorDecl();
|
|
|
|
#ifndef NO_TYPEDEF_ENUM
|
|
if (typeDef & ((tok == '=') | (tok == '{')))
|
|
errorDecl();
|
|
#endif
|
|
|
|
// Error conditions in declarations(/definitions/initializations):
|
|
// Legend:
|
|
// + error
|
|
// - no error
|
|
//
|
|
// file scope fxn fxn {} var arr[] arr[]...[] arr[incomplete] arr[incomplete]...[]
|
|
// - - - - - + +
|
|
// file scope fxn= var= arr[]= arr[]...[]= arr[incomplete]= arr[incomplete]...[]=
|
|
// + - - + - +
|
|
// file scope extern fxn fxn {} var arr[] arr[]...[] arr[incomplete] arr[incomplete]...[]
|
|
// - - - - - - -
|
|
// file scope extern fxn= var= arr[]= arr[]...[]= arr[incomplete]= arr[incomplete]...[]=
|
|
// + + + + + +
|
|
// file scope static fxn fxn {} var arr[] arr[]...[] arr[incomplete] arr[incomplete]...[]
|
|
// - - - - - + +
|
|
// file scope static fxn= var= arr[]= arr[]...[]= arr[incomplete]= arr[incomplete]...[]=
|
|
// + - - + - +
|
|
// fxn scope fxn fxn {} var arr[] arr[]...[] arr[incomplete] arr[incomplete]...[]
|
|
// - + - - - + +
|
|
// fxn scope fxn= var= arr[]= arr[]...[]= arr[incomplete]= arr[incomplete]...[]=
|
|
// + - + + + +
|
|
// fxn scope extern fxn fxn {} var arr[] arr[]...[] arr[incomplete] arr[incomplete]...[]
|
|
// - + - - - - -
|
|
// fxn scope extern fxn= var= arr[]= arr[]...[]= arr[incomplete]= arr[incomplete]...[]=
|
|
// + + + + + +
|
|
// fxn scope static fxn fxn {} var arr[] arr[]...[] arr[incomplete] arr[incomplete]...[]
|
|
// + + + + + + +
|
|
// fxn scope static fxn= var= arr[]= arr[]...[]= arr[incomplete]= arr[incomplete]...[]=
|
|
// + + + + + +
|
|
|
|
if (isFxn & (tok == '='))
|
|
//error("ParseDecl(): cannot initialize a function\n");
|
|
errorInit();
|
|
|
|
if ((isFxn & (tok == '{')) && ParseLevel)
|
|
//error("ParseDecl(): cannot define a nested function\n");
|
|
errorDecl();
|
|
|
|
if ((isFxn & Static) && ParseLevel)
|
|
//error("ParseDecl(): cannot declare a static function in this scope\n");
|
|
errorDecl();
|
|
|
|
if (external & (tok == '='))
|
|
//error("ParseDecl(): cannot initialize an external variable\n");
|
|
errorInit();
|
|
|
|
if (isIncompleteArr & !(external |
|
|
#ifndef NO_TYPEDEF_ENUM
|
|
typeDef |
|
|
#endif
|
|
(tok == '=')))
|
|
//error("ParseDecl(): cannot define an array of incomplete type\n");
|
|
errorDecl();
|
|
|
|
// TBD!!! de-uglify
|
|
if (!strcmp(IdentTable + SyntaxStack[lastSyntaxPtr][1], "<something>"))
|
|
{
|
|
// Disallow nameless variables, prototypes, structure/union members and typedefs.
|
|
if (structInfo ||
|
|
#ifndef NO_TYPEDEF_ENUM
|
|
typeDef ||
|
|
#endif
|
|
!ExprLevel)
|
|
error("Identifier expected in declaration\n");
|
|
}
|
|
else
|
|
{
|
|
// Disallow named variables and prototypes in sizeof(typedecl) and (typedecl).
|
|
if (ExprLevel && !structInfo)
|
|
error("Identifier unexpected in declaration\n");
|
|
}
|
|
|
|
if (!isFxn
|
|
#ifndef NO_TYPEDEF_ENUM
|
|
&& !typeDef
|
|
#endif
|
|
)
|
|
{
|
|
// This is a variable or a variable (member) in a struct/union declaration
|
|
int sz = GetDeclSize(lastSyntaxPtr, 0);
|
|
|
|
if (!((sz | isIncompleteArr) || ExprLevel)) // incomplete type
|
|
errorDecl(); // TBD!!! different error when struct/union tag is not found
|
|
|
|
if (isArray && !GetDeclSize(lastSyntaxPtr + 4, 0))
|
|
// incomplete type of array element (e.g. struct/union)
|
|
errorDecl();
|
|
|
|
alignment = GetDeclAlignment(lastSyntaxPtr);
|
|
|
|
if (structInfo)
|
|
{
|
|
// It's a variable (member) in a struct/union declaration
|
|
unsigned tmp;
|
|
unsigned newAlignment = alignment;
|
|
#ifndef NO_PPACK
|
|
if (alignment > PragmaPackValue + 0u)
|
|
newAlignment = PragmaPackValue;
|
|
#endif
|
|
// Update structure/union alignment
|
|
if (structInfo[1] < newAlignment)
|
|
structInfo[1] = newAlignment;
|
|
// Align structure member
|
|
tmp = structInfo[2];
|
|
structInfo[2] = (structInfo[2] + newAlignment - 1) & ~(newAlignment - 1);
|
|
if (structInfo[2] < tmp || structInfo[2] != truncUint(structInfo[2]))
|
|
errorVarSize();
|
|
// Change tokIdent to tokMemberIdent and insert a local var offset token
|
|
SyntaxStack[lastSyntaxPtr][0] = tokMemberIdent;
|
|
InsertSyntax2(lastSyntaxPtr + 1, tokLocalOfs, uint2int(structInfo[2]));
|
|
|
|
// Advance member offset for structures, keep it zero for unions
|
|
if (structInfo[0] == tokStruct)
|
|
{
|
|
tmp = structInfo[2];
|
|
structInfo[2] += sz;
|
|
if (structInfo[2] < tmp || structInfo[2] != truncUint(structInfo[2]))
|
|
errorVarSize();
|
|
}
|
|
// Update max member size for unions
|
|
else if (structInfo[3] < sz + 0u)
|
|
{
|
|
structInfo[3] = sz;
|
|
}
|
|
}
|
|
else if (ParseLevel && !((external | Static) || ExprLevel))
|
|
{
|
|
// It's a local variable
|
|
isLocal = 1;
|
|
// Defer size calculation until initialization
|
|
// Insert a local var offset token, the offset is to be updated
|
|
InsertSyntax2(lastSyntaxPtr + 1, tokLocalOfs, 0);
|
|
}
|
|
else if (!ExprLevel)
|
|
{
|
|
// It's a global variable (external, static or neither)
|
|
isGlobal = 1;
|
|
if (Static && ParseLevel)
|
|
{
|
|
// It's a static variable in function scope, "rename" it by providing
|
|
// an alternative unique numeric identifier right next to it and use it
|
|
int staticLabel = LabelCnt++;
|
|
InsertSyntax2(++lastSyntaxPtr, tokIdent, AddNumericIdent__(staticLabel));
|
|
}
|
|
}
|
|
}
|
|
|
|
// If it's a type declaration in a sizeof(typedecl) expression or
|
|
// in an expression with a cast, e.g. (typedecl)expr, we're done
|
|
if (ExprLevel && !structInfo)
|
|
{
|
|
#ifndef NO_ANNOTATIONS
|
|
DumpDecl(lastSyntaxPtr, 0);
|
|
#endif
|
|
return tok;
|
|
}
|
|
|
|
#ifndef NO_TYPEDEF_ENUM
|
|
if (typeDef)
|
|
{
|
|
int CurScope;
|
|
char* s = IdentTable + SyntaxStack[lastSyntaxPtr][1];
|
|
#ifndef NO_ANNOTATIONS
|
|
DumpDecl(lastSyntaxPtr, 0);
|
|
#endif
|
|
SyntaxStack[lastSyntaxPtr][0] = 0; // hide tokIdent for now
|
|
if (FindTypedef(s, &CurScope, 0) >= 0 && CurScope)
|
|
errorRedef(s);
|
|
SyntaxStack[lastSyntaxPtr][0] = tokTypedef; // change tokIdent to tokTypedef
|
|
}
|
|
else
|
|
// fallthrough
|
|
#endif
|
|
if (isLocal | isGlobal)
|
|
{
|
|
int hasInit = tok == '=';
|
|
int needsGlobalInit = isGlobal & !external;
|
|
int sz = GetDeclSize(lastSyntaxPtr, 0);
|
|
int skipLabel = 0;
|
|
int initLabel = 0;
|
|
|
|
#ifndef NO_ANNOTATIONS
|
|
if (isGlobal)
|
|
DumpDecl(lastSyntaxPtr, 0);
|
|
#endif
|
|
|
|
if (hasInit)
|
|
{
|
|
tok = GetToken();
|
|
}
|
|
|
|
if (isLocal & hasInit)
|
|
needsGlobalInit = isArray | (isStruct & (tok == '{'));
|
|
|
|
if (needsGlobalInit)
|
|
{
|
|
if (isLocal | (Static && ParseLevel))
|
|
{
|
|
// Global data appears inside code of a function
|
|
if (OutputFormat == FormatFlat)
|
|
{
|
|
skipLabel = LabelCnt++;
|
|
GenJumpUncond(skipLabel);
|
|
}
|
|
else
|
|
{
|
|
puts2(CodeFooter);
|
|
puts2(DataHeader);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Global data appears between functions
|
|
if (OutputFormat != FormatFlat)
|
|
{
|
|
puts2(DataHeader);
|
|
}
|
|
}
|
|
|
|
// DONE: imperfect condition for alignment
|
|
if (alignment != 1)
|
|
GenWordAlignment();
|
|
|
|
if (isGlobal)
|
|
{
|
|
GenLabel(IdentTable + SyntaxStack[lastSyntaxPtr][1], Static);
|
|
}
|
|
else
|
|
{
|
|
// Generate numeric labels for global initializers of local vars
|
|
char s[1 + 2 + (2 + CHAR_BIT * sizeof StructCpyLabel) / 3];
|
|
char *p = s + sizeof s;
|
|
initLabel = LabelCnt++;
|
|
*--p = '\0';
|
|
p = lab2str(p, initLabel);
|
|
*--p = '_';
|
|
*--p = '_';
|
|
GenLabel(p, 1);
|
|
}
|
|
|
|
// Generate global initializers
|
|
if (hasInit)
|
|
{
|
|
#ifndef NO_ANNOTATIONS
|
|
if (isGlobal)
|
|
{
|
|
GenStartCommentLine(); printf2("=\n");
|
|
}
|
|
#endif
|
|
tok = InitVar(lastSyntaxPtr, tok);
|
|
// Update the size in case it's an incomplete array
|
|
sz = GetDeclSize(lastSyntaxPtr, 0);
|
|
}
|
|
else
|
|
{
|
|
GenZeroData(sz);
|
|
}
|
|
|
|
if (isLocal | (Static && ParseLevel))
|
|
{
|
|
// Global data appears inside code of a function
|
|
if (OutputFormat == FormatFlat)
|
|
{
|
|
GenNumLabel(skipLabel);
|
|
}
|
|
else
|
|
{
|
|
puts2(DataFooter);
|
|
puts2(CodeHeader);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Global data appears between functions
|
|
if (OutputFormat != FormatFlat)
|
|
{
|
|
puts2(DataFooter);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (isLocal)
|
|
{
|
|
// Now that the size of the local is certainly known,
|
|
// update its offset in the offset token
|
|
SyntaxStack[lastSyntaxPtr + 1][1] = AllocLocal(sz);
|
|
|
|
#ifndef NO_ANNOTATIONS
|
|
DumpDecl(lastSyntaxPtr, 0);
|
|
#endif
|
|
}
|
|
|
|
// Copy global initializers into local vars
|
|
if (isLocal & needsGlobalInit)
|
|
{
|
|
#ifndef NO_ANNOTATIONS
|
|
GenStartCommentLine(); printf2("=\n");
|
|
#endif
|
|
if (!StructCpyLabel)
|
|
StructCpyLabel = LabelCnt++;
|
|
|
|
sp = 0;
|
|
|
|
push2('(', SizeOfWord * 3);
|
|
|
|
push2(tokLocalOfs, SyntaxStack[lastSyntaxPtr + 1][1]);
|
|
|
|
push(',');
|
|
|
|
push2(tokIdent, AddNumericIdent__(initLabel));
|
|
|
|
push(',');
|
|
|
|
push2(tokNumUint, sz);
|
|
|
|
push(',');
|
|
|
|
push2(tokIdent, AddNumericIdent__(StructCpyLabel));
|
|
|
|
push2(')', SizeOfWord * 3);
|
|
|
|
GenExpr();
|
|
}
|
|
// Initialize local vars with expressions
|
|
else if (hasInit & !needsGlobalInit)
|
|
{
|
|
int gotUnary, synPtr, constExpr, exprVal;
|
|
|
|
// ParseExpr() will transform the initializer expression into an assignment expression here
|
|
tok = ParseExpr(tok, &gotUnary, &synPtr, &constExpr, &exprVal, '=', SyntaxStack[lastSyntaxPtr][1]);
|
|
|
|
if (!gotUnary)
|
|
errorUnexpectedToken(tok);
|
|
|
|
if (!isStruct)
|
|
{
|
|
// This is a special case for initialization of integers smaller than int.
|
|
// Since a local integer variable always takes as much space as a whole int,
|
|
// we can optimize code generation a bit by storing the initializer as an int.
|
|
// This is an old accidental optimization and I preserve it for now.
|
|
// Note, this implies a little-endian CPU.
|
|
stack[sp - 1][1] = SizeOfWord;
|
|
}
|
|
|
|
// Storage of string literal data from the initializing expression
|
|
// occurs here.
|
|
GenExpr();
|
|
}
|
|
}
|
|
else if (tok == '{')
|
|
{
|
|
// It's a function body. Let's add function parameters as
|
|
// local variables to the symbol table and parse the body.
|
|
int undoSymbolsPtr = SyntaxStackCnt;
|
|
int undoIdents = IdentTableLen;
|
|
int locAllocLabel = (LabelCnt += 2) - 2;
|
|
int i;
|
|
int Main;
|
|
|
|
#ifndef NO_ANNOTATIONS
|
|
DumpDecl(lastSyntaxPtr, 0);
|
|
#endif
|
|
|
|
CurFxnName = IdentTable + SyntaxStack[lastSyntaxPtr][1];
|
|
Main = !strcmp(CurFxnName, "main");
|
|
|
|
gotoLabCnt = 0;
|
|
|
|
if (verbose && OutFile)
|
|
printf("%s()\n", CurFxnName);
|
|
|
|
ParseLevel++;
|
|
GetFxnInfo(lastSyntaxPtr, &CurFxnParamCntMin, &CurFxnParamCntMax, &CurFxnReturnExprTypeSynPtr, NULL); // get return type
|
|
|
|
#ifndef NO_STRUCT_BY_VAL
|
|
// Make sure the return structure type is complete
|
|
if (CurFxnReturnExprTypeSynPtr >= 0 &&
|
|
SyntaxStack[CurFxnReturnExprTypeSynPtr][0] == tokStructPtr &&
|
|
!GetDeclSize(CurFxnReturnExprTypeSynPtr, 0))
|
|
errorDecl();
|
|
#endif
|
|
|
|
if (OutputFormat != FormatFlat)
|
|
puts2(CodeHeader);
|
|
|
|
GenLabel(IdentTable + SyntaxStack[lastSyntaxPtr][1], Static);
|
|
CurFxnEpilogLabel = LabelCnt++;
|
|
|
|
#ifndef MIPS
|
|
#ifdef CAN_COMPILE_32BIT
|
|
if (SyntaxStack[lastSyntaxPtr + 1][1] & 1)
|
|
GenIsrProlog();
|
|
else // fallthrough
|
|
#endif
|
|
#endif
|
|
GenFxnProlog();
|
|
|
|
GenJumpUncond(locAllocLabel + 1);
|
|
GenNumLabel(locAllocLabel);
|
|
|
|
AddFxnParamSymbols(lastSyntaxPtr);
|
|
|
|
#ifndef NO_FUNC_
|
|
{
|
|
CurFxnNameLabel = LabelCnt++;
|
|
SyntaxStack[SymFuncPtr][1] = AddNumericIdent__(CurFxnNameLabel);
|
|
SyntaxStack[SymFuncPtr + 2][1] = strlen(CurFxnName) + 1;
|
|
}
|
|
#endif
|
|
|
|
#ifdef CAN_COMPILE_32BIT
|
|
if (MainPrologCtorFxn &&
|
|
Main &&
|
|
OutputFormat == FormatSegmented && SizeOfWord == 4)
|
|
{
|
|
sp = 0;
|
|
push('(');
|
|
push2(tokIdent, AddIdent(MainPrologCtorFxn));
|
|
push(')');
|
|
GenExpr();
|
|
}
|
|
#endif
|
|
|
|
tok = ParseBlock(NULL, 0);
|
|
ParseLevel--;
|
|
if (tok != '}')
|
|
//error("ParseDecl(): '}' expected\n");
|
|
errorUnexpectedToken(tok);
|
|
|
|
for (i = 0; i < gotoLabCnt; i++)
|
|
if (gotoLabStat[i] == 2)
|
|
error("Undeclared label '%s'\n", IdentTable + gotoLabels[i][0]);
|
|
|
|
// DONE: if execution of main() reaches here, before the epilog (i.e. without using return),
|
|
// main() should return 0.
|
|
if (Main)
|
|
{
|
|
sp = 0;
|
|
push(tokNumInt);
|
|
GenExpr();
|
|
}
|
|
|
|
GenNumLabel(CurFxnEpilogLabel);
|
|
|
|
#ifndef MIPS
|
|
#ifdef CAN_COMPILE_32BIT
|
|
if (SyntaxStack[lastSyntaxPtr + 1][1] & 1)
|
|
GenIsrEpilog();
|
|
else // fallthrough
|
|
#endif
|
|
#endif
|
|
GenFxnEpilog();
|
|
|
|
GenNumLabel(locAllocLabel + 1);
|
|
GenFxnProlog2();
|
|
GenJumpUncond(locAllocLabel);
|
|
if (OutputFormat != FormatFlat)
|
|
puts2(CodeFooter);
|
|
|
|
#ifndef NO_FUNC_
|
|
if (CurFxnNameLabel < 0)
|
|
{
|
|
PurgeStringTable();
|
|
AddString(-CurFxnNameLabel, CurFxnName, SyntaxStack[SymFuncPtr + 2][1]);
|
|
|
|
if (OutputFormat != FormatFlat)
|
|
puts2(DataHeader);
|
|
|
|
GenLabel(IdentTable + SyntaxStack[SymFuncPtr][1], 1);
|
|
|
|
sp = 1;
|
|
stack[0][0] = tokIdent;
|
|
stack[0][1] = SyntaxStack[SymFuncPtr][1] + 2;
|
|
GenStrData(0, 0);
|
|
|
|
if (OutputFormat != FormatFlat)
|
|
puts2(DataFooter);
|
|
|
|
CurFxnNameLabel = 0;
|
|
}
|
|
#endif
|
|
|
|
CurFxnName = NULL;
|
|
IdentTableLen = undoIdents; // remove all identifier names
|
|
SyntaxStackCnt = undoSymbolsPtr; // remove all params and locals
|
|
}
|
|
#ifndef NO_ANNOTATIONS
|
|
else if (isFxn)
|
|
{
|
|
// function prototype
|
|
DumpDecl(lastSyntaxPtr, 0);
|
|
}
|
|
#endif
|
|
|
|
if ((tok == ';') | (tok == '}'))
|
|
break;
|
|
|
|
tok = GetToken();
|
|
continue;
|
|
}
|
|
|
|
//error("ParseDecl(): unexpected token %s\n", GetTokenName(tok));
|
|
errorUnexpectedToken(tok);
|
|
}
|
|
|
|
tok = GetToken();
|
|
return tok;
|
|
}
|
|
|
|
STATIC
|
|
void ParseFxnParams(int tok)
|
|
{
|
|
int base[2];
|
|
int lastSyntaxPtr;
|
|
int cnt = 0;
|
|
int ellCnt = 0;
|
|
|
|
for (;;)
|
|
{
|
|
lastSyntaxPtr = SyntaxStackCnt;
|
|
|
|
if (tok == ')') /* unspecified params */
|
|
break;
|
|
|
|
if (!TokenStartsDeclaration(tok, 1))
|
|
{
|
|
if (tok == tokEllipsis)
|
|
{
|
|
// "..." cannot be the first parameter and
|
|
// it can be only one
|
|
if (!cnt || ellCnt)
|
|
//error("ParseFxnParams(): '...' unexpected here\n");
|
|
errorUnexpectedToken(tok);
|
|
ellCnt++;
|
|
}
|
|
else
|
|
//error("ParseFxnParams(): Unexpected token %s\n", GetTokenName(tok));
|
|
errorUnexpectedToken(tok);
|
|
base[0] = tok; // "..."
|
|
base[1] = 0;
|
|
PushSyntax2(tokIdent, AddIdent("<something>"));
|
|
tok = GetToken();
|
|
}
|
|
else
|
|
{
|
|
if (ellCnt)
|
|
//error("ParseFxnParams(): '...' must be the last in the parameter list\n");
|
|
errorUnexpectedToken(tok);
|
|
|
|
/* base type */
|
|
tok = ParseBase(tok, base);
|
|
|
|
/* derived type */
|
|
tok = ParseDerived(tok);
|
|
}
|
|
|
|
/* base type */
|
|
PushBase(base);
|
|
|
|
#ifndef NO_TYPEDEF_ENUM
|
|
// Convert enums into ints
|
|
if (SyntaxStack[SyntaxStackCnt - 1][0] == tokEnumPtr)
|
|
{
|
|
SyntaxStack[SyntaxStackCnt - 1][0] = tokInt;
|
|
SyntaxStack[SyntaxStackCnt - 1][1] = 0;
|
|
}
|
|
#endif
|
|
|
|
/* Decay arrays to pointers */
|
|
lastSyntaxPtr++; /* skip name */
|
|
if (SyntaxStack[lastSyntaxPtr][0] == '[')
|
|
{
|
|
int t;
|
|
DeleteSyntax(lastSyntaxPtr, 1);
|
|
t = SyntaxStack[lastSyntaxPtr][0];
|
|
if (t == tokNumInt || t == tokNumUint)
|
|
DeleteSyntax(lastSyntaxPtr, 1);
|
|
SyntaxStack[lastSyntaxPtr][0] = '*';
|
|
}
|
|
/* "(Un)decay" functions to function pointers */
|
|
else if (SyntaxStack[lastSyntaxPtr][0] == '(')
|
|
{
|
|
InsertSyntax(lastSyntaxPtr, '*');
|
|
}
|
|
lastSyntaxPtr--; /* "unskip" name */
|
|
|
|
cnt++;
|
|
|
|
if (tok == ')' || tok == ',')
|
|
{
|
|
int t = SyntaxStack[SyntaxStackCnt - 2][0];
|
|
if (SyntaxStack[SyntaxStackCnt - 1][0] == tokVoid)
|
|
{
|
|
// Disallow void variables. TBD!!! de-uglify
|
|
if (t == tokIdent &&
|
|
!(!strcmp(IdentTable + SyntaxStack[SyntaxStackCnt - 2][1], "<something>") &&
|
|
cnt == 1 && tok == ')'))
|
|
//error("ParseFxnParams(): Cannot declare a variable ('%s') of type 'void'\n", IdentTable + SyntaxStack[lastSyntaxPtr][1]);
|
|
errorUnexpectedVoid();
|
|
}
|
|
|
|
#ifdef NO_STRUCT_BY_VAL
|
|
if (SyntaxStack[SyntaxStackCnt - 1][0] == tokStructPtr &&
|
|
t != '*' &&
|
|
t != ']')
|
|
// structure passing and returning isn't supported currently
|
|
errorDecl();
|
|
#endif
|
|
|
|
if (tok == ')')
|
|
break;
|
|
|
|
tok = GetToken();
|
|
continue;
|
|
}
|
|
|
|
//error("ParseFxnParams(): Unexpected token %s\n", GetTokenName(tok));
|
|
errorUnexpectedToken(tok);
|
|
}
|
|
}
|
|
|
|
STATIC
|
|
void AddFxnParamSymbols(int SyntaxPtr)
|
|
{
|
|
int i;
|
|
unsigned paramOfs = 2 * SizeOfWord; // ret addr, xbp
|
|
|
|
if (SyntaxPtr < 0 ||
|
|
SyntaxPtr > SyntaxStackCnt - 3 ||
|
|
SyntaxStack[SyntaxPtr][0] != tokIdent ||
|
|
SyntaxStack[SyntaxPtr + 1][0] != '(')
|
|
//error("Internal error: AddFxnParamSymbols(): Invalid input\n");
|
|
errorInternal(6);
|
|
|
|
CurFxnSyntaxPtr = SyntaxPtr;
|
|
CurFxnLocalOfs = 0;
|
|
CurFxnMinLocalOfs = 0;
|
|
|
|
#ifndef NO_STRUCT_BY_VAL
|
|
if (CurFxnReturnExprTypeSynPtr >= 0 &&
|
|
SyntaxStack[CurFxnReturnExprTypeSynPtr][0] == tokStructPtr)
|
|
{
|
|
// The function returns a struct/union via an implicit param/arg (pointer to struct/union)
|
|
// before its first formal param/arg, add this implicit param/arg
|
|
#ifndef NO_ANNOTATIONS
|
|
int paramPtr = SyntaxStackCnt;
|
|
#endif
|
|
PushSyntax2(tokIdent, AddIdent("@")); // special implicit param/arg (pretval) pointing to structure receptacle
|
|
PushSyntax2(tokLocalOfs, paramOfs);
|
|
PushSyntax('*');
|
|
PushSyntax2(tokStructPtr, SyntaxStack[CurFxnReturnExprTypeSynPtr][1]);
|
|
paramOfs += SizeOfWord;
|
|
#ifndef NO_ANNOTATIONS
|
|
DumpDecl(paramPtr, 0);
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
SyntaxPtr += 2; // skip "ident("
|
|
|
|
for (i = SyntaxPtr; i < SyntaxStackCnt; i++)
|
|
{
|
|
int tok = SyntaxStack[i][0];
|
|
|
|
if (tok == tokIdent)
|
|
{
|
|
unsigned sz;
|
|
#ifndef NO_ANNOTATIONS
|
|
int paramPtr;
|
|
#endif
|
|
|
|
if (i + 1 >= SyntaxStackCnt)
|
|
//error("Internal error: AddFxnParamSymbols(): Invalid input\n");
|
|
errorInternal(7);
|
|
|
|
if (SyntaxStack[i + 1][0] == tokVoid) // "ident(void)" = no params
|
|
break;
|
|
if (SyntaxStack[i + 1][0] == tokEllipsis) // "ident(something,...)" = no more params
|
|
break;
|
|
|
|
// Make sure the parameter is not an incomplete structure
|
|
sz = GetDeclSize(i, 0);
|
|
if (sz == 0)
|
|
//error("Internal error: AddFxnParamSymbols(): GetDeclSize() = 0\n");
|
|
//errorInternal(8);
|
|
errorDecl();
|
|
|
|
// Let's calculate this parameter's relative on-stack location
|
|
#ifndef NO_ANNOTATIONS
|
|
paramPtr = SyntaxStackCnt;
|
|
#endif
|
|
PushSyntax2(SyntaxStack[i][0], SyntaxStack[i][1]);
|
|
PushSyntax2(tokLocalOfs, paramOfs);
|
|
|
|
if (sz + SizeOfWord - 1 < sz)
|
|
errorVarSize();
|
|
sz = (sz + SizeOfWord - 1) & ~(SizeOfWord - 1u);
|
|
if (paramOfs + sz < paramOfs)
|
|
errorVarSize();
|
|
paramOfs += sz;
|
|
if (paramOfs > (unsigned)GenMaxLocalsSize())
|
|
errorVarSize();
|
|
|
|
// Duplicate this parameter in the symbol table
|
|
i++;
|
|
while (i < SyntaxStackCnt)
|
|
{
|
|
tok = SyntaxStack[i][0];
|
|
if (tok == tokIdent || tok == ')')
|
|
{
|
|
#ifndef NO_ANNOTATIONS
|
|
DumpDecl(paramPtr, 0);
|
|
#endif
|
|
i--;
|
|
break;
|
|
}
|
|
else if (tok == '(')
|
|
{
|
|
int c = 1;
|
|
i++;
|
|
PushSyntax(tok);
|
|
while (c && i < SyntaxStackCnt)
|
|
{
|
|
tok = SyntaxStack[i][0];
|
|
c += (tok == '(') - (tok == ')');
|
|
PushSyntax2(SyntaxStack[i][0], SyntaxStack[i][1]);
|
|
i++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
PushSyntax2(SyntaxStack[i][0], SyntaxStack[i][1]);
|
|
i++;
|
|
}
|
|
}
|
|
}
|
|
else if (tok == ')') // endof "ident(" ... ")"
|
|
break;
|
|
else
|
|
//error("Internal error: AddFxnParamSymbols(): Unexpected token %s\n", GetTokenName(tok));
|
|
errorInternal(9);
|
|
}
|
|
}
|
|
|
|
STATIC
|
|
int ParseStatement(int tok, int BrkCntTarget[2], int casesIdx)
|
|
{
|
|
/*
|
|
labeled statements:
|
|
+ ident : statement
|
|
+ case const-expr : statement
|
|
+ default : statement
|
|
|
|
compound statement:
|
|
+ { declaration(s)/statement(s)-opt }
|
|
|
|
expression statement:
|
|
+ expression-opt ;
|
|
|
|
selection statements:
|
|
+ if ( expression ) statement
|
|
+ if ( expression ) statement else statement
|
|
+ switch ( expression ) { statement(s)-opt }
|
|
|
|
iteration statements:
|
|
+ while ( expression ) statement
|
|
+ do statement while ( expression ) ;
|
|
+ for ( expression-opt ; expression-opt ; expression-opt ) statement
|
|
|
|
jump statements:
|
|
+ goto ident ;
|
|
+ continue ;
|
|
+ break ;
|
|
+ return expression-opt ;
|
|
*/
|
|
int gotUnary, synPtr, constExpr, exprVal;
|
|
int brkCntTarget[2];
|
|
int statementNeeded;
|
|
|
|
do
|
|
{
|
|
statementNeeded = 0;
|
|
|
|
if (tok == ';')
|
|
{
|
|
tok = GetToken();
|
|
}
|
|
else if (tok == '{')
|
|
{
|
|
// A new {} block begins in the function body
|
|
int undoSymbolsPtr = SyntaxStackCnt;
|
|
int undoLocalOfs = CurFxnLocalOfs;
|
|
int undoIdents = IdentTableLen;
|
|
#ifndef NO_ANNOTATIONS
|
|
GenStartCommentLine(); printf2("{\n");
|
|
#endif
|
|
ParseLevel++;
|
|
tok = ParseBlock(BrkCntTarget, casesIdx);
|
|
ParseLevel--;
|
|
if (tok != '}')
|
|
//error("ParseStatement(): '}' expected. Unexpected token %s\n", GetTokenName(tok));
|
|
errorUnexpectedToken(tok);
|
|
UndoNonLabelIdents(undoIdents); // remove all identifier names, except those of labels
|
|
SyntaxStackCnt = undoSymbolsPtr; // remove all params and locals
|
|
CurFxnLocalOfs = undoLocalOfs; // destroy on-stack local variables
|
|
#ifndef NO_ANNOTATIONS
|
|
GenStartCommentLine(); printf2("}\n");
|
|
#endif
|
|
tok = GetToken();
|
|
}
|
|
else if (tok == tokReturn)
|
|
{
|
|
// DONE: functions returning void vs non-void
|
|
// TBD??? functions returning void should be able to return void
|
|
// return values from other functions returning void
|
|
int retVoid = CurFxnReturnExprTypeSynPtr >= 0 &&
|
|
SyntaxStack[CurFxnReturnExprTypeSynPtr][0] == tokVoid;
|
|
#ifndef NO_ANNOTATIONS
|
|
GenStartCommentLine(); printf2("return\n");
|
|
#endif
|
|
tok = GetToken();
|
|
if (tok == ';')
|
|
{
|
|
gotUnary = 0;
|
|
if (!retVoid)
|
|
//error("ParseStatement(): missing return value\n");
|
|
errorUnexpectedToken(tok);
|
|
}
|
|
else
|
|
{
|
|
if (retVoid)
|
|
//error("Error: ParseStatement(): cannot return a value from a function returning 'void'\n");
|
|
errorUnexpectedToken(tok);
|
|
if ((tok = ParseExpr(tok, &gotUnary, &synPtr, &constExpr, &exprVal, 0, 0)) != ';')
|
|
//error("ParseStatement(): ';' expected\n");
|
|
errorUnexpectedToken(tok);
|
|
if (gotUnary)
|
|
//error("ParseStatement(): cannot return a value of type 'void'\n");
|
|
#ifdef NO_STRUCT_BY_VAL
|
|
scalarTypeCheck(synPtr);
|
|
#else
|
|
nonVoidTypeCheck(synPtr);
|
|
#endif
|
|
}
|
|
if (gotUnary)
|
|
{
|
|
#ifndef NO_STRUCT_BY_VAL
|
|
int structs = (synPtr >= 0 && SyntaxStack[synPtr][0] == tokStructPtr) +
|
|
(CurFxnReturnExprTypeSynPtr >= 0 && SyntaxStack[CurFxnReturnExprTypeSynPtr][0] == tokStructPtr) * 2;
|
|
|
|
if (structs)
|
|
{
|
|
if (structs != 3 ||
|
|
SyntaxStack[synPtr][1] != SyntaxStack[CurFxnReturnExprTypeSynPtr][1])
|
|
errorOpType();
|
|
|
|
// Transform "return *pstruct" into structure assignment ("*pretval = *pstruct")
|
|
// via function call "fxn(sizeof *pretval, pstruct, pretval)".
|
|
|
|
// There are a couple of differences to how this is implemented in the assignment operator:
|
|
// - the structure dereference has already been dropped from *pstruct by ParseExpr(),
|
|
// so it isn't removed here
|
|
// - we don't add the structure dereference on top of the value returned by "fxn()"
|
|
// because the return statement is not an expression that can be an operand into another
|
|
// operator
|
|
|
|
ins(0, ',');
|
|
ins2(0, tokUnaryStar, SizeOfWord); // dereference to extract the implicit param/arg (pretval) from the stack
|
|
ins2(0, tokLocalOfs, SyntaxStack[FindSymbol("@") + 1][1]); // special implicit param/arg (pretval) pointing to structure receptacle
|
|
ins2(0, '(', SizeOfWord * 3);
|
|
push(',');
|
|
push2(tokNumUint, GetDeclSize(synPtr, 0));
|
|
push(',');
|
|
if (!StructCpyLabel)
|
|
StructCpyLabel = LabelCnt++;
|
|
push2(tokIdent, AddNumericIdent__(StructCpyLabel));
|
|
push2(')', SizeOfWord * 3);
|
|
}
|
|
else // fallthrough
|
|
#endif
|
|
{
|
|
// If return value (per function declaration) is a scalar type smaller than machine word,
|
|
// properly zero- or sign-extend the returned value to machine word size.
|
|
// TBD??? Move this cast to the caller?
|
|
int castSize = GetDeclSize(CurFxnReturnExprTypeSynPtr, 1);
|
|
if (castSize != SizeOfWord &&
|
|
(synPtr < 0 || castSize != GetDeclSize(synPtr, 1)))
|
|
{
|
|
switch (castSize)
|
|
{
|
|
case 1:
|
|
push(tokUChar);
|
|
break;
|
|
case -1:
|
|
push(tokSChar);
|
|
break;
|
|
#ifdef CAN_COMPILE_32BIT
|
|
case 2:
|
|
push(tokUShort);
|
|
break;
|
|
case -2:
|
|
push(tokShort);
|
|
break;
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
GenExpr();
|
|
}
|
|
GenJumpUncond(CurFxnEpilogLabel);
|
|
tok = GetToken();
|
|
}
|
|
else if (tok == tokWhile)
|
|
{
|
|
int labelBefore = LabelCnt++;
|
|
int labelAfter = LabelCnt++;
|
|
#ifndef NO_ANNOTATIONS
|
|
GenStartCommentLine(); printf2("while\n");
|
|
#endif
|
|
|
|
tok = GetToken();
|
|
if (tok != '(')
|
|
//error("ParseStatement(): '(' expected after 'while'\n");
|
|
errorUnexpectedToken(tok);
|
|
|
|
tok = GetToken();
|
|
if ((tok = ParseExpr(tok, &gotUnary, &synPtr, &constExpr, &exprVal, 0, 0)) != ')')
|
|
//error("ParseStatement(): ')' expected after 'while ( expression'\n");
|
|
errorUnexpectedToken(tok);
|
|
|
|
if (!gotUnary)
|
|
//error("ParseStatement(): expression expected in 'while ( expression )'\n");
|
|
errorUnexpectedToken(tok);
|
|
|
|
// DONE: void control expressions
|
|
//error("ParseStatement(): unexpected 'void' expression in 'while ( expression )'\n");
|
|
scalarTypeCheck(synPtr);
|
|
|
|
GenNumLabel(labelBefore);
|
|
|
|
switch (stack[sp - 1][0])
|
|
{
|
|
case '<':
|
|
case '>':
|
|
case tokEQ:
|
|
case tokNEQ:
|
|
case tokLEQ:
|
|
case tokGEQ:
|
|
case tokULess:
|
|
case tokUGreater:
|
|
case tokULEQ:
|
|
case tokUGEQ:
|
|
push2(tokIfNot, labelAfter);
|
|
GenExpr();
|
|
break;
|
|
default:
|
|
GenExpr();
|
|
GenJumpIfZero(labelAfter);
|
|
break;
|
|
}
|
|
|
|
tok = GetToken();
|
|
brkCntTarget[0] = labelAfter; // break target
|
|
brkCntTarget[1] = labelBefore; // continue target
|
|
tok = ParseStatement(tok, brkCntTarget, casesIdx);
|
|
|
|
GenJumpUncond(labelBefore);
|
|
GenNumLabel(labelAfter);
|
|
}
|
|
else if (tok == tokDo)
|
|
{
|
|
int labelBefore = LabelCnt++;
|
|
int labelWhile = LabelCnt++;
|
|
int labelAfter = LabelCnt++;
|
|
#ifndef NO_ANNOTATIONS
|
|
GenStartCommentLine(); printf2("do\n");
|
|
#endif
|
|
GenNumLabel(labelBefore);
|
|
|
|
tok = GetToken();
|
|
brkCntTarget[0] = labelAfter; // break target
|
|
brkCntTarget[1] = labelWhile; // continue target
|
|
tok = ParseStatement(tok, brkCntTarget, casesIdx);
|
|
if (tok != tokWhile)
|
|
//error("ParseStatement(): 'while' expected after 'do statement'\n");
|
|
errorUnexpectedToken(tok);
|
|
|
|
#ifndef NO_ANNOTATIONS
|
|
GenStartCommentLine(); printf2("while\n");
|
|
#endif
|
|
tok = GetToken();
|
|
if (tok != '(')
|
|
//error("ParseStatement(): '(' expected after 'while'\n");
|
|
errorUnexpectedToken(tok);
|
|
|
|
tok = GetToken();
|
|
if ((tok = ParseExpr(tok, &gotUnary, &synPtr, &constExpr, &exprVal, 0, 0)) != ')')
|
|
//error("ParseStatement(): ')' expected after 'while ( expression'\n");
|
|
errorUnexpectedToken(tok);
|
|
|
|
if (!gotUnary)
|
|
//error("ParseStatement(): expression expected in 'while ( expression )'\n");
|
|
errorUnexpectedToken(tok);
|
|
|
|
tok = GetToken();
|
|
if (tok != ';')
|
|
//error("ParseStatement(): ';' expected after 'do statement while ( expression )'\n");
|
|
errorUnexpectedToken(tok);
|
|
|
|
// DONE: void control expressions
|
|
//error("ParseStatement(): unexpected 'void' expression in 'while ( expression )'\n");
|
|
scalarTypeCheck(synPtr);
|
|
|
|
GenNumLabel(labelWhile);
|
|
|
|
switch (stack[sp - 1][0])
|
|
{
|
|
case '<':
|
|
case '>':
|
|
case tokEQ:
|
|
case tokNEQ:
|
|
case tokLEQ:
|
|
case tokGEQ:
|
|
case tokULess:
|
|
case tokUGreater:
|
|
case tokULEQ:
|
|
case tokUGEQ:
|
|
push2(tokIf, labelBefore);
|
|
GenExpr();
|
|
break;
|
|
default:
|
|
GenExpr();
|
|
GenJumpIfNotZero(labelBefore);
|
|
break;
|
|
}
|
|
|
|
GenNumLabel(labelAfter);
|
|
|
|
tok = GetToken();
|
|
}
|
|
else if (tok == tokIf)
|
|
{
|
|
int labelAfterIf = LabelCnt++;
|
|
int labelAfterElse = LabelCnt++;
|
|
#ifndef NO_ANNOTATIONS
|
|
GenStartCommentLine(); printf2("if\n");
|
|
#endif
|
|
|
|
tok = GetToken();
|
|
if (tok != '(')
|
|
//error("ParseStatement(): '(' expected after 'if'\n");
|
|
errorUnexpectedToken(tok);
|
|
|
|
tok = GetToken();
|
|
if ((tok = ParseExpr(tok, &gotUnary, &synPtr, &constExpr, &exprVal, 0, 0)) != ')')
|
|
//error("ParseStatement(): ')' expected after 'if ( expression'\n");
|
|
errorUnexpectedToken(tok);
|
|
|
|
if (!gotUnary)
|
|
//error("ParseStatement(): expression expected in 'if ( expression )'\n");
|
|
errorUnexpectedToken(tok);
|
|
|
|
// DONE: void control expressions
|
|
//error("ParseStatement(): unexpected 'void' expression in 'if ( expression )'\n");
|
|
scalarTypeCheck(synPtr);
|
|
|
|
switch (stack[sp - 1][0])
|
|
{
|
|
case '<':
|
|
case '>':
|
|
case tokEQ:
|
|
case tokNEQ:
|
|
case tokLEQ:
|
|
case tokGEQ:
|
|
case tokULess:
|
|
case tokUGreater:
|
|
case tokULEQ:
|
|
case tokUGEQ:
|
|
push2(tokIfNot, labelAfterIf);
|
|
GenExpr();
|
|
break;
|
|
default:
|
|
GenExpr();
|
|
GenJumpIfZero(labelAfterIf);
|
|
break;
|
|
}
|
|
|
|
tok = GetToken();
|
|
tok = ParseStatement(tok, BrkCntTarget, casesIdx);
|
|
|
|
// DONE: else
|
|
if (tok == tokElse)
|
|
{
|
|
GenJumpUncond(labelAfterElse);
|
|
GenNumLabel(labelAfterIf);
|
|
#ifndef NO_ANNOTATIONS
|
|
GenStartCommentLine(); printf2("else\n");
|
|
#endif
|
|
tok = GetToken();
|
|
tok = ParseStatement(tok, BrkCntTarget, casesIdx);
|
|
GenNumLabel(labelAfterElse);
|
|
}
|
|
else
|
|
{
|
|
GenNumLabel(labelAfterIf);
|
|
}
|
|
}
|
|
else if (tok == tokFor)
|
|
{
|
|
int labelBefore = LabelCnt++;
|
|
int labelExpr3 = LabelCnt++;
|
|
int labelBody = LabelCnt++;
|
|
int labelAfter = LabelCnt++;
|
|
#ifndef NO_FOR_DECL
|
|
int decl = 0;
|
|
int undoSymbolsPtr = 0, undoLocalOfs = 0, undoIdents = 0;
|
|
#endif
|
|
#ifndef NO_ANNOTATIONS
|
|
GenStartCommentLine(); printf2("for\n");
|
|
#endif
|
|
tok = GetToken();
|
|
if (tok != '(')
|
|
//error("ParseStatement(): '(' expected after 'for'\n");
|
|
errorUnexpectedToken(tok);
|
|
|
|
tok = GetToken();
|
|
#ifndef NO_FOR_DECL
|
|
if (TokenStartsDeclaration(tok, 1))
|
|
{
|
|
decl = 1;
|
|
undoSymbolsPtr = SyntaxStackCnt;
|
|
undoLocalOfs = CurFxnLocalOfs;
|
|
undoIdents = IdentTableLen;
|
|
// Declarations made in the first clause of for should not:
|
|
// - collide with previous outer declarations
|
|
// - be visible/exist outside for
|
|
// For this reason the declaration gets its own subscope.
|
|
PushSyntax('#'); // mark the beginning of a new scope
|
|
tok = ParseDecl(tok, NULL, 0, 0);
|
|
}
|
|
else
|
|
// fallthrough
|
|
#endif
|
|
{
|
|
if ((tok = ParseExpr(tok, &gotUnary, &synPtr, &constExpr, &exprVal, 0, 0)) != ';')
|
|
//error("ParseStatement(): ';' expected after 'for ( expression'\n");
|
|
errorUnexpectedToken(tok);
|
|
if (gotUnary)
|
|
{
|
|
GenExpr();
|
|
}
|
|
tok = GetToken();
|
|
}
|
|
|
|
GenNumLabel(labelBefore);
|
|
if ((tok = ParseExpr(tok, &gotUnary, &synPtr, &constExpr, &exprVal, 0, 0)) != ';')
|
|
//error("ParseStatement(): ';' expected after 'for ( expression ; expression'\n");
|
|
errorUnexpectedToken(tok);
|
|
if (gotUnary)
|
|
{
|
|
// DONE: void control expressions
|
|
//error("ParseStatement(): unexpected 'void' expression in 'for ( ; expression ; )'\n");
|
|
scalarTypeCheck(synPtr);
|
|
|
|
switch (stack[sp - 1][0])
|
|
{
|
|
case '<':
|
|
case '>':
|
|
case tokEQ:
|
|
case tokNEQ:
|
|
case tokLEQ:
|
|
case tokGEQ:
|
|
case tokULess:
|
|
case tokUGreater:
|
|
case tokULEQ:
|
|
case tokUGEQ:
|
|
push2(tokIfNot, labelAfter);
|
|
GenExpr();
|
|
break;
|
|
default:
|
|
GenExpr();
|
|
GenJumpIfZero(labelAfter);
|
|
break;
|
|
}
|
|
}
|
|
GenJumpUncond(labelBody);
|
|
|
|
GenNumLabel(labelExpr3);
|
|
tok = GetToken();
|
|
if ((tok = ParseExpr(tok, &gotUnary, &synPtr, &constExpr, &exprVal, 0, 0)) != ')')
|
|
//error("ParseStatement(): ')' expected after 'for ( expression ; expression ; expression'\n");
|
|
errorUnexpectedToken(tok);
|
|
if (gotUnary)
|
|
{
|
|
GenExpr();
|
|
}
|
|
GenJumpUncond(labelBefore);
|
|
|
|
GenNumLabel(labelBody);
|
|
tok = GetToken();
|
|
brkCntTarget[0] = labelAfter; // break target
|
|
brkCntTarget[1] = labelExpr3; // continue target
|
|
tok = ParseStatement(tok, brkCntTarget, casesIdx);
|
|
GenJumpUncond(labelExpr3);
|
|
|
|
GenNumLabel(labelAfter);
|
|
|
|
#ifndef NO_FOR_DECL
|
|
// undo any declarations made in the first clause of for
|
|
if (decl)
|
|
{
|
|
UndoNonLabelIdents(undoIdents); // remove all identifier names, except those of labels
|
|
SyntaxStackCnt = undoSymbolsPtr; // remove all params and locals
|
|
CurFxnLocalOfs = undoLocalOfs; // destroy on-stack local variables
|
|
}
|
|
#endif
|
|
}
|
|
else if (tok == tokBreak)
|
|
{
|
|
#ifndef NO_ANNOTATIONS
|
|
GenStartCommentLine(); printf2("break\n");
|
|
#endif
|
|
if ((tok = GetToken()) != ';')
|
|
//error("ParseStatement(): ';' expected\n");
|
|
errorUnexpectedToken(tok);
|
|
tok = GetToken();
|
|
if (BrkCntTarget == NULL)
|
|
//error("ParseStatement(): 'break' must be within 'while', 'for' or 'switch' statement\n");
|
|
errorCtrlOutOfScope();
|
|
GenJumpUncond(BrkCntTarget[0]);
|
|
}
|
|
else if (tok == tokCont)
|
|
{
|
|
#ifndef NO_ANNOTATIONS
|
|
GenStartCommentLine(); printf2("continue\n");
|
|
#endif
|
|
if ((tok = GetToken()) != ';')
|
|
//error("ParseStatement(): ';' expected\n");
|
|
errorUnexpectedToken(tok);
|
|
tok = GetToken();
|
|
if (BrkCntTarget == NULL || BrkCntTarget[1] == 0)
|
|
//error("ParseStatement(): 'continue' must be within 'while' or 'for' statement\n");
|
|
errorCtrlOutOfScope();
|
|
GenJumpUncond(BrkCntTarget[1]);
|
|
}
|
|
else if (tok == tokSwitch)
|
|
{
|
|
int undoCases = CasesCnt;
|
|
int brkLabel = LabelCnt++;
|
|
#ifdef USE_SWITCH_TAB
|
|
int tblLabel = LabelCnt++;
|
|
#else
|
|
int lbl = LabelCnt++;
|
|
#endif
|
|
int i;
|
|
#ifndef NO_ANNOTATIONS
|
|
GenStartCommentLine(); printf2("switch\n");
|
|
#endif
|
|
|
|
tok = GetToken();
|
|
if (tok != '(')
|
|
//error("ParseStatement(): '(' expected after 'switch'\n");
|
|
errorUnexpectedToken(tok);
|
|
|
|
tok = GetToken();
|
|
if ((tok = ParseExpr(tok, &gotUnary, &synPtr, &constExpr, &exprVal, 0, 0)) != ')')
|
|
//error("ParseStatement(): ')' expected after 'switch ( expression'\n");
|
|
errorUnexpectedToken(tok);
|
|
|
|
if (!gotUnary)
|
|
//error("ParseStatement(): expression expected in 'switch ( expression )'\n");
|
|
errorUnexpectedToken(tok);
|
|
|
|
// DONE: void control expressions
|
|
//error("ParseStatement(): unexpected 'void' expression in 'switch ( expression )'\n");
|
|
scalarTypeCheck(synPtr);
|
|
|
|
#ifdef USE_SWITCH_TAB
|
|
// Generate a call to the function that will do table-based switch()
|
|
if (!SwitchJmpLabel)
|
|
SwitchJmpLabel = LabelCnt++;
|
|
|
|
ins2(0, '(', SizeOfWord * 2);
|
|
push(',');
|
|
push2(tokIdent, AddNumericIdent__(tblLabel));
|
|
push(',');
|
|
push2(tokIdent, AddNumericIdent__(SwitchJmpLabel));
|
|
push2(')', SizeOfWord * 2);
|
|
#else
|
|
#endif
|
|
|
|
GenExpr();
|
|
|
|
tok = GetToken();
|
|
|
|
#ifndef USE_SWITCH_TAB
|
|
// Skip the code for the cases
|
|
GenJumpUncond(lbl);
|
|
#endif
|
|
|
|
brkCntTarget[0] = brkLabel; // break target
|
|
brkCntTarget[1] = 0; // continue target
|
|
if (BrkCntTarget)
|
|
{
|
|
// Preserve the continue target
|
|
brkCntTarget[1] = BrkCntTarget[1]; // continue target
|
|
}
|
|
|
|
// Reserve a slot in the case table for the default label
|
|
AddCase(0, 0);
|
|
|
|
tok = ParseStatement(tok, brkCntTarget, CasesCnt);
|
|
|
|
// If there's no default target, will use the break target as default
|
|
if (!Cases[undoCases][1])
|
|
Cases[undoCases][1] = brkLabel;
|
|
|
|
#ifdef USE_SWITCH_TAB
|
|
GenNumLabel(brkLabel); // break label
|
|
|
|
// Generate the case/jump table
|
|
|
|
// Store the number of cases in the default slot
|
|
Cases[undoCases][0] = CasesCnt - undoCases - 1;
|
|
|
|
if (OutputFormat != FormatFlat)
|
|
{
|
|
puts2(CodeFooter);
|
|
puts2(DataHeader);
|
|
}
|
|
else
|
|
{
|
|
GenJumpUncond(brkLabel = LabelCnt++);
|
|
}
|
|
|
|
GenWordAlignment();
|
|
|
|
{
|
|
char s[1 + 2 + (2 + CHAR_BIT * sizeof tblLabel) / 3];
|
|
char *p = s + sizeof s;
|
|
|
|
*--p = '\0';
|
|
p = lab2str(p, tblLabel);
|
|
*--p = '_';
|
|
*--p = '_';
|
|
|
|
GenLabel(p, 1);
|
|
}
|
|
|
|
for (i = undoCases; i < CasesCnt; i++)
|
|
{
|
|
char s[1 + (2 + CHAR_BIT * sizeof(int)) / 3];
|
|
char *p = s + sizeof s;
|
|
*--p = '\0';
|
|
p = lab2str(p, Cases[i][1]);
|
|
GenIntData(SizeOfWord, Cases[i][0]);
|
|
GenAddrData(SizeOfWord, p, 0);
|
|
}
|
|
|
|
if (OutputFormat != FormatFlat)
|
|
{
|
|
puts2(DataFooter);
|
|
puts2(CodeHeader);
|
|
}
|
|
else
|
|
{
|
|
GenNumLabel(brkLabel);
|
|
}
|
|
#else
|
|
// End of switch reached (not via break), skip conditional jumps
|
|
GenJumpUncond(brkLabel);
|
|
// Generate conditional jumps
|
|
GenNumLabel(lbl);
|
|
for (i = undoCases + 1; i < CasesCnt; i++)
|
|
{
|
|
GenJumpIfEqual(Cases[i][0], Cases[i][1]);
|
|
}
|
|
// If none of the cases matches, take the default case
|
|
if (Cases[undoCases][1] != brkLabel)
|
|
GenJumpUncond(Cases[undoCases][1]);
|
|
GenNumLabel(brkLabel); // break label
|
|
#endif
|
|
|
|
CasesCnt = undoCases;
|
|
}
|
|
else if (tok == tokCase)
|
|
{
|
|
int i;
|
|
#ifndef NO_ANNOTATIONS
|
|
GenStartCommentLine(); printf2("case\n");
|
|
#endif
|
|
|
|
if (!casesIdx)
|
|
//error("ParseStatement(): 'case' must be within 'switch' statement\n");
|
|
errorCtrlOutOfScope();
|
|
|
|
tok = GetToken();
|
|
if ((tok = ParseExpr(tok, &gotUnary, &synPtr, &constExpr, &exprVal, 0, 0)) != ':')
|
|
//error("ParseStatement(): ':' expected after 'case expression'\n");
|
|
errorUnexpectedToken(tok);
|
|
|
|
if (!gotUnary || !constExpr || (synPtr >= 0 && SyntaxStack[synPtr][0] == tokVoid)) // TBD???
|
|
//error("ParseStatement(): constant integer expression expected in 'case expression :'\n");
|
|
errorNotConst();
|
|
|
|
// Check for dups
|
|
exprVal = truncInt(exprVal);
|
|
for (i = casesIdx; i < CasesCnt; i++)
|
|
if (Cases[i][0] == exprVal)
|
|
error("Duplicate case value\n");
|
|
|
|
AddCase(exprVal, LabelCnt);
|
|
GenNumLabel(LabelCnt++); // case exprVal:
|
|
|
|
tok = GetToken();
|
|
|
|
// a statement is needed after "case:"
|
|
statementNeeded = 1;
|
|
}
|
|
else if (tok == tokDefault)
|
|
{
|
|
#ifndef NO_ANNOTATIONS
|
|
GenStartCommentLine(); printf2("default\n");
|
|
#endif
|
|
|
|
if (!casesIdx)
|
|
//error("ParseStatement(): 'default' must be within 'switch' statement\n");
|
|
errorCtrlOutOfScope();
|
|
|
|
if (Cases[casesIdx - 1][1])
|
|
//error("ParseStatement(): only one 'default' allowed in 'switch'\n");
|
|
errorUnexpectedToken(tok);
|
|
|
|
tok = GetToken();
|
|
if (tok != ':')
|
|
//error("ParseStatement(): ':' expected after 'default'\n");
|
|
errorUnexpectedToken(tok);
|
|
|
|
tok = GetToken();
|
|
|
|
GenNumLabel(Cases[casesIdx - 1][1] = LabelCnt++); // default:
|
|
|
|
// a statement is needed after "default:"
|
|
statementNeeded = 1;
|
|
}
|
|
else if (tok == tok_Asm)
|
|
{
|
|
tok = GetToken();
|
|
if (tok != '(')
|
|
//error("ParseStatement(): '(' expected after 'asm'\n");
|
|
errorUnexpectedToken(tok);
|
|
|
|
tok = GetToken();
|
|
if (tok != tokLitStr)
|
|
//error("ParseStatement(): string literal expression expected in 'asm ( expression )'\n");
|
|
errorUnexpectedToken(tok);
|
|
|
|
puts2(TokenValueString);
|
|
|
|
tok = GetToken();
|
|
if (tok != ')')
|
|
//error("ParseStatement(): ')' expected after 'asm ( expression'\n");
|
|
errorUnexpectedToken(tok);
|
|
|
|
tok = GetToken();
|
|
if (tok != ';')
|
|
//error("ParseStatement(): ';' expected after 'asm ( expression )'\n");
|
|
errorUnexpectedToken(tok);
|
|
|
|
tok = GetToken();
|
|
}
|
|
else if (tok == tokGoto)
|
|
{
|
|
if ((tok = GetToken()) != tokIdent)
|
|
errorUnexpectedToken(tok);
|
|
#ifndef NO_ANNOTATIONS
|
|
GenStartCommentLine(); printf2("goto %s\n", TokenIdentName);
|
|
#endif
|
|
GenJumpUncond(AddGotoLabel(TokenIdentName, 0));
|
|
if ((tok = GetToken()) != ';')
|
|
errorUnexpectedToken(tok);
|
|
tok = GetToken();
|
|
}
|
|
else
|
|
{
|
|
tok = ParseExpr(tok, &gotUnary, &synPtr, &constExpr, &exprVal, tokGotoLabel, 0);
|
|
if (tok == tokGotoLabel)
|
|
{
|
|
// found a label
|
|
#ifndef NO_ANNOTATIONS
|
|
GenStartCommentLine(); printf2("%s:\n", IdentTable + stack[0][1]);
|
|
#endif
|
|
GenNumLabel(AddGotoLabel(IdentTable + stack[0][1], 1));
|
|
// a statement is needed after "label:"
|
|
statementNeeded = 1;
|
|
}
|
|
else
|
|
{
|
|
if (tok != ';')
|
|
//error("ParseStatement(): ';' expected\n");
|
|
errorUnexpectedToken(tok);
|
|
if (gotUnary)
|
|
GenExpr();
|
|
}
|
|
tok = GetToken();
|
|
}
|
|
} while (statementNeeded);
|
|
|
|
return tok;
|
|
}
|
|
|
|
// TBD!!! think of ways of getting rid of casesIdx
|
|
STATIC
|
|
int ParseBlock(int BrkCntTarget[2], int casesIdx)
|
|
{
|
|
int tok = GetToken();
|
|
|
|
PushSyntax('#'); // mark the beginning of a new scope
|
|
|
|
for (;;)
|
|
{
|
|
if (tok == 0)
|
|
return tok;
|
|
|
|
if (tok == '}' && ParseLevel > 0)
|
|
return tok;
|
|
|
|
if (TokenStartsDeclaration(tok, 0))
|
|
{
|
|
tok = ParseDecl(tok, NULL, 0, 1);
|
|
#ifndef NO_TYPEDEF_ENUM
|
|
if (tok == tokGotoLabel)
|
|
{
|
|
// found a label
|
|
#ifndef NO_ANNOTATIONS
|
|
GenStartCommentLine(); printf2("%s:\n", TokenIdentName);
|
|
#endif
|
|
GenNumLabel(AddGotoLabel(TokenIdentName, 1));
|
|
tok = GetToken();
|
|
// a statement is needed after "label:"
|
|
tok = ParseStatement(tok, BrkCntTarget, casesIdx);
|
|
}
|
|
#endif
|
|
}
|
|
else if (ParseLevel > 0 || tok == tok_Asm)
|
|
{
|
|
tok = ParseStatement(tok, BrkCntTarget, casesIdx);
|
|
}
|
|
else
|
|
//error("ParseBlock(): Unexpected token %s\n", GetTokenName(tok));
|
|
errorUnexpectedToken(tok);
|
|
}
|
|
}
|
|
|
|
int main(int argc, char** argv)
|
|
{
|
|
// gcc/MinGW inserts a call to __main() here.
|
|
int i;
|
|
|
|
#ifdef __SMALLER_C__
|
|
#ifdef DETERMINE_VA_LIST
|
|
DetermineVaListType();
|
|
#endif
|
|
#endif
|
|
|
|
GenInit();
|
|
|
|
// Parse the command line arguments
|
|
for (i = 1; i < argc; i++)
|
|
{
|
|
// DONE: move code-generator-specific options to
|
|
// the code generator
|
|
if (GenInitParams(argc, argv, &i))
|
|
{
|
|
continue;
|
|
}
|
|
else if (!strcmp(argv[i], "-signed-char"))
|
|
{
|
|
// this is the default option
|
|
CharIsSigned = 1;
|
|
continue;
|
|
}
|
|
else if (!strcmp(argv[i], "-unsigned-char"))
|
|
{
|
|
CharIsSigned = 0;
|
|
continue;
|
|
}
|
|
#ifdef CAN_COMPILE_32BIT
|
|
else if (!strcmp(argv[i], "-ctor-fxn"))
|
|
{
|
|
if (i + 1 < argc)
|
|
{
|
|
MainPrologCtorFxn = argv[++i];
|
|
continue;
|
|
}
|
|
}
|
|
#endif
|
|
else if (!strcmp(argv[i], "-leading-underscore"))
|
|
{
|
|
// this is the default option for x86
|
|
UseLeadingUnderscores = 1;
|
|
continue;
|
|
}
|
|
else if (!strcmp(argv[i], "-no-leading-underscore"))
|
|
{
|
|
// this is the default option for MIPS
|
|
UseLeadingUnderscores = 0;
|
|
continue;
|
|
}
|
|
else if (!strcmp(argv[i], "-label"))
|
|
{
|
|
if (i + 1 < argc)
|
|
{
|
|
LabelCnt = atoi(argv[++i]);
|
|
continue;
|
|
}
|
|
}
|
|
else if (!strcmp(argv[i], "-no-externs"))
|
|
{
|
|
GenExterns = 0;
|
|
continue;
|
|
}
|
|
else if (!strcmp(argv[i], "-verbose"))
|
|
{
|
|
warnings = verbose = 1;
|
|
continue;
|
|
}
|
|
else if (!strcmp(argv[i], "-Wall"))
|
|
{
|
|
warnings = 1;
|
|
continue;
|
|
}
|
|
#ifndef NO_PREPROCESSOR
|
|
else if (!strcmp(argv[i], "-I") || !strcmp(argv[i], "-SI"))
|
|
{
|
|
if (i + 1 < argc)
|
|
{
|
|
int len = strlen(argv[++i]) + 1;
|
|
if (argv[i - 1][1] == 'I')
|
|
{
|
|
if (MAX_SEARCH_PATH - SearchPathsLen < len)
|
|
//error("Path name too long\n");
|
|
errorFileName();
|
|
strcpy(SearchPaths + SearchPathsLen, argv[i]);
|
|
SearchPathsLen += len;
|
|
}
|
|
else
|
|
{
|
|
if (MAX_SEARCH_PATH - SysSearchPathsLen < len)
|
|
//error("Path name too long\n");
|
|
errorFileName();
|
|
strcpy(SysSearchPaths + SysSearchPathsLen, argv[i]);
|
|
SysSearchPathsLen += len;
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
/*
|
|
else if (!strcmp(argv[i], "-no-pp"))
|
|
{
|
|
// TBD!!! don't do preprocessing when this option is present
|
|
}
|
|
*/
|
|
// DONE: '-D macro[=expansion]': '#define macro 1' when there's no '=expansion'
|
|
else if (!strcmp(argv[i], "-D"))
|
|
{
|
|
if (i + 1 < argc)
|
|
{
|
|
char id[MAX_IDENT_LEN + 1];
|
|
char* e = strchr(argv[++i], '=');
|
|
int len;
|
|
if (e)
|
|
{
|
|
len = e - argv[i];
|
|
e++;
|
|
}
|
|
else
|
|
{
|
|
len = strlen(argv[i]);
|
|
e = "1";
|
|
}
|
|
if (len > 0 && len <= MAX_IDENT_LEN)
|
|
{
|
|
int j, bad = 1;
|
|
memcpy(id, argv[i], len);
|
|
id[len] = '\0';
|
|
for (j = 0; j < len; j++)
|
|
if ((bad = !(id[j] == '_' || (!j * isalpha(id[j] & 0xFFu) + j * isalnum(id[j] & 0xFFu)))) != 0)
|
|
break;
|
|
if (!bad)
|
|
{
|
|
DefineMacro(id, e);
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif // #ifndef NO_PREPROCESSOR
|
|
else if (argv[i][0] == '-')
|
|
{
|
|
// unknown option
|
|
}
|
|
else if (FileCnt == 0)
|
|
{
|
|
// If it's none of the known options,
|
|
// assume it's the source code file name
|
|
if (strlen(argv[i]) > MAX_FILE_NAME_LEN)
|
|
//error("File name too long\n");
|
|
errorFileName();
|
|
strcpy(FileNames[0], argv[i]);
|
|
if ((Files[0] = fopen(FileNames[0], "r")) == NULL)
|
|
//error("Cannot open file \"%s\"\n", FileNames[0]);
|
|
errorFile(FileNames[0]);
|
|
LineNos[0] = LineNo;
|
|
LinePoss[0] = LinePos;
|
|
FileCnt++;
|
|
continue;
|
|
}
|
|
else if (FileCnt == 1 && OutFile == NULL)
|
|
{
|
|
// This should be the output file name
|
|
if ((OutFile = fopen(argv[i], "w")) == NULL)
|
|
//error("Cannot open output file \"%s\"\n", argv[i]);
|
|
errorFile(argv[i]);
|
|
continue;
|
|
}
|
|
|
|
error("Invalid or unsupported command line option\n");
|
|
}
|
|
|
|
if (!FileCnt)
|
|
error("Input file not specified\n");
|
|
|
|
GenInitFinalize();
|
|
|
|
#ifndef NO_PREPROCESSOR
|
|
// Define a few macros useful for conditional compilation
|
|
DefineMacro("__SMALLER_C__", "0x0100");
|
|
if (SizeOfWord == 2)
|
|
DefineMacro("__SMALLER_C_16__", "");
|
|
#ifdef CAN_COMPILE_32BIT
|
|
else if (SizeOfWord == 4)
|
|
DefineMacro("__SMALLER_C_32__", "");
|
|
#endif
|
|
#ifdef CAN_COMPILE_32BIT
|
|
if (OutputFormat == FormatSegHuge)
|
|
DefineMacro("__HUGE__", "");
|
|
#endif
|
|
if (CharIsSigned)
|
|
DefineMacro("__SMALLER_C_SCHAR__", "");
|
|
else
|
|
DefineMacro("__SMALLER_C_UCHAR__", "");
|
|
#endif
|
|
|
|
// populate CharQueue[] with the initial file characters
|
|
ShiftChar();
|
|
|
|
puts2(FileHeader);
|
|
|
|
// compile
|
|
#ifndef NO_PPACK
|
|
PragmaPackValue = SizeOfWord;
|
|
#endif
|
|
ParseBlock(NULL, 0);
|
|
|
|
GenFin();
|
|
|
|
#ifndef NO_ANNOTATIONS
|
|
DumpSynDecls();
|
|
#endif
|
|
#ifndef NO_PREPROCESSOR
|
|
#ifndef NO_ANNOTATIONS
|
|
DumpMacroTable();
|
|
#endif
|
|
#endif
|
|
#ifndef NO_ANNOTATIONS
|
|
DumpIdentTable();
|
|
#endif
|
|
|
|
GenStartCommentLine(); printf2("Next label number: %d\n", LabelCnt);
|
|
|
|
if (warnings && warnCnt && OutFile)
|
|
printf("%d warnings\n", warnCnt);
|
|
GenStartCommentLine(); printf2("Compilation succeeded.\n");
|
|
|
|
if (OutFile)
|
|
fclose(OutFile);
|
|
|
|
return 0;
|
|
}
|