Question

Syntax "How To" Question

Posted 5 years ago by Avatar David Mullin
If this question is beyond the scope of what you are willing to do via support, just let me know. But, on the off chance that it's easy (for you), I thought I'd ask...

In our application, we allow the authoring of XAML containing what we call "Merge Codes" which are pre-processed into valid strings prior to the XAML being parsed. Merge Codes are of the form: <<stuff>>, and may contain quotes. An example of XAML containing a Merge Code might be:
<TextBlock Text="<<Case.Code("DOB").Value>>" />
I understand that this is not, as written, valid XAML, but it will always be processed prior to parsing.

Given the above example, I'd like the entire token <<Case.Code("DOB").Value>> to be recognized as a Merge Code and formatted differently. I tried to hack the XAML language definition, but couldn't get any traction (because I'm a little clueless at the moment). Furthermore, I expect the nested quotes to be problematic.

So, if you're willing and able to just say "hey, use this XML and it'll work!", that'd be great. If not, is there some portion of the documentation that I have not found that gives much insight into how the language syntax is put together?

Thanks in advance!

David Mullin

Comments (21)

Posted 5 years ago by Actipro Software Support - Cleveland, OH, USA
David,

Since you really are talking about two languages here, the XAML one and the MergeCode one, you would be best off looking at our dynamic lexer sample for HTML. That one uses language transitions to ASP, etc. which is similar to what you are doing.

You'd want to have the << act like <% does in the HTML language. This way the quotes in your MergeCode language would not affect the quotes in the parent XAML language.

Give that a look and a try and if you have problems, feel free to email over your XML definition files so we can see them.

Actipro Software Support
Posted 5 years ago by David Mullin
I think I understand - and you're right, that is a better way (since the Merge Code syntax will be consumed inside of Text, XAML and HTML in our application). If I understand the process correctly, then all I need to put into the XAML language definition is:
<State Key="MergeCodeState" Filename="CaseTrakker.MergeCode.xml" ChildLanguageBackColor="WhiteSmoke">
  <!-- Scopes -->
  <Scopes>
    <Scope>
      <ExplicitPatternGroup Type="StartScope" TokenKey="MergeCodeStartToken" Style="MergeCodeDelimiterStyle" PatternValue="&lt;&lt;" />
      <RegexPatternGroup Type="EndScope" TokenKey="MergeCodeEndToken" Style="MergeCodeDelimiterStyle" PatternValue="&gt;&gt;" />
    </Scope>
  </Scopes>
</State>
And add the MergeCodeState as the first child state of the DefaultState. That much seems to work - as the << and >> get styled correctly. However, my attempt to hack together a simple language definition that just styles everything maroon completely failed (either that, or it failed to locate CaseTrakker.MergeCode.xml - I can't tell which).

I can email you my sample project, if that'd help...
Posted 5 years ago by Actipro Software Support - Cleveland, OH, USA
David,

Yes that should work however note that I believe there is a bug in the current version with multiple languages in dynamic lexers. Basically the current code that loads dynamic lexer XML definitions is temporary. We're currently working on a new language definition format that will replace the old SE4 format. We hope to have the new format (and a converter from the old to the new) for the next build or two.

Anyhow, with the bug, it will only find the child language if it can be found as a manifest resource with this path in your assembly:
string path = "ActiproSoftware.Windows.ProductSamples.SyntaxEditorSamples.Languages.Dynamic." + filename;
Once we get the new format in place, we'll get this all working correctly like it did in SE4. As a workaround for the time being, I'd recommend not specifying the filename in the parent language. Then load the parent and child ones separately. Then use the object model to programmatically direct the state in the parent language to transition to the default state in the child language. That should link them together until we get the new format working.

Actipro Software Support
Posted 5 years ago by David Mullin
I think I like the programatic solution for another reason - if the Merge Code language has code behind (like for intelisence), this approach lets me load a code module rather than just parse an XML file.

Could you provide an example of how I'd detect the language transition and redirect to the other language via the object model? In the version that I have, the samples project doesn't have an example of this...

Thanks

David Mullin

PS - we think the SyntaxEditor is COOL!
Posted 5 years ago by Actipro Software Support - Cleveland, OH, USA
Sure, we have a programmatic language transitioning QuickStart stubbed out but not included in the Sample Browser yet as we're still working on that area a bit. But if you email over, we can reply back with a ZIP containing the QuickStart code that should work in the latest WPF Studio build.

It shows how to transition from a parent language (with a programmatic lexer) to a child language (also with a programmatic lexer) that uses <% %> delimiters to transition.

Actipro Software Support
Posted 5 years ago by David Mullin
Thanks for sending me the ZIP file. I've looked at the code and tried to wrap my head around it. I just want to confirm that I'm more or less understanding things - since, in the QuickStart, dynamic language definitions are not used (i.e., from an XML file), this is all new stuff for me.

It looks like the important stuff happens in the constructor of ParentLexer - where the language transition is defined and wired in to the mix. So, if I were to continue using the XML based language definition, then right after I get the parser from DynamicLexicalParser.LoadFromXml(), I programmatically add the ProgammaticLexicalState objects to the parser.DefaultLexicalStateCore.ChildLexicalStates.

Or something in that general direction.

Or, am I on drugs, and there is no way to simply add code for the programmatic transition, but keep the language definitions in the XML file?

David
Posted 5 years ago by Actipro Software Support - Cleveland, OH, USA
David,

Yes the sample I sent shows two languages that use programmatic (not dynamic) lexers. Sorry, I thought that's what you were asking for.

Anyhow the transition magic happens in the ParentLexer constructor with this line:
childLanguageTransitionState.TransitionTo = childLexer.DefaultLexicalState;

That is saying when the lexical state we defined for the <% %> tags is entered, auto-transition to a new state. And the new state is the child language's lexer's default state. Thus we have achieved a language transition. When the end scope of the childLanguageTransitionState is recognized, it will pop back out to the parent state.

The same concepts can apply to dynamic lexers (defined in XML) too though. If you had a parent langauge with a dynamic lexer state defined with scopes for <% and %>, then if you loaded a child language with dynamic lexer, you'd do the same thing. You'd just set that TransitionTo property and it will all wire up.

As I mentioned in a previous post, the auto-wire up of language transitions without codebehind is broken right now, but will be fixed in a future build so this extra work isn't needed if you are only doing languages with dynamic lexers.

Actipro Software Support
Posted 5 years ago by David Mullin
Ah, I see the confusion.

I am defining both my parent and child languages along the same lines at the Html language in your Sample Browser - there is a dynamic language definition, and an associated class (HtmlDynamicSyntaxLanguage) for providing the Completion Provider and such. So, my language is defined as a dynamic language in an XML file, but there is code-behind.

Given that, I'm not sure how to wire them together. The dynamic language wiring seems to want to point to another dynamic language (i.e., without code behind). The example you sent me seems to require subclassing the lexer - but I'm getting the lexer from DynamicLexicalParser.LoadFromXml, so I'm not sure how to do the wiring in code.

I'll play with it to see if I can figure it out, but any guidance would be helpful...

David
Posted 5 years ago by Actipro Software Support - Cleveland, OH, USA
The same concept would apply. You could load each XML definition into a separate language. Then get the DynamicLexer of the first language. Find the lexical state there that causes the transition. Set that lexical state's TransitionTo property to the default lexical state of the second language's lexer. That should be all you need to do.

Actipro Software Support
Posted 5 years ago by David Mullin
Thanks!.

I am having one obstacle to doing this...I can't see to find the appropriate reference/namespace for ILexer or DynamicLexer. I've got v502 - were these things added after that? Or, is there just some reference that I have not added to my project? I've done a search through the SyntaxEditor assembly, but can't find them...

What am I missing?

David Mullin
Posted 5 years ago by David Mullin
Ah, never mind. I found the release notes for v503, and see that things were renamed. I'll revise my code for now (we're waiting on the v504 release to upgrade).

Thanks!
Posted 5 years ago by Actipro Software Support - Cleveland, OH, USA
Yes I noticed that with your emailed sample. In build 503 we renamed LexicalParser to Lexer and moved it to a new ActiproSoftware.Text.Lexing namespace instead. This complies better with industry standard naming of Lexers and Parsers (which do the syntax/semantic parsing). Check the build 503 release notes for details on what changed.

Actipro Software Support
Posted 5 years ago by David Mullin
This has brought me much closer. There are three ways in which the MergeCode language can be consumed inside of XAML:
1) Free Floating: <Grid><<MergeCode>></Grid> - this works.
2) Inside a Node: <TextBlock <<MergeCode>> /> - this works
3) Inside the value of an Attribute: <TextBlock Text="<<MergeCode>>" /> - this does not work.

To try to address this 3rd case, I add my MergeCodeState as a child state of the 4 StartTagAttribute*ValueState states (this was really just a wild guess on my part, since the syntax is still a little opaque to me).

Just to confirm my understanding of these 4 states:
StartTagAttributeValueState - this is an unquoted attribute value? I.e., Thing=3 (is this actually legal in XAML?)
StartTagAttributeStringValueState - this is a quoted attribute value? I.e., Thing="3"
StartTagAttributeSingleQuoteStringValueState - this is a single-quoted attribute value? I.e., Thing='3'
StartTagAttributeMarkupExtensionValueState - this is a quoted attribute containing a Markup Extension? I.e., Thing="{Binding }"

Based on this, I think that if I were to make a new state for a MergeCode inside the value of an Attribute patterend after the one for Markup Extension and doing a look ahead for "<<" rather than "{", it would maybe work, but only if the MergeCode were the entire content of the value (since Text="{Binding}" gets styled differently than Text="Fred{Binding}")

I think that I'm going to want to end up adding a ChildState to the 4 attribute value states, but there is clearly something else that I'll need to change to make it work...any suggestions?

I can provide an updated sandbox application...

David Mullin
Posted 5 years ago by Actipro Software Support - Cleveland, OH, USA
To get it working in the attribute value, you'd want to add this attribute to the Scope element in your MergeCodeState:
AncestorEndScopeCheckEnabled="False"
That tells SE that when in your MergeCode language, not to look for end scope patterns above this scope. Since otherwise, a double-quote end would be found in the XAML language's end scope for the string state.

Also just add this to your StartTagAttributeStringValueState:
<ChildStates>
    <ChildState Key="MergeCodeState" />
</ChildStates>
Same thing for StartTagAttributeSingleQuoteStringValueState too. Try that out and see if it gets you going.

Actipro Software Support
Posted 5 years ago by David Mullin
There was no change in behavior. It looks like it isn't successfully detecting the transition into the MergeCodeState when inside of an Attribute value - there is absolutely no formatting of the Merge Code, other than as part of the attribute content.

Just in case I've done something wrong, this is what I've got for my MergeCodeState:
<State Key="MergeCodeState" ChildLanguageBackColor="WhiteSmoke">
  <!-- Scopes -->
  <Scopes>
    <Scope AncestorEndScopeCheckEnabled="False">
      <ExplicitPatternGroup Type="StartScope" TokenKey="MergeCodeStartToken" Style="MergeCodeDelimiterStyle" PatternValue="&lt;&lt;" />
      <RegexPatternGroup Type="EndScope" TokenKey="MergeCodeEndToken" Style="MergeCodeDelimiterStyle" PatternValue="&gt;&gt;" />
    </Scope>
  </Scopes>
</State>
And this is what I've got for the StartTagAttributeStringValueState:
<State Key="StartTagAttributeStringValueState" TokenKey="StartTagAttributeStringValueDefaultToken" Style="TagAttributeValueStyle">
  <!-- Scopes -->
  <Scopes>
    <Scope>
      <RegexPatternGroup Type="StartScope" TokenKey="StartTagAttributeStringValueStartToken" Style="TagAttributeValueStyle" PatternValue="= {LineTerminatorWhitespaceMacro}* \&quot;" />
      <RegexPatternGroup Type="EndScope" TokenKey="StartTagAttributeStringValueEndToken" Style="TagAttributeValueStyle" PatternValue="\&quot;" />
    </Scope>
  </Scopes>
  <!-- Patterns Groups -->
  <PatternGroups>
    <RegexPatternGroup TokenKey="StartTagAttributeStringValueDefaultToken" PatternValue="[^&quot;]+" />
  </PatternGroups>
  <ChildStates>
    <ChildState Key="MergeCodeState" />
  </ChildStates>
</State>
I've made a similair change (adding the ChildStates tag) to StartTagAttributeMarkupExtensionValueState, StartTagAttributeSingleQuoteStringValueState, and StartTagAttributeValueState.

David
Posted 5 years ago by Actipro Software Support - Cleveland, OH, USA
That looks like what I did. When I do that, run your sample and change the text to this:
<TextBlock Text="<<Case.Code("DOB").value>>" />
Everything appears to be colored correctly.

Actipro Software Support
Posted 5 years ago by David Mullin
Aha! I think we are closer. I tried the exact text that you had there, and it worked in my world too. However, if I changed it to:
<TextBlock Text="Something - <<Case.Code("DOB").Value>>" />
then it did not work.

David Mullin
Posted 5 years ago by Actipro Software Support - Cleveland, OH, USA
I see, it's this sort of line in StartTagAttributeStringValueState:
<RegexPatternGroup TokenKey="StartTagAttributeStringValueDefaultToken" PatternValue="[^&quot;]+" />
That is consuming < characters, thus your child state never has an opportunity to be recognized if not at the start of the state. If you change it to this it should work:
<RegexPatternGroup TokenKey="StartTagAttributeStringValueDefaultToken" PatternValue="[^&quot;&lt;]+" />

Actipro Software Support
Posted 5 years ago by David Mullin
Success!

All use-cases work in all permutations that I can think of.

Thank you!

David Mullin

PS - What's the ETA for the next release of SyntaxEditor? I'm especially looking forward to the fix that makes it detect Bold formatting in Styles...
Posted 5 years ago by Actipro Software Support - Cleveland, OH, USA
Not sure on the release date yet. We are trying to finish up a couple of significant new features for the next WPF Studio, one being ANTLR/SyntaxEditor integration.

Actipro Software Support
Posted 4 years ago by Miguel A Castro
Hey guys. I sent an email with some code to you guys today and then continued searching the forums for a solution to my problem. I think that the post to which i'm replying deals with the same needs I have. You offered to send the developer a zip with some quickstart code that does programmatic language transition that uses <% %> to delimit the transition. Is it possible that you send me that sample as well. I think that's the answer to my problem. I was doing this great in the 3.0 version of syntax editor but I need to know how to do it with the WPF version.
Thanks! This can really save me.
Miguel
Information The latest build of this product (2014.2 build 0610) was released 10 days ago, which was after the last post in this thread.

Add a Comment

Please log in to a validated account to post comments.