summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/func.cc144
-rw-r--r--testdata/scad/misc/search-tests.scad63
-rw-r--r--tests/CMakeLists.txt3
-rw-r--r--tests/regression/echotest/search-tests-expected.txt20
4 files changed, 229 insertions, 1 deletions
diff --git a/src/func.cc b/src/func.cc
index 6686cb9..1dc1364 100644
--- a/src/func.cc
+++ b/src/func.cc
@@ -33,6 +33,7 @@
#include "mathc99.h"
#include <algorithm>
#include "stl-utils.h"
+#include "printutils.h"
AbstractFunction::~AbstractFunction()
{
@@ -345,6 +346,148 @@ Value builtin_lookup(const Context *, const std::vector<std::string>&, const std
return Value(high_v * f + low_v * (1-f));
}
+/*
+ Pattern:
+
+ "search" "(" ( match_value | list_of_match_values ) "," vector_of_vectors
+ ("," num_returns_per_match
+ ("," index_col_num )? )?
+ ")";
+ match_value : ( Value::NUMBER | Value::STRING );
+ list_of_values : "[" match_value ("," match_value)* "]";
+ vector_of_vectors : "[" ("[" Value ("," Value)* "]")+ "]";
+ num_returns_per_match : int;
+ index_col_num : int;
+
+ Examples:
+ Index values return as list:
+ search("a","abcdabcd");
+ - returns [0,4]
+ search("a","abcdabcd",1);
+ - returns [0]
+ search("e","abcdabcd",1);
+ - returns []
+ search("a",[ ["a",1],["b",2],["c",3],["d",4],["a",5],["b",6],["c",7],["d",8],["e",9] ]);
+ - returns [0,4]
+
+ Search on different column; return Index values:
+ search(3,[ ["a",1],["b",2],["c",3],["d",4],["a",5],["b",6],["c",7],["d",8],["e",3] ], 0, 1);
+ - returns [0,8]
+
+ Search on list of values:
+ Return all matches per search vector element:
+ search("abc",[ ["a",1],["b",2],["c",3],["d",4],["a",5],["b",6],["c",7],["d",8],["e",9] ], 0);
+ - returns [[0,4],[1,5],[2,6]]
+
+ Return first match per search vector element; special case return vector:
+ search("abc",[ ["a",1],["b",2],["c",3],["d",4],["a",5],["b",6],["c",7],["d",8],["e",9] ], 1);
+ - returns [0,1,2]
+
+ Return first two matches per search vector element; vector of vectors:
+ search("abce",[ ["a",1],["b",2],["c",3],["d",4],["a",5],["b",6],["c",7],["d",8],["e",9] ], 2);
+ - returns [[0,4],[1,5],[2,6],[8]]
+
+*/
+Value builtin_search(const Context *, const std::vector<std::string>&, const std::vector<Value> &args)
+{
+ Value findThis;
+ Value searchTable;
+ Value returnVector;
+ returnVector.type = Value::VECTOR;
+ unsigned int num_returns_per_match = 1;
+ unsigned int index_col_num=0;
+ if (args.size() < 2 )
+ return Value();
+ findThis=args[0];
+ // PRINTB(" builtin_search: findThis = %s",findThis);
+ searchTable=args[1];
+ // PRINTB(" builtin_search: searchTable = %s",searchTable);
+ if ( args.size() > 2 ) num_returns_per_match=args[2].num;
+ if ( args.size() > 3 ) index_col_num=args[3].num;
+ if ( findThis.type==Value::NUMBER ) {
+ // PRINTB(" builtin_search: findThis type: NUMBER %s",findThis);
+ unsigned int matchCount=0;
+ Value *resultVector = new Value();
+ resultVector->type = Value::VECTOR;
+ for (size_t j = 0; j < searchTable.vec.size(); j++) {
+ if ( searchTable.vec[j]->vec[index_col_num]->type==Value::NUMBER && findThis.num == searchTable.vec[j]->vec[index_col_num]->num ) {
+ Value *resultValue;
+ resultValue = new Value(double(j));
+ returnVector.append(resultValue);
+ matchCount++;
+ if(num_returns_per_match!=0 && matchCount>=num_returns_per_match) break;
+ }
+ }
+ } else if ( findThis.type==Value::STRING ) {
+ //PRINTB(" builtin_search: findThis type STRING %s",findThis);
+ //PRINTB(" builtin_search: checking findThis.text.size()==%s",findThis.text.size());
+ unsigned int searchTableSize;
+ if(searchTable.type == Value::STRING) {
+ searchTableSize=searchTable.text.size();
+ } else {
+ searchTableSize=searchTable.vec.size();
+ }
+ for (size_t i = 0; i < findThis.text.size(); i++) {
+ unsigned int matchCount=0;
+ Value *resultVector = new Value();
+ resultVector->type = Value::VECTOR;
+ for (size_t j = 0; j < searchTableSize; j++) {
+ // PRINTB(" builtin_search: checking findThis.text[i]==%s",findThis.text[i]);
+ if ( searchTable.type==Value::VECTOR && findThis.text[i] == searchTable.vec[j]->vec[index_col_num]->text[0]
+ || searchTable.type==Value::STRING && findThis.text[i] == searchTable.text[j]
+ ) {
+ Value *resultValue;
+ resultValue = new Value(double(j));
+ matchCount++;
+ if(num_returns_per_match==1) {
+ returnVector.append(resultValue);
+ break;
+ } else {
+ resultVector->append(resultValue);
+ }
+ if(num_returns_per_match>1 && matchCount>=num_returns_per_match) break;
+ }
+ }
+ if(matchCount==0) PRINTB(" search term not found: \"%s\"",findThis.text[i]);
+ if(num_returns_per_match==0 || num_returns_per_match>1) returnVector.append(resultVector);
+ }
+ ;
+ } else if ( findThis.type==Value::VECTOR ) {
+ // PRINTB(" builtin_search: findThis type: VECTOR %s",findThis);
+ for (size_t i = 0; i < findThis.vec.size(); i++) {
+ unsigned int matchCount=0;
+ Value *resultVector = new Value();
+ resultVector->type = Value::VECTOR;
+ for (size_t j = 0; j < searchTable.vec.size(); j++) {
+ Value *resultValue;
+ resultValue = new Value(double(j));
+ if ( findThis.vec[i]->type==Value::NUMBER && searchTable.vec[j]->vec[index_col_num]->type==Value::NUMBER && findThis.vec[i]->num == searchTable.vec[j]->vec[index_col_num]->num
+ || findThis.vec[i]->type==Value::STRING && searchTable.vec[j]->vec[index_col_num]->type==Value::STRING && findThis.vec[i]->text == searchTable.vec[j]->vec[index_col_num]->text ) {
+ resultValue = new Value(double(j));
+ matchCount++;
+ if(num_returns_per_match==1) {
+ returnVector.append(resultValue);
+ break;
+ } else {
+ resultVector->append(resultValue);
+ }
+ if(num_returns_per_match>1 && matchCount>=num_returns_per_match) break;
+ }
+ }
+ if( num_returns_per_match==1 && matchCount==0 ) {
+ if(findThis.vec[i]->type==Value::NUMBER) PRINTB(" search term not found: %s",findThis.vec[i]->num);
+ if(findThis.vec[i]->type==Value::STRING) PRINTB(" search term not found: \"%s\"",findThis.vec[i]->text);
+ returnVector.append(resultVector);
+ }
+ if(num_returns_per_match==0 || num_returns_per_match>1) returnVector.append(resultVector);
+ }
+ } else {
+ PRINTB(" search: none performed on input %s",findThis);
+ return Value();
+ }
+ return returnVector;
+}
+
#define QUOTE(x__) # x__
#define QUOTED(x__) QUOTE(x__)
@@ -397,6 +540,7 @@ void register_builtin_functions()
Builtins::init("ln", new BuiltinFunction(&builtin_ln));
Builtins::init("str", new BuiltinFunction(&builtin_str));
Builtins::init("lookup", new BuiltinFunction(&builtin_lookup));
+ Builtins::init("search", new BuiltinFunction(&builtin_search));
Builtins::init("version", new BuiltinFunction(&builtin_version));
Builtins::init("version_num", new BuiltinFunction(&builtin_version_num));
}
diff --git a/testdata/scad/misc/search-tests.scad b/testdata/scad/misc/search-tests.scad
new file mode 100644
index 0000000..fb85109
--- /dev/null
+++ b/testdata/scad/misc/search-tests.scad
@@ -0,0 +1,63 @@
+// string searches
+
+simpleSearch1=search("a","abcdabcd");
+echo(str("Characters in string (\"a\"): ",simpleSearch1));
+
+simpleSearch2=search("adeq","abcdeabcd",0);
+echo(str("Characters in string (\"adeq\"): ",simpleSearch2));
+
+sTable1=[ ["a",1],["b",2],["c",3],["d",4],["a",5],["b",6],["c",7],["d",8],["e",9] ];
+s1= search("abe",sTable1);
+echo(str("Default string search (\"abe\"): ",s1));
+
+sTable2=[ ["a",1],["b",2],["c",3],["d",4],["a",5],["b",6],["c",7],["d",8],["e",9],["a",10],["a",11] ];
+s2= search("abe",sTable2,0);
+echo(str("Return all matches for string search (\"abe\"): ",s2));
+
+sTable3=[ ["a",1],["b",2],["c",3],["d",4],["a",5],["b",6],["c",7],["d",8],["e",9],["a",10],["a",11] ];
+s3= search("abe",sTable3,2);
+echo(str("Return up to 2 matches for string search (\"abe\"): ",s3));
+
+sTable4=[ [1,"a",[20]],[2,"b",21],[3,"c",22],[4,"d",23],[5,"a",24],[6,"b",25],[7,"c",26],[8,"d",27],[9,"e",28],[10,"a",29],[11,"a",30] ];
+s4= search("aebe",sTable4,2,1);
+echo(str("Return up to 2 matches for string search; alternate columns (\"aebe\"): ",s4));
+
+// s5= search("abe",sTable4,2,1,3); // bounds checking needs fixing.
+// echo(str("Return up to 2 matches for string search; alternate columns: ",s4));
+
+
+// number searches
+nTable1=[ [1,"a"],[3,"b"],[2,"c"],[4,"d"],[1,"a"],[7,"b"],[2,"c"],[8,"d"],[9,"e"],[10,"a"],[1,"a"] ];
+n1 = search(7,nTable1);
+echo(str("Default number search (7): ",n1));
+n2 = search(1,nTable1,0);
+echo(str("Return all matches for number search (1): ",n2));
+n3 = search(1,nTable1,2);
+echo(str("Return up to 2 matches for number search (1): ",n3));
+
+// list searches
+lTable1=[ [1,"a"],[3,"b"],[2,"c"],[4,"d"],[1,"a"],[7,"b"],[2,"c"],[8,"d"],[9,"e"],[10,"a"],[1,"a"] ];
+lSearch1=[1,3,1000];
+l1=search(lSearch1,lTable1);
+echo(str("Default list number search (",lSearch1,"): ",l1));
+
+lTable2=[ ["cat",1],["b",2],["c",3],["dog",4],["a",5],["b",6],["c",7],["d",8],["e",9],["apple",10],["a",11] ];
+lSearch2=["b","zzz","a","c","apple","dog"];
+l2=search(lSearch2,lTable2);
+echo(str("Default list string search (",lSearch2,"): ",l2));
+
+lTable3=[ ["cat",1],["b",2],["c",3],[4,"dog"],["a",5],["b",6],["c",7],["d",8],["e",9],["apple",10],["a",11] ];
+lSearch3=["b",4,"zzz","c","apple",500,"a",""];
+l3=search(lSearch3,lTable3);
+echo(str("Default list mixed search (",lSearch3,"): ",l3));
+
+l4=search(lSearch3,lTable3,0);
+echo(str("Return all matches for mixed search (",lSearch3,"): ",l4));
+
+lSearch5=[1,"zz","dog",500,11];
+l5=search(lSearch5,lTable3,0,1);
+echo(str("Return all matches for mixed search; alternate columns (",lSearch5,"): ",l5));
+
+
+// for completeness
+cube(1.0);
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 4329795..f8c66d8 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -609,7 +609,8 @@ list(APPEND ECHO_FILES ${FUNCTION_FILES}
${CMAKE_SOURCE_DIR}/../testdata/scad/misc/dim-all.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/misc/string-test.scad
${CMAKE_SOURCE_DIR}/../testdata/scad/misc/string-indexing.scad
- ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/vector-values.scad)
+ ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/vector-values.scad
+ ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/search-tests.scad)
list(APPEND DUMPTEST_FILES ${MINIMAL_FILES} ${FEATURES_FILES} ${EXAMPLE_FILES})
list(APPEND DUMPTEST_FILES ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/escape-test.scad
diff --git a/tests/regression/echotest/search-tests-expected.txt b/tests/regression/echotest/search-tests-expected.txt
new file mode 100644
index 0000000..64df0b6
--- /dev/null
+++ b/tests/regression/echotest/search-tests-expected.txt
@@ -0,0 +1,20 @@
+ search term not found: "q"
+ search term not found: 1000
+ search term not found: "zzz"
+ search term not found: "zzz"
+ search term not found: 500
+ search term not found: ""
+ECHO: "Characters in string (\"a\"): [0]"
+ECHO: "Characters in string (\"adeq\"): [[0, 5], [3, 8], [4], []]"
+ECHO: "Default string search (\"abe\"): [0, 1, 8]"
+ECHO: "Return all matches for string search (\"abe\"): [[0, 4, 9, 10], [1, 5], [8]]"
+ECHO: "Return up to 2 matches for string search (\"abe\"): [[0, 4], [1, 5], [8]]"
+ECHO: "Return up to 2 matches for string search; alternate columns (\"aebe\"): [[0, 4], [8], [1, 5], [8]]"
+ECHO: "Default number search (7): [5]"
+ECHO: "Return all matches for number search (1): [0, 4, 10]"
+ECHO: "Return up to 2 matches for number search (1): [0, 4]"
+ECHO: "Default list number search ([1, 3, 1000]): [0, 1, []]"
+ECHO: "Default list string search ([\"b\", \"zzz\", \"a\", \"c\", \"apple\", \"dog\"]): [1, [], 4, 2, 9, 3]"
+ECHO: "Default list mixed search ([\"b\", 4, \"zzz\", \"c\", \"apple\", 500, \"a\", \"\"]): [1, 3, [], 2, 9, [], 4, []]"
+ECHO: "Return all matches for mixed search ([\"b\", 4, \"zzz\", \"c\", \"apple\", 500, \"a\", \"\"]): [[1, 5], [3], [], [2, 6], [9], [], [4, 10], []]"
+ECHO: "Return all matches for mixed search; alternate columns ([1, \"zz\", \"dog\", 500, 11]): [[0], [], [3], [], [10]]"
contact: Jan Huwald // Impressum