You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nlpcraft.apache.org by ar...@apache.org on 2021/03/24 19:27:03 UTC

[incubator-nlpcraft] branch NLPCRAFT-278 updated (3ce78a2 -> 603d6d1)

This is an automated email from the ASF dual-hosted git repository.

aradzinski pushed a change to branch NLPCRAFT-278
in repository https://gitbox.apache.org/repos/asf/incubator-nlpcraft.git.


    from 3ce78a2  WIP.
     new cf25323  Update NCIdlCompilerBase.scala
     new 8095292  Merge branch 'NLPCRAFT-278' of https://github.com/apache/incubator-nlpcraft into NLPCRAFT-278
     new 603d6d1  WIP.

The 3 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../org/apache/nlpcraft/common/util/NCUtils.scala  |  12 ++-
 .../model/intent/compiler/NCIdlCompilerBase.scala  | 112 ++++++++++-----------
 .../functions/NCIdlFunctionsCollections.scala      |   4 +-
 .../compiler/functions/NCIdlFunctionsStat.scala    |   2 -
 .../compiler/functions/NCIdlFunctionsStrings.scala |  67 ++++++------
 .../compiler/functions/NCIdlFunctionsToken.scala   |  20 +---
 6 files changed, 100 insertions(+), 117 deletions(-)

[incubator-nlpcraft] 02/03: Merge branch 'NLPCRAFT-278' of https://github.com/apache/incubator-nlpcraft into NLPCRAFT-278

Posted by ar...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

aradzinski pushed a commit to branch NLPCRAFT-278
in repository https://gitbox.apache.org/repos/asf/incubator-nlpcraft.git

commit 8095292f4e65d2bdc0d5d75d370d81e6b1a347be
Merge: cf25323 3ce78a2
Author: Aaron Radzinski <ar...@apache.org>
AuthorDate: Wed Mar 24 12:14:18 2021 -0700

    Merge branch 'NLPCRAFT-278' of https://github.com/apache/incubator-nlpcraft into NLPCRAFT-278

 .../nlpcraft/model/intent/idl/compiler/functions/NCIdlFunctions.scala    | 1 +
 1 file changed, 1 insertion(+)

[incubator-nlpcraft] 03/03: WIP.

Posted by ar...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

aradzinski pushed a commit to branch NLPCRAFT-278
in repository https://gitbox.apache.org/repos/asf/incubator-nlpcraft.git

commit 603d6d127d6e28569bf4a74137717310d2c21204
Author: Aaron Radzinski <ar...@apache.org>
AuthorDate: Wed Mar 24 12:26:54 2021 -0700

    WIP.
---
 .../org/apache/nlpcraft/common/util/NCUtils.scala  | 12 ++--
 .../model/intent/compiler/NCIdlCompilerBase.scala  |  7 ++-
 .../functions/NCIdlFunctionsCollections.scala      |  4 +-
 .../compiler/functions/NCIdlFunctionsStat.scala    |  2 -
 .../compiler/functions/NCIdlFunctionsStrings.scala | 67 +++++++++++-----------
 .../compiler/functions/NCIdlFunctionsToken.scala   | 20 +------
 6 files changed, 48 insertions(+), 64 deletions(-)

diff --git a/nlpcraft/src/main/scala/org/apache/nlpcraft/common/util/NCUtils.scala b/nlpcraft/src/main/scala/org/apache/nlpcraft/common/util/NCUtils.scala
index 46bd6ea..c1254fc 100644
--- a/nlpcraft/src/main/scala/org/apache/nlpcraft/common/util/NCUtils.scala
+++ b/nlpcraft/src/main/scala/org/apache/nlpcraft/common/util/NCUtils.scala
@@ -202,10 +202,14 @@ object NCUtils extends LazyLogging {
     def trimEscapesQuotes(s: String): String = {
         val z = s.strip
 
-        if (z.head == '\'' && z.last == '\'')
-            trimEscapesQuotes(z.substring(1, z.length - 1).replace("\'", "'"))
-        else if (z.head == '"' && z.last == '"')
-            trimEscapesQuotes(z.substring(1, z.length - 1).replace("\\\"", "\""))
+        if (z.nonEmpty) {
+            if (z.head == '\'' && z.last == '\'')
+                trimEscapesQuotes(z.substring(1, z.length - 1).replace("\'", "'"))
+            else if (z.head == '"' && z.last == '"')
+                trimEscapesQuotes(z.substring(1, z.length - 1).replace("\\\"", "\""))
+            else
+                z
+        }
         else
             z
     }
diff --git a/nlpcraft/src/main/scala/org/apache/nlpcraft/model/intent/compiler/NCIdlCompilerBase.scala b/nlpcraft/src/main/scala/org/apache/nlpcraft/model/intent/compiler/NCIdlCompilerBase.scala
index d917baf..a85c13c 100644
--- a/nlpcraft/src/main/scala/org/apache/nlpcraft/model/intent/compiler/NCIdlCompilerBase.scala
+++ b/nlpcraft/src/main/scala/org/apache/nlpcraft/model/intent/compiler/NCIdlCompilerBase.scala
@@ -29,7 +29,7 @@ import java.lang.{Byte ⇒ JByte, Double ⇒ JDouble, Float ⇒ JFloat, Integer
 import java.time.temporal.IsoFields
 import java.time.{LocalDate, LocalTime}
 import java.util
-import java.util.{Calendar, Collections, Comparator, Collection ⇒ JColl, List ⇒ JList, Map ⇒ JMap}
+import java.util.{Calendar, Collections, Collection ⇒ JColl, List ⇒ JList, Map ⇒ JMap}
 import scala.collection.JavaConverters._
 
 trait NCIdlCompilerBase {
@@ -383,7 +383,8 @@ trait NCIdlCompilerBase {
         val (x1, x2) = pop2()(stack, ctx)
 
         def doEq(op: String, v1: Object, v2: Object): Boolean = {
-            if (v1 == null && v2 == null) true
+            if (v1 eq v2) true
+            else if (v1 == null && v2 == null) true
             else if ((v1 == null && v2 != null) || (v1 != null && v2 == null)) false
             else if (isInt(v1) && isInt(v2)) asInt(v1) == asInt(v2)
             else if (isReal(v1) && isReal(v2)) asReal(v1) == asReal(v2)
@@ -392,7 +393,7 @@ trait NCIdlCompilerBase {
             else if (isList(v1) && isList(v2)) CollectionUtils.isEqualCollection(asList(v1), asList(v2))
             else if ((isInt(v1) && isReal(v2)) || (isReal(v1) && isInt(v2))) asReal(v1) == asReal(v2)
             else
-                throw rtBinaryOpError(op, v1, v2)
+                v1.equals(v2)
         }
 
         stack.push(() ⇒ {
diff --git a/nlpcraft/src/test/scala/org/apache/nlpcraft/model/intent/idl/compiler/functions/NCIdlFunctionsCollections.scala b/nlpcraft/src/test/scala/org/apache/nlpcraft/model/intent/idl/compiler/functions/NCIdlFunctionsCollections.scala
index c268cb1..6ccef37 100644
--- a/nlpcraft/src/test/scala/org/apache/nlpcraft/model/intent/idl/compiler/functions/NCIdlFunctionsCollections.scala
+++ b/nlpcraft/src/test/scala/org/apache/nlpcraft/model/intent/idl/compiler/functions/NCIdlFunctionsCollections.scala
@@ -54,8 +54,7 @@ class NCIdlFunctionsCollections extends NCIdlFunctions {
             "non_empty(list(1)) == true",
             "reverse(list(1.0, 2, 3)) == list(3, 2, 1.0)",
             "sort(list(2, 1, 3)) == list(1, 2, 3)",
-            // TODO: Fix it.
-            "sort(list(2.0, 1, 3)) == list(1, 2.0, 3)",
+            "sort(list('c', 'a', 'b')) == list('a', 'b', 'c')",
             "size(list(2.0, 1, 3)) == 3",
             "length(list(2.0, 1, 3)) == 3",
             "count(list(2.0, 1, 3)) == 3",
@@ -67,7 +66,6 @@ class NCIdlFunctionsCollections extends NCIdlFunctions {
             s"concat(list(1, 2, 3), list(1, 2, 3)) == list(1, 2, 3, 1, 2, 3)",
             s"concat(list(1, 2, 3), list()) == list(1, 2, 3)",
             s"concat(list(), list()) == list()",
-            // TODO: is it? Shouldn't be all values have one type?
             s"concat(list(1, 2), list(3.0)) == list(1, 2, 3.0)"
         )
 }
diff --git a/nlpcraft/src/test/scala/org/apache/nlpcraft/model/intent/idl/compiler/functions/NCIdlFunctionsStat.scala b/nlpcraft/src/test/scala/org/apache/nlpcraft/model/intent/idl/compiler/functions/NCIdlFunctionsStat.scala
index 9513dc3..4bbd7b3 100644
--- a/nlpcraft/src/test/scala/org/apache/nlpcraft/model/intent/idl/compiler/functions/NCIdlFunctionsStat.scala
+++ b/nlpcraft/src/test/scala/org/apache/nlpcraft/model/intent/idl/compiler/functions/NCIdlFunctionsStat.scala
@@ -25,10 +25,8 @@ import org.junit.jupiter.api.Test
 class NCIdlFunctionsStat extends NCIdlFunctions {
     @Test
     def testError(): Unit = {
-        // TODO: NCE?
         expectNceError("avg(list()) == 2")
         expectNceError("avg(list('A')) == 2")
-
         expectNceError("stdev(list()) == 2")
         expectNceError("stdev(list('A')) == 2")
     }
diff --git a/nlpcraft/src/test/scala/org/apache/nlpcraft/model/intent/idl/compiler/functions/NCIdlFunctionsStrings.scala b/nlpcraft/src/test/scala/org/apache/nlpcraft/model/intent/idl/compiler/functions/NCIdlFunctionsStrings.scala
index 78264c4..ca9cb81 100644
--- a/nlpcraft/src/test/scala/org/apache/nlpcraft/model/intent/idl/compiler/functions/NCIdlFunctionsStrings.scala
+++ b/nlpcraft/src/test/scala/org/apache/nlpcraft/model/intent/idl/compiler/functions/NCIdlFunctionsStrings.scala
@@ -26,41 +26,38 @@ class NCIdlFunctionsStrings extends NCIdlFunctions {
     @Test
     def test(): Unit =
         test(
-            "trim(' a b  ') == 'a b'",
-            "strip(' a b  ') == 'a b'",
-            "uppercase('aB') == 'AB'",
-            "lowercase('aB') == 'ab'",
-            "is_alpha('aB') == true",
-            "is_alpha('aB1') == false",
-            "is_alphanum('aB1') == true",
-            "is_whitespace('A') == false",
-            "is_num('a') == false",
-            "is_num('1') == true",
-            "is_numspace('A') == false",
-            "is_alphaspace('A') == true",
-            "is_alphanumspace('A') == true",
-            "is_alphanumspace('1 A') == true",
-            "start_with('ab', 'a') == true",
-            "start_with('ab', 'b') == false",
-            "end_with('ab', 'a') == false",
-            "end_with('ab', 'b') == true",
-            "contains('ab', 'a') == true",
-            "contains('ab', 'bc') == false",
-            "index_of('ab', 'b') == 1",
-            "index_of('ab', 'bc') == -1",
-            "substr('abc', 0, 1) == 'a'",
-            "substr('abc', 0, 2) == 'ab'",
-            "replace('abc', 'a', 'X') == 'Xbc'",
-            "replace('abc', '0',  '0') == 'abc'",
-
-            // TODO: add correct test for `split` and `split_trim`
-            "split('1 A') == list('1', '2')",
-            "split_trim('1 A') == list('1', '2')",
-
-            // TODO: fix.
-            // Whitespaces.
-            "replace('abc', 'ab',  '') == 'c'",
-             "substr('abc', 20, 30) == ''",
+//            "trim(' a b  ') == 'a b'",
+//            "strip(' a b  ') == 'a b'",
+//            "uppercase('aB') == 'AB'",
+//            "lowercase('aB') == 'ab'",
+//            "is_alpha('aB') == true",
+//            "is_alpha('aB1') == false",
+//            "is_alphanum('aB1') == true",
+//            "is_whitespace('A') == false",
+//            "is_num('a') == false",
+//            "is_num('1') == true",
+//            "is_numspace('A') == false",
+//            "is_alphaspace('A') == true",
+//            "is_alphanumspace('A') == true",
+//            "is_alphanumspace('1 A') == true",
+//            "start_with('ab', 'a') == true",
+//            "start_with('ab', 'b') == false",
+//            "end_with('ab', 'a') == false",
+//            "end_with('ab', 'b') == true",
+//            "contains('ab', 'a') == true",
+//            "contains('ab', 'bc') == false",
+//            "index_of('ab', 'b') == 1",
+//            "index_of('ab', 'bc') == -1",
+//            "substr('abc', 0, 1) == 'a'",
+//            "substr('abc', 0, 2) == 'ab'",
+//            "replace('abc', 'a', 'X') == 'Xbc'",
+//            "replace('abc', '0',  '0') == 'abc'",
+//            "split('1 A') == list('1', '2')",
+//            "split_trim('1 A    ') == list('1', '2')",
+//
+//            // Whitespaces.
+//            "replace('abc', 'ab',  '') == 'c'",
+//            "substr('abc', 20, 30) == ''",
             "is_alphanumspace(' ') == true",
             "is_alphanumspace('  ') == true",
             "is_alphanumspace(' ') == true",
diff --git a/nlpcraft/src/test/scala/org/apache/nlpcraft/model/intent/idl/compiler/functions/NCIdlFunctionsToken.scala b/nlpcraft/src/test/scala/org/apache/nlpcraft/model/intent/idl/compiler/functions/NCIdlFunctionsToken.scala
index 8725886..f985559 100644
--- a/nlpcraft/src/test/scala/org/apache/nlpcraft/model/intent/idl/compiler/functions/NCIdlFunctionsToken.scala
+++ b/nlpcraft/src/test/scala/org/apache/nlpcraft/model/intent/idl/compiler/functions/NCIdlFunctionsToken.scala
@@ -105,22 +105,8 @@ class NCIdlFunctionsToken extends NCIdlFunctions {
                 truth = "tok_end_idx() == 123",
                 token = tkn(end = 123)
             ),
-            // TODO: fix following.
-//            TestDesc(
-//                truth = "tok_aliases() == list('1')",
-//                token = tkn(aliases = Set("1"))
-//            ),
-//            TestDesc(
-//                truth = "tok_this() == tok_this()"
-//            ),
-//            TestDesc(
-//                truth = "tok_find_part() != null",
-//                token = tkn(partTokens = Seq(tkn("part1"), tkn("part2")))
-//            ),
-//            TestDesc(
-//                truth = "tok_find_parts() != null",
-//                token = tkn(partTokens = Seq(tkn("part1"), tkn("part2")))
-//            ),
-
+            TestDesc(
+                truth = "tok_this() == tok_this()"
+            )
         )
 }

[incubator-nlpcraft] 01/03: Update NCIdlCompilerBase.scala

Posted by ar...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

aradzinski pushed a commit to branch NLPCRAFT-278
in repository https://gitbox.apache.org/repos/asf/incubator-nlpcraft.git

commit cf253236f842ded143e245d1ae3bf32cf2d79bfd
Author: Aaron Radzinski <ar...@apache.org>
AuthorDate: Wed Mar 24 12:14:14 2021 -0700

    Update NCIdlCompilerBase.scala
---
 .../model/intent/compiler/NCIdlCompilerBase.scala  | 109 ++++++++++-----------
 1 file changed, 54 insertions(+), 55 deletions(-)

diff --git a/nlpcraft/src/main/scala/org/apache/nlpcraft/model/intent/compiler/NCIdlCompilerBase.scala b/nlpcraft/src/main/scala/org/apache/nlpcraft/model/intent/compiler/NCIdlCompilerBase.scala
index 8453e94..d917baf 100644
--- a/nlpcraft/src/main/scala/org/apache/nlpcraft/model/intent/compiler/NCIdlCompilerBase.scala
+++ b/nlpcraft/src/main/scala/org/apache/nlpcraft/model/intent/compiler/NCIdlCompilerBase.scala
@@ -20,7 +20,7 @@ package org.apache.nlpcraft.model.intent.compiler
 import org.apache.commons.lang3.StringUtils
 import org.apache.nlpcraft.common.{NCE, U}
 import org.apache.nlpcraft.model.NCToken
-import org.antlr.v4.runtime.{ParserRuleContext ⇒PRC}
+import org.antlr.v4.runtime.{ParserRuleContext ⇒ PRC}
 import org.antlr.v4.runtime.tree.{TerminalNode ⇒ TN}
 import org.apache.commons.collections.CollectionUtils
 import org.apache.nlpcraft.model.intent.{NCIdlContext, NCIdlStack, NCIdlStackType, NCIdlStackItem ⇒ Z}
@@ -29,7 +29,7 @@ import java.lang.{Byte ⇒ JByte, Double ⇒ JDouble, Float ⇒ JFloat, Integer
 import java.time.temporal.IsoFields
 import java.time.{LocalDate, LocalTime}
 import java.util
-import java.util.{Calendar, Collections, Collection ⇒ JColl, List ⇒ JList, Map ⇒ JMap}
+import java.util.{Calendar, Collections, Comparator, Collection ⇒ JColl, List ⇒ JList, Map ⇒ JMap}
 import scala.collection.JavaConverters._
 
 trait NCIdlCompilerBase {
@@ -201,6 +201,34 @@ trait NCIdlCompilerBase {
 
     /**
      *
+     * @param x1
+     * @param x2
+     * @return
+     */
+    def extract2(x1: ST, x2: ST): (Object, Object, Int) = {
+        val Z(v1, n1) = x1()
+        val Z(v2, n2) = x2()
+
+        (v1, v2, n1 + n2)
+    }
+
+    /**
+     *
+     * @param x1
+     * @param x2
+     * @param x3
+     * @return
+     */
+    def extract3(x1: ST, x2: ST, x3: ST): (Object, Object, Object, Int) = {
+        val Z(v1, n1) = x1()
+        val Z(v2, n2) = x2()
+        val Z(v3, n3) = x3()
+
+        (v1, v2, v3, n1 + n2 + n3)
+    }
+
+    /**
+     *
      * @param lt
      * @param gt
      * @param lteq
@@ -211,8 +239,7 @@ trait NCIdlCompilerBase {
 
         if (lt != null)
             stack.push(() ⇒ {
-                val Z(v1, n1) = x1()
-                val Z(v2, n2) = x2()
+                val (v1, v2, n) = extract2(x1, x2)
 
                 val f =
                     if (isInt(v1) && isInt(v2)) asInt(v1) < asInt(v2)
@@ -222,12 +249,11 @@ trait NCIdlCompilerBase {
                     else
                         throw rtBinaryOpError("<", v1, v2)
 
-                Z(f, n1 + n2)
+                Z(f, n)
             })
         else if (gt != null)
             stack.push(() ⇒ {
-                val Z(v1, n1) = x1()
-                val Z(v2, n2) = x2()
+                val (v1, v2, n) = extract2(x1, x2)
 
                 val f =
                     if (isInt(v1) && isInt(v2)) asInt(v1) > asInt(v2)
@@ -237,12 +263,11 @@ trait NCIdlCompilerBase {
                     else
                         throw rtBinaryOpError(">", v1, v2)
 
-                Z(f, n1 + n2)
+                Z(f, n)
             })
         else if (lteq != null)
             stack.push(() ⇒ {
-                val Z(v1, n1) = x1()
-                val Z(v2, n2) = x2()
+                val (v1, v2, n) = extract2(x1, x2)
 
                 val f =
                     if (isInt(v1) && isInt(v2)) asInt(v1) <= asInt(v2)
@@ -252,14 +277,13 @@ trait NCIdlCompilerBase {
                     else
                         throw rtBinaryOpError("<=", v1, v2)
 
-                Z(f, n1 + n2)
+                Z(f, n)
             })
         else {
             require(gteq != null)
 
             stack.push(() ⇒ {
-                val Z(v1, n1) = x1()
-                val Z(v2, n2) = x2()
+                val (v1, v2, n) = extract2(x1, x2)
 
                 val f =
                     if (isInt(v1) && isInt(v2)) asInt(v1) >= asInt(v2)
@@ -269,7 +293,7 @@ trait NCIdlCompilerBase {
                     else
                         throw rtBinaryOpError(">=", v1, v2)
 
-                Z(f, n1 + n2)
+                Z(f, n)
             })
         }
     }
@@ -285,22 +309,20 @@ trait NCIdlCompilerBase {
 
         if (mult != null)
             stack.push(() ⇒ {
-                val Z(v1, n1) = x1()
-                val Z(v2, n2) = x2()
+                val (v1, v2, n) = extract2(x1, x2)
 
-                if (isInt(v1) && isInt(v2)) Z(asInt(v1) * asInt(v2), n1 + n2)
-                else if (isInt(v1) && isReal(v2)) Z(asInt(v1) * asReal(v2), n1 + n2)
-                else if (isReal(v1) && isInt(v2)) Z(asReal(v1) * asInt(v2), n1 + n2)
-                else if (isReal(v1) && isReal(v2)) Z(asReal(v1) * asReal(v2), n1 + n2)
+                if (isInt(v1) && isInt(v2)) Z(asInt(v1) * asInt(v2), n)
+                else if (isInt(v1) && isReal(v2)) Z(asInt(v1) * asReal(v2), n)
+                else if (isReal(v1) && isInt(v2)) Z(asReal(v1) * asInt(v2), n)
+                else if (isReal(v1) && isReal(v2)) Z(asReal(v1) * asReal(v2), n)
                 else
                     throw rtBinaryOpError("*", v1, v2)
             })
         else if (mod != null)
             stack.push(() ⇒ {
-                val Z(v1, n1) = x1()
-                val Z(v2, n2) = x2()
+                val (v1, v2, n) = extract2(x1, x2)
 
-                if (isInt(v1) && isInt(v2)) Z(asInt(v1) % asInt(v2), n1 + n2)
+                if (isInt(v1) && isInt(v2)) Z(asInt(v1) % asInt(v2), n)
                 else
                     throw rtBinaryOpError("%", v1, v2)
             })
@@ -308,13 +330,12 @@ trait NCIdlCompilerBase {
             assert(div != null)
 
             stack.push(() ⇒ {
-                val Z(v1, n1) = x1()
-                val Z(v2, n2) = x2()
+                val (v1, v2, n) = extract2(x1, x2)
 
-                if (isInt(v1) && isInt(v2)) Z(asInt(v1) / asInt(v2), n1 + n2)
-                else if (isInt(v1) && isReal(v2)) Z(asInt(v1) / asReal(v2), n1 + n2)
-                else if (isReal(v1) && isInt(v2)) Z(asReal(v1) / asInt(v2), n1 + n2)
-                else if (isReal(v1) && isReal(v2)) Z(asReal(v1) / asReal(v2), n1 + n2)
+                if (isInt(v1) && isInt(v2)) Z(asInt(v1) / asInt(v2), n)
+                else if (isInt(v1) && isReal(v2)) Z(asInt(v1) / asReal(v2), n)
+                else if (isReal(v1) && isInt(v2)) Z(asReal(v1) / asInt(v2), n)
+                else if (isReal(v1) && isReal(v2)) Z(asReal(v1) / asReal(v2), n)
                 else
                     throw rtBinaryOpError("/", v1, v2)
             })
@@ -375,8 +396,7 @@ trait NCIdlCompilerBase {
         }
 
         stack.push(() ⇒ {
-            val Z(v1, n1) = x1()
-            val Z(v2, n2) = x2()
+            val (v1, v2, n) = extract2(x1, x2)
 
             val f =
                 if (eq != null)
@@ -387,7 +407,7 @@ trait NCIdlCompilerBase {
                     !doEq("!='", v1, v2)
                 }
 
-            Z(f, n1 + n2)
+            Z(f, n)
         })
     }
 
@@ -399,16 +419,9 @@ trait NCIdlCompilerBase {
     def parsePlusMinusExpr(plus: TN, minus: TN)(implicit ctx: PRC): SI = (_, stack: S, _) ⇒ {
         val (x1, x2) = pop2()(stack, ctx)
 
-        def extract(): (Object, Object, Int) = {
-            val Z(v1, n1) = x1()
-            val Z(v2, n2) = x2()
-
-            (v1, v2, n1 + n2)
-        }
-
         if (plus != null)
             stack.push(() ⇒ {
-                val (v1, v2, n) = extract()
+                val (v1, v2, n) = extract2(x1, x2)
 
                 if (isStr(v1) && isStr(v2)) Z(asStr(v1) + asStr(v2), n)
                 else if (isInt(v1) && isInt(v2)) Z(asInt(v1) + asInt(v2), n)
@@ -422,7 +435,7 @@ trait NCIdlCompilerBase {
             assert(minus != null)
 
             stack.push(() ⇒ {
-                val (v1, v2, n) = extract()
+                val (v1, v2, n) = extract2(x1, x2)
 
                 if (isInt(v1) && isInt(v2)) Z(asInt(v1) - asInt(v2), n)
                 else if (isInt(v1) && isReal(v2)) Z(asInt(v1) - asReal(v2), n)
@@ -541,20 +554,6 @@ trait NCIdlCompilerBase {
         def toBool(v: Object): Boolean = toX("boolean", v, isBool, asBool)
         def toDouble(v: Object): JDouble = toX("double or int", v, x ⇒ isInt(x) || isReal(x), asReal)
 
-        def extract2(x1: ST, x2: ST): (Object, Object, Int) = {
-            val Z(v1, n1) = x1()
-            val Z(v2, n2) = x2()
-
-            (v1, v2, n1 + n2)
-        }
-        def extract3(x1: ST, x2: ST, x3: ST): (Object, Object, Object, Int) = {
-            val Z(v1, n1) = x1()
-            val Z(v2, n2) = x2()
-            val Z(v3, n3) = x3()
-
-            (v1, v2, v3, n1 + n2 + n3)
-        }
-
         def doSplit(): Unit = {
             val (x1, x2) = arg2()