Posted 13 years ago by Kelly Leahy - Software Architect, Milliman
Version: 4.0.0246
Avatar
I have a language that is line based. I would like to recognize certain words as "keywords" only if they are on certain lines...

For instance, I have a line that starts "#setup" that I want to recognize the following:

#setup letter landscape regular 8 margin commas scale 1000 unlimited

I want to highlight #setup, letter, ..., except 8 and 1000 as keywords.

However, letter is only a keyword when it is on a line starting with #setup.

It seems to me that using lexical states is the correct way to handle this, so I tried to use the following code (sorry, it's long):

    public class ReportLexer : IMergableLexicalParser
    {
        static Dictionary<int, DefaultLexicalState> states;
        static Dictionary<string, int> setupKeywords;
        static Dictionary<string, int> pagesizeKeywords;
        static Dictionary<string, int> colHeadKeywords;
        static Dictionary<string, int> variableLineKeywords;

        static int MakeStateId(int previous, int current)
        {
            return (previous << 8) | current;
        }

        static void AddStatePair(int previous, int current)
        {
            int id = MakeStateId(previous, current);
            states.Add(id, new DefaultLexicalState(id, id.ToString("x8")));
        }

        public static IEnumerable<ILexicalState> LexicalStates
        {
            get
            {
                foreach (KeyValuePair<int, DefaultLexicalState> kvp in states)
                    yield return kvp.Value;
            }
        }

        static ReportLexer() {
            states = new Dictionary<int, DefaultLexicalState>();
            setupKeywords = new Dictionary<string, int>();
            pagesizeKeywords = new Dictionary<string, int>();
            colHeadKeywords = new Dictionary<string, int>();
            variableLineKeywords = new Dictionary<string, int>();

            setupKeywords.Add("letter", ReportTokenID.SetupLetter);
            setupKeywords.Add("portrait", ReportTokenID.SetupPortrait);
            setupKeywords.Add("landscape", ReportTokenID.SetupLandscape);
            setupKeywords.Add("regular", ReportTokenID.SetupRegular);
            setupKeywords.Add("compressed", ReportTokenID.SetupCompressed);
            setupKeywords.Add("margin", ReportTokenID.SetupMargin);
            setupKeywords.Add("commas", ReportTokenID.SetupCommas);
            setupKeywords.Add("scale", ReportTokenID.SetupScale);
            setupKeywords.Add("unlimited", ReportTokenID.SetupUnlimited);

            pagesizeKeywords.Add("lines", ReportTokenID.PageSizeLines);
            pagesizeKeywords.Add("columns", ReportTokenID.PageSizeColumns);

            colHeadKeywords.Add("date", ReportTokenID.ColHeadDate);
            colHeadKeywords.Add("number", ReportTokenID.ColHeadNumber);

            variableLineKeywords.Add("as", ReportTokenID.VariablesAs);
            variableLineKeywords.Add("columns", ReportTokenID.VariablesColumns);
            variableLineKeywords.Add("rows", ReportTokenID.VariablesRows);
            variableLineKeywords.Add("for", ReportTokenID.VariablesFor);
            variableLineKeywords.Add("monthly", ReportTokenID.VariablesMonthly);
            
            // default state (and section states)
            states.Add(ReportLexicalStateID.Default, new DefaultLexicalState(ReportLexicalStateID.Default, "Default"));
            states.Add(ReportLexicalStateID.ConstantsSection, new DefaultLexicalState(ReportLexicalStateID.ConstantsSection, "ConstantsSection"));
            states.Add(ReportLexicalStateID.FormulasSection, new DefaultLexicalState(ReportLexicalStateID.FormulasSection, "FormulasSection"));
            states.Add(ReportLexicalStateID.VariablesSection, new DefaultLexicalState(ReportLexicalStateID.VariablesSection, "VariablesSection"));

            // this one doesn't care, since it's always going to go to VariablesSection when parsed.
            states.Add(ReportLexicalStateID.VariablesLine, new DefaultLexicalState(ReportLexicalStateID.VariablesLine, "VariablesLine"));

            // previous state default, current state x (line type)
            int prev = ReportLexicalStateID.Default;
            AddStatePair(prev, ReportLexicalStateID.ColHeadLine);
            AddStatePair(prev, ReportLexicalStateID.HeaderLine);
            AddStatePair(prev, ReportLexicalStateID.LineBreakLine);
            AddStatePair(prev, ReportLexicalStateID.PageSizeLine);
            AddStatePair(prev, ReportLexicalStateID.SetupLine);
            AddStatePair(prev, ReportLexicalStateID.TextLine);

            prev = ReportLexicalStateID.ConstantsSection;
            AddStatePair(prev, ReportLexicalStateID.ColHeadLine);
            AddStatePair(prev, ReportLexicalStateID.HeaderLine);
            AddStatePair(prev, ReportLexicalStateID.LineBreakLine);
            AddStatePair(prev, ReportLexicalStateID.PageSizeLine);
            AddStatePair(prev, ReportLexicalStateID.SetupLine);
            AddStatePair(prev, ReportLexicalStateID.TextLine);

            prev = ReportLexicalStateID.FormulasSection;
            AddStatePair(prev, ReportLexicalStateID.ColHeadLine);
            AddStatePair(prev, ReportLexicalStateID.HeaderLine);
            AddStatePair(prev, ReportLexicalStateID.LineBreakLine);
            AddStatePair(prev, ReportLexicalStateID.PageSizeLine);
            AddStatePair(prev, ReportLexicalStateID.SetupLine);
            AddStatePair(prev, ReportLexicalStateID.TextLine);

            prev = ReportLexicalStateID.VariablesSection;
            AddStatePair(prev, ReportLexicalStateID.ColHeadLine);
            AddStatePair(prev, ReportLexicalStateID.HeaderLine);
            AddStatePair(prev, ReportLexicalStateID.LineBreakLine);
            AddStatePair(prev, ReportLexicalStateID.PageSizeLine);
            AddStatePair(prev, ReportLexicalStateID.SetupLine);
            AddStatePair(prev, ReportLexicalStateID.TextLine);
        }

        public ReportLexer()
        {
        }

        public ITokenLexicalParseData GetLexicalStateDefaultTokenLexicalParseData(ITextBufferReader reader, ILexicalState lexicalState)
        {
            reader.Read();
            return new LexicalStateAndIDTokenLexicalParseData(lexicalState, (byte)lexicalState.DefaultTokenID);
        }

        public int ParseLineDirective(ITextBufferReader reader, ref int lexicalStateIDnext)
        {
            int startOfs = reader.Offset;

            // already got the '#'...
            while (char.IsLetter(reader.Peek()))
            {
                // consume the char and add it to the token.
                reader.Read();
            }

            // now, the directive text is at range from startOfs to reader.Offset
            string directive = reader.GetSubstring(startOfs, reader.Offset - startOfs).ToLower();

            // check the directive against known directives
            switch (directive)
            {
                case "setup":
                    lexicalStateIDnext = ReportLexicalStateID.SetupLine;
                    return ReportTokenID.Setup;
                case "pagesize":
                    lexicalStateIDnext = ReportLexicalStateID.PageSizeLine;
                    return ReportTokenID.PageSize;
                case "header":
                    lexicalStateIDnext = ReportLexicalStateID.HeaderLine;
                    return ReportTokenID.Header;
                case "text":
                    lexicalStateIDnext = ReportLexicalStateID.TextLine;
                    return ReportTokenID.Text;
                case "variables":
                    lexicalStateIDnext = ReportLexicalStateID.VariablesLine;
                    return ReportTokenID.Variables;
                case "line":
                    lexicalStateIDnext = ReportLexicalStateID.LineBreakLine;
                    return ReportTokenID.Line;
                case "colhead":
                    lexicalStateIDnext = ReportLexicalStateID.ColHeadLine;
                    return ReportTokenID.ColHead;
                case "constants":
                    lexicalStateIDnext = ReportLexicalStateID.ConstantsSection;
                    return ReportTokenID.Constants;
                case "formulas":
                    lexicalStateIDnext = ReportLexicalStateID.FormulasSection;
                    return ReportTokenID.Formulas;
                case "column":
                    return ReportTokenID.Column;
            }
            return ReportTokenID.Invalid;
        }

        public MatchType GetNextTokenLexicalParseData(ITextBufferReader reader, ILexicalState lexicalState, ref ITokenLexicalParseData lexicalParseData)
        {
            // Initialize
            int tokenID = ReportTokenID.Invalid;
            int lexicalStateIDcurrent = lexicalState.ID;
            int lexicalStateIDprevious = (lexicalState.ID >> 8) & 0xFF;
            int lexicalStateIDnext = lexicalState.ID;
            bool handled = false;

            // Get the next character
            int startOfs = reader.Offset;
            char ch = reader.Read();

            // handle stuff that's valid for any state
            handled = true;
            switch (ch)
            {
                case '#':
                    tokenID = ParseLineDirective(reader, ref lexicalStateIDnext);
                    break;
                case ';':
                    tokenID = ParseSingleLineComment(reader);
                    break;
                default:
                    // mark this item as unhandled.
                    handled = false;
                    break;
            }

            // handle whitespace
            if (ch != '\n' && char.IsWhiteSpace(ch))
            {
                ch = reader.Peek();
                while (!reader.IsAtEnd && ch != '\n' && char.IsWhiteSpace(ch))
                {
                    reader.Read();
                    ch = reader.Peek();
                }
                tokenID = ReportTokenID.Whitespace;
                handled = true;
            }

            // handle state-specific items.
            if (!handled)
            {
                Dictionary<string, int> keywordDict = null;
                bool supportsString = false;

                // figure out how to parse this state
                switch (lexicalStateIDcurrent)
                {
                    case ReportLexicalStateID.Default:
                        // handle stuff for the default state.
                        // no valid tokens that aren't handled above.
                        break;
                    case ReportLexicalStateID.SetupLine:
                        keywordDict = setupKeywords;
                        supportsString = false;
                        goto case -1;
                    case ReportLexicalStateID.PageSizeLine:
                        keywordDict = pagesizeKeywords;
                        supportsString = false;
                        goto case -1;
                    case ReportLexicalStateID.ColHeadLine:
                        keywordDict = colHeadKeywords;
                        supportsString = true;
                        goto case -1;
                    case ReportLexicalStateID.VariablesLine:
                        keywordDict = variableLineKeywords;
                        supportsString = false;
                        goto case -1;
                    case -1:
                        // handle one of the "line" states with keywords
                        if (char.IsLetter(ch))
                        {
                            do ch = reader.Read(); while (!reader.IsAtEnd && char.IsLetter(ch));
                            if (!reader.IsAtEnd) reader.ReadReverse();

                            // get the keyword in a string
                            string keyword = reader.GetSubstring(startOfs, reader.Offset - startOfs).ToLower();

                            // check against known keywords.
                            int value;
                            if (keywordDict.TryGetValue(keyword, out value))
                                tokenID = value;
                            else if (supportsString)
                            {
                                // this must be a string, so match the rest of it.
                                tokenID = parseString(reader);
                            }
                            else
                            {
                                // this must be an identifier, so read the rest and consider it so.
                                ch = reader.Peek();
                                while (!reader.IsAtEnd && ch != '\n' && char.IsLetterOrDigit(ch))
                                {
                                    reader.Read();
                                    ch = reader.Peek();
                                }
                                // consume '%' or '$' as last char of identifier
                                if (ch == '%' || ch == '$')
                                    reader.Read();
                                tokenID = ReportTokenID.Id;
                            }
                        }
                        // handle an integer
                        else if (char.IsDigit(ch))
                            tokenID = ParseNumber(reader, ch);
                        else if (ch == '\n')
                        {
                            // exit this state to the previous state.
                            lexicalStateIDnext = lexicalStateIDprevious;
                            tokenID = ReportTokenID.LineTerminator;
                        }
                        break;
                    case ReportLexicalStateID.LineBreakLine:
                        if (char.IsDigit(ch))
                            tokenID = ParseNumber(reader, ch);
                        break;
                    case ReportLexicalStateID.HeaderLine:
                    case ReportLexicalStateID.TextLine:
                        if (char.IsLetterOrDigit(ch) || ch == '>')
                        {
                            tokenID = parseString(reader);
                        }
                        else if (ch == '\n')
                        {
                            // exit this state to the previous state.
                            lexicalStateIDnext = lexicalStateIDprevious;
                            tokenID = ReportTokenID.LineTerminator;
                        }
                        break;
                    case ReportLexicalStateID.ConstantsSection:
                        // don't support anything here...
                        break;
                    case ReportLexicalStateID.VariablesSection:
                        // don't support anything here (for now...)
                        break;
                    case ReportLexicalStateID.FormulasSection:
                        // don't support anything here (for now...)
                        break;
                }
            }

            DefaultLexicalState nextState;
            int stateID = MakeStateId(lexicalStateIDcurrent, lexicalStateIDnext);
            if (!states.TryGetValue(stateID, out nextState))
            {
                if (!states.TryGetValue(lexicalStateIDnext, out nextState))
                    throw new Exception("Unknown state ID: " + lexicalStateIDnext.ToString());
            }

            if (tokenID != ReportTokenID.Invalid)
            {
                lexicalParseData = new LexicalStateAndIDTokenLexicalParseData(nextState, (byte)tokenID);
                return MatchType.ExactMatch;
            }
            else
            {
                reader.ReadReverse();
                return MatchType.NoMatch;
            }
        }
The "lexicalState" that is passed to this function the next time around, unfortunately, doesn't seem to be the state I told it to use in my "lexicalParseData" value. Any idea why? What am I doing wrong?

Kelly Leahy Software Architect Milliman, USA

Comments (11)

Posted 13 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar
Hi Kelly,

Sorry to say it but this is starting to get pretty complex and would most likely require some debugging of a sample project you would need to provide us in order to see what is going on. If you would like us to dig in then please supply us with a tiny sample project that has your code and anything else that is needed to show the issue and purchase an hour of consulting services here:
http://www.actiprosoftware.com/Purchase/ConsultingServices.aspx


Actipro Software Support

Posted 13 years ago by Kelly Leahy - Software Architect, Milliman
Avatar
Ok... I can live with that.

Let me ask a couple of questions first though...

1) is there an example somewhere of using a lexical state for this type of purpose?
2) do you believe that my intended approach is a valid use of lexical state within a mergable syntax language?
3) is a mergeable syntax language the proper place for this, or should I be looking at a non-mergable language instead?
4) is there any other way to pass state information around in the lexer that is preserved by the calling routine of the mergeable syntax language? In other words, if I need to associate information x with token y, which needs to be passed back to the lexer on the next token, is there any way to do this other than lexical states, and are lexical states the proper method for doing this?

It seems that the documentation on lexical states and scopes is very scant in the syntaxeditor documentation. I'm just looking for enough information to solve the problems myself, but I'm willing to pay for your services too, if that's necessary.

Kelly Leahy Software Architect Milliman, USA

Posted 13 years ago by Kelly Leahy - Software Architect, Milliman
Avatar
Ok... How about this - I've narrowed it down to the lexical parser's caller not "remembering" the lexical state of the previous token. If I make my changes to the lexical parser in the "simple" language, and look at the results in the "SDI application" I can see that my #setup shows a lexical state of "SetupLine" just as it should, but the next token is parsed with a lexicalState of "DefaultState".

How do I get the code that is calling GetNextTokenLexicalParseData to pass the lexicalState from the previous token as the lexicalState parameter for the next call to GetNextTokenLexicalParseData? Is this the wrong way to use lexicalState?

The changes I made are:

1) add the lexical state "SetupLine" to the grammar xml file
2) add the tokens "setup" and "foo" to the .xml - "foo" as a keyword, setup as a normal token (non keyword - non operator).
3) run the parser generator to get a new SimpleSemanticParser.cs
4) Modify the SimpleSyntaxLanguage to add the LexicalState "SetupLine" to the lexicalstates collection on the language, and add a default highlighting style for this new state. I added it using the "DefaultLexicalState" object - is that the wrong one?
5) Change the lexer to return the setup token when it sees #setup.
6) Change the lexer to return "foo" as a keyword only if we're in the "SetupLine" lexical state, otherwise treat "foo" as an identifier.
7) Change the lexer to make the lexical state "SetupLine" when encountering #setup, and change it from "SetupLine" back to the default state when it encounters a LineTerminator.

I did #4 as (in SimpleSyntaxLanguage constructor):

            this.LexicalStates.Add(new DefaultLexicalState(SimpleLexicalStateID.SetupLine, "SetupLine"));
            this.LexicalStates["SetupLine"].DefaultHighlightingStyle = this.HighlightingStyles["DefaultStyle"];
I did #5 as (in GetNextTokenLexicalParseData):

                switch (ch) {
                    case '#':
                        // this is crap code, but it will work so long as nothing else uses # as its start.
                        tokenID = this.ParseDirective(reader);
                        break;
and (as new method on SimpleLexicalParser)

        protected virtual int ParseDirective(ITextBufferReader reader)
        {
            int startOffset = reader.Offset;
            while (!reader.IsAtEnd)
            {
                char ch2 = reader.Read();
                if ((!char.IsLetterOrDigit(ch2)) && (ch2 != '_'))
                {
                    reader.ReadReverse();
                    break;
                }
            }

            string keyword = reader.GetSubstring(startOffset, reader.Offset - startOffset).ToLower();

            if (keyword == "setup")
                return SimpleTokenID.Setup;
            else
                return SimpleTokenID.Invalid;
        }
and I did #7 as (in GetNextTokenLexicalParseData):

            if (tokenID != SimpleTokenID.Invalid) {
                if (tokenID == SimpleTokenID.Setup || tokenID == SimpleTokenID.foo)
                    lexicalParseData = new LexicalStateAndIDTokenLexicalParseData(lexicalState.Language.LexicalStates["SetupLine"], (byte)tokenID);
                else if (tokenID == SimpleTokenID.LineTerminator && lexicalState.Key == "SetupLine")
                    lexicalParseData = new LexicalStateAndIDTokenLexicalParseData(lexicalState.Language.DefaultLexicalState, (byte)tokenID);
                else
                    lexicalParseData = new LexicalStateAndIDTokenLexicalParseData(lexicalState, (byte)tokenID);
                return MatchType.ExactMatch;
            }
            else {
                reader.ReadReverse();
                return MatchType.NoMatch;
            }
Do I need to pay for some support, or is this good enough of a question for you to deal with it directly? If you can get the simple language addon working with these changes (and tell me what to do in addition) I'll happily pay for a couple hours consulting... Just tell me where to pay :)

[Modified at 03/15/2007 04:01 PM]

Kelly Leahy Software Architect Milliman, USA

Posted 13 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar
Ok lots of info you're giving us so let me try and give some free advice first without getting too far into the code.

Yes lexical states are a good way to set up things like #setup. What you need to do is create lexical state with a scope that starts with the pattern #setup and then ends on a line feed. If you do that, then you can define your special keywords that are valid in that state like "portrait".

I think you are missing the lexical scope portion. That is probably why the lexical state being passed to you is not changing. In our C# language for instance we define a scope for documentation comments. Something like this:
/// <summary>
/// Represents the method that will handle <see cref="ITokenLexicalParseData"/> matching callbacks.
/// </summary>
/// <param name="reader">An <see cref="ITextBufferReader"/> that is reading a text source.</param>
/// <param name="lexicalScope">The <see cref="ILexicalScope"/> that specifies the lexical scope to check.</param>
/// <param name="lexicalParseData">Returns the <see cref="ITokenLexicalParseData"/> that was parsed, if any.</param>
/// <returns>A <see cref="MatchType"/> indicating the type of match that was made.</returns>
public MatchType IsDocumentationCommentStateScopeEnd(ITextBufferReader reader, ILexicalScope lexicalScope, ref ITokenLexicalParseData lexicalParseData) {
    if (reader.Peek() == '\n') {
        reader.Read();
        lexicalParseData = new LexicalScopeAndIDTokenLexicalParseData(lexicalScope, CSharpTokenID.LineTerminator);
        return MatchType.ExactMatch;
    }
    return MatchType.NoMatch;
}

/// <summary>
/// Represents the method that will handle <see cref="ITokenLexicalParseData"/> matching callbacks.
/// </summary>
/// <param name="reader">An <see cref="ITextBufferReader"/> that is reading a text source.</param>
/// <param name="lexicalScope">The <see cref="ILexicalScope"/> that specifies the lexical scope to check.</param>
/// <param name="lexicalParseData">Returns the <see cref="ITokenLexicalParseData"/> that was parsed, if any.</param>
/// <returns>A <see cref="MatchType"/> indicating the type of match that was made.</returns>
public MatchType IsDocumentationCommentStateScopeStart(ITextBufferReader reader, ILexicalScope lexicalScope, ref ITokenLexicalParseData lexicalParseData) {
    if (reader.Peek() == '/') {
        reader.Read();
        if (reader.Peek() == '/') {
            reader.Read();
            if (reader.Peek() == '/') {
                reader.Read();
                lexicalParseData = new LexicalScopeAndIDTokenLexicalParseData(lexicalScope, CSharpTokenID.DocumentationCommentDelimiter);
                return MatchType.ExactMatch;
            }
            reader.ReadReverse();
        }
        reader.ReadReverse();
    }
    return MatchType.NoMatch;
}
Then back in your language constructor when you are setting up states, you need a line like this:
this.LexicalStates["DocumentationCommentState"].LexicalScopes.Add(new ProgrammaticLexicalScope(new ProgrammaticLexicalScopeMatchDelegate(lexicalParser.IsDocumentationCommentStateScopeStart), new ProgrammaticLexicalScopeMatchDelegate(lexicalParser.IsDocumentationCommentStateScopeEnd)));
Try that sort of thing out and let us know if it helps. Then perhaps we won't need to do any consulting arrangements.


Actipro Software Support

Posted 13 years ago by Kelly Leahy - Software Architect, Milliman
Avatar
Thanks, I'll give that a try. I knew it was probably something stupid missing from my code, but none of the examples anywhere had it.

How (in)efficient is it to use scopes like this? It seems like the "Is...Start" and "Is...End" routines will all have to get called prior to each call of the "GetNextToken..." routine. Am I correct?

At the very least, it seems the "start" routines for all scopes within the current state must be called, and all "end" routines for the scope the parser is currently in will need to be called.

Kelly Leahy Software Architect Milliman, USA

Posted 13 years ago by Kelly Leahy - Software Architect, Milliman
Avatar
Yet another question - hopefully an easy one...

How can I determine which state the lexer should return to after leaving one state. For instance, I have:

Default -> (on #setup) SetupLine -> (on \n) Default
Default -> (on #variables) VariablesLine -> (on \n) VariablesSection
Default -> (on #constants) ConstantsLine -> (on \n) ConstantsSection
Default -> (on #formulas) FormulasLine -> (on \n) FormulasSection
FormulasSection -> (on #constants) ConstantsLine -> (on \n) ConstantsSection
FormulasSection -> (on #variables) VariablesLine -> (on \n) VariablesSection
ConstantsSection -> (on #variables) VariablesLine -> (on \n) VariablesSection

How can I tell the parser which state to go to when leaving the VariablesLine state (always the VariablesSection)?

Also, for the ConstantsSection, I really don't need a separate scope for ConstantsLine, since nothing (except \n) can appear legally on a line that starts with #constants. So, should I just design my scope to transition directly to ConstantsSection and have that state handle the #constants, or is it better to just stick with a separate scope for each "directive line"?

Thanks,

Kelly Leahy Software Architect Milliman, USA

Posted 13 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar
Yes you are correct, the scopes must be checked as you described on each token pass. It makes mergable languages very flexible but does have the downside of possibly causing processing to take slightly longer than if you coded a straight programmatic lexical parser specifically for a language.

It will travel up the stack of states (deepest first) and look for the first end scope match. So that's where it knows where to stop.

You might as well make the state for #constants as well so no other keywords get highlighted if they are typed on there.


Actipro Software Support

Posted 13 years ago by Kelly Leahy - Software Architect, Milliman
Avatar
OK... yet another followup.

Sorry, I may have missed something, or maybe you did.

How can I make the transition occur between VariablesLine and VariablesSection or ConstantsLine and ConstantsSection? I understand that the SetupLine -> DefaultState transition will happen automatically as a result of the "IsEndScope..." for SetupLine, but what about VariablesLine -> VariablesSection? Since it entered VariablesLine from Default, it will want to go there, won't it? Is there a way I can tell it to transition to VariablesSection instead?

Kelly Leahy Software Architect Milliman, USA

Posted 13 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar
Yes I believe so. I think that situation is somewhat similar to a <script> tag in HTML where it leaves the tag state and enters another state (not the parent).

Take a look at the tutorial help topic "Merging Two Languages at Run-Time Using a Lexical Scope Lexical State Transition". You want to define a lexical state transition for those scopes similar to what is done in that sample.


Actipro Software Support

Posted 13 years ago by Kelly Leahy - Software Architect, Milliman
Avatar
I'm sorry, I'm just not getting it...

I tried this:

// Initialize lexical states
            this.LexicalStates.Add(new DefaultLexicalState(SimpleLexicalStateID.Default, "DefaultState"));
            this.DefaultLexicalState = this.LexicalStates["DefaultState"];
            this.LexicalStates["DefaultState"].DefaultHighlightingStyle = this.HighlightingStyles["DefaultStyle"];

            // add my setupline state.
            this.LexicalStates.Add(new DefaultLexicalState(SimpleLexicalStateID.SetupLine, "SetupLine"));
            this.LexicalStates["SetupLine"].DefaultHighlightingStyle = this.HighlightingStyles["DefaultStyle"];

            // add a scope for this state.
            ProgrammaticLexicalScope pls1 = new ProgrammaticLexicalScope(
                new ProgrammaticLexicalScopeMatchDelegate(IsStartOfSetupLine), 
                new ProgrammaticLexicalScopeMatchDelegate(IsEndOfSetupLine));
            pls1.LexicalStateTransition = new LexicalScopeLexicalStateTransition(this.LexicalStates["SetupSection"], pls1);
            this.LexicalStates["SetupLine"].LexicalScopes.Add(pls1);

            // add the setupsection state (should follow #setup line's \n)
            this.LexicalStates.Add(new DefaultLexicalState(SimpleLexicalStateID.SetupSection, "SetupSection"));
            this.LexicalStates["SetupSection"].DefaultHighlightingStyle = this.HighlightingStyles["RedStyle"];

            // add a scope for that state.
            ProgrammaticLexicalScope pls2 = new ProgrammaticLexicalScope(
                new ProgrammaticLexicalScopeMatchDelegate(IsStartOfSetupSection),
                new ProgrammaticLexicalScopeMatchDelegate(IsEndOfSetupSection));
            pls1.LexicalStateTransition = new LexicalScopeLexicalStateTransition(this.LexicalStates["DefaultState"], pls2);
            this.LexicalStates["SetupSection"].LexicalScopes.Add(pls2);
it doesn't seem to ever call my IsStartOfSetupLine method... What am I doing wrong?

[Modified at 03/16/2007 05:30 PM]

Kelly Leahy Software Architect Milliman, USA

Posted 13 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar
You need to add the states with scopes as child states of the appropriate "parent" states. So like in your default state you need to add this.LexicalStates["SetupLine"] to the ChildLexicalStates collection. Otherwise it won't know to look at the child state's scopes.


Actipro Software Support

The latest build of this product (v2018.1 build 0341) was released 10 months ago, which was after the last post in this thread.

Add Comment

Please log in to a validated account to post comments.