/// <summary> /// Add a comma node to the left, and a binary tree to the right. /// </summary> /// <param name="root">root node</param> /// <param name="newTree"></param> private static void NodeAddCommaToken(ref BinaryTree <EditorTokenInfo> root, BinaryTree <EditorTokenInfo> newTree, bool onlyLeft = false) { EditorTokenInfo CommaToken = new EditorTokenInfo(",", "COMMA"); CommaToken.Precedence = 150; BinaryTree <EditorTokenInfo> current = root; //find the the last left leaf, :) sounds like a tongue-twister while (current.Left != null) { current = current.Left; } if (!onlyLeft) { //add a comma to the left current.Left = new BinaryTree <EditorTokenInfo>(CommaToken); //add a binary tree to the right current.Left.Right = newTree; } else { current.Left = newTree; } //go back to top parent while (current.Parent != null) { current = current.Parent; } root = current; }
/// <summary> /// Traverse inorder (INFIX NOTATION) all nodes of Btree. /// </summary> /// <param name="rootnode">Current Node</param> /// <param name="priorPrecedence">Prior Precedence</param> /// <returns></returns> static string TraverseInOrder (BinaryTree <EditorTokenInfo> rootnode, int priorPrecedence = 0) { string Result = ""; EditorTokenInfo token = rootnode.Data; if (token.Precedence < 50) { //its an operand, return the text return(token.Text); } else { //its a operator, visit left, concatenate with root and right. if (rootnode.Right == null) { if (token.Precedence == 200) //Its a function, add parenthesis here!! { Result = token.Text + "(" + TraverseInOrder(rootnode.Left, token.Precedence) + ")"; } else //just a unary operator!? { Result = token.Text + " " + TraverseInOrder(rootnode.Left, token.Precedence); } } else //left and right are not null { Result = TraverseInOrder(rootnode.Left, token.Precedence) + " " + token.Text + " " + TraverseInOrder(rootnode.Right, token.Precedence); } //take account of prior precedence vs current node precedence, and add parenthesis if (token.Precedence < priorPrecedence && priorPrecedence != 200 && priorPrecedence != 150) { Result = "(" + Result + ")"; } } return(Result); }
/// <summary> /// Parse a postfix list of tokens into INFIX /// </summary> /// <param name="ExprTokens"></param> /// <returns></returns> static string ParseRPN2Infix(List <EditorTokenInfo> ExprTokens) { string Result = ""; Stack <BinaryTree <EditorTokenInfo> > BTStack = new Stack <BinaryTree <EditorTokenInfo> >(); //parse using BTreeStack every token in RPN list of preprocessed tokens for (int idxtoken = 0; idxtoken < ExprTokens.Count; idxtoken++) { EditorTokenInfo token = new EditorTokenInfo("", ""); token = ExprTokens[idxtoken]; if (token.Precedence < 50) { //It's an operand, just push to stack BTStack.Push(new BinaryTree <EditorTokenInfo>(token)); } else { //it's an operator, push two operands, create a new tree and push to stack again. BinaryTree <EditorTokenInfo> operatornode = new BinaryTree <EditorTokenInfo>(token); switch (operatornode.Data.TerminalName) { //Multiple expressions functions case "AVG": if (BTStack.Count < operatornode.Data.Index) { throw new ArgumentException("Not enough arguments in BTStack for AVG Function"); } for (int i = 1; i < operatornode.Data.Index; i++) { NodeAddCommaToken(ref operatornode, BTStack.Pop()); //default, add to the right. } NodeAddCommaToken(ref operatornode, BTStack.Pop(), true); //just add to the left BTStack.Push(operatornode); break; default: //Other simple functions and operators if (BTStack.Count > 1) //avoid unary operators and functions exception { operatornode.Right = BTStack.Pop(); } operatornode.Left = BTStack.Pop(); BTStack.Push(operatornode); break; } } } if (BTStack.Count > 1) { throw new ArgumentException("Too many expressions in final BTStack"); } BinaryTree <EditorTokenInfo> root = BTStack.Peek(); //now we end the the top most node on stack as a complete tree of RPN string rootprint = root.ToString(); Result = TraverseInOrder(root); return(Result); }
/// <summary> /// Parse tokens from postfix (RPN) into infix notation /// </summary> /// <param name="source">Byte encoded source</param> /// <param name="offset">start of expression</param> /// <returns></returns> private static string GetExpression(byte[] source, ref int offset) { //Create a list of ordered tokeneditorsinfo List <EditorTokenInfo> ExprTokens = new List <EditorTokenInfo>(); bool isEOL = false; bool ExpressionAhead = false; string NextExpression = ""; EditorTokenInfo fxtoken; string Result = ""; #region Operators precedence, same as in grammar // 4. Operators precedence, same as in grammar ////RegisterOperators(100, Associativity.Right, EXP); ////RegisterOperators(90, MUL, DIV, IDIV); ////RegisterOperators(80, MOD); ////RegisterOperators(70, SUM, SUB); ////RegisterOperators(60, LT, GT, LTE, GTE, EQ, NEQ); ////RegisterOperators(50, Associativity.Right, NOT); ////RegisterOperators(50, AND, OR, XOR); #endregion while (!isEOL) { switch (source[offset]) { #region Identifiers case (byte)PCODE_CONST.LOCAL_POINT_PRG: EditorTokenInfo localpoint = new EditorTokenInfo("Identifier", "Identifier"); localpoint.Token = source[offset]; localpoint.Index = source[offset + 1]; localpoint.Type = source[offset + 2]; //text will be ready with identifier label localpoint.Text = GetIdentifierLabel(source, ref offset); //increments offset after reading identifier ExprTokens.Add(localpoint); break; #endregion #region Numeric Constants case (byte)PCODE_CONST.CONST_VALUE_PRG: EditorTokenInfo constvalue = new EditorTokenInfo("NUMBER", "NUMBER"); constvalue.Token = source[offset]; constvalue.Text = GetConstValue(source, ref offset); //incrementes offset after reading const ExprTokens.Add(constvalue); break; #endregion #region Single Tokens case (byte)TYPE_TOKEN.PLUS: EditorTokenInfo plustoken = new EditorTokenInfo("+", "PLUS"); plustoken.Token = source[offset]; plustoken.Precedence = 70; ExprTokens.Add(plustoken); offset++; break; case (byte)TYPE_TOKEN.MINUS: EditorTokenInfo minustoken = new EditorTokenInfo("-", "MINUS"); minustoken.Token = source[offset]; minustoken.Precedence = 70; ExprTokens.Add(minustoken); offset++; break; case (byte)TYPE_TOKEN.DIV: EditorTokenInfo divtoken = new EditorTokenInfo("/", "DIV"); divtoken.Token = source[offset]; divtoken.Precedence = 90; ExprTokens.Add(divtoken); offset++; break; case (byte)TYPE_TOKEN.IDIV: EditorTokenInfo idivtoken = new EditorTokenInfo("\\", "IDIV"); idivtoken.Token = source[offset]; idivtoken.Precedence = 90; ExprTokens.Add(idivtoken); offset++; break; case (byte)TYPE_TOKEN.MUL: EditorTokenInfo multoken = new EditorTokenInfo("*", "MUL"); multoken.Token = source[offset]; multoken.Precedence = 90; ExprTokens.Add(multoken); offset++; break; case (byte)TYPE_TOKEN.POW: EditorTokenInfo powtoken = new EditorTokenInfo("^", "POW"); powtoken.Token = source[offset]; powtoken.Precedence = 100; ExprTokens.Add(powtoken); offset++; break; case (byte)TYPE_TOKEN.MOD: EditorTokenInfo modtoken = new EditorTokenInfo("MOD", "MOD"); modtoken.Token = source[offset]; modtoken.Precedence = 80; ExprTokens.Add(modtoken); offset++; break; case (byte)TYPE_TOKEN.LT: EditorTokenInfo lttoken = new EditorTokenInfo("<", "LT"); lttoken.Token = source[offset]; lttoken.Precedence = 60; ExprTokens.Add(lttoken); offset++; break; case (byte)TYPE_TOKEN.LE: EditorTokenInfo letoken = new EditorTokenInfo("<=", "LE"); letoken.Token = source[offset]; letoken.Precedence = 60; ExprTokens.Add(letoken); offset++; break; case (byte)TYPE_TOKEN.GT: EditorTokenInfo gttoken = new EditorTokenInfo(">", "GT"); gttoken.Token = source[offset]; gttoken.Precedence = 60; ExprTokens.Add(gttoken); offset++; break; case (byte)TYPE_TOKEN.GE: EditorTokenInfo getoken = new EditorTokenInfo(">=", "GE"); getoken.Token = source[offset]; getoken.Precedence = 60; ExprTokens.Add(getoken); offset++; break; case (byte)TYPE_TOKEN.EQ: EditorTokenInfo eqtoken = new EditorTokenInfo("=", "EQ"); eqtoken.Token = source[offset]; eqtoken.Precedence = 60; ExprTokens.Add(eqtoken); offset++; break; case (byte)TYPE_TOKEN.NE: EditorTokenInfo netoken = new EditorTokenInfo("<>", "NE"); netoken.Token = source[offset]; netoken.Precedence = 60; ExprTokens.Add(netoken); offset++; break; case (byte)TYPE_TOKEN.XOR: EditorTokenInfo xortoken = new EditorTokenInfo("XOR", "XOR"); xortoken.Token = source[offset]; xortoken.Precedence = 50; ExprTokens.Add(xortoken); offset++; break; case (byte)TYPE_TOKEN.OR: EditorTokenInfo ortoken = new EditorTokenInfo("OR", "OR"); ortoken.Token = source[offset]; ortoken.Precedence = 50; ExprTokens.Add(ortoken); offset++; break; case (byte)TYPE_TOKEN.AND: EditorTokenInfo andtoken = new EditorTokenInfo("AND", "AND"); andtoken.Token = source[offset]; andtoken.Precedence = 50; ExprTokens.Add(andtoken); offset++; break; case (byte)TYPE_TOKEN.NOT: EditorTokenInfo nottoken = new EditorTokenInfo("NOT", "NOT"); nottoken.Token = source[offset]; nottoken.Precedence = 50; ExprTokens.Add(nottoken); offset++; break; //functions that are low precedence as identifiers in expressions case (byte)FUNCTION_TOKEN.DOY: fxtoken = new EditorTokenInfo("DOY", "DOY"); fxtoken.Token = source[offset]; fxtoken.Precedence = 0; ExprTokens.Add(fxtoken); offset++; break; case (byte)FUNCTION_TOKEN.DOW: fxtoken = new EditorTokenInfo("DOW", "DOW"); fxtoken.Token = source[offset]; fxtoken.Precedence = 0; ExprTokens.Add(fxtoken); offset++; break; case (byte)FUNCTION_TOKEN.DOM: fxtoken = new EditorTokenInfo("DOM", "DOM"); fxtoken.Token = source[offset]; fxtoken.Precedence = 0; ExprTokens.Add(fxtoken); offset++; break; case (byte)FUNCTION_TOKEN.POWER_LOSS: fxtoken = new EditorTokenInfo("POWER-LOSS", "POWER_LOSS"); fxtoken.Token = source[offset]; fxtoken.Precedence = 0; ExprTokens.Add(fxtoken); offset++; break; case (byte)FUNCTION_TOKEN.USER_A: fxtoken = new EditorTokenInfo("USER-B", "USER_A"); fxtoken.Token = source[offset]; fxtoken.Precedence = 0; ExprTokens.Add(fxtoken); offset++; break; case (byte)FUNCTION_TOKEN.USER_B: fxtoken = new EditorTokenInfo("USER-B", "USER_B"); fxtoken.Token = source[offset]; fxtoken.Precedence = 0; ExprTokens.Add(fxtoken); offset++; break; case (byte)FUNCTION_TOKEN.SCANS: fxtoken = new EditorTokenInfo("SCANS", "SCANS"); fxtoken.Token = source[offset]; fxtoken.Precedence = 0; ExprTokens.Add(fxtoken); offset++; break; case (byte)FUNCTION_TOKEN.UNACK: fxtoken = new EditorTokenInfo("UNACK", "UNACK"); fxtoken.Token = source[offset]; fxtoken.Precedence = 0; ExprTokens.Add(fxtoken); offset++; break; #endregion #region Functions with single expression case (byte)FUNCTION_TOKEN.ABS: fxtoken = new EditorTokenInfo("ABS", "ABS"); fxtoken.Token = source[offset]; fxtoken.Precedence = 200; ExprTokens.Add(fxtoken); offset++; break; case (byte)FUNCTION_TOKEN._INT: fxtoken = new EditorTokenInfo("INT", "_INT"); fxtoken.Token = source[offset]; fxtoken.Precedence = 200; ExprTokens.Add(fxtoken); offset++; break; case (byte)FUNCTION_TOKEN.INTERVAL: fxtoken = new EditorTokenInfo("INTERVAL", "INTERVAL"); fxtoken.Token = source[offset]; fxtoken.Precedence = 200; ExprTokens.Add(fxtoken); offset++; break; case (byte)FUNCTION_TOKEN.LN: fxtoken = new EditorTokenInfo("LN", "LN"); fxtoken.Token = source[offset]; fxtoken.Precedence = 200; ExprTokens.Add(fxtoken); offset++; break; case (byte)FUNCTION_TOKEN.LN_1: fxtoken = new EditorTokenInfo("LN-1", "LN_1"); fxtoken.Token = source[offset]; fxtoken.Precedence = 200; ExprTokens.Add(fxtoken); offset++; break; case (byte)FUNCTION_TOKEN.SQR: fxtoken = new EditorTokenInfo("SQR", "SQR"); fxtoken.Token = source[offset]; fxtoken.Precedence = 200; ExprTokens.Add(fxtoken); offset++; break; case (byte)FUNCTION_TOKEN._Status: fxtoken = new EditorTokenInfo("STATUS", "_Status"); fxtoken.Token = source[offset]; fxtoken.Precedence = 200; ExprTokens.Add(fxtoken); offset++; break; #region This functions hadnt been tested, PENDING FROM ENCONDING case (byte)FUNCTION_TOKEN.CONPROP: fxtoken = new EditorTokenInfo("CONPROP", "CONPROP"); fxtoken.Token = source[offset]; fxtoken.Precedence = 200; ExprTokens.Add(fxtoken); offset++; break; case (byte)FUNCTION_TOKEN.CONRATE: fxtoken = new EditorTokenInfo("CONRATE", "CONRATE"); fxtoken.Token = source[offset]; fxtoken.Precedence = 200; ExprTokens.Add(fxtoken); offset++; break; case (byte)FUNCTION_TOKEN.CONRESET: fxtoken = new EditorTokenInfo("CONRESET", "CONRESET"); fxtoken.Token = source[offset]; fxtoken.Precedence = 200; ExprTokens.Add(fxtoken); offset++; break; case (byte)FUNCTION_TOKEN.TBL: fxtoken = new EditorTokenInfo("TBL", "TBL"); fxtoken.Token = source[offset]; fxtoken.Precedence = 200; ExprTokens.Add(fxtoken); offset++; break; case (byte)FUNCTION_TOKEN.TIME: fxtoken = new EditorTokenInfo("TIME", "TIME"); fxtoken.Token = source[offset]; fxtoken.Precedence = 200; ExprTokens.Add(fxtoken); offset++; break; case (byte)FUNCTION_TOKEN.TIME_ON: fxtoken = new EditorTokenInfo("TIME-ON", "TIME_ON"); fxtoken.Token = source[offset]; fxtoken.Precedence = 200; ExprTokens.Add(fxtoken); offset++; break; case (byte)FUNCTION_TOKEN.TIME_OFF: fxtoken = new EditorTokenInfo("TIME-OFF", "TIME_OFF"); fxtoken.Token = source[offset]; fxtoken.Precedence = 200; ExprTokens.Add(fxtoken); offset++; break; case (byte)FUNCTION_TOKEN.WR_ON: fxtoken = new EditorTokenInfo("WR-ON", "WR_ON"); fxtoken.Token = source[offset]; fxtoken.Precedence = 200; ExprTokens.Add(fxtoken); offset++; break; case (byte)FUNCTION_TOKEN.WR_OFF: fxtoken = new EditorTokenInfo("WR-OFF", "WR_OFF"); fxtoken.Token = source[offset]; fxtoken.Precedence = 200; ExprTokens.Add(fxtoken); offset++; break; #endregion #region Functions ended with count of variable arguments case (byte)FUNCTION_TOKEN.AVG: fxtoken = new EditorTokenInfo("AVG", "AVG"); fxtoken.Token = source[offset]; fxtoken.Precedence = 200; fxtoken.Index = source[offset + 1]; ExprTokens.Add(fxtoken); offset += 2; break; case (byte)FUNCTION_TOKEN.MAX: fxtoken = new EditorTokenInfo("MAX", "MAX"); fxtoken.Token = source[offset]; fxtoken.Precedence = 200; fxtoken.Index = source[offset + 1]; ExprTokens.Add(fxtoken); offset += 2; break; case (byte)FUNCTION_TOKEN.MIN: fxtoken = new EditorTokenInfo("MIN", "MIN"); fxtoken.Token = source[offset]; fxtoken.Precedence = 200; fxtoken.Index = source[offset + 1]; ExprTokens.Add(fxtoken); offset += 2; break; #endregion #endregion #region End of expressions (MARKERS) case (byte)LINE_TOKEN.EOE: //Also known as end of expression marker (THEN) case (byte)LINE_TOKEN.EOF: case (byte)LINE_TOKEN.THEN: case (byte)LINE_TOKEN.REM: case (byte)LINE_TOKEN.ELSE: case (byte)TYPE_TOKEN.NUMBER: //line number default: isEOL = true; //expression ends here, this byte-token should be processed outside this function break; #endregion } }// after this, we should have a list of all tokens in the expression. //lets parse RPN into Infix, if (ExpressionAhead) { Result = ParseRPN2Infix(ExprTokens) + NextExpression; } else { Result = ParseRPN2Infix(ExprTokens); } return(Result); }