diff --git a/src/ld65/cfgexpr.c b/src/ld65/cfgexpr.c new file mode 100644 index 000000000..2ed08c613 --- /dev/null +++ b/src/ld65/cfgexpr.c @@ -0,0 +1,340 @@ +/*****************************************************************************/ +/* */ +/* cfgexpr.c */ +/* */ +/* Simple expressions for use with in configuration file */ +/* */ +/* */ +/* */ +/* (C) 2005, Ullrich von Bassewitz */ +/* Römerstrasse 52 */ +/* D-70794 Filderstadt */ +/* EMail: uz@cc65.org */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +/* common */ +#include "strbuf.h" + +/* ld65 */ +#include "cfgexpr.h" +#include "error.h" +#include "exports.h" +#include "scanner.h" +#include "spool.h" + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Type of a CfgExpr */ +enum { + ceEmpty, + ceInt, + ceString +}; + +typedef struct CfgExpr CfgExpr; +struct CfgExpr { + unsigned Type; /* Type of the expression */ + long IVal; /* Integer value if it's a string */ + StrBuf SVal; /* String value if it's a string */ +}; + +#define CFGEXPR_INITIALIZER { ceEmpty, 0, STATIC_STRBUF_INITIALIZER } + + + +/*****************************************************************************/ +/* Forwards */ +/*****************************************************************************/ + + + +static void Expr (CfgExpr* E); +/* Full expression */ + + + +/*****************************************************************************/ +/* struct CfgExpr */ +/*****************************************************************************/ + + + +static void CE_Done (CfgExpr* E) +/* Cleanup a CfgExpr struct */ +{ + /* If the type is a string, we must delete the string buffer */ + if (E->Type == ceString) { + DoneStrBuf (&E->SVal); + } +} + + + +static void CE_AssureInt (const CfgExpr* E) +/* Make sure, E contains an integer */ +{ + if (E->Type != ceInt) { + CfgError ("Integer type expected"); + } +} + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +static void Factor (CfgExpr* E) +/* Read and return a factor in E */ +{ + Export* Sym; + + + switch (CfgTok) { + + case CFGTOK_IDENT: + /* An identifier - search an export with the given name */ + Sym = FindExport (GetStringId (CfgSVal)); + if (Sym == 0) { + CfgError ("Unknown symbol in expression: `%s'", CfgSVal); + } + /* We can only handle constants */ + if (!IsConstExport (Sym)) { + CfgError ("Value for symbol `%s' is not constant", CfgSVal); + } + + /* Use the symbol value */ + E->IVal = GetExportVal (Sym); + E->Type = ceInt; + + /* Skip the symbol name */ + CfgNextTok (); + break; + + case CFGTOK_INTCON: + /* An integer constant */ + E->IVal = CfgIVal; + E->Type = ceInt; + CfgNextTok (); + break; + + case CFGTOK_STRCON: + /* A string constant */ + SB_CopyStr (&E->SVal, CfgSVal); + E->Type = ceString; + CfgNextTok (); + break; + + case CFGTOK_PLUS: + /* Unary plus */ + CfgNextTok (); + Factor (E); + CE_AssureInt (E); + break; + + case CFGTOK_MINUS: + /* Unary minus */ + CfgNextTok (); + Factor (E); + CE_AssureInt (E); + E->IVal = -E->IVal; + break; + + case CFGTOK_LPAR: + /* Left parenthesis */ + CfgNextTok (); + Expr (E); + CfgConsume (CFGTOK_RPAR, "')' expected"); + break; + + default: + CfgError ("Invalid expression: %d", CfgTok); + break; + } +} + + + +static void Term (CfgExpr* E) +/* Multiplicative operators: * and / */ +{ + /* Left operand */ + Factor (E); + + /* Handle multiplicative operators */ + while (CfgTok == CFGTOK_MUL || CfgTok == CFGTOK_DIV) { + + CfgExpr RightSide = CFGEXPR_INITIALIZER; + + /* Remember the token */ + cfgtok_t Tok = CfgTok; + + /* Left side must be an int */ + CE_AssureInt (E); + + /* Get the right operand and make sure it's an int */ + Factor (&RightSide); + CE_AssureInt (&RightSide); + + /* Handle the operation */ + switch (Tok) { + + case CFGTOK_MUL: + E->IVal *= RightSide.IVal; + break; + + case CFGTOK_DIV: + if (RightSide.IVal == 0) { + CfgError ("Division by zero"); + } + E->IVal /= RightSide.IVal; + break; + + default: + Internal ("Unhandled token in Term: %d", Tok); + } + + /* Cleanup RightSide (this is not really needed since it may not + * contain strings at this point, but call it anyway for clarity. + */ + CE_Done (&RightSide); + } +} + + + +static void SimpleExpr (CfgExpr* E) +/* Additive operators: + and - */ +{ + /* Left operand */ + Term (E); + + /* Handle additive operators */ + while (CfgTok == CFGTOK_PLUS || CfgTok == CFGTOK_MINUS) { + + CfgExpr RightSide = CFGEXPR_INITIALIZER; + + /* Remember the token, then skip it */ + cfgtok_t Tok = CfgTok; + CfgNextTok (); + + /* Get the right operand */ + Term (&RightSide); + + /* Make sure, left and right side are of the same type */ + if (E->Type != RightSide.Type) { + CfgError ("Incompatible types in expression"); + } + + /* Handle the operation */ + switch (Tok) { + + case CFGTOK_PLUS: + /* Plus is defined for strings and ints */ + if (E->Type == ceInt) { + E->IVal += RightSide.IVal; + } else if (E->Type == ceString) { + SB_Append (&E->SVal, &RightSide.SVal); + } else { + Internal ("Unhandled type in '+' operator: %u", E->Type); + } + break; + + case CFGTOK_MINUS: + /* Operands must be ints */ + CE_AssureInt (E); + E->IVal -= RightSide.IVal; + break; + + default: + Internal ("Unhandled token in SimpleExpr: %d", Tok); + } + + /* Cleanup RightSide */ + CE_Done (&RightSide); + } +} + + + +static void Expr (CfgExpr* E) +/* Full expression */ +{ + SimpleExpr (E); +} + + + +long CfgIntExpr (void) +/* Read an expression, make sure it's an int, and return its value */ +{ + long Val; + + CfgExpr E = CFGEXPR_INITIALIZER; + + /* Parse the expression */ + Expr (&E); + + /* Make sure it's an integer */ + CE_AssureInt (&E); + + /* Get the value */ + Val = E.IVal; + + /* Cleaup E */ + CE_Done (&E); + + /* Return the value */ + return Val; +} + + + +long CfgCheckedIntExpr (long Min, long Max) +/* Read an expression, make sure it's an int and in range, then return its + * value. + */ +{ + /* Get the value */ + long Val = CfgIntExpr (); + + /* Check the range */ + if (Val < Min || Val > Max) { + CfgError ("Range error"); + } + + /* Return the value */ + return Val; +} + + + diff --git a/src/ld65/cfgexpr.h b/src/ld65/cfgexpr.h new file mode 100644 index 000000000..dc6b99bb4 --- /dev/null +++ b/src/ld65/cfgexpr.h @@ -0,0 +1,61 @@ +/*****************************************************************************/ +/* */ +/* cfgexpr.h */ +/* */ +/* Simple expressions for use with in configuration file */ +/* */ +/* */ +/* */ +/* (C) 2005, Ullrich von Bassewitz */ +/* Römerstrasse 52 */ +/* D-70794 Filderstadt */ +/* EMail: uz@cc65.org */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef CFGEXPR_H +#define CFGEXPR_H + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +long CfgIntExpr (void); +/* Read an expression, make sure it's an int, and return its value */ + +long CfgCheckedIntExpr (long Min, long Max); +/* Read an expression, make sure it's an int and in range, then return its + * value. + */ + + + +/* End of cfgexpr.h */ +#endif + + + diff --git a/src/ld65/config.c b/src/ld65/config.c index eb7dbdee4..0a4df83e3 100644 --- a/src/ld65/config.c +++ b/src/ld65/config.c @@ -48,6 +48,7 @@ /* ld65 */ #include "bin.h" #include "binfmt.h" +#include "cfgexpr.h" #include "condes.h" #include "config.h" #include "error.h" @@ -406,11 +407,11 @@ static void ParseMemory (void) { static const IdentTok Attributes [] = { { "START", CFGTOK_START }, - { "SIZE", CFGTOK_SIZE }, + { "SIZE", CFGTOK_SIZE }, { "TYPE", CFGTOK_TYPE }, { "FILE", CFGTOK_FILE }, { "DEFINE", CFGTOK_DEFINE }, - { "FILL", CFGTOK_FILL }, + { "FILL", CFGTOK_FILL }, { "FILLVAL", CFGTOK_FILLVAL }, }; static const IdentTok Types [] = { @@ -444,22 +445,21 @@ static void ParseMemory (void) case CFGTOK_START: FlagAttr (&M->Attr, MA_START, "START"); - CfgAssureInt (); - M->Start = CfgIVal; + M->Start = CfgIntExpr (); break; case CFGTOK_SIZE: FlagAttr (&M->Attr, MA_SIZE, "SIZE"); - CfgAssureInt (); - M->Size = CfgIVal; + M->Size = CfgIntExpr (); break; case CFGTOK_TYPE: FlagAttr (&M->Attr, MA_TYPE, "TYPE"); CfgSpecialToken (Types, ENTRY_COUNT (Types), "Type"); if (CfgTok == CFGTOK_RO) { - M->Flags |= MF_RO; + M->Flags |= MF_RO; } + CfgNextTok (); break; case CFGTOK_FILE: @@ -467,6 +467,7 @@ static void ParseMemory (void) CfgAssureStr (); /* Get the file entry and insert the memory area */ FileInsert (GetFile (GetStringId (CfgSVal)), M); + CfgNextTok (); break; case CFGTOK_DEFINE: @@ -476,6 +477,7 @@ static void ParseMemory (void) if (CfgTok == CFGTOK_TRUE) { M->Flags |= MF_DEFINE; } + CfgNextTok (); break; case CFGTOK_FILL: @@ -485,13 +487,12 @@ static void ParseMemory (void) if (CfgTok == CFGTOK_TRUE) { M->Flags |= MF_FILL; } + CfgNextTok (); break; case CFGTOK_FILLVAL: FlagAttr (&M->Attr, MA_FILLVAL, "FILLVAL"); - CfgAssureInt (); - CfgRangeCheck (0, 0xFF); - M->FillVal = (unsigned char) CfgIVal; + M->FillVal = (unsigned char) CfgCheckedIntExpr (0, 0xFF); break; default: @@ -499,8 +500,7 @@ static void ParseMemory (void) } - /* Skip the attribute value and an optional comma */ - CfgNextTok (); + /* Skip an optional comma */ CfgOptionalComma (); } @@ -641,6 +641,7 @@ static void ParseSegments (void) }; unsigned Count; + long Val; /* The MEMORY section must preceed the SEGMENTS section */ if ((SectionsEncountered & SE_MEMORY) == 0) { @@ -674,22 +675,20 @@ static void ParseSegments (void) switch (AttrTok) { case CFGTOK_ALIGN: - CfgAssureInt (); FlagAttr (&S->Attr, SA_ALIGN, "ALIGN"); - CfgRangeCheck (1, 0x10000); - S->Align = BitFind (CfgIVal); - if ((0x01UL << S->Align) != CfgIVal) { + Val = CfgCheckedIntExpr (1, 0x10000); + S->Align = BitFind (Val); + if ((0x01L << S->Align) != Val) { CfgError ("Alignment must be a power of 2"); } S->Flags |= SF_ALIGN; break; case CFGTOK_ALIGN_LOAD: - CfgAssureInt (); FlagAttr (&S->Attr, SA_ALIGN_LOAD, "ALIGN_LOAD"); - CfgRangeCheck (1, 0x10000); - S->AlignLoad = BitFind (CfgIVal); - if ((0x01UL << S->AlignLoad) != CfgIVal) { + Val = CfgCheckedIntExpr (1, 0x10000); + S->AlignLoad = BitFind (Val); + if ((0x01L << S->AlignLoad) != Val) { CfgError ("Alignment must be a power of 2"); } S->Flags |= SF_ALIGN_LOAD; @@ -702,18 +701,18 @@ static void ParseSegments (void) if (CfgTok == CFGTOK_TRUE) { S->Flags |= SF_DEFINE; } + CfgNextTok (); break; case CFGTOK_LOAD: FlagAttr (&S->Attr, SA_LOAD, "LOAD"); S->Load = CfgGetMemory (GetStringId (CfgSVal)); + CfgNextTok (); break; case CFGTOK_OFFSET: - CfgAssureInt (); FlagAttr (&S->Attr, SA_OFFSET, "OFFSET"); - CfgRangeCheck (1, 0x1000000); - S->Addr = CfgIVal; + S->Addr = CfgCheckedIntExpr (1, 0x1000000); S->Flags |= SF_OFFSET; break; @@ -723,18 +722,18 @@ static void ParseSegments (void) if (CfgTok == CFGTOK_TRUE) { S->Flags |= SF_OPTIONAL; } + CfgNextTok (); break; case CFGTOK_RUN: FlagAttr (&S->Attr, SA_RUN, "RUN"); S->Run = CfgGetMemory (GetStringId (CfgSVal)); + CfgNextTok (); break; case CFGTOK_START: - CfgAssureInt (); FlagAttr (&S->Attr, SA_START, "START"); - CfgRangeCheck (1, 0x1000000); - S->Addr = CfgIVal; + S->Addr = CfgCheckedIntExpr (1, 0x1000000); S->Flags |= SF_START; break; @@ -748,6 +747,7 @@ static void ParseSegments (void) case CFGTOK_ZP: S->Flags |= (SF_BSS | SF_ZP); break; default: Internal ("Unexpected token: %d", CfgTok); } + CfgNextTok (); break; default: @@ -755,8 +755,7 @@ static void ParseSegments (void) } - /* Skip the attribute value and an optional comma */ - CfgNextTok (); + /* Skip an optional comma */ CfgOptionalComma (); } @@ -921,6 +920,8 @@ static void ParseO65 (void) } /* Insert the symbol into the table */ O65SetExport (O65FmtDesc, CfgSValId); + /* Eat the identifier token */ + CfgNextTok (); break; case CFGTOK_IMPORT: @@ -943,6 +944,8 @@ static void ParseO65 (void) } /* Insert the symbol into the table */ O65SetImport (O65FmtDesc, CfgSValId); + /* Eat the identifier token */ + CfgNextTok (); break; case CFGTOK_TYPE: @@ -963,6 +966,8 @@ static void ParseO65 (void) default: CfgError ("Unexpected type token"); } + /* Eat the attribute token */ + CfgNextTok (); break; case CFGTOK_OS: @@ -984,24 +989,21 @@ static void ParseO65 (void) default: CfgError ("Unexpected OS token"); } } + CfgNextTok (); break; case CFGTOK_ID: /* Cannot have this attribute twice */ FlagAttr (&AttrFlags, atID, "ID"); /* We're expecting a number in the 0..$FFFF range*/ - CfgAssureInt (); - CfgRangeCheck (0, 0xFFFF); - ModuleId = (unsigned) CfgIVal; + ModuleId = (unsigned) CfgCheckedIntExpr (0, 0xFFFF); break; case CFGTOK_VERSION: /* Cannot have this attribute twice */ FlagAttr (&AttrFlags, atVersion, "VERSION"); /* We're expecting a number in byte range */ - CfgAssureInt (); - CfgRangeCheck (0, 0xFF); - Version = (unsigned) CfgIVal; + Version = (unsigned) CfgCheckedIntExpr (0, 0xFF); break; default: @@ -1009,8 +1011,7 @@ static void ParseO65 (void) } - /* Skip the attribute value and an optional comma */ - CfgNextTok (); + /* Skip an optional comma */ CfgOptionalComma (); } @@ -1267,11 +1268,8 @@ static void ParseStartAddress (void) case CFGTOK_DEFAULT: /* Don't allow this twice */ FlagAttr (&AttrFlags, atDefault, "DEFAULT"); - /* We expect a number */ - CfgAssureInt (); - CfgRangeCheck (0, 0xFFFFFF); - /* Remember the value for later */ - DefStartAddr = CfgIVal; + /* We expect a numeric expression */ + DefStartAddr = CfgCheckedIntExpr (0, 0xFFFFFF); break; default: @@ -1279,9 +1277,6 @@ static void ParseStartAddress (void) } - /* Skip the attribute value */ - CfgNextTok (); - /* Semicolon ends the ConDes decl, otherwise accept an optional comma */ if (CfgTok == CFGTOK_SEMI) { break; @@ -1376,16 +1371,16 @@ static void ParseSymbols (void) /* Allow an optional assignment */ CfgOptionalAssign (); - /* Make sure the next token is an integer, read and skip it */ - CfgAssureInt (); - Val = CfgIVal; - CfgNextTok (); + /* Make sure the next token is an integer expression, read and + * skip it. + */ + Val = CfgIntExpr (); } else { /* Bitmask to remember the attributes we got already */ enum { - atNone = 0x0000, + atNone = 0x0000, atValue = 0x0001, atWeak = 0x0002 }; @@ -1403,8 +1398,10 @@ static void ParseSymbols (void) CfgSpecialToken (Attributes, ENTRY_COUNT (Attributes), "Attribute"); AttrTok = CfgTok; - /* An optional assignment follows */ + /* Skip the attribute name */ CfgNextTok (); + + /* An optional assignment follows */ CfgOptionalAssign (); /* Check which attribute was given */ @@ -1413,10 +1410,8 @@ static void ParseSymbols (void) case CFGTOK_VALUE: /* Don't allow this twice */ FlagAttr (&AttrFlags, atValue, "VALUE"); - /* We expect a number */ - CfgAssureInt (); - /* Remember the value for later */ - Val = CfgIVal; + /* We expect a numeric expression */ + Val = CfgIntExpr (); break; case CFGTOK_WEAK: @@ -1424,6 +1419,7 @@ static void ParseSymbols (void) FlagAttr (&AttrFlags, atWeak, "WEAK"); CfgBoolToken (); Weak = (CfgTok == CFGTOK_TRUE); + CfgNextTok (); break; default: @@ -1431,9 +1427,6 @@ static void ParseSymbols (void) } - /* Skip the attribute value */ - CfgNextTok (); - /* Semicolon ends the decl, otherwise accept an optional comma */ if (CfgTok == CFGTOK_SEMI) { break; diff --git a/src/ld65/make/gcc.mak b/src/ld65/make/gcc.mak index ab4f20fd8..c8112c61e 100644 --- a/src/ld65/make/gcc.mak +++ b/src/ld65/make/gcc.mak @@ -24,6 +24,7 @@ CVT=cfg/cvt-cfg.pl OBJS = asserts.o \ bin.o \ binfmt.o \ + cfgexpr.o \ condes.o \ config.o \ dbgfile.o \ diff --git a/src/ld65/make/watcom.mak b/src/ld65/make/watcom.mak index 6246441bb..c288ed5fc 100644 --- a/src/ld65/make/watcom.mak +++ b/src/ld65/make/watcom.mak @@ -63,6 +63,7 @@ endif OBJS = asserts.obj \ bin.obj \ binfmt.obj \ + cfgexpr.obj \ condes.obj \ config.obj \ dbgfile.obj \ diff --git a/src/ld65/scanner.c b/src/ld65/scanner.c index 8767c368c..d67e6327d 100644 --- a/src/ld65/scanner.c +++ b/src/ld65/scanner.c @@ -225,6 +225,36 @@ Again: /* Other characters */ switch (C) { + case '-': + NextChar (); + CfgTok = CFGTOK_MINUS; + break; + + case '+': + NextChar (); + CfgTok = CFGTOK_PLUS; + break; + + case '*': + NextChar (); + CfgTok = CFGTOK_MUL; + break; + + case '/': + NextChar (); + CfgTok = CFGTOK_DIV; + break; + + case '(': + NextChar (); + CfgTok = CFGTOK_LPAR; + break; + + case ')': + NextChar (); + CfgTok = CFGTOK_RPAR; + break; + case '{': NextChar (); CfgTok = CFGTOK_LCURLY; diff --git a/src/ld65/scanner.h b/src/ld65/scanner.h index 9f2efa4d8..f7f6caca5 100644 --- a/src/ld65/scanner.h +++ b/src/ld65/scanner.h @@ -47,9 +47,15 @@ /* Config file tokens */ typedef enum { CFGTOK_NONE, - CFGTOK_INTCON, - CFGTOK_STRCON, - CFGTOK_IDENT, + CFGTOK_INTCON, /* Integer constant */ + CFGTOK_STRCON, /* String constant */ + CFGTOK_IDENT, /* Identifier */ + CFGTOK_PLUS, + CFGTOK_MINUS, + CFGTOK_MUL, + CFGTOK_DIV, + CFGTOK_LPAR, + CFGTOK_RPAR, CFGTOK_LCURLY, CFGTOK_RCURLY, CFGTOK_SEMI,