/// <summary> /// Gets a null denotation token for given token using the null denotation getter. /// </summary> /// <param name="self"></param> /// <returns></returns> public Token GetNullDenotation(Token self) { if (this.NullDenotationGetter == null) throw new InvalidOperationException("Unable to invoke null denotation getter: getter is not set."); if (self.Type != this.TokenType) throw new ArgumentException("Unable to invoke null denotation getter: invalid self type.", "self"); return this.NullDenotationGetter(self); }
/// <summary> /// Initializes a new instance of the <see cref="Token"/> class with given NumPlurals and abstract syntax tree. /// </summary> /// <param name="numPlurals"></param> /// <param name="astRoot">Abstract syntax tree root.</param> public AstPluralRule(int numPlurals, Token astRoot) { if (numPlurals <= 0) throw new ArgumentOutOfRangeException("numPlurals"); if (astRoot == null) throw new ArgumentNullException("astRoot"); this.NumPlurals = numPlurals; this.AstRoot = astRoot; }
/// <summary> /// Compiles a plural rule abstract syntax tree into managed dynamic method delegate using an IL code generator. /// </summary> /// <param name="astRoot">abstract syntax tree root node.</param> /// <param name="outputDelegateType">Type of output delegate.</param> /// <returns>Compiled dynamic method of given type.</returns> public virtual Delegate CompileToDynamicMethod(Token astRoot, Type outputDelegateType) { var dynamicMethod = this.CreateDynamicMethod(outputDelegateType); var il = dynamicMethod.GetILGenerator(); this.CompileStart(il); this.CompileNode(il, astRoot); this.CompileFinish(il); return dynamicMethod.CreateDelegate(outputDelegateType); }
/// <summary> /// Recursively compiles an AST node to the IL instructions. /// </summary> /// <param name="il">IL generator instance.</param> /// <param name="node">AST node.</param> protected virtual void CompileNode(ILGenerator il, Token node) { switch (node.Type) { case TokenType.Number: il.Emit(OpCodes.Ldc_I8, node.Value); break; case TokenType.N: il.Emit(OpCodes.Ldarg_0); break; case TokenType.Plus: this.CompileNode(il, node.Children[0]); this.CompileNode(il, node.Children[1]); il.Emit(OpCodes.Add); break; case TokenType.Minus: this.CompileNode(il, node.Children[0]); this.CompileNode(il, node.Children[1]); il.Emit(OpCodes.Sub); break; case TokenType.Divide: this.CompileNode(il, node.Children[0]); this.CompileNode(il, node.Children[1]); il.Emit(OpCodes.Div); break; case TokenType.Multiply: this.CompileNode(il, node.Children[0]); this.CompileNode(il, node.Children[1]); il.Emit(OpCodes.Mul); break; case TokenType.Modulo: this.CompileNode(il, node.Children[0]); this.CompileNode(il, node.Children[1]); il.Emit(OpCodes.Rem); break; case TokenType.GreaterThan: this.CompileNode(il, node.Children[0]); this.CompileNode(il, node.Children[1]); this.EmitConditionalValue(il, OpCodes.Bgt); break; case TokenType.GreaterOrEquals: this.CompileNode(il, node.Children[0]); this.CompileNode(il, node.Children[1]); this.EmitConditionalValue(il, OpCodes.Bge); break; case TokenType.LessThan: this.CompileNode(il, node.Children[0]); this.CompileNode(il, node.Children[1]); this.EmitConditionalValue(il, OpCodes.Blt); break; case TokenType.LessOrEquals: this.CompileNode(il, node.Children[0]); this.CompileNode(il, node.Children[1]); this.EmitConditionalValue(il, OpCodes.Ble); break; case TokenType.Equals: this.CompileNode(il, node.Children[0]); this.CompileNode(il, node.Children[1]); this.EmitConditionalValue(il, OpCodes.Beq); break; case TokenType.NotEquals: this.CompileNode(il, node.Children[0]); this.CompileNode(il, node.Children[1]); this.EmitConditionalValue(il, OpCodes.Beq, 0, 1); break; case TokenType.And: this.CompileNode(il, node.Children[0]); this.CompileNode(il, node.Children[1]); il.Emit(OpCodes.And); break; case TokenType.Or: this.CompileNode(il, node.Children[0]); this.CompileNode(il, node.Children[1]); il.Emit(OpCodes.Or); break; case TokenType.Not: this.CompileNode(il, node.Children[0]); this.EmitConditionalValue(il, OpCodes.Brfalse); break; case TokenType.TernaryIf: this.CompileNode(il, node.Children[0]); this.EmitConditionalBranch(il, OpCodes.Brtrue, node.Children[1], node.Children[2]); break; } }
/// <summary> /// Emits instructions required for conditional branch using given OpCode to check condition and given nodes for positive and negative branches. /// </summary> /// <param name="il">IL generator instance.</param> /// <param name="conditionOpCode">OpCode of the condition check operation.</param> /// <param name="trueNode">AST node that will be executed when condition returns positive result.</param> /// <param name="falseNode">AST node that will be executed when condition returns negative result.</param> protected virtual void EmitConditionalBranch(ILGenerator il, OpCode conditionOpCode, Token trueNode, Token falseNode) { var trueLabel = il.DefineLabel(); var endLabel = il.DefineLabel(); il.Emit(conditionOpCode, trueLabel); this.CompileNode(il, falseNode); il.Emit(OpCodes.Br, endLabel); il.MarkLabel(trueLabel); this.CompileNode(il, trueNode); il.MarkLabel(endLabel); }
protected long Evaluate(Token node, long number) { switch (node.Type) { case TokenType.Number: return node.Value; case TokenType.N: return number; case TokenType.Plus: return this.Evaluate(node.Children[0], number) + this.Evaluate(node.Children[1], number); case TokenType.Minus: return this.Evaluate(node.Children[0], number) - this.Evaluate(node.Children[1], number); case TokenType.Divide: return this.Evaluate(node.Children[0], number) / this.Evaluate(node.Children[1], number); case TokenType.Multiply: return this.Evaluate(node.Children[0], number) * this.Evaluate(node.Children[1], number); case TokenType.Modulo: return this.Evaluate(node.Children[0], number) % this.Evaluate(node.Children[1], number); case TokenType.GreaterThan: return this.Evaluate(node.Children[0], number) > this.Evaluate(node.Children[1], number) ? 1 : 0; case TokenType.GreaterOrEquals: return this.Evaluate(node.Children[0], number) >= this.Evaluate(node.Children[1], number) ? 1 : 0; case TokenType.LessThan: return this.Evaluate(node.Children[0], number) < this.Evaluate(node.Children[1], number) ? 1 : 0; case TokenType.LessOrEquals: return this.Evaluate(node.Children[0], number) <= this.Evaluate(node.Children[1], number) ? 1 : 0; case TokenType.Equals: return this.Evaluate(node.Children[0], number) == this.Evaluate(node.Children[1], number) ? 1 : 0; case TokenType.NotEquals: return this.Evaluate(node.Children[0], number) != this.Evaluate(node.Children[1], number) ? 1 : 0; case TokenType.And: return this.Evaluate(node.Children[0], number) != 0 && this.Evaluate(node.Children[1], number) != 0 ? 1 : 0; case TokenType.Or: return this.Evaluate(node.Children[0], number) != 0 || this.Evaluate(node.Children[1], number) != 0 ? 1 : 0; case TokenType.Not: return this.Evaluate(node.Children[0], number) == 0 ? 1 : 0; case TokenType.TernaryIf: return this.Evaluate(node.Children[0], number) != 0 ? this.Evaluate(node.Children[1], number) : this.Evaluate(node.Children[2], number); default: throw new InvalidOperationException(String.Format("Can not evaluate token: {0}.", node.Type)); } }
/// <summary> /// Parses the input string that contains a plural rule formula and generates an abstract syntax tree. /// </summary> /// <param name="input">Input string.</param> /// <returns>Root node of the abstract syntax tree.</returns> public Token Parse(string input) { this.Input = input + "\0"; this.Position = 0; this.CurrentToken = this.GetNextToken(); return this.ParseNextExpression(); }
protected Token ParseNextExpression(int rightBindingPower = 0) { var token = this.CurrentToken; this.CurrentToken = this.GetNextToken(); var left = this.GetDefinition(token.Type).GetNullDenotation(token); while (rightBindingPower < this.GetDefinition(this.CurrentToken.Type).LeftBindingPower) { token = this.CurrentToken; this.CurrentToken = this.GetNextToken(); left = this.GetDefinition(token.Type).GetLeftDenotation(token, left); } return left; }
protected void AdvancePosition() { this.CurrentToken = this.GetNextToken(); }