Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

"Phase 2" after Parsing #32

Open
TheColonel2688 opened this issue Mar 5, 2020 · 3 comments
Open

"Phase 2" after Parsing #32

TheColonel2688 opened this issue Mar 5, 2020 · 3 comments

Comments

@TheColonel2688
Copy link

TheColonel2688 commented Mar 5, 2020

@rivantsov mentioned in one of his emails to me

The recognition of 'what is that number' will come later after parsing (phase 2), and recognizing the place of this number inside the parse tree.

So I assume that implementing phase to is something I need to do from scratch by traversing the parse tree and that there is no built-in way in irony to handle this, or really even help. Correct?

Basically I want to get this grammar into a flat class like this

 public class Register
   {
       public string FilePrefix { get; set; }
       public int? FileNumber { get; set; }
       public int? ElementNumber { get; set; }
       public int? WordAsNumber { get; set; }
       public string? WordAsString { get; set; }
       public int? BitAsNumber { get; set; }
       public string? BitAsString { get; set; }
   }

This would be an example of grammar where a number can mean different things in different places.

N250:1.0/5
T40:5.PRE
T41:3.234
N5:24
T40:5/DN
N5:23/15
[Language("SLC", "1.0", "RS Logix 500 SLC")]
    public class SLCGrammar : Grammar
    {
        public SLCGrammar()
        {


            //Terminals           
            var realNumber = new NumberLiteral("realNumber");
            var intNumber = new NumberLiteral("intNumber", NumberOptions.IntOnly);
            var FilePrefix = new FixedLengthLiteral("FilePrefix", 1, TypeCode.String);
            var Comment = new CommentTerminal("Comment", "%", "%");
            var LetterOnly = new LetterOnlyTerminal("LetterOnly");
            FilePrefix.ValidateToken += (e, s) =>
            {
                if (char.IsLetter(s.Token.ValueString, 0) == false || char.IsUpper(s.Token.ValueString, 0) == false)
                {
                    s.RejectToken();
                }
            };

            //Nonterminals
            var intOrLetters = new NonTerminal("IntOrLetters");
            var ReferenceBitOpt = new NonTerminal("ReferenceBitOpt");
            var RegisterReferenceWord = new NonTerminal("ReferenceWord");
            var RegisterElementWord = new NonTerminal("ElementWord");
            var RegisterElementBit = new NonTerminal("ElementBit");
            var RegisterReferenceElement = new NonTerminal("ReferenceElement");
            var RegisterReferenceElementOpt = new NonTerminal("ReferenceElementOpt");
            var RegisterReference = new NonTerminal("RegisterReference");
           
            //** Rules
            intOrLetters.Rule = intNumber | LetterOnly;
            ReferenceBitOpt.Rule = PreferShiftHere() + "/" + intOrLetters | Empty;
            RegisterReferenceWord.Rule = intNumber + ReferenceBitOpt;
            RegisterElementWord.Rule = RegisterReferenceWord | LetterOnly;

            RegisterReferenceElementOpt.Rule = intNumber + "." + RegisterElementWord;
            RegisterReferenceElement.Rule = RegisterReferenceElementOpt | RegisterReferenceWord;

            RegisterReference.Rule = FilePrefix + intNumber + ":" + RegisterReferenceElement;

            
            //Root.Rule = RegisterReferenceWord;

            //Set grammar root
            this.Root = RegisterReference;

            MarkPunctuation(" ");

        }//constructor

    }//class

    class LetterOnlyTerminal : IdentifierTerminal
    {
        public LetterOnlyTerminal(string name) : base(name)
        {
            this.AllChars = Strings.AllLatinLetters;
            this.AllFirstChars = Strings.AllLatinLetters;

        }
    }
@rivantsov
Copy link
Contributor

there's something called AST (abstract syntax tree), google it, Irony supports this, AST construction, and you can treat your model as AST. Look at expression evaluator grammar - the executable model of the expression is a graph of expr objects.

@TheColonel2688
Copy link
Author

Ok so I created an AstNode for the RegisterReference

   public class Register: AstNode
   {
       public string FilePrefix { get; set; }
       public byte? FileNumber { get; set; }
       public byte? ElementNumber { get; set; }
       public byte? WordAsNumber { get; set; }
       public string? WordAsString { get; set; }
       public ushort? BitAsNumber { get; set; }
       public string? BitAsString { get; set; }

       public override void Init(AstContext context, ParseTreeNode treeNode)
       {
           // Keep this
           base.Init(context, treeNode);
           // treeNode is the corresponding node in parse tree, contains information like:
           // Token
           var token = treeNode.Token;
           // Term
           var term = treeNode.Term;
           // Child nodes
           var nodes = treeNode.GetMappedChildNodes();

           // Set AsString to a human readable format. It will be used to display AST in Irony.GrammarExplorer
           AsString = "Id: " + token.Text;
           // Use AddChild to build tree structure, it returns an AstNode instance of child's AST node 
          
       }
   }

I then added the following to the SLCGrammar Class

RegisterReference.AstConfig.NodeType = typeof(Register);
this.LanguageFlags = LanguageFlags.CreateAst;

I did a rebuild but I still have a blank AST, obviously, my new Properties will be null, but I expect at least something to show in the AST. Am I missing something?

Further, if I want to get all of the children of RegisterReference, do I need to create an AstNode for each one? I'm looking to flatten everything below RegisterReference.

@rivantsov
Copy link
Contributor

no, defining a single node is not enough to see something in AST tab in GE. GE shows the AST tree starting from the top, building the tree by accessing each node's children using IBrowsableAstNode interface, from top/root. So if you don't anything at the top-root, you won't see anything.
Yes, if you want to build AST through default AST builder, you have to define AST node type for every terminal/nonterminal.
I would suggest to go instead with your own ast builder - traverse parse tree and build it all. What you actually need I guess is not AST (some intermediate compiler-related structure), but the tree/model for your own app and whatever functionality you may have with it. Default AST building process (with assigning AST node types to all terms) is quite tricky and cumbersome, admittedly. Mea culpa

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants