You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucenenet.apache.org by sy...@apache.org on 2015/01/31 21:03:53 UTC
[5/5] lucenenet git commit: Adding Expressions with some failing tests
Adding Expressions with some failing tests
Thanks to work by @hakeemsm
Closes #64
Project: http://git-wip-us.apache.org/repos/asf/lucenenet/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucenenet/commit/b6c1b5d2
Tree: http://git-wip-us.apache.org/repos/asf/lucenenet/tree/b6c1b5d2
Diff: http://git-wip-us.apache.org/repos/asf/lucenenet/diff/b6c1b5d2
Branch: refs/heads/master
Commit: b6c1b5d2e29185e167f37f6f86c711913fe6f0b5
Parents: 69f2911
Author: Itamar Syn-Hershko <it...@code972.com>
Authored: Sat Jan 31 22:03:24 2015 +0200
Committer: Itamar Syn-Hershko <it...@code972.com>
Committed: Sat Jan 31 22:03:24 2015 +0200
----------------------------------------------------------------------
Lucene.Net.sln | 26 +-
src/Lucene.Net.Expressions/Bindings.cs | 38 +
src/Lucene.Net.Expressions/Expression.cs | 98 +
.../ExpressionComparator.cs | 94 +
.../ExpressionFunctionValues.cs | 48 +
.../ExpressionRescorer.cs | 120 +
.../ExpressionSortField.cs | 86 +
.../ExpressionValueSource.cs | 169 ++
.../JS/JavascriptCompiler.cs | 749 ++++++
.../JS/JavascriptLexer.cs | 2177 ++++++++++++++++++
.../JS/JavascriptParser.cs | 1879 +++++++++++++++
.../Lucene.Net.Expressions.csproj | 92 +
.../Properties/AssemblyInfo.cs | 36 +
.../Properties/Settings.Designer.cs | 323 +++
.../Properties/Settings.settings | 81 +
.../ScoreFunctionValues.cs | 34 +
src/Lucene.Net.Expressions/ScoreValueSource.cs | 59 +
src/Lucene.Net.Expressions/SimpleBindings.cs | 130 ++
src/Lucene.Net.Expressions/app.config | 94 +
src/Lucene.Net.Expressions/packages.config | 4 +
.../JS/TestCustomFunctions.cs | 262 +++
.../JS/TestJavascriptCompiler.cs | 187 ++
.../JS/TestJavascriptFunction.cs | 309 +++
.../JS/TestJavascriptOperations.cs | 373 +++
.../Lucene.Net.Tests.Expressions.csproj | 84 +
.../Properties/AssemblyInfo.cs | 36 +
.../TestDemoExpressions.cs | 202 ++
.../TestExpressionRescorer.cs | 92 +
.../TestExpressionSortField.cs | 92 +
.../TestExpressionSorts.cs | 151 ++
.../TestExpressionValidation.cs | 151 ++
.../TestExpressionValueSource.cs | 157 ++
32 files changed, 8432 insertions(+), 1 deletion(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/lucenenet/blob/b6c1b5d2/Lucene.Net.sln
----------------------------------------------------------------------
diff --git a/Lucene.Net.sln b/Lucene.Net.sln
index 02dee4a..8fe7746 100644
--- a/Lucene.Net.sln
+++ b/Lucene.Net.sln
@@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2013
-VisualStudioVersion = 12.0.30110.0
+VisualStudioVersion = 12.0.30501.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lucene.Net", "src\Lucene.Net.Core\Lucene.Net.csproj", "{5D4AD9BE-1FFB-41AB-9943-25737971BF57}"
EndProject
@@ -28,6 +28,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lucene.Net.Tests.Classifica
{69D7956C-C2CC-4708-B399-A188FEC384C4} = {69D7956C-C2CC-4708-B399-A188FEC384C4}
EndProjectSection
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lucene.Net.Expressions", "src\Lucene.Net.Expressions\Lucene.Net.Expressions.csproj", "{DC83004C-183A-4E1A-ABEA-4FE95B4BC079}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lucene.Net.Tests.Expressions", "src\Lucene.Net.Tests.Expressions\Lucene.Net.Tests.Expressions.csproj", "{F4873D95-4300-4E83-AFFA-EF796495D0F0}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -119,6 +123,26 @@ Global
{866723F4-E3A4-47C5-A49F-9A68ADD4CFAE}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{866723F4-E3A4-47C5-A49F-9A68ADD4CFAE}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{866723F4-E3A4-47C5-A49F-9A68ADD4CFAE}.Release|x86.ActiveCfg = Release|Any CPU
+ {DC83004C-183A-4E1A-ABEA-4FE95B4BC079}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {DC83004C-183A-4E1A-ABEA-4FE95B4BC079}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {DC83004C-183A-4E1A-ABEA-4FE95B4BC079}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {DC83004C-183A-4E1A-ABEA-4FE95B4BC079}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {DC83004C-183A-4E1A-ABEA-4FE95B4BC079}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {DC83004C-183A-4E1A-ABEA-4FE95B4BC079}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {DC83004C-183A-4E1A-ABEA-4FE95B4BC079}.Release|Any CPU.Build.0 = Release|Any CPU
+ {DC83004C-183A-4E1A-ABEA-4FE95B4BC079}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {DC83004C-183A-4E1A-ABEA-4FE95B4BC079}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {DC83004C-183A-4E1A-ABEA-4FE95B4BC079}.Release|x86.ActiveCfg = Release|Any CPU
+ {F4873D95-4300-4E83-AFFA-EF796495D0F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F4873D95-4300-4E83-AFFA-EF796495D0F0}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F4873D95-4300-4E83-AFFA-EF796495D0F0}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {F4873D95-4300-4E83-AFFA-EF796495D0F0}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {F4873D95-4300-4E83-AFFA-EF796495D0F0}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {F4873D95-4300-4E83-AFFA-EF796495D0F0}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F4873D95-4300-4E83-AFFA-EF796495D0F0}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F4873D95-4300-4E83-AFFA-EF796495D0F0}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {F4873D95-4300-4E83-AFFA-EF796495D0F0}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {F4873D95-4300-4E83-AFFA-EF796495D0F0}.Release|x86.ActiveCfg = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
http://git-wip-us.apache.org/repos/asf/lucenenet/blob/b6c1b5d2/src/Lucene.Net.Expressions/Bindings.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Expressions/Bindings.cs b/src/Lucene.Net.Expressions/Bindings.cs
new file mode 100644
index 0000000..c299f15
--- /dev/null
+++ b/src/Lucene.Net.Expressions/Bindings.cs
@@ -0,0 +1,38 @@
+using Lucene.Net.Queries.Function;
+
+namespace Lucene.Net.Expressions
+{
+ /// <summary>Binds variable names in expressions to actual data.</summary>
+ /// <remarks>
+ /// Binds variable names in expressions to actual data.
+ /// <p>
+ /// These are typically DocValues fields/FieldCache, the document's
+ /// relevance score, or other ValueSources.
+ /// </remarks>
+ /// <lucene.experimental></lucene.experimental>
+ public abstract class Bindings
+ {
+ /// <summary>Sole constructor.</summary>
+ /// <remarks>
+ /// Sole constructor. (For invocation by subclass
+ /// constructors, typically implicit.)
+ /// </remarks>
+ public Bindings()
+ {
+ }
+
+ /// <summary>Returns a ValueSource bound to the variable name.</summary>
+
+ public abstract ValueSource GetValueSource(string name);
+
+ /// <summary>
+ /// Returns a
+ /// <code>ValueSource</code>
+ /// over relevance scores
+ /// </summary>
+ protected internal ValueSource GetScoreValueSource()
+ {
+ return new ScoreValueSource();
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/lucenenet/blob/b6c1b5d2/src/Lucene.Net.Expressions/Expression.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Expressions/Expression.cs b/src/Lucene.Net.Expressions/Expression.cs
new file mode 100644
index 0000000..cd33d92
--- /dev/null
+++ b/src/Lucene.Net.Expressions/Expression.cs
@@ -0,0 +1,98 @@
+using Lucene.Net.Queries.Function;
+using Lucene.Net.Search;
+
+namespace Lucene.Net.Expressions
+{
+ /// <summary>Base class that computes the value of an expression for a document.</summary>
+ /// <remarks>
+ /// Base class that computes the value of an expression for a document.
+ /// <p>
+ /// Example usage:
+ /// <pre class="prettyprint">
+ /// // compile an expression:
+ /// Expression expr = JavascriptCompiler.compile("sqrt(_score) + ln(popularity)");
+ /// // SimpleBindings just maps variables to SortField instances
+ /// SimpleBindings bindings = new SimpleBindings();
+ /// bindings.add(new SortField("_score", SortField.Type.SCORE));
+ /// bindings.add(new SortField("popularity", SortField.Type.INT));
+ /// // create a sort field and sort by it (reverse order)
+ /// Sort sort = new Sort(expr.getSortField(bindings, true));
+ /// Query query = new TermQuery(new Term("body", "contents"));
+ /// searcher.search(query, null, 10, sort);
+ /// </pre>
+ /// </remarks>
+ /// <seealso cref="Lucene.Net.Expressions.JS.JavascriptCompiler.Compile(string)">Lucene.Net.Expressions.JS.JavascriptCompiler.Compile(string)</seealso>
+ /// <lucene.experimental></lucene.experimental>
+ public abstract class Expression
+ {
+ /// <summary>The original source text</summary>
+ public readonly string sourceText;
+
+ /// <summary>Named variables referred to by this expression</summary>
+ public readonly string[] variables;
+
+ /// <summary>
+ /// Creates a new
+ /// <code>Expression</code>
+ /// .
+ /// </summary>
+ /// <param name="sourceText">
+ /// Source text for the expression: e.g.
+ /// <code>ln(popularity)</code>
+ /// </param>
+ /// <param name="variables">
+ /// Names of external variables referred to by the expression
+ /// </param>
+ public Expression(string sourceText, string[] variables)
+ {
+ // javadocs
+ this.sourceText = sourceText;
+ this.variables = variables;
+ }
+
+ /// <summary>Evaluates the expression for the given document.</summary>
+ /// <remarks>Evaluates the expression for the given document.</remarks>
+ /// <param name="document"><code>docId</code> of the document to compute a value for</param>
+ /// <param name="functionValues">
+ ///
+ /// <see cref="Lucene.Net.Queries.Function.FunctionValues">Lucene.Net.Queries.Function.FunctionValues
+ /// </see>
+ /// for each element of
+ /// <see cref="variables">variables</see>
+ /// .
+ /// </param>
+ /// <returns>The computed value of the expression for the given document.</returns>
+ public abstract double Evaluate(int document, FunctionValues[] functionValues);
+
+ /// <summary>Get a value source which can compute the value of this expression in the context of the given bindings.
+ /// </summary>
+ /// <remarks>Get a value source which can compute the value of this expression in the context of the given bindings.
+ /// </remarks>
+ /// <param name="bindings">Bindings to use for external values in this expression</param>
+ /// <returns>A value source which will evaluate this expression when used</returns>
+ public virtual ValueSource GetValueSource(Bindings bindings)
+ {
+ return new ExpressionValueSource(bindings, this);
+ }
+
+ /// <summary>Get a sort field which can be used to rank documents by this expression.
+ /// </summary>
+ /// <remarks>Get a sort field which can be used to rank documents by this expression.
+ /// </remarks>
+ public virtual SortField GetSortField(Bindings bindings, bool reverse)
+ {
+ return GetValueSource(bindings).GetSortField(reverse);
+ }
+
+ /// <summary>
+ /// Get a
+ /// <see cref="Lucene.Net.Search.Rescorer">Lucene.Net.Search.Rescorer</see>
+ /// , to rescore first-pass hits
+ /// using this expression.
+ /// </summary>
+ public virtual Rescorer GetRescorer(Bindings bindings)
+ {
+ return new ExpressionRescorer(this, bindings);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/lucenenet/blob/b6c1b5d2/src/Lucene.Net.Expressions/ExpressionComparator.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Expressions/ExpressionComparator.cs b/src/Lucene.Net.Expressions/ExpressionComparator.cs
new file mode 100644
index 0000000..2f67621
--- /dev/null
+++ b/src/Lucene.Net.Expressions/ExpressionComparator.cs
@@ -0,0 +1,94 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using Lucene.Net.Index;
+using Lucene.Net.Queries.Function;
+using Lucene.Net.Search;
+using Lucene.Net.Support;
+
+namespace Lucene.Net.Expressions
+{
+ /// <summary>A custom comparator for sorting documents by an expression</summary>
+ internal class ExpressionComparator : FieldComparator<double>
+ {
+ private readonly double[] values;
+
+ private double bottom;
+
+ private double topValue;
+
+ private ValueSource source;
+
+ private FunctionValues scores;
+
+ private AtomicReaderContext readerContext;
+
+ public ExpressionComparator(ValueSource source, int numHits)
+ {
+ values = new double[numHits];
+ this.source = source;
+ }
+
+ // TODO: change FieldComparator.setScorer to throw IOException and remove this try-catch
+ public override Scorer Scorer
+ {
+ set
+ {
+ base.Scorer = value;
+ // TODO: might be cleaner to lazy-init 'source' and set scorer after?
+
+ Debug.Assert(readerContext != null);
+ var context = new Dictionary<string, object>();
+ Debug.Assert(value != null);
+ context["scorer"] = value;
+ scores = source.GetValues(context, readerContext);
+ }
+ }
+
+ public override int Compare(int slot1, int slot2)
+ {
+ return values[slot1].CompareTo(values[slot2]);
+ }
+
+ public override int Bottom
+ {
+ set { bottom = values[value]; }
+ }
+
+ public override object TopValue
+ {
+ set { topValue = (double)value; }
+ }
+
+
+ public override int CompareBottom(int doc)
+ {
+ return bottom.CompareTo(scores.DoubleVal(doc));
+ }
+
+
+ public override void Copy(int slot, int doc)
+ {
+ values[slot] = scores.DoubleVal(doc);
+ }
+
+
+ public override FieldComparator SetNextReader(AtomicReaderContext context)
+ {
+ this.readerContext = context;
+ return this;
+ }
+
+ public override IComparable Value(int slot)
+ {
+ return (values[slot]);
+ }
+
+
+ public override int CompareTop(int doc)
+ {
+ return topValue.CompareTo(scores.DoubleVal(doc));
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/lucenenet/blob/b6c1b5d2/src/Lucene.Net.Expressions/ExpressionFunctionValues.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Expressions/ExpressionFunctionValues.cs b/src/Lucene.Net.Expressions/ExpressionFunctionValues.cs
new file mode 100644
index 0000000..63b8f67
--- /dev/null
+++ b/src/Lucene.Net.Expressions/ExpressionFunctionValues.cs
@@ -0,0 +1,48 @@
+using System;
+using Lucene.Net.Queries.Function;
+using Lucene.Net.Queries.Function.DocValues;
+
+namespace Lucene.Net.Expressions
+{
+ /// <summary>
+ /// A
+ /// <see cref="Lucene.Net.Queries.Function.FunctionValues">Lucene.Net.Queries.Function.FunctionValues
+ /// </see>
+ /// which evaluates an expression
+ /// </summary>
+ internal class ExpressionFunctionValues : DoubleDocValues
+ {
+ internal readonly Expression expression;
+
+ internal readonly FunctionValues[] functionValues;
+
+ internal int currentDocument = -1;
+
+ internal double currentValue;
+
+ internal ExpressionFunctionValues(ValueSource parent, Expression expression, FunctionValues
+ [] functionValues) : base(parent)
+ {
+ if (expression == null)
+ {
+ throw new ArgumentNullException();
+ }
+ if (functionValues == null)
+ {
+ throw new ArgumentNullException();
+ }
+ this.expression = expression;
+ this.functionValues = functionValues;
+ }
+
+ public override double DoubleVal(int document)
+ {
+ if (currentDocument != document)
+ {
+ currentDocument = document;
+ currentValue = expression.Evaluate(document, functionValues);
+ }
+ return currentValue;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/lucenenet/blob/b6c1b5d2/src/Lucene.Net.Expressions/ExpressionRescorer.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Expressions/ExpressionRescorer.cs b/src/Lucene.Net.Expressions/ExpressionRescorer.cs
new file mode 100644
index 0000000..8d0fd7e
--- /dev/null
+++ b/src/Lucene.Net.Expressions/ExpressionRescorer.cs
@@ -0,0 +1,120 @@
+using System;
+using System.Collections.Generic;
+using Lucene.Net.Index;
+using Lucene.Net.Search;
+
+namespace Lucene.Net.Expressions
+{
+ /// <summary>
+ /// A
+ /// <see cref="Lucene.Net.Search.Rescorer">Lucene.Net.Search.Rescorer</see>
+ /// that uses an expression to re-score
+ /// first pass hits. Functionally this is the same as
+ /// <see cref="Lucene.Net.Search.SortRescorer">Lucene.Net.Search.SortRescorer
+ /// </see>
+ /// (if you build the
+ /// <see cref="Lucene.Net.Search.Sort">Lucene.Net.Search.Sort</see>
+ /// using
+ /// <see cref="Expression.GetSortField(Bindings, bool)">Expression.GetSortField(Bindings, bool)
+ /// </see>
+ /// ), except for the explain method
+ /// which gives more detail by showing the value of each
+ /// variable.
+ /// </summary>
+ /// <lucene.experimental></lucene.experimental>
+ internal class ExpressionRescorer : SortRescorer
+ {
+ private readonly Expression expression;
+
+ private readonly Bindings bindings;
+
+ /// <summary>
+ /// Uses the provided
+ /// <see cref="Lucene.Net.Queries.Function.ValueSource">Lucene.Net.Queries.Function.ValueSource
+ /// </see>
+ /// to assign second
+ /// pass scores.
+ /// </summary>
+ public ExpressionRescorer(Expression expression, Bindings bindings)
+ : base(new Sort
+ (expression.GetSortField(bindings, true)))
+ {
+ this.expression = expression;
+ this.bindings = bindings;
+ }
+
+ private class FakeScorer : Scorer
+ {
+ internal float score;
+
+ internal int doc = -1;
+
+ internal int freq = 1;
+
+ public FakeScorer()
+ : base(null)
+ {
+ }
+
+ public override int Advance(int target)
+ {
+ throw new NotSupportedException("FakeScorer doesn't support advance(int)");
+ }
+
+ public override int DocID()
+ {
+ return doc;
+ }
+
+ public override int Freq()
+ {
+ return freq;
+ }
+
+ public override int NextDoc()
+ {
+ throw new NotSupportedException("FakeScorer doesn't support nextDoc()");
+ }
+
+ public override float Score()
+ {
+ return score;
+ }
+
+ public override long Cost()
+ {
+ return 1;
+ }
+
+ public override Weight Weight
+ {
+ get { throw new NotSupportedException(); }
+ }
+
+ public override ICollection<Scorer.ChildScorer> Children
+ {
+ get { throw new NotSupportedException(); }
+ }
+ }
+
+
+ public override Explanation Explain(IndexSearcher searcher, Explanation firstPassExplanation, int docID)
+ {
+ Explanation result = base.Explain(searcher, firstPassExplanation, docID);
+ IList<AtomicReaderContext> leaves = searcher.IndexReader.Leaves;
+ int subReader = ReaderUtil.SubIndex(docID, leaves);
+ AtomicReaderContext readerContext = leaves[subReader];
+ int docIDInSegment = docID - readerContext.DocBase;
+ var context = new Dictionary<string, object>();
+ var fakeScorer = new FakeScorer { score = firstPassExplanation.Value, doc = docIDInSegment };
+ context["scorer"] = fakeScorer;
+ foreach (string variable in expression.variables)
+ {
+ result.AddDetail(new Explanation((float)bindings.GetValueSource(variable).GetValues
+ (context, readerContext).DoubleVal(docIDInSegment), "variable \"" + variable + "\""
+ ));
+ }
+ return result;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/lucenenet/blob/b6c1b5d2/src/Lucene.Net.Expressions/ExpressionSortField.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Expressions/ExpressionSortField.cs b/src/Lucene.Net.Expressions/ExpressionSortField.cs
new file mode 100644
index 0000000..e7ca2ea
--- /dev/null
+++ b/src/Lucene.Net.Expressions/ExpressionSortField.cs
@@ -0,0 +1,86 @@
+using System.Text;
+using Lucene.Net.Search;
+
+namespace Lucene.Net.Expressions
+{
+ /// <summary>
+ /// A
+ /// <see cref="Lucene.Net.Search.SortField">Lucene.Net.Search.SortField
+ /// </see>
+ /// which sorts documents by the evaluated value of an expression for each document
+ /// </summary>
+ internal class ExpressionSortField : SortField
+ {
+ private readonly ExpressionValueSource source;
+
+ internal ExpressionSortField(string name, ExpressionValueSource source, bool reverse
+ ) : base(name, Type_e.CUSTOM, reverse)
+ {
+ this.source = source;
+ }
+
+
+ public override FieldComparator GetComparator(int numHits, int sortPos)
+ {
+ return new ExpressionComparator(source, numHits);
+ }
+
+ public override int GetHashCode()
+ {
+ int prime = 31;
+ int result = base.GetHashCode();
+ result = prime * result + ((source == null) ? 0 : source.GetHashCode());
+ return result;
+ }
+
+ public override bool Equals(object obj)
+ {
+ if (this == obj)
+ {
+ return true;
+ }
+ if (!base.Equals(obj))
+ {
+ return false;
+ }
+ if (GetType() != obj.GetType())
+ {
+ return false;
+ }
+ ExpressionSortField other = (ExpressionSortField)obj;
+ if (source == null)
+ {
+ if (other.source != null)
+ {
+ return false;
+ }
+ }
+ else
+ {
+ if (!source.Equals(other.source))
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public override string ToString()
+ {
+ StringBuilder buffer = new StringBuilder();
+ buffer.Append("<expr \"");
+ buffer.Append(Field);
+ buffer.Append("\">");
+ if (Reverse)
+ {
+ buffer.Append('!');
+ }
+ return buffer.ToString();
+ }
+
+ public override bool NeedsScores()
+ {
+ return source.NeedsScores();
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/lucenenet/blob/b6c1b5d2/src/Lucene.Net.Expressions/ExpressionValueSource.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Expressions/ExpressionValueSource.cs b/src/Lucene.Net.Expressions/ExpressionValueSource.cs
new file mode 100644
index 0000000..e4501a9
--- /dev/null
+++ b/src/Lucene.Net.Expressions/ExpressionValueSource.cs
@@ -0,0 +1,169 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using Lucene.Net.Index;
+using Lucene.Net.Queries.Function;
+using Lucene.Net.Search;
+using Lucene.Net.Support;
+
+namespace Lucene.Net.Expressions
+{
+ /// <summary>
+ /// A
+ /// <see cref="Lucene.Net.Queries.Function.ValueSource">Lucene.Net.Queries.Function.ValueSource
+ /// </see>
+ /// which evaluates a
+ /// <see cref="Expression">Expression</see>
+ /// given the context of an
+ /// <see cref="Bindings">Bindings</see>
+ /// .
+ /// </summary>
+ internal sealed class ExpressionValueSource : ValueSource
+ {
+ internal readonly ValueSource[] variables;
+
+ internal readonly Expression expression;
+
+ internal readonly bool needsScores;
+
+ internal ExpressionValueSource(Bindings bindings, Expression expression)
+ {
+ if (bindings == null)
+ {
+ throw new ArgumentNullException();
+ }
+ if (expression == null)
+ {
+ throw new ArgumentNullException();
+ }
+ this.expression = expression;
+ variables = new ValueSource[expression.variables.Length];
+ bool needsScores = false;
+ for (int i = 0; i < variables.Length; i++)
+ {
+ ValueSource source = bindings.GetValueSource(expression.variables[i]);
+ if (source is ScoreValueSource)
+ {
+ needsScores = true;
+ }
+ else
+ {
+ var valueSource = source as ExpressionValueSource;
+ if (valueSource != null)
+ {
+ if (valueSource.NeedsScores())
+ {
+ needsScores = true;
+ }
+ }
+ else
+ {
+ if (source == null)
+ {
+ throw new SystemException("Internal error. Variable (" + expression.variables[i]
+ + ") does not exist.");
+ }
+ }
+ }
+ variables[i] = source;
+ }
+ this.needsScores = needsScores;
+ }
+
+
+ public override FunctionValues GetValues(IDictionary context, AtomicReaderContext
+ readerContext)
+ {
+ IDictionary<string, FunctionValues> valuesCache = (IDictionary<string, FunctionValues>)context["valuesCache"];
+ if (valuesCache == null)
+ {
+ valuesCache = new Dictionary<string, FunctionValues>();
+ context = new Hashtable(context);
+ context["valuesCache"] = valuesCache;
+ }
+ FunctionValues[] externalValues = new FunctionValues[expression.variables.Length];
+ for (int i = 0; i < variables.Length; ++i)
+ {
+ string externalName = expression.variables[i];
+ FunctionValues values;
+ if (!valuesCache.TryGetValue(externalName,out values))
+ {
+ values = variables[i].GetValues(context, readerContext);
+ if (values == null)
+ {
+ throw new SystemException("Internal error. External (" + externalName + ") does not exist.");
+ }
+ valuesCache[externalName] = values;
+ }
+ externalValues[i] = values;
+ }
+ return new ExpressionFunctionValues(this, expression, externalValues);
+ }
+
+ public override SortField GetSortField(bool reverse)
+ {
+ return new ExpressionSortField(expression.sourceText, this, reverse);
+ }
+
+ public override string Description
+ {
+ get { return "expr(" + expression.sourceText + ")"; }
+ }
+
+ public override int GetHashCode()
+ {
+ int prime = 31;
+ int result = 1;
+ result = prime * result + ((expression == null) ? 0 : expression.GetHashCode());
+ result = prime * result + (needsScores ? 1231 : 1237);
+ result = prime * result + Arrays.GetHashCode(variables);
+ return result;
+ }
+
+ public override bool Equals(object obj)
+ {
+ if (this == obj)
+ {
+ return true;
+ }
+ if (obj == null)
+ {
+ return false;
+ }
+ if (GetType() != obj.GetType())
+ {
+ return false;
+ }
+ Lucene.Net.Expressions.ExpressionValueSource other = (Lucene.Net.Expressions.ExpressionValueSource
+ )obj;
+ if (expression == null)
+ {
+ if (other.expression != null)
+ {
+ return false;
+ }
+ }
+ else
+ {
+ if (!expression.Equals(other.expression))
+ {
+ return false;
+ }
+ }
+ if (needsScores != other.needsScores)
+ {
+ return false;
+ }
+ if (!Arrays.Equals(variables, other.variables))
+ {
+ return false;
+ }
+ return true;
+ }
+
+ internal bool NeedsScores()
+ {
+ return needsScores;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/lucenenet/blob/b6c1b5d2/src/Lucene.Net.Expressions/JS/JavascriptCompiler.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Expressions/JS/JavascriptCompiler.cs b/src/Lucene.Net.Expressions/JS/JavascriptCompiler.cs
new file mode 100644
index 0000000..cda3042
--- /dev/null
+++ b/src/Lucene.Net.Expressions/JS/JavascriptCompiler.cs
@@ -0,0 +1,749 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Configuration;
+using System.Diagnostics;
+using System.Diagnostics.SymbolStore;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Reflection.Emit;
+using System.Security.AccessControl;
+using Antlr.Runtime;
+using Antlr.Runtime.Tree;
+using Lucene.Net.Queries.Function;
+using Lucene.Net.Support;
+
+namespace Lucene.Net.Expressions.JS
+{
+ /// <summary>An expression compiler for javascript expressions.</summary>
+ /// <remarks>
+ /// An expression compiler for javascript expressions.
+ /// <p>
+ /// Example:
+ /// <pre class="prettyprint">
+ /// Expression foo = JavascriptCompiler.compile("((0.3*popularity)/10.0)+(0.7*score)");
+ /// </pre>
+ /// <p>
+ /// See the
+ /// <see cref="Lucene.Net.Expressions.JS">package documentation</see>
+ /// for
+ /// the supported syntax and default functions.
+ /// <p>
+ /// You can compile with an alternate set of functions via
+ /// <see cref="Compile(string, System.Collections.Generic.IDictionary{K, V})
+ /// ">Compile(string, System.Collections.Generic.IDictionary<K, V>)
+ /// </see>
+ /// .
+ /// For example:
+ /// <pre class="prettyprint">
+ /// Map<String,Method> functions = new HashMap<String,Method>();
+ /// // add all the default functions
+ /// functions.putAll(JavascriptCompiler.DEFAULT_FUNCTIONS);
+ /// // add cbrt()
+ /// functions.put("cbrt", Math.class.getMethod("cbrt", double.class));
+ /// // call compile with customized function map
+ /// Expression foo = JavascriptCompiler.compile("cbrt(score)+ln(popularity)",
+ /// functions,
+ /// getClass().getClassLoader());
+ /// </pre>
+ /// </remarks>
+ /// <lucene.experimental></lucene.experimental>
+ public class JavascriptCompiler
+ {
+
+ private static readonly string COMPILED_EXPRESSION_CLASS = typeof(Expression).Namespace + ".CompiledExpression";
+
+ private static readonly string COMPILED_EXPRESSION_INTERNAL = COMPILED_EXPRESSION_CLASS.Replace('.', '/');
+
+ private static readonly Type EXPRESSION_TYPE = Type.GetType(typeof(Expression).FullName);
+
+ private static readonly Type FUNCTION_VALUES_TYPE = typeof(FunctionValues);
+
+ private static readonly ConstructorInfo EXPRESSION_CTOR = typeof(Expression).
+ GetConstructor(new Type[] { typeof(String), typeof(String[]) });
+
+ private static readonly MethodInfo EVALUATE_METHOD = GetMethod(EXPRESSION_TYPE, "Evaluate",
+ new[] { typeof(int), typeof(FunctionValues[]) });
+
+ private static readonly MethodInfo DOUBLE_VAL_METHOD = GetMethod(FUNCTION_VALUES_TYPE, "DoubleVal",
+ new[] { typeof(int) });
+
+
+ // We use the same class name for all generated classes as they all have their own class loader.
+ // The source code is displayed as "source file name" in stack trace.
+ // to work around import clash:
+ private static MethodInfo GetMethod(Type type, string method, Type[] parms)
+ {
+ return type.GetMethod(method, parms);
+ }
+
+ private const int MAX_SOURCE_LENGTH = 16384;
+
+ private readonly string sourceText;
+
+ private readonly IDictionary<string, int> externalsMap = new HashMap<string, int>();
+
+
+
+ private TypeBuilder dynamicType;
+
+ private readonly IDictionary<string, MethodInfo> functions;
+
+ /// <summary>The default set of functions available to expressions.</summary>
+ /// <remarks>
+ /// The default set of functions available to expressions.
+ /// <p>
+ /// See the
+ /// <see cref="Lucene.Net.Expressions.JS">package documentation</see>
+ /// for a list.
+ /// </remarks>
+ public static readonly IDictionary<string, MethodInfo> DEFAULT_FUNCTIONS;
+
+ private ILGenerator gen;
+ private AssemblyBuilder asmBuilder;
+ private string fileName;
+ private ISymbolDocumentWriter debugDoc;
+ private int lineNum = 1;
+ private StreamWriter file;
+ private MethodBuilder evalMethod;
+ private bool negate;
+
+ // This maximum length is theoretically 65535 bytes, but as its CESU-8 encoded we dont know how large it is in bytes, so be safe
+ // rcmuir: "If your ranking function is that large you need to check yourself into a mental institution!"
+ /// <summary>Compiles the given expression.</summary>
+ /// <remarks>Compiles the given expression.</remarks>
+ /// <param name="sourceText">The expression to compile</param>
+ /// <returns>A new compiled expression</returns>
+
+ public static Expression Compile(string sourceText)
+ {
+ return new JavascriptCompiler(sourceText).CompileExpression();
+ }
+
+ /// <summary>Compiles the given expression with the supplied custom functions.</summary>
+ /// <remarks>
+ /// Compiles the given expression with the supplied custom functions.
+ /// <p>
+ /// Functions must be
+ /// <code>public static</code>
+ /// , return
+ /// <code>double</code>
+ /// and
+ /// can take from zero to 256
+ /// <code>double</code>
+ /// parameters.
+ /// </remarks>
+ /// <param name="sourceText">The expression to compile</param>
+ /// <param name="functions">map of String names to functions</param>
+ /// <param name="parent">
+ /// a
+ /// <code>ClassLoader</code>
+ /// that should be used as the parent of the loaded class.
+ /// It must contain all classes referred to by the given
+ /// <code>functions</code>
+ /// .
+ /// </param>
+ /// <returns>A new compiled expression</returns>
+
+ public static Expression Compile(string sourceText, IDictionary<string, MethodInfo> functions)
+ {
+
+ foreach (MethodInfo m in functions.Values)
+ {
+ CheckFunction(m);
+ }
+ return new JavascriptCompiler(sourceText, functions).CompileExpression();
+ }
+
+ /// <summary>This method is unused, it is just here to make sure that the function signatures don't change.
+ /// </summary>
+ /// <remarks>
+ /// This method is unused, it is just here to make sure that the function signatures don't change.
+ /// If this method fails to compile, you also have to change the byte code generator to correctly
+ /// use the FunctionValues class.
+ /// </remarks>
+ private static void UnusedTestCompile()
+ {
+ FunctionValues f = null;
+ double ret = f.DoubleVal(2);
+ }
+
+ /// <summary>Constructs a compiler for expressions.</summary>
+
+ /// <param name="sourceText">The expression to compile</param>
+ private JavascriptCompiler(string sourceText)
+ : this(sourceText, DEFAULT_FUNCTIONS
+ )
+ {
+ }
+
+ /// <summary>Constructs a compiler for expressions with specific set of functions</summary>
+ /// <param name="sourceText">The expression to compile</param>
+ private JavascriptCompiler(string sourceText, IDictionary<string, MethodInfo> functions
+ )
+ {
+ if (sourceText == null)
+ {
+ throw new ArgumentNullException();
+ }
+ this.sourceText = sourceText;
+ this.functions = functions;
+ }
+
+ /// <summary>Compiles the given expression with the specified parent classloader</summary>
+ /// <returns>A new compiled expression</returns>
+
+ private Expression CompileExpression()
+ {
+ try
+ {
+
+ ITree antlrTree = GetAntlrComputedExpressionTree();
+ BeginCompile();
+ RecursiveCompile(antlrTree, typeof(double));
+ EndCompile();
+ return
+ (Expression)
+ Activator.CreateInstance(dynamicType.CreateType(), sourceText, externalsMap.Keys.ToArray());
+
+ }
+
+ catch (MemberAccessException exception)
+ {
+ throw new InvalidOperationException("An internal error occurred attempting to compile the expression ("
+ + sourceText + ").", exception);
+ }
+ catch (TargetInvocationException exception)
+ {
+ throw new InvalidOperationException("An internal error occurred attempting to compile the expression ("
+ + sourceText + ").", exception);
+ }
+ }
+
+ private void BeginCompile()
+ {
+ var assemblyName = new AssemblyName("Lucene.Net.Expressions.Dynamic" + new Random().Next());
+ asmBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndCollect);
+
+ ModuleBuilder modBuilder = asmBuilder.DefineDynamicModule(assemblyName.Name + ".dll");
+ dynamicType = modBuilder.DefineType(COMPILED_EXPRESSION_CLASS,
+ TypeAttributes.AnsiClass | TypeAttributes.AutoClass | TypeAttributes.Public | TypeAttributes.Class |
+ TypeAttributes.BeforeFieldInit | TypeAttributes.AutoLayout, EXPRESSION_TYPE);
+
+
+ ConstructorBuilder constructorBuilder = dynamicType.DefineConstructor(MethodAttributes.Public,
+ CallingConventions.HasThis,
+ new[] { typeof(string), typeof(string[]) });
+
+ ILGenerator ctorGen = constructorBuilder.GetILGenerator();
+ ctorGen.Emit(OpCodes.Ldarg_0);
+ ctorGen.Emit(OpCodes.Ldarg_1);
+ ctorGen.Emit(OpCodes.Ldarg_2);
+ ctorGen.Emit(OpCodes.Call, EXPRESSION_CTOR);
+ ctorGen.Emit(OpCodes.Nop);
+ ctorGen.Emit(OpCodes.Nop);
+ ctorGen.Emit(OpCodes.Ret);
+
+ evalMethod = dynamicType.DefineMethod("Evaluate", MethodAttributes.Public | MethodAttributes.Virtual,
+ typeof(double), new[] { typeof(int), typeof(FunctionValues[]) });
+ gen = evalMethod.GetILGenerator();
+ }
+
+ private void RecursiveCompile(ITree current, Type expected)
+ {
+ int type = current.Type;
+ string text = current.Text;
+
+
+ switch (type)
+ {
+ case JavascriptParser.AT_CALL:
+ {
+ ITree identifier = current.GetChild(0);
+ string call = identifier.Text;
+ int arguments = current.ChildCount - 1;
+ MethodInfo method = functions[call];
+ if (method == null)
+ {
+ throw new ArgumentException("Unrecognized method call (" + call + ").");
+ }
+ int arity = method.GetParameters().Length;
+ if (arguments != arity)
+ {
+ throw new ArgumentException("Expected (" + arity + ") arguments for method call ("
+ + call + "), but found (" + arguments + ").");
+ }
+ for (int argument = 1; argument <= arguments; ++argument)
+ {
+ RecursiveCompile(current.GetChild(argument), typeof(double));
+ }
+ gen.Emit(OpCodes.Call, method);
+
+
+
+ break;
+ }
+
+ case JavascriptParser.NAMESPACE_ID:
+ {
+ int index;
+ if (externalsMap.ContainsKey(text))
+ {
+ index = externalsMap[text];
+ }
+ else
+ {
+ index = externalsMap.Count;
+ externalsMap[text] = index;
+ }
+ gen.Emit(OpCodes.Nop);
+
+ gen.Emit(OpCodes.Ldarg_2);
+ gen.Emit(OpCodes.Ldc_I4, index);
+
+ gen.Emit(OpCodes.Ldelem_Ref);
+ gen.Emit(OpCodes.Ldarg_1);
+ gen.Emit(OpCodes.Callvirt, DOUBLE_VAL_METHOD);
+
+
+ break;
+ }
+
+ case JavascriptParser.HEX:
+ {
+ PushLong(expected, Convert.ToInt64(text.Substring(2), 16));
+ break;
+ }
+
+ case JavascriptParser.OCTAL:
+ {
+ PushLong(expected, Convert.ToInt64(text.Substring(2), 8));
+ break;
+ }
+
+ case JavascriptParser.DECIMAL:
+ {
+ gen.Emit(OpCodes.Ldc_R8, double.Parse(text));
+ if (negate)
+ {
+ gen.Emit(OpCodes.Neg);
+ negate = false;
+ }
+ break;
+ }
+
+ case JavascriptParser.AT_NEGATE:
+ {
+ negate = true;
+ RecursiveCompile(current.GetChild(0), typeof(double));
+
+ break;
+ }
+
+ case JavascriptParser.AT_ADD:
+ {
+ PushArith(OpCodes.Add, current, expected);
+ break;
+ }
+
+ case JavascriptParser.AT_SUBTRACT:
+ {
+ PushArith(OpCodes.Sub, current, expected);
+ break;
+ }
+
+ case JavascriptParser.AT_MULTIPLY:
+ {
+ PushArith(OpCodes.Mul, current, expected);
+ break;
+ }
+
+ case JavascriptParser.AT_DIVIDE:
+ {
+ PushArith(OpCodes.Div, current, expected);
+ break;
+ }
+
+ case JavascriptParser.AT_MODULO:
+ {
+ PushArith(OpCodes.Rem, current, expected);
+ break;
+ }
+
+ case JavascriptParser.AT_BIT_SHL:
+ {
+ PushShift(OpCodes.Shl, current, expected);
+ break;
+ }
+
+ case JavascriptParser.AT_BIT_SHR:
+ {
+ PushShift(OpCodes.Shr, current, expected);
+ break;
+ }
+
+ case JavascriptParser.AT_BIT_SHU:
+ {
+ PushShift(OpCodes.Shr_Un, current, expected);
+ break;
+ }
+
+ case JavascriptParser.AT_BIT_AND:
+ {
+ PushBitwise(OpCodes.And, current, expected);
+ break;
+ }
+
+ case JavascriptParser.AT_BIT_OR:
+ {
+ PushBitwise(OpCodes.Or, current, expected);
+ break;
+ }
+
+ case JavascriptParser.AT_BIT_XOR:
+ {
+ PushBitwise(OpCodes.Xor, current, expected);
+ break;
+ }
+
+ case JavascriptParser.AT_BIT_NOT:
+ {
+ RecursiveCompile(current.GetChild(0), typeof(long));
+ gen.Emit(OpCodes.Ldc_I4_M1);
+ //dynamicType.Push(-1L);
+ //dynamicType.VisitInsn(Opcodes.LXOR);
+ //dynamicType.Cast(typeof(long), expected);
+ break;
+ }
+
+ case JavascriptParser.AT_COMP_EQ:
+ {
+ PushCond(OpCodes.Ceq, current, expected);
+ break;
+ }
+
+ case JavascriptParser.AT_COMP_NEQ:
+ {
+ //PushCond(OpCodes, current, expected);
+ break;
+ }
+
+ case JavascriptParser.AT_COMP_LT:
+ {
+ PushCond(OpCodes.Clt, current, expected);
+ break;
+ }
+
+ case JavascriptParser.AT_COMP_GT:
+ {
+ PushCond(OpCodes.Cgt, current, expected);
+ break;
+ }
+
+ case JavascriptParser.AT_COMP_LTE:
+ {
+ //PushCond(OpCodes.Clt | OpCodes.Ceq, current, expected);
+ break;
+ }
+
+ case JavascriptParser.AT_COMP_GTE:
+ {
+ //PushCond(GeneratorAdapter.GE, current, expected);
+ break;
+ }
+
+ case JavascriptParser.AT_BOOL_NOT:
+ {
+ /*Label labelNotTrue = new Label();
+ Label labelNotReturn = new Label();
+ RecursiveCompile(current.GetChild(0), Type.INT_TYPE);
+ dynamicType.VisitJumpInsn(Opcodes.IFEQ, labelNotTrue);
+ PushBoolean(expected, false);
+ dynamicType.GoTo(labelNotReturn);
+ dynamicType.VisitLabel(labelNotTrue);
+ PushBoolean(expected, true);
+ dynamicType.VisitLabel(labelNotReturn);*/
+ break;
+ }
+
+ case JavascriptParser.AT_BOOL_AND:
+ {
+ /*Label andFalse = new Label();
+ Label andEnd = new Label();
+ RecursiveCompile(current.GetChild(0), Type.INT_TYPE);
+ dynamicType.VisitJumpInsn(Opcodes.IFEQ, andFalse);
+ RecursiveCompile(current.GetChild(1), Type.INT_TYPE);
+ dynamicType.VisitJumpInsn(Opcodes.IFEQ, andFalse);
+ PushBoolean(expected, true);
+ dynamicType.GoTo(andEnd);
+ dynamicType.VisitLabel(andFalse);
+ PushBoolean(expected, false);
+ dynamicType.VisitLabel(andEnd);*/
+ break;
+ }
+
+ case JavascriptParser.AT_BOOL_OR:
+ {
+ Label orTrue = new Label();
+ Label orEnd = new Label();
+ /*RecursiveCompile(current.GetChild(0), Type.INT_TYPE);
+ dynamicType.VisitJumpInsn(Opcodes.IFNE, orTrue);
+ RecursiveCompile(current.GetChild(1), Type.INT_TYPE);
+ dynamicType.VisitJumpInsn(Opcodes.IFNE, orTrue);
+ PushBoolean(expected, false);
+ dynamicType.GoTo(orEnd);
+ dynamicType.VisitLabel(orTrue);
+ PushBoolean(expected, true);
+ dynamicType.VisitLabel(orEnd);*/
+ break;
+ }
+
+ case JavascriptParser.AT_COND_QUE:
+ {
+ /*Label condFalse = new Label();
+ Label condEnd = new Label();
+ RecursiveCompile(current.GetChild(0), Type.INT_TYPE);
+ dynamicType.VisitJumpInsn(Opcodes.IFEQ, condFalse);
+ RecursiveCompile(current.GetChild(1), expected);
+ dynamicType.GoTo(condEnd);
+ dynamicType.VisitLabel(condFalse);
+ RecursiveCompile(current.GetChild(2), expected);
+ dynamicType.VisitLabel(condEnd);*/
+ break;
+ }
+
+ default:
+ {
+ throw new InvalidOperationException("Unknown operation specified: (" + current.Text + ").");
+ }
+ }
+
+ }
+
+ private void PushArith(OpCode op, ITree current, Type expected)
+ {
+ PushBinaryOp(op, current, expected, typeof(double), typeof(double), typeof(double));
+ }
+
+ private void PushShift(OpCode op, ITree current, Type expected)
+ {
+ PushBinaryOp(op, current, expected, typeof(long), typeof(int), typeof(long));
+ }
+
+ private void PushBitwise(OpCode op, ITree current, Type expected)
+ {
+ PushBinaryOp(op, current, expected, typeof(long), typeof(long), typeof(long));
+ }
+
+ private void PushBinaryOp(OpCode op, ITree current, Type expected, Type arg1, Type arg2, Type returnType)
+ {
+ gen.Emit(OpCodes.Nop);
+ RecursiveCompile(current.GetChild(0), arg1);
+ RecursiveCompile(current.GetChild(1), arg2);
+ /* gen.Emit(OpCodes.Add, debugDoc, file, lineNum++);
+ gen.Emit(OpCodes.Stloc_0, debugDoc, file, lineNum++);
+ gen.Emit(OpCodes.Br_S, debugDoc, file, lineNum++);
+ gen.Emit(OpCodes.Ldloc_0, debugDoc, file, lineNum++);*/
+ gen.Emit(op);
+ /*dynamicType.VisitInsn(op);
+ dynamicType.Cast(returnType, expected);*/
+ }
+
+ private void PushCond(OpCode @operator, ITree current, Type expected)
+ {
+ Label labelTrue = new Label();
+ Label labelReturn = new Label();
+ RecursiveCompile(current.GetChild(0), typeof(double));
+ RecursiveCompile(current.GetChild(1), typeof(double));
+ /*dynamicType.IfCmp(typeof(double), @operator, labelTrue);
+ PushBoolean(expected, false);
+ dynamicType.GoTo(labelReturn);
+ dynamicType.VisitLabel(labelTrue);
+ PushBoolean(expected, true);
+ dynamicType.VisitLabel(labelReturn);*/
+ }
+
+ /*
+ private void PushBoolean(Type expected, bool truth)
+ {
+ switch (expected.GetSort())
+ {
+ case Type.INT:
+ {
+ dynamicType.Push(truth);
+ break;
+ }
+
+ case Type.LONG:
+ {
+ dynamicType.Push(truth ? 1L : 0L);
+ break;
+ }
+
+ case Type.DOUBLE:
+ {
+ dynamicType.Push(truth ? 1. : 0.);
+ break;
+ }
+
+ default:
+ {
+ throw new InvalidOperationException("Invalid expected type: " + expected);
+ }
+ }
+ }
+ */
+
+ private void PushLong(Type expected, long i)
+ {
+ /*switch (expected.GetSort())
+ {
+ case Type.INT:
+ {
+ dynamicType.Push((int)i);
+ break;
+ }
+
+ case Type.LONG:
+ {
+ dynamicType.Push(i);
+ break;
+ }
+
+ case Type.DOUBLE:
+ {
+ dynamicType.Push((double)i);
+ break;
+ }
+
+ default:
+ {
+ throw new InvalidOperationException("Invalid expected type: " + expected);
+ }
+ }*/
+ gen.Emit(OpCodes.Ldarg_0);
+ }
+
+ private void EndCompile()
+ {
+ gen.Emit(OpCodes.Ret);
+ dynamicType.DefineMethodOverride(evalMethod, EVALUATE_METHOD);
+ }
+
+
+
+ private ITree GetAntlrComputedExpressionTree()
+ {
+ ICharStream input = new ANTLRStringStream(sourceText);
+ JavascriptLexer lexer = new JavascriptLexer(input);
+ CommonTokenStream tokens = new CommonTokenStream(lexer);
+ JavascriptParser parser = new JavascriptParser(tokens);
+ try
+ {
+ return parser.Expression().Tree;
+ }
+ catch (RecognitionException re)
+ {
+ throw new ArgumentException(re.Message, re);
+ }
+ catch (SystemException exception)
+ {
+ //TODO: Uncomment after implementing ParseException in QueryParsers
+ //if (exception.InnerException is ParseException)
+ //{
+ // throw (ParseException)exception.InnerException;
+ //}
+ throw;
+ }
+ }
+
+
+
+ static JavascriptCompiler()
+ {
+ IDictionary<string, MethodInfo> map = new Dictionary<string, MethodInfo>();
+ try
+ {
+ var props = Properties.Settings.Default;
+ foreach (SettingsProperty property in props.Properties)
+ {
+ string[] vals = props[property.Name].ToString().Split(',');
+ if (vals.Length != 3)
+ {
+ throw new Exception("Error reading Javascript functions from settings");
+ }
+ string typeName = vals[0];
+ if (vals[0].Contains("Lucene.Net"))
+ {
+ typeName = vals[0] + ",Lucene.Net";
+ }
+ Type clazz = Type.GetType(typeName, true);
+ string methodName = vals[1].Trim();
+ int arity = int.Parse(vals[2]);
+ Type[] args = new Type[arity];
+ Arrays.Fill(args, typeof(double));
+ MethodInfo method = clazz.GetMethod(methodName, args);
+ CheckFunction(method);
+ map[property.Name] = method;
+ }
+
+
+ }
+ catch (Exception e)
+ {
+ throw new Exception("Cannot resolve function", e);
+ }
+ DEFAULT_FUNCTIONS = map;
+ }
+
+ private static void CheckFunction(MethodInfo method)
+ {
+
+ // do some checks if the signature is "compatible":
+ if (!(method.IsStatic))
+ {
+ throw new ArgumentException(method + " is not static.");
+ }
+ if (!(method.IsPublic))
+ {
+ throw new ArgumentException(method + " is not public.");
+ }
+ if (!method.DeclaringType.IsPublic)
+ {
+ //.NET Port. Inner class is being returned as not public even when declared public
+ if (method.DeclaringType.IsNestedAssembly)
+ {
+ throw new ArgumentException(method.DeclaringType.FullName + " is not public.");
+ }
+ }
+ if (method.GetParameters().Any(parmType => parmType.ParameterType != (typeof(double))))
+ {
+ throw new ArgumentException(method + " must take only double parameters");
+ }
+ if (method.ReturnType != typeof(double))
+ {
+ throw new ArgumentException(method + " does not return a double.");
+ }
+ }
+ }
+
+
+}