diff options
-rw-r--r-- | src/func.cc | 144 | ||||
-rw-r--r-- | testdata/scad/misc/search-tests.scad | 63 | ||||
-rw-r--r-- | tests/CMakeLists.txt | 3 | ||||
-rw-r--r-- | tests/regression/echotest/search-tests-expected.txt | 20 |
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]]" |