📜 ⬆️ ⬇️

Salad "Microsoft Guide for SymbolTable with a delicate aftertaste Antlr4"

Is it always necessary to use what is offered?

Having dealt with the visitor and listener, it's time to try "Bicycle". Studying ways to build external DSL, I was faced with the problem of getting practice. I will not go far to the book Fowler about how to build a dream language "DSL", unfortunately, turned out to be not at all anesthetic, but rather on the back footpath in nettle bushes.

Chapter 12 Symbol Table
Storage space during the parsing of all
identifiable objects to resolve links.

Clear? Of course it is clear that there are no complaints about the description.
Many languages ​​have to refer to objects at many points in the code. If there is
a language that defines the configuration of tasks and their dependencies means that you need a way
to which one task could refer to dependent tasks in its definition.


The book presents the task of collecting for work
go_to_work -> drink_coffee dress drink_coffee -> make_coffee wash dress -> wash 

And here’s the DSL magic, what it is created for, namely, the code does not need to be commented, it’s enough just to read to determine the dependencies between the tasks.
But I do not drink coffee, and the book “Microsoft's Guide to Designing Application Architecture” turned up. Excellent task - Develop an application for Windows, and now let's do it.
And so MS assures
')
This guide will help:
• Understand the basic principles and patterns of building architecture and design to develop successful solutions on the Microsoft platform.
• Choose the right strategies and design patterns that will help in the design of layers, components, and solution services.
• Identify and implement key technical solutions.
• Identify and implement key quality indicators and end-to-end functions for the solution.
• Choose the right technology to implement the solution.
• Create a possible version of the basic solution architecture.
• Choose the solutions and guidance offered by the patterns & practices group that will help in the implementation of the solution.

Fine. We have a free to-do list, which after reorganization looks like
1. Understand base principles
2. Select_strategies_and_shavals
3. Implement_key_technical_resolutions
4. Implement_main_quality indicators
5. Select_technology
6. Create_based_variable_base_architecture
7. Choose_of_proposals_and_guides

Well, the dependencies between them
6 -> 3 4 5
2 -> 1 7
5 -> 7
7 -> 1
3 -> 2 5
4 -> 3

So far, so good.
The grammar of this DSL is exceptionally simple.
 grammar file... network : SEP? dependency (SEP dependency)* SEP?; dependency : lhs=ID '->' rhs+=ID+ {helper.recognizedDependency($lhs, $rhs);} ; 

And it's all? What is SEP, what is ID? Well, not really, I just recently started to deal with grammar, but generally with an external DSL that is different from simple XML. Behind this came the pit. After 60 pages of paging, I saw
 SEP : ('-' | ' ' ); 

Through another 50
 fragment LETTER : ('a'..'z' | 'A'..'Z' | '_'); fragment DIGIT : ('0'..'9'); ID : LETTER (LETTER | DIGIT)* ; 

Tasks are described in Russian, oh and let DSL be on it!
Putting it all together and pretty danced around the encoding for Cyrillic support
 grammar WorkGrammar; network : SEP? dependency (SEP dependency)* SEP?; dependency : lhs=ID '->' rhs+=ID+; ID : LETTER (LETTER | DIGIT)* ; fragment LETTER : ( ''..'' | ''..'' | '_' ); fragment DIGIT : ('0'..'9'); SEP : ('-' | ' ' ); WS : [\t\r]+->skip; 

Of course, I’m not a writer (not a developer), but a reader (user), and as a user, this was not the most pleasant problem (and you don’t call for those in support; you’ve heard beeps in Google requests, nor did anyone take it).
With grammar apparently all! F5 spawned Lexer and Parser!
Task something to call the class well, not very good, so just Work
 class Work { List<Work> _dep; public Work( string name ) { Name = name; _dep = new List<Work>(); } public string Name { get; private set; } public IEnumerable<Work> Dependencies { get { return _dep; } } public bool Completed { get; set; } public void AddDependency( Work dep ) { _dep.Add( dep ); } } 

As it turned out in the Battle “Listener vs Visitor” at the antlr4 stadium, the visitor and the listener are quite small and heavy (although it is possible to use them), as well as for self-education, we try to bypass the tree on the “Bicycle”.
 class WorksLoader { public Dictionary<string, Work> Load( WorkGrammarParser.NetworkContext context ) { Dictionary<string, Work> result = new Dictionary<string, Work>(); foreach ( var dep in context.dependency() ) { var work = GetWork( dep.lhs.Text, result ); foreach ( var right in dep._rhs ) { var depWork = GetWork( right.Text, result ); work.AddDependency( depWork ); } } return result; } Work GetWork( string name, Dictionary<string, Work> works ) { if ( !works.ContainsKey( name ) ) works[name] = new Work( name ); return works[name]; } } 

The possibilities of Antlr4 to generate contexts are encouraging, it greatly simplifies the traversal.
A class with the Load method that returns a table of characters (a dictionary of jobs) with already defined dependencies.
The time has come for the most interesting source code.
 ____ -> ___ ___ _ ___ -> __ ____ _ -> ____ ____ -> __ ___ -> ___ _ ___ -> ___ 

Not very readable delimiters (if you shorten the names of the works, it is quite) well, okay, this is many times better A -> BC
Defining the text of our language in the resources, you can get a list of works
 class Program { static void Main( string[] args ) { AntlrInputStream input = new AntlrInputStream(Resource.Input); WorkGrammarLexer lexer = new WorkGrammarLexer( input ); CommonTokenStream tokens = new CommonTokenStream( lexer ); WorkGrammarParser parser = new WorkGrammarParser( tokens ); var works = new WorksLoader().Load( parser.network()); var ready = GetReadyWorks(works); } static Dictionary<string, Work> GetReadyWorks( Dictionary<string, Work> works ) { Dictionary<string, Work> ready = new Dictionary<string, Work>(); foreach ( var currentWork in works.Select(e=>e.Value) ) { if ( currentWork.Dependencies.Count() == 0 || currentWork.Dependencies.All( e => e.Completed ) ) ready[currentWork.Name] = currentWork; } return ready; } } 

F5! Works! If ... The result is not the one that was expected. Having spent a fair amount of time identifying the problem -> the ambush settled where no one expected it -> grammar. In general, it’s not real, not only is it not complete and there are no source materials, it’s also with errors. Outrageous!
Working grammar
 grammar WorkGrammar; network : SEP? dependency ('\n' dependency)*; dependency : lhs=ID '->' rhs+=ID+; ID : LETTER (LETTER | DIGIT)* ; fragment LETTER : ( ''..'' | ''..'' | '_' ); fragment DIGIT : ('0'..'9'); SEP : ('-' | ' ' ); WS : [\t\r]+->skip; 


When developing DSL on Antlr4 from the book of Fowler, the feelings are two-sided, taste with bitterness, description on one side, text at a height, on the other, besides the fact that you cannot find examples in C #, it’s also difficult to study the search for parts of the description in these sections, Bugs that the developer carefully scattered here and there.

PS


Is it always necessary to use what is offered?
My opinion is that if time permits, then it is better to write your own classes to crawl trees! Although much depends on the task itself.

Source: https://habr.com/ru/post/260271/


All Articles