public AstNode Parse(IInputIterator inputIterator) { string grammar = @" WhiteSpace: [\s\n\t ]+; Comment: '/*' (!'*/' .)* '*/'; (?<S>): (WhiteSpace / Comment)*; (?<Variable>): '\k<' S (?<Name>[a-zA-Z][a-zA-Z0-9]*) S '>'; (?<Digit>): [0-9]+('.'[0-9]+)?; Value: Variable / Digit / '(' S Expr S ')'; (?<Product \rsc>): Value S ((?<Symbol> '*' / '/') S Value)*; (?<Sum \rsc>): Product S ((?<Symbol>'+' / '-') S Product)*; (?<Expr \rsc>): S Sum S; ".Trim(); AExpression rules = PEGrammar.Load(grammar); var visitor = new NpegParserVisitor(inputIterator); rules.Accept(visitor); if (visitor.IsMatch) { return(visitor.AST); } throw new InvalidInputException(); }
public LimitingRepetition(AExpression exp) { Exp = exp; Min = null; Max = null; VariableLengthExpression = null; }
public CapturingGroup(String uniquename, AExpression exp) { Exp = exp; Name = uniquename; DoReplaceBySingleChildNode = false; DoCreateCustomAstNode = false; }
//[ExpectedException(typeof (InfiniteLoopDetectedException))] public void PEGrammar_OneOrMore_InfiniteLoopTest() { string input = ""; AExpression caseSensitive = PEGrammar.Load(@"(?<Expression>): (.*)+;"); var bytes = Encoding.UTF8.GetBytes(input); var visitor = new NpegParserVisitor(new ByteInputIterator(bytes)); caseSensitive.Accept(visitor); Assert.IsTrue(visitor.IsMatch); }
private EvaluationResult EvaluateExpression(Context context, AExpression expression, out int frameIndex) { var result = EvaluationResult.NotSure; for (frameIndex = context.Frames.Count - 1; frameIndex >= 0; frameIndex--) { result = context.Evaluate(expression, frameIndex); if (result == EvaluationResult.True) { return(EvaluationResult.True); } } return(result); }
public void PEGrammar_Interpreter_CodePoint() { AExpression ROOT = PEGrammar.Load( @" (?<Value>): #x20; " ); String input = " "; var bytes = Encoding.UTF8.GetBytes(input); var iterator = new ByteInputIterator(bytes); var visitor = new NpegParserVisitor(iterator); ROOT.Accept(visitor); Assert.IsTrue(visitor.IsMatch); }
public void PEGrammar_Literal() { AExpression caseSensitive = PEGrammar.Load(@"(?<Expression>): 'Hello World';"); var input = "hello world"; var bytes = Encoding.UTF8.GetBytes(input); var iterator = new ByteInputIterator(bytes); var visitor = new NpegParserVisitor(iterator); caseSensitive.Accept(visitor); Assert.IsFalse(visitor.IsMatch); AExpression notCaseSensitive = PEGrammar.Load(@"(?<Expression>): 'Hello World'\i;"); input = "hello world"; bytes = Encoding.UTF8.GetBytes(input); iterator = new ByteInputIterator(bytes); visitor = new NpegParserVisitor(iterator); notCaseSensitive.Accept(visitor); Assert.IsTrue(visitor.IsMatch); AstNode node = visitor.AST; Assert.IsTrue(node.Token.Name == "Expression"); Assert.IsTrue(node.Token.ValueAsString(iterator) == input); // not sure if it would be better to use verbatim identifier @"" for escaping // escape back slash inside double quotes input = @"\"; AExpression escape = PEGrammar.Load(@"(?<Literal>): ""\\"";"); bytes = Encoding.UTF8.GetBytes(input); iterator = new ByteInputIterator(bytes); visitor = new NpegParserVisitor(iterator); escape.Accept(visitor); Assert.IsTrue(visitor.IsMatch); Assert.IsTrue(@"\" == visitor.AST.Token.ValueAsString(iterator)); input = @"\"; escape = PEGrammar.Load(@"(?<Literal>): '\\';"); bytes = Encoding.UTF8.GetBytes(input); iterator = new ByteInputIterator(bytes); visitor = new NpegParserVisitor(iterator); escape.Accept(visitor); Assert.IsTrue(visitor.IsMatch); Assert.IsTrue(@"\" == visitor.AST.Token.ValueAsString(iterator)); }
public EvaluationResult Evaluate(AExpression expression, int frameStart = -1) { if (frameStart == -1) { frameStart = _frames.Count - 1; } var facts = new Dictionary <ExpressionLeaf, EvaluationResult>(); var leafNodes = expression.GetLeafNodes(); foreach (var node in leafNodes) { var leaf = node.Leaf; var attr = leaf as KnowledgeAttribute; var rel = leaf as KnowledgeRelation; if (!(attr != null | rel != null)) { continue; } for (int i = frameStart; i >= 0; i--) { var frame = _frames[i]; var result = attr != null?frame.Evaluate(attr) : frame.Evaluate(rel); if (result == EvaluationResult.NotSure) { continue; } facts.Add(node, result); break; } } if (!facts.Any()) { return(EvaluationResult.NotSure); } if (facts.Values.Any(x => x == EvaluationResult.NotSure)) { return(EvaluationResult.NotSure); } return(expression.TransformEvaluation(facts)); }
public void PEGrammar_RecursiveParentheses() { var input = "((((((123))))))"; var bytes = Encoding.UTF8.GetBytes(input); AExpression ROOT = PEGrammar.Load( @" (?<DIGITS>): ([0-9])+; (?<ENCLOSEDDIGITS>): '(' ParethesisFunction ')'; ParethesisFunction: (DIGITS / ENCLOSEDDIGITS); (?<RECURSIONTEST>): ParethesisFunction; " .Trim()); var iterator = new ByteInputIterator(bytes); var visitor = new NpegParserVisitor(iterator); ROOT.Accept(visitor); Assert.IsTrue(visitor.IsMatch); AstNode node = visitor.AST; Assert.IsTrue(node.Token.ValueAsString(iterator) == input); }
public void PEGrammar_MathematicalFormula_Recursion() { AExpression ROOT = PEGrammar.Load( @" (?<Value>): [0-9]+ / '(' Expr ')'; (?<Product>): Value ((?<Symbol>'*' / '/') Value)*; (?<Sum>): Product ((?<Symbol>'+' / '-') Product)*; (?<Expr>): Sum; " ); String input = "((((12/3)+5-2*(81/9))+1))"; var bytes = Encoding.UTF8.GetBytes(input); var iterator = new ByteInputIterator(bytes); var visitor = new NpegParserVisitor(iterator); ROOT.Accept(visitor); Assert.IsTrue(visitor.IsMatch); AstNode node = visitor.AST; Assert.IsTrue(node.Token.ValueAsString(iterator) == input); #warning does not specify expected tree }
private AExpression RequireEndOfInput(AExpression expression) { return new Sequence(expression, new NotPredicate(new AnyCharacter())); }
public PrioritizedChoice(AExpression left, AExpression right) { this.left = left; this.right = right; }
public override void VisitLeave(AstNode node) { AExpression left; AExpression right; if (hasPassedNodeDefinition) { switch (node.Token.Name) { case "Statement": hasPassedNodeDefinition = false; var statement = (StatementAstNode)node; if (statement.IsCaptured) { var captureStatement = new CapturingGroup(statement.Name, expressionStack.Pop()); if ( statement.Children[0].Children[0].Children.Any(child => child.Token.Name == "OptionalFlags") && statement.Children[0].Children[0].Children[1].Children.Any(child => child.Token.Name == "ReplaceBySingleChild") ) { captureStatement.DoReplaceBySingleChildNode = true; // default is false } expression = captureStatement; } else { expression = expressionStack.Pop(); } // Assumes Terminals are at the top of the file and // final root non terminal expression is at the bottom. if (wrapWithRecursionRule.Contains(statement.Name)) { expression = new RecursionCreate(statement.Name, expression); } completedStatements.Add(statement.Name, expression); break; case "Sequence": var reverse = new Stack <AExpression>(); for (int i = 0; i < node.Children.Count; i++) { reverse.Push(expressionStack.Pop()); } Decimal sequence_cnt = (decimal)node.Children.Count - 1; for (; sequence_cnt > 0; sequence_cnt--) { left = reverse.Pop(); right = reverse.Pop(); reverse.Push( new Sequence(left, right) ); } expressionStack.Push(reverse.Pop()); break; case "PrioritizedChoice": Int32 cnt = node.Children.Count - 1; for (Int32 i = 0; i < cnt; i++) { right = expressionStack.Pop(); left = expressionStack.Pop(); expressionStack.Push( new PrioritizedChoice(left, right) ); } break; case "Prefix": switch (node.Token.ValueAsString(_inputIterator)[0].ToString()) { case "!": expressionStack.Push(new NotPredicate(expressionStack.Pop())); break; case "&": expressionStack.Push(new AndPredicate(expressionStack.Pop())); break; default: throw new Exception("Unsupported PEG Prefix."); } break; case "Suffix": switch (node.Children[0].Token.Name) { case "ZeroOrMore": expressionStack.Push(new ZeroOrMore(expressionStack.Pop())); break; case "OneOrMore": expressionStack.Push(new OneOrMore(expressionStack.Pop())); break; case "Optional": expressionStack.Push(new Optional(expressionStack.Pop())); break; case "LimitingRepetition": switch (node.Children[0].Children[1].Token.Name) { case "BETWEEN": expressionStack.Push(new LimitingRepetition(expressionStack.Pop()) { Min = Int32.Parse( node.Children[0].Children[1].Children[0]. Token.ValueAsString(_inputIterator)), Max = Int32.Parse( node.Children[0].Children[1].Children[1]. Token.ValueAsString(_inputIterator)) }); break; case "ATMOST": expressionStack.Push(new LimitingRepetition(expressionStack.Pop()) { Min = null, Max = Int32.Parse(node.Children[0].Children[1].Children[0].Token .ValueAsString(_inputIterator)) }); break; case "ATLEAST": expressionStack.Push(new LimitingRepetition(expressionStack.Pop()) { Min = Int32.Parse(node.Children[0].Children[1].Children[0]. Token.ValueAsString(_inputIterator)), Max = null }); break; case "EXACT": Int32 exactcount = Int32.Parse(node.Children[0].Children[1].Token.ValueAsString(_inputIterator)); expressionStack.Push(new LimitingRepetition(expressionStack.Pop()) { Min = exactcount, Max = exactcount }); break; case "VariableLength": var variableLengthExpression = node.Children[0].Children[1].Token.ValueAsString(_inputIterator); expressionStack.Push(new LimitingRepetition(expressionStack.Pop()) { VariableLengthExpression = variableLengthExpression }); break; } break; default: throw new Exception("Unsupported PEG Suffix."); } break; case "CapturingGroup": var capture = new CapturingGroup(node.Children[0].Token.ValueAsString(_inputIterator), expressionStack.Pop()); if (node.Children.Any(child => child.Token.Name == "OptionalFlags")) { if (node.Children[1].Children.Any(child => child.Token.Name == "ReplaceBySingleChild")) { capture.DoReplaceBySingleChildNode = true; // default is false } if (node.Children[1].Children.Any(child => child.Token.Name == "ReplacementNode")) { capture.DoCreateCustomAstNode = true; // default is false } } expressionStack.Push(capture); break; case "Group": break; case "AnyCharacter": expressionStack.Push(new AnyCharacter()); break; case "Literal": Boolean isCaseSensitive = true; if (node.Children.Count == 2) { isCaseSensitive = false; } expressionStack.Push(new Literal { IsCaseSensitive = isCaseSensitive, MatchText = Regex.Replace( Regex.Replace(node.Children[0].Token.ValueAsString(_inputIterator), @"\\(?<quote>""|')", @"${quote}") , @"\\\\", @"\") }); break; case "CharacterClass": expressionStack.Push(new CharacterClass { ClassExpression = node.Token.ValueAsString(_inputIterator) }); break; case "RecursionCall": expressionStack.Push((this)[node.Children[0].Token.ValueAsString(_inputIterator)]); break; case "CodePoint": expressionStack.Push(new CodePoint { Match = "#" + node.Children[0].Token.ValueAsString(_inputIterator) }); break; case "Fatal": expressionStack.Push(new Fatal { Message = node.Children[0].Token.ValueAsString(_inputIterator) }); break; case "Warn": expressionStack.Push(new Warn { Message = node.Children[0].Token.ValueAsString(_inputIterator) }); break; case "DynamicBackReferencing": if (node.Children.Count == 1) { // no options specified only tag name. expressionStack.Push(new DynamicBackReference { BackReferenceName = node.Children[0].Token.ValueAsString(_inputIterator) }); } else { throw new NotImplementedException( "Add IsCaseSensitive using children[1].Token.Name == IsCasesensitive"); } break; } } if (node.Token.Name == "NodeDefinition") { hasPassedNodeDefinition = true; } }
public IfStatement(AExpression expr, IStatement thenStatement, IStatement elseStatement) { this.expr = expr; this.thenStatement = thenStatement; this.elseStatement = elseStatement; }
public AssignmentStatement(string id, AExpression expr) { this.id = id; this.expr = expr; }
public ZeroOrMore(AExpression exp) { Exp = exp; }
public NotPredicate(AExpression exp) { Exp = exp; }
public OneOrMore(AExpression exp) { Exp = exp; }
public ReadFileStatement(AExpression var_file_id, string var_name) { this.var_file_id = var_file_id; this.var_name = var_name; }
private AExpression WrapInCapturedGroup(String groupname, AExpression expression) { return new CapturingGroup(groupname, expression); }
public override void VisitLeave(AstNode node) { AExpression left; AExpression right; if (hasPassedNodeDefinition) { switch (node.Token.Name) { case "Statement": hasPassedNodeDefinition = false; var statement = (StatementAstNode) node; if (statement.IsCaptured) { var captureStatement = new CapturingGroup(statement.Name, expressionStack.Pop()); if ( statement.Children[0].Children[0].Children.Any(child => child.Token.Name == "OptionalFlags") && statement.Children[0].Children[0].Children[1].Children.Any(child => child.Token.Name == "ReplaceBySingleChild") ) { captureStatement.DoReplaceBySingleChildNode = true; // default is false } expression = captureStatement; } else { expression = expressionStack.Pop(); } // Assumes Terminals are at the top of the file and // final root non terminal expression is at the bottom. if (wrapWithRecursionRule.Contains(statement.Name)) { expression = new RecursionCreate(statement.Name, expression); } completedStatements.Add(statement.Name, expression); break; case "Sequence": var reverse = new Stack<AExpression>(); for (int i = 0; i < node.Children.Count; i++) { reverse.Push(expressionStack.Pop()); } Decimal sequence_cnt = (decimal) node.Children.Count - 1; for (; sequence_cnt > 0; sequence_cnt--) { left = reverse.Pop(); right = reverse.Pop(); reverse.Push( new Sequence(left, right) ); } expressionStack.Push(reverse.Pop()); break; case "PrioritizedChoice": Int32 cnt = node.Children.Count - 1; for (Int32 i = 0; i < cnt; i++) { right = expressionStack.Pop(); left = expressionStack.Pop(); expressionStack.Push( new PrioritizedChoice(left, right) ); } break; case "Prefix": switch (node.Token.ValueAsString(_inputIterator)[0].ToString()) { case "!": expressionStack.Push(new NotPredicate(expressionStack.Pop())); break; case "&": expressionStack.Push(new AndPredicate(expressionStack.Pop())); break; default: throw new Exception("Unsupported PEG Prefix."); } break; case "Suffix": switch (node.Children[0].Token.Name) { case "ZeroOrMore": expressionStack.Push(new ZeroOrMore(expressionStack.Pop())); break; case "OneOrMore": expressionStack.Push(new OneOrMore(expressionStack.Pop())); break; case "Optional": expressionStack.Push(new Optional(expressionStack.Pop())); break; case "LimitingRepetition": switch (node.Children[0].Children[1].Token.Name) { case "BETWEEN": expressionStack.Push(new LimitingRepetition(expressionStack.Pop()) { Min = Int32.Parse( node.Children[0].Children[1].Children[0]. Token.ValueAsString(_inputIterator)), Max = Int32.Parse( node.Children[0].Children[1].Children[1]. Token.ValueAsString(_inputIterator)) }); break; case "ATMOST": expressionStack.Push(new LimitingRepetition(expressionStack.Pop()) { Min = null, Max = Int32.Parse(node.Children[0].Children[1].Children[0].Token .ValueAsString(_inputIterator)) }); break; case "ATLEAST": expressionStack.Push(new LimitingRepetition(expressionStack.Pop()) { Min = Int32.Parse(node.Children[0].Children[1].Children[0]. Token.ValueAsString(_inputIterator)), Max = null }); break; case "EXACT": Int32 exactcount = Int32.Parse(node.Children[0].Children[1].Token.ValueAsString(_inputIterator)); expressionStack.Push(new LimitingRepetition(expressionStack.Pop()) {Min = exactcount, Max = exactcount}); break; case "VariableLength": var variableLengthExpression = node.Children[0].Children[1].Token.ValueAsString(_inputIterator); expressionStack.Push(new LimitingRepetition(expressionStack.Pop()) { VariableLengthExpression = variableLengthExpression }); break; } break; default: throw new Exception("Unsupported PEG Suffix."); } break; case "CapturingGroup": var capture = new CapturingGroup(node.Children[0].Token.ValueAsString(_inputIterator), expressionStack.Pop()); if (node.Children.Any(child => child.Token.Name == "OptionalFlags")) { if (node.Children[1].Children.Any(child => child.Token.Name == "ReplaceBySingleChild")) { capture.DoReplaceBySingleChildNode = true; // default is false } if (node.Children[1].Children.Any(child => child.Token.Name == "ReplacementNode")) { capture.DoCreateCustomAstNode = true; // default is false } } expressionStack.Push(capture); break; case "Group": break; case "AnyCharacter": expressionStack.Push(new AnyCharacter()); break; case "Literal": Boolean isCaseSensitive = true; if (node.Children.Count == 2) isCaseSensitive = false; expressionStack.Push(new Literal { IsCaseSensitive = isCaseSensitive, MatchText = Regex.Replace( Regex.Replace(node.Children[0].Token.ValueAsString(_inputIterator), @"\\(?<quote>""|')", @"${quote}") , @"\\\\", @"\") }); break; case "CharacterClass": expressionStack.Push(new CharacterClass { ClassExpression = node.Token.ValueAsString(_inputIterator) }); break; case "RecursionCall": expressionStack.Push((this)[node.Children[0].Token.ValueAsString(_inputIterator)]); break; case "CodePoint": expressionStack.Push(new CodePoint { Match = "#" + node.Children[0].Token.ValueAsString(_inputIterator) }); break; case "Fatal": expressionStack.Push(new Fatal { Message = node.Children[0].Token.ValueAsString(_inputIterator) }); break; case "Warn": expressionStack.Push(new Warn { Message = node.Children[0].Token.ValueAsString(_inputIterator) }); break; case "DynamicBackReferencing": if (node.Children.Count == 1) { // no options specified only tag name. expressionStack.Push(new DynamicBackReference { BackReferenceName = node.Children[0].Token.ValueAsString(_inputIterator) }); } else { throw new NotImplementedException( "Add IsCaseSensitive using children[1].Token.Name == IsCasesensitive"); } break; } } if (node.Token.Name == "NodeDefinition") { hasPassedNodeDefinition = true; } }
public Optional(AExpression exp) { Exp = exp; }
public AndPredicate(AExpression exp) { this.exp = exp; }
public PrintStatement(AExpression expr) { this.expr = expr; }
public void PEGrammar_BooleanAlgebra() { String grammar = @" S: [\s]+; (?<Gate>): ('*' / 'AND') / ('~*' / 'NAND') / ('+' / 'OR') / ('~+' / 'NOR') / ('^' / 'XOR') / ('~^' / 'XNOR'); ValidVariable: '""' (?<Variable>[a-zA-Z0-9]+) '""' / '\'' (?<Variable>[a-zA-Z0-9]+) '\'' / (?<Variable>[a-zA-Z]); VarProjection1: ValidVariable / (?<Invertor>'!' ValidVariable); VarProjection2: VarProjection1 / '(' Expression ')' / (?<Invertor>'!' '(' Expression ')'); Expression: S? VarProjection2 S? (Gate S? VarProjection2 S?)*; (?<BooleanEquation>): Expression !.; " .Trim(); AExpression ROOT = PEGrammar.Load(grammar); // single variable var input = ("A*!B+!A*B"); var bytes = Encoding.UTF8.GetBytes(input); var iterator = new ByteInputIterator(bytes); var visitor = new NpegParserVisitor(iterator); ROOT.Accept(visitor); Assert.IsTrue(visitor.IsMatch); AstNode node = visitor.AST; #warning Assert.IsTrue(node.Token.Value == input); // quoted variable input = ("'aA'*!'bB'+!'aA'*'bB'"); bytes = Encoding.UTF8.GetBytes(input); iterator = new ByteInputIterator(bytes); visitor = new NpegParserVisitor(iterator); ROOT.Accept(visitor); Assert.IsTrue(visitor.IsMatch); node = visitor.AST; #warning Assert.IsTrue(node.Token.Value == input); // expression + gate + variable .star() input = ("A*!B*C+!A*B*C"); bytes = Encoding.UTF8.GetBytes(input); iterator = new ByteInputIterator(bytes); visitor = new NpegParserVisitor(iterator); ROOT.Accept(visitor); Assert.IsTrue(visitor.IsMatch); node = visitor.AST; #warning Assert.IsTrue(node.Token.Value == input); // parethesis input = ("((A)*(!B)+(!A)*(B))"); bytes = Encoding.UTF8.GetBytes(input); iterator = new ByteInputIterator(bytes); visitor = new NpegParserVisitor(iterator); ROOT.Accept(visitor); Assert.IsTrue(visitor.IsMatch); node = visitor.AST; #warning Assert.IsTrue(node.Token.Value == input); input = ("((A)*!(B)+!(A)*(B))"); bytes = Encoding.UTF8.GetBytes(input); iterator = new ByteInputIterator(bytes); visitor = new NpegParserVisitor(iterator); ROOT.Accept(visitor); Assert.IsTrue(visitor.IsMatch); node = visitor.AST; #warning Assert.IsTrue(node.Token.Value == input); input = ("((A)*(!(B))+(!(A))*(B))"); bytes = Encoding.UTF8.GetBytes(input); iterator = new ByteInputIterator(bytes); visitor = new NpegParserVisitor(iterator); ROOT.Accept(visitor); Assert.IsTrue(visitor.IsMatch); node = visitor.AST; #warning Assert.IsTrue(node.Token.Value == input); input = ("(!X*Y*!Z)"); bytes = Encoding.UTF8.GetBytes(input); iterator = new ByteInputIterator(bytes); visitor = new NpegParserVisitor(iterator); ROOT.Accept(visitor); Assert.IsTrue(visitor.IsMatch); node = visitor.AST; #warning Assert.IsTrue(node.Token.Value == input); input = ("(!X*Y*!Z)+(!X*Y*Z)"); bytes = Encoding.UTF8.GetBytes(input); iterator = new ByteInputIterator(bytes); visitor = new NpegParserVisitor(iterator); ROOT.Accept(visitor); Assert.IsTrue(visitor.IsMatch); node = visitor.AST; #warning Assert.IsTrue(node.Token.Value == input); input = ("(X*Z)"); bytes = Encoding.UTF8.GetBytes(input); iterator = new ByteInputIterator(bytes); visitor = new NpegParserVisitor(iterator); ROOT.Accept(visitor); Assert.IsTrue(visitor.IsMatch); node = visitor.AST; #warning Assert.IsTrue(node.Token.Value == input); input = ("(!X*Y*!Z)+(!X*Y*Z)+(X*Z)"); bytes = Encoding.UTF8.GetBytes(input); iterator = new ByteInputIterator(bytes); visitor = new NpegParserVisitor(iterator); ROOT.Accept(visitor); Assert.IsTrue(visitor.IsMatch); node = visitor.AST; #warning Assert.IsTrue(node.Token.Value == input); input = ("((((!X*Y*Z)+(!X*Y*!Z)+(X*Z))))"); bytes = Encoding.UTF8.GetBytes(input); iterator = new ByteInputIterator(bytes); visitor = new NpegParserVisitor(iterator); ROOT.Accept(visitor); Assert.IsTrue(visitor.IsMatch); node = visitor.AST; #warning Assert.IsTrue(node.Token.Value == input); }
public Sequence(AExpression left, AExpression right) { this.left = left; this.right = right; }
public CloseRFileStatement(AExpression var_file_id) { this.var_file_id = var_file_id; }
public RelativeAll(AExpression expression) { _expression = expression; }
public RecursionCreate(String unique, AExpression exp) { FunctionName = unique; this.exp = exp; }
private AExpression OneOrMore(AExpression expression) { return new OneOrMore(expression); }
public RelativeBefore(AExpression left, AExpression right) { _left = left; _right = right; }