It is currently Mon Oct 20, 2014 4:38 pm

All times are UTC [ DST ]




Post new topic Reply to topic  [ 11 posts ] 
Author Message
 Post subject: RFE "DEFINED(foo)"
PostPosted: Mon Apr 08, 2013 5:22 pm 
Offline
 Profile

Joined: Sat Mar 10, 2012 10:27 pm
Posts: 41
(I don't see anything like this in the docs)

IF DEFINED(MY_VARIABLE)
dostuff
ENDIF

I can't just do
IF MY_VARIABLE
'cos if variable isn't defined then the assembly aborts.

This would make it easier to do conditional compiles
MY_VARIABLE = TRUE
include "main_program"

(If there is already a way of doing this, please enlighten me :-))


Top
 
 Post subject: Re: RFE "DEFINED(foo)"
PostPosted: Mon Apr 08, 2013 5:52 pm 
Offline
User avatar
 Profile

Joined: Mon Jan 07, 2008 6:46 pm
Posts: 380
Location: Málaga, Spain
No, I'm afraid there's nothing like this at the moment.

For my projects, I just have two source files which include the 'main' one, and they just look like this:
Code:
DEBUG = TRUE
INCLUDE "Main.6502"

Code:
DEBUG = FALSE
INCLUDE "Main.6502"

and then launch the appropriate one from my makefile.

Given that you already have to create one stub file to define the variable (as you can't do it on the command line either), it's not such a biggie to create a second stub file to define it in the 'opposite' sense.

Agreed, it's kinda messy though.

I used makefiles to launch my build process, so another option would be to generate a file to be INCLUDEd by the main source file, prior to starting BeebAsm.


Top
 
 Post subject: Re: RFE "DEFINED(foo)"
PostPosted: Mon Apr 08, 2013 6:55 pm 
Offline
 Profile

Joined: Sat Mar 10, 2012 10:27 pm
Posts: 41
Yeah, the two stubs are what I'm currently do. It'd be nice to not need that.

Of course the next step will then be to add a "-D" option to the command line to define values so I could do
Code:
all: source
        beebasm -DDEBUG -oDEBUG.ROM source
        beebasm -oPROD.ROM source

:-)

Looking at the code in expression.cpp (I'm not a C++ programmer), I'm not sure DEFINED() fits nicely into your parser. We might need to kludge and break layers. Hmm... that'll be ugly 'cos of the stack model. Hmm...


Top
 
 Post subject: Re: RFE "DEFINED(foo)"
PostPosted: Tue Apr 09, 2013 12:32 am 
Offline
 Profile

Joined: Sat Mar 10, 2012 10:27 pm
Posts: 41
So instead of DEFINED(), how about IFDEF ?

This is maybe not correct...
Code:
Only in beebasm-new: beebasm
diff -cr beebasm-c35bc523430d/src/commands.cpp beebasm-new/src/commands.cpp
*** beebasm-c35bc523430d/src/commands.cpp   Mon Apr  8 21:04:15 2013
--- beebasm-new/src/commands.cpp   Mon Apr  8 21:09:06 2013
***************
*** 56,61 ****
--- 56,63 ----
     { "SAVE",      &LineParser::HandleSave,         0 },
     { "FOR",      &LineParser::HandleFor,            0 },
     { "NEXT",      &LineParser::HandleNext,         0 },
+    { "IFDEF",      &LineParser::HandleIfDef,         &SourceFile::AddIfLevel },
+    { "ELIFDEF",      &LineParser::HandleIfDef,         &SourceFile::StartElif },
     { "IF",         &LineParser::HandleIf,            &SourceFile::AddIfLevel },
     { "ELIF",      &LineParser::HandleIf,            &SourceFile::StartElif },
     { "ELSE",      &LineParser::HandleDirective,         &SourceFile::StartElse },
***************
*** 1296,1301 ****
--- 1298,1317 ----
     }
  }
 
+ void LineParser::HandleIfDef()
+ {
+    if (!LineParser::MoveToNextAtom())
+    {
+       throw AsmException_SyntaxError_EmptyExpression( m_line, m_column );
+    }
+    else
+    {
+       string symbolName = GetSymbolName();
+       bool condition = SymbolTable::Instance().IsSymbolDefined(symbolName);
+       m_sourceCode->SetCurrentIfCondition( condition );
+    }
+ }   
+
 
 
  /*************************************************************************************************/
diff -cr beebasm-c35bc523430d/src/lineparser.h beebasm-new/src/lineparser.h
*** beebasm-c35bc523430d/src/lineparser.h   Mon Apr  8 21:04:15 2013
--- beebasm-new/src/lineparser.h   Mon Apr  8 21:05:53 2013
***************
*** 143,148 ****
--- 143,149 ----
     void         HandleOpenBrace();
     void         HandleCloseBrace();
     void         HandleIf();
+    void         HandleIfDef();
     void         HandleAlign();
     void         HandleSkip();
     void         HandleSkipTo();
diff -cr beebasm-c35bc523430d/src/main.cpp beebasm-new/src/main.cpp
*** beebasm-c35bc523430d/src/main.cpp   Mon Apr  8 21:04:15 2013
--- beebasm-new/src/main.cpp   Mon Apr  8 21:15:56 2013
***************
*** 67,72 ****
--- 67,73 ----
        READY,
        WAITING_FOR_INPUT_FILENAME,
        WAITING_FOR_OUTPUT_FILENAME,
+       WAITING_FOR_DEFINE_NAME,
        WAITING_FOR_DISC_INPUT_FILENAME,
        WAITING_FOR_DISC_OUTPUT_FILENAME,
        WAITING_FOR_BOOT_FILENAME,
***************
*** 77,82 ****
--- 78,84 ----
     bool bDumpSymbols = false;
 
     GlobalData::Create();
+    SymbolTable::Create();
 
     // Parse command line parameters
 
***************
*** 94,99 ****
--- 96,105 ----
              {
                 state = WAITING_FOR_OUTPUT_FILENAME;
              }
+             else if ( strcmp( argv[i], "-D" ) == 0 )
+             {
+                state = WAITING_FOR_DEFINE_NAME;
+             }
              else if ( strcmp( argv[i], "-do" ) == 0 )
              {
                 state = WAITING_FOR_DISC_OUTPUT_FILENAME;
***************
*** 124,129 ****
--- 130,136 ----
                 cout << "Possible options:" << endl;
                 cout << " -i <file>      Specify source filename" << endl;
                 cout << " -o <file>      Specify output filename (when not specified by SAVE command)" << endl;
+                cout << " -D NAME        Sets NAME = TRUE as a global variable" << endl;
                 cout << " -di <file>     Specify a disc image file to be added to" << endl;
                 cout << " -do <file>     Specify a disc image file to output" << endl;
                 cout << " -boot <file>   Specify a filename to be run by !BOOT on a new disc image" << endl;
***************
*** 156,161 ****
--- 163,173 ----
              state = READY;
              break;
 
+          case WAITING_FOR_DEFINE_NAME:
+             SymbolTable::Instance().AddSymbol (argv[i],-1);
+             state = READY;
+             break;
+
 
           case WAITING_FOR_DISC_OUTPUT_FILENAME:
 
***************
*** 213,219 ****
 
     int exitCode = EXIT_SUCCESS;
 
-    SymbolTable::Create();
     ObjectCode::Create();
     MacroTable::Create();
     SetupBASICTables();
--- 225,230 ----
Only in beebasm-new/src: objects


Whadya think?

(Edited to fix no-parameter case)
(Edited for "-D FOO" functionality)


Top
 
 Post subject: Re: RFE "DEFINED(foo)"
PostPosted: Wed Apr 10, 2013 3:06 pm 
Offline
User avatar
 Profile

Joined: Mon Jan 07, 2008 6:46 pm
Posts: 380
Location: Málaga, Spain
Yeah, to add a DEFINED(...) function would require some reworking, as variables are never held as lvalues on the expression stack, but immediately expanded to their current value. (I don't really like much of the parsing code to be honest, and it'd be the first thing to be revisited in a future version of BeebAsm, among other things to make defining the syntax of each command much more simple.)

I think your code will fail if you write IFDEF 10 or something like that, because GetSymbolName assumes we're already pointing to a valid symbol name. Without trying it, I think you probably need something more like this:
Code:
void LineParser::HandleIfDef()
{
    // check there's something following the IFDEF
    if (!AdvanceAndCheckEndOfStatement())
    {
        throw AsmException_SyntaxError_EmptyExpression(m_line, m_column);
    }
    else
    {
        // check it's a valid symbol name
        if (!isalpha(m_line[m_column]) && m_line[m_column] != '_')
        {
            throw AsmException_SyntaxError_InvalidSymbolName(m_line, m_column);
        }

        string symbolName = GetSymbolName();
        bool bSymbolDefined = (SymbolTable::Instance().IsSymbolDefined(symbolName);
        m_sourceCode->SetCurrentIfCondition(bSymbolDefined);

        // now insist that there's nothing following the symbol name other than a valid statement separator
        if (AdvanceAndCheckEndOfStatement())
        {
            throw AsmException_SyntaxError_InvalidCharacter(m_line, m_column);
        }
    }
}

This is still not 100% correct in that it will only search the global scope for the symbol rather than the current one upwards, leading to this behaviour:
Code:
globalsymbol = TRUE
{
    localsymbol = TRUE
    IFDEF globalsymbol
        PRINT"You will see this..."
    ENDIF
    IFDEF localsymbol
        PRINT"...but you will never see this"
    ENDIF
}

This could be fixed using similar code to that in LineParser::GetValue(), but I don't really see the value in IFDEF working in anything other than global scope, because its only real use is surely in conjunction with the command-line switch to define a variable.

For this reason, I don't really like the idea of IFDEF at all as:
  1. it goes against its BBC BASIC roots
  2. it requires two additional keywords (of which ELIFDEF has a very ugly name :lol: )
  3. it has a very narrow use-case, as 99% of the time you don't need to check if a variable has been defined or not

My personal preference would be to expand the syntax of the -D command-line switch, allowing you to write -D DEBUG=0 or -D DEBUG=1. This results in the same functionality, but using a simpler set of control commands (i.e. IF...ELIF...ELSE...ENDIF works for all).

Edit to add: these days, in C/C++ I don't even use #ifdef to conditionally compile. Instead I #define macros with a value of 1 and use #if as I find it much neater to write #if FOO | BAR than #if defined(FOO) | defined(BAR). It also force me to define macros explicitly with a value of 0 to make the point that such-and-such feature is disabled. I guess this is more or less on the same track...


Top
 
 Post subject: Re: RFE "DEFINED(foo)"
PostPosted: Wed Apr 10, 2013 6:21 pm 
Offline
 Profile

Joined: Sat Mar 10, 2012 10:27 pm
Posts: 41
The use case I'm primarily after is in "branching" code.

For example, I have an existing piece of code and I want to add some test case or random functionality or enhancement. In C I might do
#ifdef SWEH_HAS_BROKEN_THIS
dostuff
#endif

and all other users won't notice my change and I don't need to maintain multiple start points. The BeebASM equivalent doesn't work
IF SWEH_HAS_BROKEN_THIS
causes a missing definition error.

My attempts at IFDEF and ELIFDEF (yeah, I agree bletch) was to workaround this.

Another approach could be to have a command to set a value if it's not already set. Ideally it'd be "infix" (DEBUG?=0) but that'll probably cause the parser issues as well. "?DEBUG=0" or something like that, maybe?

Then I can do
?SWEH_HAS_BROKEN_THIS=0
IF SWEH_HAS_BROKEN_THIS
dostuff
ENDIF

It adds a lot of flexibility, especially if I want to turn on/off multiple functions at compile time. Currently I have 3 variables:
UPURS
DEBUG
SWEH_BOOT_EXTENSION

It might get more complicated in the future 'cos I want to retain the ability to assemble to upstream conformance :-)

(As for BBC BASIC roots; BASIC variables don't need to be defined before use, and can be redefined, so this code isn't BASIC-like anyway :-))


Top
 
 Post subject: Re: RFE "DEFINED(foo)"
PostPosted: Wed Apr 10, 2013 8:33 pm 
Offline
User avatar
 Profile

Joined: Mon Jan 07, 2008 6:46 pm
Posts: 380
Location: Málaga, Spain
OK, yeah I see what you mean - it sounds like it'd be a useful addition after all, certainly better than having to set a whole load of variables to zero just in order for it to build.

My suggestion would be that we go with IFDEF...ELIFDEF...ENDIF, plus the command-line defining thing as you've added. Would you mind testing the implementation of LineParser::HandleIfdef() that I posted (try it with a few correct and incorrect inputs) and if it seems to work ok, I'll create a new version in the repository as soon as I have a moment.

I'm trying to remember what justification I had for not allowing a variable to be redefined - there's no reason why it shouldn't work after all. I guess it was to protect against duplicate labels or somesuch thing, or in particular the case where you want a label to point to the operand rather than the opcode (for self-modifying code) and have to write 'addr=P%+1:LDA &FFFF' (because of course the label syntax could otherwise be treated differently). If this seems like an annoying limitation, I could also remove this in the next version.


Top
 
 Post subject: Re: RFE "DEFINED(foo)"
PostPosted: Wed Apr 10, 2013 9:03 pm 
Offline
 Profile

Joined: Sat Mar 10, 2012 10:27 pm
Posts: 41
OK
bool bSymbolDefined = (SymbolTable::Instance().IsSymbolDefined(symbolName);

has an exta "(" before SymbolTable that's not needed :-)

Random testing...
numeric tag:
Code:
/tmp/a:10: error: Invalid symbol name; must start with a letter and contain only letters, numbers and underscore.

IFDEF 645
      ^

space or stuff after tag
Code:
../beebasm -i /tmp/a -o /tmp/b -v
/tmp/a:16: error: Bad expression.

IFDEF _fli4 hello there
            ^

Missing tag
Code:
../beebasm -i /tmp/a -o /tmp/b -v
/tmp/a:22: error: Expression not found.

IFDEF
     ^

Looks good!

Re: redefining variables.. I was thinking about this last night; hmm, I thought... "UNDEFINE" would be a good command as well!
HELLO=1
UNDEFINE HELLO
HELLO=2

Of course this failed until I realised you were doing definitions in pass 1 only. So I hacked up a solution that allowed redefining of variables and now it works. Yay. But this changes the mathematical basis of how beebasm works. But then I realised IFDEF does this as well
Code:
IFDEF FOO
   EQUB 1
ELSE
  FOO=TRUE
  EQUB 2
ENDIF

Heh that'll break things 'cos the second pass will have FOO defined and so generate "1" whereas first pass would generate "2". Hmm...

I'm mostly of a mind that a programmer who does this deserves all they get :-)


Top
 
 Post subject: Re: RFE "DEFINED(foo)"
PostPosted: Thu Apr 11, 2013 7:22 am 
Offline
User avatar
 Profile

Joined: Mon Jan 07, 2008 6:46 pm
Posts: 380
Location: Málaga, Spain
In BeebAsm, neither IF nor FOR can take a forward reference to a variable which hasn't yet been defined; the rationale being that on the first pass, it has to calculate all the label addresses, and so it already has to be able to assemble the correct instructions, albeit with missing operands where there's a forward reference.

I could fix this limitation by doing an optional third pass if the second pass yields different results to the first, but I won't be doing it any time soon ;)

That said, BeebAsm already spots if the second pass has yielded sufficiently different results to the first, i.e. if the actual instructions are different rather than the operands, so while it wouldn't catch the problem in your example, it possibly would if you were assembling different instructions in the different blocks.

I have to say I'm still not completely sold on IFDEF, since it's possible to create these ambiguities as you pointed out in your example. I think my preferred solution for the moment would be that variables defined on the command line (in the form -D DEBUG=1) simply override any definitions encountered in the source file. So, if you have DEBUG=FALSE in the source file, it'll do nothing if you've already specified a different value on the command line. This is the same behaviour as in GNU Make, and would be pretty easy to implement, without requiring new commands or creating difficult problems.

Regarding redefinition of variables, at the moment I can't see any particular reason why it wouldn't work without problems (apart from the danger of replacing one label with another by mistake, which would have to be handled specially). Is there a particular circumstance in which you can imagine that redefining a variable would break the internal logic of BeebAsm? Maybe it's still just a bit too early in the morning for me...!


Top
 
 Post subject: Re: RFE "DEFINED(foo)"
PostPosted: Thu Apr 11, 2013 12:00 pm 
Offline
 Profile

Joined: Sat Mar 10, 2012 10:27 pm
Posts: 41
If you allow redefinition then you'll have to remember what variables were set on the command line, and restore those values at the start of the second pass, and also allow variables to be set on the second pass. Otherwise you'll have this case:

beebasm -D FOO=0

EQUB FOO
FOO=FOO+1
EQUB FOO

FOO will have different values each time round the loop; pass one wouid generate 00 01 but pass 2 would generate 01 02.

(Even without -D you'll need to allow variables to change second pass; the same code starting FOO=0 would give similarly results otherwise).

You'll probably also need to protect constants, somehow (TRUE, FALSE, PI...).

My gut is telling me there's a bigger problem, not so easily resolved; but I can't think what it is at the moment!

To be honest, I'm not that worried about the IFDEF discrepency; BBC BASIC assembler allows the same problem cases, in essence, if you don't initialise all variables inside the 2-pass FOR loop.


Top
 
 Post subject: Re: RFE "DEFINED(foo)"
PostPosted: Thu Apr 11, 2013 12:48 pm 
Offline
User avatar
 Profile

Joined: Mon Jan 07, 2008 6:46 pm
Posts: 380
Location: Málaga, Spain
Forgetting variable redefinition for the moment, my idea was simply to add an extra property to the Symbol class, specifying that it was defined on the command line, and then having SymbolTable::AddSymbol do nothing if the symbol already existed and was a command line symbol (at this point, it could also be unmarked as a command line symbol, so that it flags attempts to redefine the variable).

Yeah you're right that allowing redefinition would require the variables to be set on each pass. I also just spotted that it already notes if a variable is a label or not, so it's easy to prevent duplicate labels. But I think probably my main reason for disallowing redefinition was that it ruins the elegance of forward references - when there's only one value, there's no ambiguity and it 'just works', and it suggests a paradigm wherein it's not important where you declare values of variables. But as soon as you can redefine a variable, which value should a forward reference use? The first one? The last one? Only allow forward references to labels? It's all a bit icky.


Top
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 11 posts ] 

All times are UTC [ DST ]


Who is online

Users browsing this forum: No registered users and 0 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
cron