initially, I ended up using Lua.
They seem to be about the same, speed-wise, so it's a matter of what you prefer working with.
(updated to include "SQLite like" statement for Lua matches 2023/04/18 01:41UTC)
Code: Select all
EnableExplicit
XIncludeFile "lua\module_lua.pbi" ;http://forums.purebasic.com/english/viewtopic.php?t=67365
DeclareModule LuaEval
Declare uate( condition.s )
Declare done()
EndDeclareModule
Module LuaEval
Global Lua = #Null
Global NewMap regex()
Declare init() : init()
#likeStatement = ~"\".*\" like \".*\""
#likeElements = ~"(?<=\").*?(?=\")";~"\".*?\""
UseModule Lua
;- Exported functions to Lua
ProcedureC LuaRegEx( *l )
Protected ParamCount,i.i, str.s, pattern.s, isLike = #False
ParamCount = lua_gettop(*L)
If ParamCount = 2
If lua_isstring(*l,1) And lua_isstring(*l,2)
str = lua_tostring(*L, 1)
pattern = lua_tostring(*l,2)
If FindMapElement(regex(), pattern)=#False
regex(pattern) = CreateRegularExpression(#PB_Any,pattern)
EndIf
If regex(pattern)
isLike = MatchRegularExpression(regex(pattern),str)
lua_pushboolean(*l,islike)
EndIf
EndIf
EndIf
ProcedureReturn isLike
EndProcedure
;- Our functions.
Procedure uate( condition.s )
;Static lua = #Null
Protected retval = -1,*script, scriptLength
Protected Dim like.s(0)
If lua
If MatchRegularExpression(regex(#likeStatement), condition)
If ExtractRegularExpression(regex(#likeElements), condition, like()) = 3 And Trim(LCase(like(1)))="like"
condition = "match('"+like(0)+"','"+ReplaceString(like(2),"%",".*")+"')"
Debug condition
EndIf
EndIf
*script = Ascii(~"function eval1()\n"+
~"return("+condition+~")\n"+
~"end")
scriptLength = MemorySize(*script)-1 ; Remove final Null
If luaL_loadbuffer(lua,*script,scriptLength,"Eval1") Or lua_pcall(LUA, 0, #LUA_MULTRET, 0)
Debug "Lua error loading buffer - " + lua_tostring(LUA, -1)
lua_pop(LUA, 1)
Else
lua_getglobal(lua,"eval1")
If lua_pcall(lua,0,1,0)
Debug "Lua error calling eval1() - "+lua_tostring(lua,-1)
lua_pop(LUA, 1)
Else
retval = lua_toboolean(lua,-1)
EndIf
EndIf
FreeMemory(*script)
EndIf
ProcedureReturn retval
EndProcedure
Procedure init()
If Lua_Initialize(".\")
lua=luaL_newstate() ; Create a new lua-vm, it is possible to have more than one.
If lua
luaL_openlibs(LUA) ; initalize the default librarys
lua_register(LUA, "match", @LuaRegEx()) ; Replace print, so it output to the debug
EndIf
regex(#likeStatement) = CreateRegularExpression(#PB_Any, #likeStatement)
regex(#likeElements) = CreateRegularExpression(#PB_Any, #likeElements )
EndIf
EndProcedure
Procedure done()
Lua_close(lua)
Lua_Dispose()
lua=#Null
ForEach regex()
FreeRegularExpression(regex())
Next
ClearMap(regex())
EndProcedure
UnuseModule Lua
EndModule
CompilerIf #PB_Compiler_IsMainFile
Debug "No: " + Str(LuaEval::uate(~"\"this\" == \"that\"" ))
Debug "Yes: " + Str(LuaEval::uate(~"\"this\" == \"this\"" ))
Debug "No: " + Str(LuaEval::uate("3 > 4"))
Debug "Yes: " + Str(LuaEval::uate("3 < 4"))
;No such phrasing like in SQLite "this LIKE that"
Debug "---"
Debug "Yes: " + Str(LuaEval::uate(~"\"I am josh,and I am here\" like \"%josh%\""))
Debug "No: " + Str(LuaEval::uate(~"\"I am joXh,and I am here\" like \"%josh%\""))
Debug "Yes: " + Str(LuaEval::uate(~"match(\"I am josh, and i am here\",\"josh\")"))
Debug "Yes: " + Str(LuaEval::uate(~"match(\"Find the number 423 but not 324\",\"4[0-9]{2}\")"))
Debug "NO: " + Str(LuaEval::uate(~"match(\"I am josh, and i am here\",\"Josh\")"))
Debug "yes: " + Str(LuaEval::uate(~"match(\"I am josh, and i am here\",\"(?i)Josh\")"))
Debug "No: " + Str(LuaEval::uate(~"match(\"Won't Find neither the number 4.23 nor not 324\",\"4[0-9]{2}\")"))
; more realistic example
Define user.s = "%dow% == 2 and %hour% > 12" ; ie: User entered the condition in a stringgadget.\
; now replace the application specific tokens
user = ReplaceString(user,"%hour%",FormatDate("%hh",Date()))
user = ReplaceString(user,"%dow%", Str(DayOfWeek(Date())))
Debug "Tuesday, after 12? "+Str(LuaEval::uate( user ))
LuaEval::done()
CompilerEndIf
Code: Select all
UseSQLiteDatabase()
DeclareModule SQLeval
Declare uate( condition.s )
EndDeclareModule
Module SQLeval
ImportC ""
sqlite3_create_function.i(DatabaseID, zFunctionName.p-utf8, nArg.i, eTextRep.i, *pApp, *xFunc, *xStep, *xFinal)
sqlite3_aggregate_context(*sqlite3_context, nBytes.i)
sqlite3_result_int(*sqlite3_value, lVal.l)
sqlite3_result_int64(*sqlite3_value, qVal.q)
sqlite3_result_double(*sqlite3_context, dblVal.d)
sqlite3_result_text(*sqlite3_context, *char, cBytes, *void1, *void2)
sqlite3_result_text16(*sqlite3_context, *char, cBytes, *void1, *void2)
sqlite3_value_numeric_type.i(*sqlite3_value)
sqlite3_value_int.l(*sqlite3_value)
sqlite3_value_int64.q(*sqlite3_value)
sqlite3_value_double.d(*sqlite3_value)
sqlite3_value_text(*sqlite3_value)
sqlite3_value_text16(*sqlite3_value)
sqlite3_value_type.i(*argv)
EndImport
;- Constants
;{
#SQLITE_UTF8 = 1 ; IMP: R-37514-35566
#SQLITE_UTF16LE = 2 ; IMP: R-03371-37637
#SQLITE_UTF16BE = 3 ; IMP: R-51971-34154
#SQLITE_UTF16 = 4 ; Use native byte order
#SQLITE_ANY = 5 ; Deprecated
#SQLITE_UTF16_ALIGNED = 8 ; sqlite3_create_collation only
#SQLITE_INTEGER = 1
#SQLITE_FLOAT = 2
#SQLITE_TEXT = 3
#SQLITE_BLOB = 4
#SQLITE_NULL = 5
#SQLITE_STATIC = 0
#SQLITE_TRANSIENT = -1
;}
Structure udtArgv
*Index[0]
EndStructure
Global dbID
Procedure Unicode(String.s)
Protected *mem
*mem = AllocateMemory(StringByteLength(String) + SizeOf(Character))
If *mem
PokeS(*mem, String, -1, #PB_UTF16)
EndIf
ProcedureReturn *mem
EndProcedure
CompilerIf #False ;- Example functions
ProcedureC sql_sin(*context, argc.i, *argv.udtArgv)
Protected a.d
a = sqlite3_value_double(*argv\Index[0])
a = Sin(a)
sqlite3_result_double(*context, a)
EndProcedure
ProcedureC sql_cos(*context, argc.i, *argv.udtArgv)
Protected a.d
a = sqlite3_value_double(*argv\Index[0])
a = Cos(a)
sqlite3_result_double(*context, a)
EndProcedure
ProcedureC sql_tan(*context, argc.i, *argv.udtArgv)
Protected a.d
a = sqlite3_value_double(*argv\Index[0])
a = Tan(a)
sqlite3_result_double(*context, a)
EndProcedure
ProcedureC sql_destructor_freememory(*void)
If *void
Debug "SQL-Destructor: FreeMemory: " + *void
FreeMemory(*void)
EndIf
EndProcedure
ProcedureC sql_lcase(*context, argc.i, *argv.udtArgv)
Protected *string, *result
*string = sqlite3_value_text(*argv\Index[0])
*result = UTF8(LCase(PeekS(*string, -1, #PB_UTF8)))
sqlite3_result_text(*context, *result, -1, @sql_destructor_freememory(), 0)
EndProcedure
ProcedureC sql_ucase(*context, argc.i, *argv.udtArgv)
Protected *string, *result
*string = sqlite3_value_text(*argv\Index[0])
*result = UTF8(UCase(PeekS(*string, -1, #PB_UTF8)))
sqlite3_result_text(*context, *result, -1, @sql_destructor_freememory(), 0)
EndProcedure
CompilerEndIf
;- Our exported functions
ProcedureC sql_regex( *context, argc.i, *argv.udtArgv )
Protected *string, *pattern, result
Protected.s string, pattern
Static NewMap regex()
*string = sqlite3_value_text(*argv\Index[0])
*pattern = sqlite3_value_text(*argv\index[1])
pattern = PeekS(*pattern,-1,#PB_UTF8)
string = PeekS(*string,-1,#PB_UTF8)
If FindMapElement(regex(),pattern)=#False
regex(pattern)=CreateRegularExpression(#PB_Any,pattern)
EndIf
result = MatchRegularExpression(regex(pattern),string)
sqlite3_result_int( *context, result)
EndProcedure
Procedure uate( condition.s )
;Debug condition
Protected result = -1
;ReplaceString(condition,#DQUOTE$,"'",#PB_String_InPlace)
condition = ReplaceString(condition,"'","''")
If DatabaseQuery(dbid, "select ("+condition+");")
If NextDatabaseRow(dbid)
result = GetDatabaseLong(dbid, 0)
EndIf
FinishDatabaseQuery(dbid)
Else : Debug condition
EndIf
ProcedureReturn result
EndProcedure
Procedure init( )
;Protected dataBaseID.i = DatabaseID()
UseSQLiteDatabase()
dbID = OpenDatabase(#PB_Any,":memory:","","")
sqlite3_create_function(DatabaseID(dbID), "match", 2, #SQLITE_UTF8, #Null, @sql_regex(), #Null, #Null)
EndProcedure
init()
EndModule
CompilerIf #PB_Compiler_IsMainFile
debug "No: " + Str(SQLeval::uate(~"\"this\" == \"that\"" ))
debug "Yes: " + Str(SQLeval::uate(~"\"this\" == \"this\"" ))
debug "No: " + Str(SQLeval::uate("3 > 4"))
debug "Yes: " + Str(SQLeval::uate("3 < 4"))
; sqlite specific "like"
debug "Yes: " + Str(SQLeval::uate(~"\"I am josh,and I am here\" like \"%josh%\""))
debug "No: " + Str(SQLeval::uate(~"\"I am joXh,and I am here\" like \"%josh%\""))
debug "Yes: " + Str(SQLeval::uate(~"match(\"I am josh, and i am here\",\"josh\")"))
debug "Yes: " + Str(SQLeval::uate(~"match(\"Find the number 423 but not 324\",\"4[0-9]{2}\")"))
debug "NO: " + Str(SQLeval::uate(~"match(\"I am josh, and i am here\",\"Josh\")"))
debug "yes: " + Str(SQLeval::uate(~"match(\"I am josh, and i am here\",\"(?i)Josh\")"))
debug "No: " + Str(SQLeval::uate(~"match(\"Won't Find neither the number 4.23 nor not 324\",\"4[0-9]{2}\")"))
; more realistic example
Define user.s = "%dow% == 2 and %hour% > 12" ; ie: User entered the condition in a stringgadget.\
; now replace the application specific tokens
user = ReplaceString(user,"%hour%",FormatDate("%hh",Date()))
user = ReplaceString(user,"%dow%", Str(DayOfWeek(Date())))
debug "Tuesday, after 12? "+Str(SQLeval::uate( user ))
CompilerEndIf
