// Examples /* -- Will call the proc for all computers in the world, thats dir is 2. CALL ex_act(1) ON /obj/machinery/computer IN world WHERE dir == 2 -- Will open a window with a list of all the closets in the world, with a link to VV them. SELECT /obj/structure/closet/secure_closet/security/cargo IN world WHERE icon_off == "secoff" -- Will change all the tube lights to green UPDATE /obj/machinery/light IN world SET color = "#0F0" WHERE icon_state == "tube1" -- Will delete all pickaxes. "IN world" is not required. DELETE /obj/item/pickaxe -- Will flicker the lights once, then turn all mobs green. The semicolon is important to separate the consecutive querys, but is not required for standard one-query use CALL flicker(1) ON /obj/machinery/light; UPDATE /mob SET color = "#00cc00" --You can use operators other than ==, such as >, <=, != and etc.. */ /client/proc/SDQL2_query() set category = "Debug" if(!check_rights(R_PROCCALL)) //Shouldn't happen... but just to be safe. message_admins("ERROR: Non-admin [key_name_admin(usr)] attempted to execute a SDQL query!") log_admin("Non-admin [key_name(usr)] attempted to execute a SDQL query!") var/query_text = input("SDQL2 query") as message if(!query_text || length(query_text) < 1) return // to_chat(world, query_text) var/list/query_list = SDQL2_tokenize(query_text) if(!query_list || query_list.len < 1) return var/list/querys = SDQL_parse(query_list) if(!querys || querys.len < 1) return var/query_log = "executed SDQL query: \"[query_text]\"." message_admins("[key_name_admin(usr)] [query_log]") query_log = "[key_name(usr)] [query_log]" log_admin(query_log) try for(var/list/query_tree in querys) var/list/from_objs = list() var/list/select_types = list() switch(query_tree[1]) if("explain") SDQL_testout(query_tree["explain"]) return if("call") if("on" in query_tree) select_types = query_tree["on"] else return if("select", "delete", "update") select_types = query_tree[query_tree[1]] from_objs = SDQL_from_objs(query_tree["from"]) var/list/objs = list() for(var/type in select_types) var/char = copytext(type, 1, 2) if(char == "/" || char == "*") for(var/from in from_objs) objs += SDQL_get_all(type, from) else if(char == "'" || char == "\"") objs += locate(copytext(type, 2, length(type))) if("where" in query_tree) var/objs_temp = objs objs = list() for(var/d in objs_temp) if(SDQL_expression(d, query_tree["where"])) objs += d switch(query_tree[1]) if("call") for(var/d in objs) SDQL_var(d, query_tree["call"][1], source = d) if("delete") for(var/d in objs) if(istype(d, /datum)) var/datum/D = d if(!D.can_vv_delete()) to_chat(usr, "[D] rejected your deletion") continue qdel(d) if("select") var/text = "" for(var/o in objs) var/datum/t = o text += "\ref[t]" if(istype(t, /atom)) var/atom/a = t if(a.x) text += ": [t] at ([a.x], [a.y], [a.z])
" else if(a.loc && a.loc.x) text += ": [t] in [a.loc] at ([a.loc.x], [a.loc.y], [a.loc.z])
" else text += ": [t]
" else text += ": [t]
" usr << browse(text, "window=SDQL-result") if("update") if("set" in query_tree) var/list/set_list = query_tree["set"] for(var/d in objs) for(var/list/sets in set_list) var/datum/temp = d var/i = 0 for(var/v in sets) if(++i == sets.len) if(istype(temp, /turf) && (v == "x" || v == "y" || v == "z")) continue if(!temp.vv_edit_var(v, SDQL_expression(d, set_list[sets]))) to_chat(usr, "[temp] rejected your varedit.") break if(temp.vars.Find(v) && (istype(temp.vars[v], /datum) || istype(temp.vars[v], /client))) temp = temp.vars[v] else break catch(var/exception/e) to_chat(usr, "An exception has occured during the execution of your query and your query has been aborted.") to_chat(usr, " [e.name]") to_chat(usr, " at: [e.file]:[e.line]") /proc/SDQL_parse(list/query_list) var/datum/SDQL_parser/parser = new() var/list/querys = list() var/list/query_tree = list() var/pos = 1 var/querys_pos = 1 var/do_parse = 0 for(var/val in query_list) if(val == ";") do_parse = 1 else if(pos >= query_list.len) query_tree += val do_parse = 1 if(do_parse) parser.query = query_tree var/list/parsed_tree parsed_tree = parser.parse() if(parsed_tree.len > 0) querys.len = querys_pos querys[querys_pos] = parsed_tree querys_pos++ else //There was an error so don't run anything, and tell the user which query has errored. to_chat(usr, "Parsing error on [querys_pos]\th query. Nothing was executed.") return list() query_tree = list() do_parse = 0 else query_tree += val pos++ qdel(parser) return querys /proc/SDQL_testout(list/query_tree, indent = 0) var/spaces = "" for(var/s = 0, s < indent, s++) spaces += "    " for(var/item in query_tree) if(istype(item, /list)) to_chat(usr, "[spaces](") SDQL_testout(item, indent + 1) to_chat(usr, "[spaces])") else to_chat(usr, "[spaces][item]") if(!isnum(item) && query_tree[item]) if(istype(query_tree[item], /list)) to_chat(usr, "[spaces]    (") SDQL_testout(query_tree[item], indent + 2) to_chat(usr, "[spaces]    )") else to_chat(usr, "[spaces]    [query_tree[item]]") /proc/SDQL_from_objs(list/tree) if("world" in tree) return list(world) var/list/out = list() for(var/type in tree) var/char = copytext(type, 1, 2) if(char == "/") out += SDQL_get_all(type, world) else if(char == "'" || char == "\"") out += locate(copytext(type, 2, length(type))) return out /proc/SDQL_get_all(type, location) var/list/out = list() if(type == "*") for(var/datum/d in location) out += d return out type = text2path(type) if(ispath(type, /mob)) for(var/mob/d in location) if(istype(d, type)) out += d else if(ispath(type, /turf)) for(var/turf/d in location) if(istype(d, type)) out += d else if(ispath(type, /obj)) for(var/obj/d in location) if(istype(d, type)) out += d else if(ispath(type, /area)) for(var/area/d in location) if(istype(d, type)) out += d else if(ispath(type, /atom)) for(var/atom/d in location) if(istype(d, type)) out += d else if(ispath(type, /client)) for(var/client/C) if((location != world) && !(C.mob in location)) continue out += C else if(location == world) for(var/datum/d) if(istype(d, type)) out += d else for(var/datum/d in location) if(istype(d, type)) out += d return out /proc/SDQL_expression(datum/object, list/expression, start = 1) var/result = 0 var/val for(var/i = start, i <= expression.len, i++) var/op = "" if(i > start) op = expression[i] i++ var/list/ret = SDQL_value(object, expression, i) val = ret["val"] i = ret["i"] if(op != "") switch(op) if("+") result += val if("-") result -= val if("*") result *= val if("/") result /= val if("&") result &= val if("|") result |= val if("^") result ^= val if("=", "==") result = (result == val) if("!=", "<>") result = (result != val) if("<") result = (result < val) if("<=") result = (result <= val) if(">") result = (result > val) if(">=") result = (result >= val) if("and", "&&") result = (result && val) if("or", "||") result = (result || val) else to_chat(usr, "SDQL2: Unknown op [op]") result = null else result = val return result /proc/SDQL_value(datum/object, list/expression, start = 1) var/i = start var/val = null if(i > expression.len) return list("val" = null, "i" = i) if(istype(expression[i], /list)) val = SDQL_expression(object, expression[i]) else if(expression[i] == "!") var/list/ret = SDQL_value(object, expression, i + 1) val = !ret["val"] i = ret["i"] else if(expression[i] == "~") var/list/ret = SDQL_value(object, expression, i + 1) val = ~ret["val"] i = ret["i"] else if(expression[i] == "-") var/list/ret = SDQL_value(object, expression, i + 1) val = -ret["val"] i = ret["i"] else if(expression[i] == "null") val = null else if(isnum(expression[i])) val = expression[i] else if(copytext(expression[i], 1, 2) in list("'", "\"")) val = copytext(expression[i], 2, length(expression[i])) else if(expression[i] == "{") var/list/expressions_list = expression[++i] val = list() for(var/list/expression_list in expressions_list) val += SDQL_expression(object, expression_list) else val = SDQL_var(object, expression, i, object) i = expression.len return list("val" = val, "i" = i) /proc/SDQL_var(datum/object, list/expression, start = 1, source) var/v var/long = start < expression.len if(object == world && long && expression[start + 1] == ".") to_chat(usr, "Sorry, but global variables are not supported at the moment.") return null if(expression[start] == "\[" && long) if(lowertext(copytext(expression[start + 1], 1, 3)) != "0x") to_chat(usr, "Invalid ref syntax: [expression[start + 1]]") return null v = locate("\[[expression[start + 1]]\]") if(!v) to_chat(usr, "Invalid ref: [expression[start + 1]]") return null start++ else if((!long || expression[start + 1] == "." || expression[start + 1] == "\[") && (expression[start] in object.vars)) v = object.vars[expression[start]] else if(long && expression[start + 1] == ":" && hascall(object, expression[start])) v = expression[start] else if(!long || expression[start + 1] == ".") switch(expression[start]) if("usr") v = usr if("src") v = source if("marked") if(usr.client && usr.client.holder && usr.client.holder.marked_datum) v = usr.client.holder.marked_datum else return null if("global") v = world // World is mostly a token, really. else return null else if(object == world) v = expression[start] if(long) if(expression[start + 1] == ".") return SDQL_var(v, expression[start + 2], source = source) else if(expression[start + 1] == ":") return SDQL_function(object, v, expression[start + 2], source) else if(expression[start + 1] == "\[" && islist(v)) var/list/L = v var/index = SDQL_expression(source, expression[start + 2]) if(isnum(index) && (!ISINTEGER(index) || L.len < index)) to_chat(world, "Invalid list index: [index]") return null return L[index] return v /proc/SDQL_function(datum/object, procname, list/arguments, source) var/list/new_args = list() for(var/arg in arguments) new_args[++new_args.len] = SDQL_expression(source, arg) if(object == world) // Global proc. procname = "/proc/[procname]" return (WrapAdminProcCall(GLOBAL_PROC, procname, new_args)) return (WrapAdminProcCall(object, procname, new_args)) /proc/SDQL2_tokenize(query_text) var/list/whitespace = list(" ", "\n", "\t") var/list/single = list("(", ")", ",", "+", "-", ".", ";", "\[", "\]", "{", "}") var/list/multi = list( "=" = list("", "="), "<" = list("", "=", ">"), ">" = list("", "="), "!" = list("", "=")) var/word = "" var/list/query_list = list() var/len = length(query_text) for(var/i = 1, i <= len, i++) var/char = copytext(query_text, i, i + 1) if(char in whitespace) if(word != "") query_list += word word = "" else if(char in single) if(word != "") query_list += word word = "" query_list += char else if(char in multi) if(word != "") query_list += word word = "" var/char2 = copytext(query_text, i + 1, i + 2) if(char2 in multi[char]) query_list += "[char][char2]" i++ else query_list += char else if(char == "'") if(word != "") to_chat(usr, "SDQL2: You have an error in your SDQL syntax, unexpected ' in query: \"[query_text]\" following \"[word]\". Please check your syntax, and try again.") return null word = "'" for(i++, i <= len, i++) char = copytext(query_text, i, i + 1) if(char == "'") if(copytext(query_text, i + 1, i + 2) == "'") word += "'" i++ else break else word += char if(i > len) to_chat(usr, "SDQL2: You have an error in your SDQL syntax, unmatched ' in query: \"[query_text]\". Please check your syntax, and try again.") return null query_list += "[word]'" word = "" else if(char == "\"") if(word != "") to_chat(usr, "SDQL2: You have an error in your SDQL syntax, unexpected \" in query: \"[query_text]\" following \"[word]\". Please check your syntax, and try again.") return null word = "\"" for(i++, i <= len, i++) char = copytext(query_text, i, i + 1) if(char == "\"") if(copytext(query_text, i + 1, i + 2) == "'") word += "\"" i++ else break else word += char if(i > len) to_chat(usr, "SDQL2: You have an error in your SDQL syntax, unmatched \" in query: \"[query_text]\". Please check your syntax, and try again.") return null query_list += "[word]\"" word = "" else word += char if(word != "") query_list += word return query_list