Overview
Examples
Screenshots
Comparisons
Applications
Download
Documentation
Tutorials
UppHub
Status & Roadmap
FAQ
Authors & License
Forums
Funding U++
Search on this site











SourceForge.net Logo

GitHub Logo

Discord Logo

Assist++ - parser directives


Table of contents

 

1. Introduction

2. Handling of source files, headers, macros and namespaces

3. Macro overrides, namespace macros

4. Grounding heuristics

5. Assist++ C++ parser directives

 


1. Introduction

Assist++ C++ parser does not follow C/C++ standards exactly, for performance and practical reasons. This documents provides information about deviations, heuristics and tricks that we use to make the machinery fast and highly error resistant.


2. Handling of source files, headers, macros and namespaces

The main difference between C++ compiler and theide C++ parser is that theide is handling any source file separately. This is an absolute performance requirement if global database is to be maintained while editing files.

For this reason, declaration and definition must be be in single file. For example

File1.h

struct Foo {

 

File2.h

#include "File1.h"

    int bar;

};

is NOT supported.

Parser preprocessor rules:

All #if/#ifdef conditions are considered true. This is useful e.g. when there is platform specific code  - parser is then able to pick all variants. #else parts are excluded. For example

#ifdef PLATFORM_WIN32

void CloseWindow(void *handle) {

....

}

#endif

#ifdef PLATFORM_GTK

void CloseWindow(void *handle) {

.....

}

#endif

both CloseWindow definitions will be in the codebase.

When expanding macro, last #define directive is used.

#define FOO 1

#define FOO 2

FOO

the last line will be expanded to "2".

#undef cancels the last definition of the same macro, if it is defined in the same file. This is useful to handle special defines used for handling name clashes when including external libraries:

#define byte win32_byte_ // RpcNdr defines byte -> class with Upp::byte

#define CY win32_CY_

#include <objidl.h>

#include <winnetwk.h>

#undef byte

#undef CY

#include in file adds all macros that are (recursively) defined by included file and also all "using namespace" directives. It DOES NOT use namespace block definitions, for example this abomination is not supported:

StartNamespace.h

namespace MyNamespace {

 

EndNamespace.h

};

 

File.cpp

#include "StartNamespace.h"

void Foo();

#include "EndNamespace.h"

is NOT supported.

However, if file gets into the project through #include, all macros, usings and namespace block definitions are correctly included/used. Consider

MasterHeader.h

#define FOO 1

using namespace Bar;

namespace Foo {

#include "SubHeader.h"

};

 

SubHeader.h

 

void Fn() { return FOO; }

 

This IS supported: Fn will be in Foo namespace and will return 1.


3. Macro overrides, namespace macros

It is possible to tell TheIDE overriding definition of specific macros in special .defs files. One global.defs file resides in TheIDE configuration and is accessible through "<meta>" package. Also, .defs files can be put into packages. If macro is defined in .defs files, it overrides all occurrences of the identifier in all cases.

Note that after changing .defs file, it is necessary to rescan the code.

.defs files are also used to amend one issue that has no better solution in the framework. Consider

File.h

#include "Core.h"

NAMESPACE_UPP

#include "subheader.h"

END_UPP_NAMESPACE

 

where NAMESPACE_UPP and END_UPP_NAMESPACE are defined somewhere in Core.h as

 

#define NAMESPACE_UPP namespace Upp {

#define END_UPP_NAMESPACE };

 

Unfortunately, withing parser logic we have not found a fast way how to detect and resolve this situation (we do not know meaning of NAMESPACE_UPP/END_UPP_NAMESPACE when extracting macros from File.h). The solution is simple, putting those #defines into .defs file fixes the issue, as such macros are detected by special code and used when handling File.h.


4. Grounding heuristics

It is a good idea to make parser highly resistant to bugs, including code that it does not understand. For this reason parser is using somewhat strange but very effective simple heuristics: If a line starts (at character zero) with identifier which is not followed by single ':' character (to exclude labels), it is a definition on 'global' level. Consider

void Fn1() {

    for(int i = 0; i < 10; i++) {

        Cout() << i;

}

 

void Fn2() {

}

 

There is missing '}' in Fn1, which would likely make parser miss the rest of file (because all would be considered to be part of Fn1 body). However Fn2 definition invokes grounding heuristics and parsing restarts at that point, not missing Fn2.


5. Assist++ C++ parser directives

Assist supports simple directives that are passed to it via '//' comments that can be used in cases that original code is confusing:

//$-

Code past this directive will not be passed to parser.

//$+

Code past this directive will be passed to parser (stops //$-).

//$ code

example: //$ void Foo(Bar&)

Code will be passed to parser (adds code that is not part of compiled sources)

 

Do you want to contribute?