/// <summary> /// Create CodeNode representing hierarchy sequence. /// </summary> /// <returns>CodeNode created according to layout.</returns> public CodeNode HierarchyLayout() { var node = new CodeNode(_lexer.Move(), NodeTypes.hierarchy); if (_checkToken("("))//function call { node.NodeType = NodeTypes.call; resolveBracket(() => { node.AddArgument(_nextTree()); return(false); }, "(", ",", ")", "Error in call, expected '{0}'"); //on fail throw exception node.EndingToken = _lexer.Current.Previous; //aby ukazoval na zavorku } if (_shiftToken(".")) { var child = HierarchyLayout(); if (child == null) { throw CSharpSyntax.ParsingException(_lexer.Current, "Expected identifier after '.'"); } node.Child = child; } if (_checkToken("[")) { var indexer = IndexerLayout(); node.SetIndexer(indexer); } if (_checkToken("{")) //initializer { var seq = InitializerLayout(); node.SetSubsequence(seq); } return(node); }
/// <summary> /// Parse given C# source into <see cref="CodeNode"/> representation. /// </summary> /// <param name="source">The source that will be parsed.</param> /// <returns>Parsed abstract syntax tree.</returns> public CodeNode Parse(Source source) { var lexer = new Lexer(source); _language = new CSharpSyntax(lexer, _getTree); return(_getTree()); }
/// <summary> /// Test if current lexer token value is equal to expectedString. /// </summary> /// <param name="expectedString">Value which is used to test lexer current value.</param> /// <param name="errorMessage">If is not null and test doesn't succeed, is used for /// throwing parsing exception. If null, no exception is thrown</param> /// <param name="msgArgs">Format arguments for errorMessage.</param> /// <returns>True if expectedString is equal to lexers current value.</returns> private bool _checkToken(string expectedString, string errorMessage = null, params object[] msgArgs) { if (_lexer.Current.Value != expectedString) { if (errorMessage != null) { var args = new List <object>(); args.Add(expectedString); args.AddRange(msgArgs); throw CSharpSyntax.ParsingException(_lexer.Current, errorMessage, args.ToArray()); } return(false); } return(true); }
/// <summary> /// Create CodeNode representing switch block. /// </summary> /// <returns>CodeNode created according to layout.</returns> public CodeNode SwitchLayout() { var switchNode = new CodeNode(_lexer.Move(), NodeTypes.block); condition(switchNode); _shiftToken("{", "expected '{' in switch layout"); var inSwitch = true; while (inSwitch) { var label = _current(); switch (label) { case "case": case "default": var labelBlock = new CodeNode(_lexer.Move(), NodeTypes.block); if (labelBlock.Value == "case") { labelBlock.AddArgument(_nextTree()); } _shiftToken(":", "expected '{0}' after '{1}', in switch statement", label); var lines = new List <CodeNode>(); while (_current() != "case" && _current() != "default" && _current() != "}") { lines.Add(_nextTree()); _shiftToken(";"); } labelBlock.SetSubsequence(lines, _lexer.Current); switchNode.AddArgument(labelBlock); break; case "}": inSwitch = false; break; default: throw CSharpSyntax.ParsingException(_lexer.Current, "unrecognized label '{0}' in switch statement", label); } } _shiftToken("}", "expected '{0}' in switch layout"); return(switchNode); }
/// <summary> /// Satisfy given operator node. Satisfied operator is added into operands stack. /// </summary> /// <param name="operatorNode">Operator to satisfy.</param> /// <param name="operands">Operands used for satisfying.</param> private void satisfyOperator(CodeNode operatorNode, Stack <CodeNode> operands) { var arity = _language.Arity(operatorNode); if (operands.Count < arity) { throw CSharpSyntax.ParsingException(operatorNode, "There aren't enough operands for the operator {0}", operatorNode.Value); } var reverseStack = new Stack <CodeNode>(); for (int i = 0; i < arity; i++) { reverseStack.Push(operands.Pop()); } for (int i = 0; i < arity; i++) { operatorNode.AddArgument(reverseStack.Pop()); } operands.Push(operatorNode); }
/// <summary> /// Build node tree from operands and operators while resolving its priorities. /// </summary> /// <returns>Built tree.</returns> /// <exception cref="System.NotSupportedException">newNode cannot be null</exception> private CodeNode _getTree() { var operands = new Stack <CodeNode>(); var operators = new Stack <CodeNode>(); CodeNode newNode = null; //determine context to next node bool _expectPrefix = true; bool _expectPostfix = false; //until tree end token is reached while (newNode == null || !newNode.IsTreeEnding) { if (_language.End) { throw CSharpSyntax.ParsingException(newNode, "Expected syntax tree ending"); } //infix to tree, according to priorities newNode = _language.Next(true); if (newNode == null) { throw new NotSupportedException("newNode cannot be null"); } //resolve prefix/postfix operators they go on stack as operands operatorContext(newNode, operands, _expectPrefix, _expectPostfix); //add operand on the stack - behind operand we expect postfix/binary operator if (newNode.NodeType != NodeTypes.binaryOperator) { _expectPostfix = true; _expectPrefix = false; operands.Push(newNode); if (newNode.NodeType == NodeTypes.block) { break; } continue; } //we are adding new operator //satisfy all operators with lesser arity while (operators.Count > 0 && isLesser(newNode, operators.Peek())) { satisfyOperator(operators.Pop(), operands); } //add operator on the stack operators.Push(newNode); //after operator we expect operand/prefix operator _expectPrefix = true; _expectPostfix = false; } //satisfy all pending operators while (operators.Count > 0) { satisfyOperator(operators.Pop(), operands); } //check stack state if (operands.Count > 1) { throw CSharpSyntax.ParsingException(operands.Peek(), "Missing operator for operand {0}", operands.Peek()); } if (operators.Count > 0) { throw CSharpSyntax.ParsingException(operators.Peek(), "Missing operand for operator {0}", operators.Peek()); } var result = operands.Pop(); result.IsTreeEnding = true; return(result); }