Assistance requested

5 views
Skip to first unread message

pasha19

unread,
Nov 6, 2009, 5:18:52 PM11/6/09
to scintilla-interest
I am attempting an adaption (as in a new version cloned from lexSQL)
as a lexer primarily to support a specific flavor of SQL. My issue
may be a windows programming question more than a scintilla question.
I have created two stack objects (C++ classes) and two element classes
to support instruction (parsing) state and fold level calculation
statically allocated with 128 elements in each stack (probable
overkill) . After inserting two items into the InstStack object
(neither of which pushed Foldstack enties and deleting one the
application aborts with the following message in an area well outside
my code changes according to the debugger. Do I have an issue using C+
+ objects (and memory management from the lexer colorise function?
Can these objects be built earlier in the code and passed to the
function to avoid the windows limitations (suggestions of a suitable
location would be helpful but not required -- I assume the first C++
class I find tracing the colorize function call back should be a
suitable object for the requested purpose. I am an experienced
programmer but relatively new to windows and C++. PS suggestions
concerning errors in my C++ coding are not considered out of scope by
anyone that may notice them (I already correct a couple of ='s that
should have been =='s in this code and elsewhere.)

This appears to be the pertinent aspects of the error message

First-chance exception at 0x10173731 (SciLexer.dll) in
notepadPlus_Debug.exe: 0xC0000005: Access violation reading location
0xccccccce.
First-chance exception at 0x7c812afb in notepadPlus_Debug.exe:
Microsoft C++ exception: Win32AccessViolation at memory location
0x000d5e30..
First-chance exception at 0x1016f5b3 (SciLexer.dll) in
notepadPlus_Debug.exe: 0xC0000005: Access violation reading location
0xccccccc8.
First-chance exception at 0x7c812afb in notepadPlus_Debug.exe:
Microsoft C++ exception: Win32AccessViolation at memory location
0x000d4b88..

the code for the objects is as follows

// Scintilla source code edit control
/** @file inststack.h
** instruction state stack and additional stack to identify fold
information
**/
// Copyright 2009 by David Shuman
// The License.txt file describes the conditions under which this
software may be distributed.

#ifndef INSTSTACK_H
#define INSTSTACK_H

#include <assert.h>


#ifdef SCI_NAMESPACE
namespace Scintilla {
#endif


const int StackSize = 128;

class FoldStackE {
public:
char type;
bool delay;
bool needHead;
int line;
int inst;
FoldStackE (int sz = 0) { init(0, sz); }
FoldStackE (const FoldStackE *array, int sz) { init(array, sz); }
FoldStackE (const FoldStackE&);
~FoldStackE () { };
FoldStackE& operator=(const FoldStackE&) { return *this; }

protected:
void init (const FoldStackE *array, int sz) {
type = ' ';
delay = false;
needHead = false;
line = 0;
inst = 0;
}
};


class FoldStack {
public:
FoldStack (int sz = StackSize) { init (0, sz); }
FoldStack (const FoldStackE *array, int sz) { init(array, sz); }
FoldStack (const FoldStack&);
~FoldStack () { delete [] fSE; };

int push (char pType, int pLine, int pInst) {
int rc = 0;

// purge pending delay before push
if (idx && fSE[idx].delay) { idx--; }

foldWhite = false;

if ((idx < StackSize) &&
((!idx) || (fSE[idx].line < pLine))) {
rc = ++idx;
fSE[idx].type = pType;
fSE[idx].delay = false;
fSE[idx].needHead = true;
fSE[idx].line = pLine;
fSE[idx].inst = pInst;
}

return rc;
}
int pop (char pType, int pFold, bool pDelay, int pLine) {
int rc = 0;

// purge pending delay before pop
if (idx && fSE[idx].delay) { idx--; }

if (idx && (fSE[idx].type = pType) && (idx == pFold)) {
if (fSE[idx].line == pLine) {
rc = idx--;
} else if (pDelay) {
fSE[idx].delay = pDelay;
rc = idx;
} else {
rc = idx--;
}
}
foldWhite = (idx && fSE[idx].delay);

return rc;
}
int getLine() { return fSE[idx].line; }

int getFold(bool pPurge) {
int rc = idx | SC_FOLDLEVELBASE;

// purge pending delays before insert
if (pPurge) {
if (idx && fSE[idx].delay) { idx--; }
}

return rc;
}

int getFoldWhite() { return foldWhite; }

bool getFoldHead() {
bool rc = fSE[idx].needHead;

fSE[idx].needHead = false;

return rc;
}

protected:

int idx;
bool foldWhite;
int size;
FoldStackE *fSE;

void init (const FoldStackE *fSE, int sz) {
idx = 0;
foldWhite = false;
size = sz;

fSE = new FoldStackE[size];
assert (fSE != 0);
}

};


class InstStackE {
public:
char type;
bool isState;
int line;
int fold;

InstStackE (int sz = 0) { init (0, sz); }
InstStackE (const InstStackE *array, int sz) { init(array, sz); }
InstStackE (const InstStackE&);
~InstStackE () { };
InstStackE& operator=(const InstStackE&) { return *this; }

protected:
void init (const InstStackE *array, int sz) {
type = ' ';
isState = false;
line = 0;
fold = 0;
}
};


class InstStack {
public:
InstStack (int sz = StackSize) { init (0, sz); }
InstStack (const InstStackE *array, int sz) { init(array, sz); }
InstStack (const InstStack&);
~InstStack () { delete [] iSE; };

int push(char pType, bool pIsState, int pLine, bool pFold) {
int rc = 0;

if (idx < StackSize) {
rc = ++idx;;
iSE[idx].type = pType;
iSE[idx].isState = pIsState;
iSE[idx].line = pLine;
if (pFold) {
iSE[idx].fold = fS.push(pType, pLine, idx);
} else {
iSE[idx].fold = 0;
}
}
return rc;
}

int pop(char pType, bool pDelay, int pLine) {
int rc = 0;

if (idx && (iSE[idx].type == pType)) {
if (iSE[idx].fold) {
fS.pop(iSE[idx].type, iSE[idx].fold, pDelay, pLine);
}
rc = idx--;
}
return rc;
}

char getType (int back = 0) {
char rc = ' ';

if (back > -idx) {
rc = iSE[idx + back].type;
}

return rc;
}

bool getIsState (int back = 0) {
bool rc = false;

if (back > -idx) {
rc = iSE[idx + back].isState;
}

return rc;
}

int getLine (int back = 0) {
int rc = 0;

if (back > -idx) {
rc = iSE[idx + back].line;
}

return rc;
}


int getFold (bool pPurge) {
return fS.getFold(pPurge);
}

int getFoldWhite () {
return fS.getFoldWhite();
}

bool getFoldHead () {
return fS.getFoldHead();
}

int updateType (char pnew, char pold) {
int rc = 0;

if (idx && (iSE[idx].type == pold)) {
iSE[idx].type = pnew;
rc = idx;
}

return rc;
}

protected:

FoldStack fS;

int idx;
int line;
int size;

InstStackE *iSE;
//
// Foldable items are included in first keyword list
// {cmd} is any item in second keyword list except
// (for, if, loop and while)
//
// omitting an item from a list negates program actions
//
// Fold Type Values
// /** ! Doxygen Stream Comment
// /*! ! Doxygen Stream Comment
// /* * Generic Stream Comment
// --{ - Block (--{, --}) Comment
// //{ / Block (//{, //}) Comment
// " Double Quoted String
// ' Single Quoted String
// ` BackQuoted String
// ( ) Parenthesis Block
// { } Curly Brace Block
// [ ] Square Brackets
// case c/C Case Statement
// do d/D Do Statement
// e/E Exists Function (SQLANY)
// for f/F For Statement
// if i/I If Statement
// loop l/L Loop Statement
// switch s/S Switch Statement
// while w/W While Statement
// <? ? XML Header
// <!-- < XML Header
// < t/T XML Tag Start
// </ u/U XML Tag End
// {cmd} ; Other statements
//
// state contains the IsStatement value when the item initiated
// internal to a statement - true
// case, exists and (opt) if
// statements that contain statements - false
// for, (opt) if, loop, switch (tbd) and while
// if with a State = true - ends with an endif
// if with a State = false - ends with an end if;
//

void init (const InstStackE *array, int sz) {
idx = 0;
line = 0;

size = sz;

iSE = new InstStackE[size];
assert( iSE != 0 );
}

};


#ifdef SCI_NAMESPACE
}
#endif

#endif

Neil Hodgson

unread,
Nov 6, 2009, 5:41:41 PM11/6/09
to scintilla...@googlegroups.com
pasha19:

> statically allocated with 128 elements in each stack (probable
> overkill) .

Static allocation is a bad idea since there may be multiple
instances existing at one time.

The faults look like uninitialized memory. Stick the program in a
debugger and trace through the code to see just where it is failing.
When pushing, you are about to stick data at idx+1 but are only
checking that idx < StackSize.

Neil

pasha19

unread,
Nov 6, 2009, 8:42:40 PM11/6/09
to scintilla-interest
Neil;

Sorry not C STATIC (my fault poor choice of words) but based on the
automatic initialization of the C++ object -- the debugger indicates
that the stack values visible are initialized properly. Also as even
the stack entries are C++ objects the initialization appears to be a
forced requirement (a good thing). (!idx) tests for an empty stack
which is a stack with the 0 dummy entry that cannot be modified
(except in one case I just found - that will be changed) Testing
(idx) assures the existence of an entry in the stack above zero. (idx
< Stacksize) blocks overflow on the other end -- fatally to the
intended processing but presumably not the application resulting in a
0 return code.

The indications of the error are in the WordList InList function which
I do not believe is the cause of the error. Also the audio volume
(completely unrelated) was ineffective and low until reboot leading me
to believe that I am corrupting memory somehow. From everything I can
tell I have no uninitialized memory in the application save the
existing code's creation of "s" in the LexSQL extraction of
identifiers and operators inside the switch statement in the function
and the debugger indicates these variables contain the expected values
just prior to the abort.

Thanks for the insight; I will continue looking.

pasha19

unread,
Nov 7, 2009, 12:32:57 PM11/7/09
to scintilla-interest
I do not yet understand the full implications of this; but here is
what I know to date. I have the string "^" (without quotes) as an
entry in the operator keyword list one of several lists for my
application. In the WordList class, if I read the code correctly the
partial match will be true on anything because the string "^" will
force an "illogical" return of true on the partial string test. This
is at least a logic flaw. Should I work at correcting this or can
someone else more familiar with the intended design of the WordList
class fix this issue? I believe that partial word testing should only
occur if the item in the list contains at least one character after
the ^. I understand, the lists are sorted for efficiency purposes
which is reasonable. This feature may not be as effective as it could
be; if the wordlists are sorted by more than the first character of
the string (or first two for ^'ed values only) (I do not understand
the results of the SortWordList function as coded.) But if this
strict sort on the entire string exists it eliminates the ability to
deal with complex situations like matching for <> and <= before < or
>= before >. The benefits of sorting the list seem to increase if the
strings are SORTED BY THE FIRST CHARACTER ONLY (two characters for
^'ed values) - maintaining list order when there are duplicates.
Meaning the input list sequence which should be preserved for matching
starting characters would dictate whether <> and <= were tested before
or after <, logically before is the option that makes the most sense.
(This may also be achieved automatically if the sort were to sort by
three keys -- key1 - the first letter of the string (should be the
first two characters for ^'ed strings (using the \0 as the second
character when none exists) - as start could then lookup the first
letter of the ^'ed strings -- a start array (current) and a new
startCaret array?) and key2 - the LENGTH of the string DESCENDING and
key3 - the whole string.

Only "^" string length = 1 would be referenced in the current starts
array. A new startsCaret array would contain all strings that begin
with "^" and have a length > 1 based on their SECOND CHARACTER.

To make this sorting most effective there would need to be a new
(additional) InList(Int?) function that returned an int instead of a
boolean that would return the length of a match (0 is no match, like
strcmp) -- the whole word[j] string for non ^'ed matches and the
length - 1 of the word[j] string when ^'ed matches occur. (The
applicable / functional purpose is the ability to parse meaningfully
groups of operators (special characters) without white space between
them.

I am suggesting a custom written sort function (tbd), a new InListInt
compare function along with improvements to the current InList and
InListAbbreviated functions. The new InListInt(const char *s, const
char marker = 0x00) could process the current InList() and
InListAbbreviated()so their bodies could be replaced respectively with
calls to return (InListInt(s) > 0); and return (InListInt(s, marker) >
0);

ie.
where in the InList... functions now contain
j = starts['^'];
if (j >= 0) {
while (words[j][0] == '^')

becomes (same efficiency for ^'ed items as non ^'ed items

j = startsCaret[s[0]];
if (j >= 0) {
while ((words[j][0]) == '^') && (words[j][1]) ==firstchar))

As ^ should not have a non zero entry in the startsCaret table unless
something starting with "^^" is in the list then "^" alone is no
longer a matching issue.

(PS: as a side note the memory access violation moved into one of my
objects after "^" was removed from the list for a test -- with the
item in the list it occurs on the } indicating the return from the
function SetClass in class StyleContext (sc.SetState
(SCE_SQLANY_DEFAULT);))

Neil Hodgson

unread,
Nov 7, 2009, 5:05:17 PM11/7/09
to scintilla...@googlegroups.com
pasha19:

> I have the string "^" (without quotes) as an
> entry in the operator keyword list one of several lists for my
> application.  In the WordList class, if I read the code correctly the
> partial match will be true on anything because the string "^" will
> force an "illogical" return of true on the partial string test.

"^" is used to indicate a prefix keyword. For example if every
string that starts with "gtk_" is supposed to be a keyword then
"^gtk_" matches them.

> I believe that partial word testing should only
> occur if the item in the list contains at least one character after
> the ^.

That looks fairly useless as it will not work in closely related
cases such as using "^=" as an operator. The solution is to use code
for operators that is similar to the other lexers with a hard coded
list of operator characters.

Neil

pasha19

unread,
Nov 7, 2009, 6:54:46 PM11/7/09
to scintilla-interest
There is one other bug in the same area I found testing options -- in
InListAbbreviated if the string in the wordlist is one character long
it will never match anything --

the following code should probably look like this

if (words[j][1] == marker) {
isSubword = true;
start++;
}


should probably look like this

if (words[j][1]) { // NOT one byte wordlist string
if (words[j][1] == marker) {
isSubword = true;
start++;
}
} else { // one byte wordlist string
return true;
}

Neil Hodgson

unread,
Nov 8, 2009, 9:19:46 PM11/8/09
to scintilla...@googlegroups.com
pasha19:

> There is one other bug in the same area I found testing options -- in
> InListAbbreviated if the string in the wordlist is one character long
> it will never match anything --

A single character word list entry ("z") matches exactly that
single character ("z") as a word. Both s[1] and words[j][1] are '\0'
in this case, so *a and *b are '\0' so true is returned. Possibly you
are expecting "z" in the word list to match "zebra" in the document.

Neil

pasha19

unread,
Nov 9, 2009, 6:24:50 PM11/9/09
to scintilla-interest


I finally progressed far enough in testing to find the initialization
issue you suggested earlier was a problem, you nailed it.

As far as the issue in InListAbbreviated I believe you are also
correct. I was using a modified version of that code based on the
change I outlined earlier and falsely inferred an error in the
existing code based on my own error without testing. Sorry for the
false report.

I was concerned with the *a && *a == *b because of the *a &&
being false when in actuality the if with ((!*a || isSubword) && !
*b) was where I should have been looking.

Reply all
Reply to author
Forward
0 new messages