diff --git a/code/modules/admin/verbs/SDQL2/SDQL_2.dm b/code/modules/admin/verbs/SDQL2/SDQL_2.dm
index 2147157dd72..b666467c7a3 100644
--- a/code/modules/admin/verbs/SDQL2/SDQL_2.dm
+++ b/code/modules/admin/verbs/SDQL2/SDQL_2.dm
@@ -71,15 +71,8 @@
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)
- CHECK_TICK
-
- else if(char == "'" || char == "\"")
- objs += locate(copytext(type, 2, length(type)))
+ objs += SDQL_get_all(type, from_objs)
+ CHECK_TICK
if("where" in query_tree)
var/objs_temp = objs
@@ -91,19 +84,8 @@
switch(query_tree[1])
if("call")
- var/list/call_list = query_tree["call"]
- var/list/args_list = query_tree["args"]
-
for(var/datum/d in objs)
- var/list/new_args = list()
- for(var/a in args_list)
- new_args += SDQL_expression(d,a)
- for(var/v in call_list)
- if(copytext(v,1,8) == "global.")
- v = "/proc/[copytext(v,8)]"
- SDQL_callproc_global(v,new_args)
- else
- SDQL_callproc(d, v, new_args)
+ SDQL_var(d, query_tree["call"][1], source = d)
CHECK_TICK
if("delete")
@@ -231,30 +213,15 @@
/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
-
+ return world
+ return SDQL_expression(world, tree)
/proc/SDQL_get_all(type, location)
var/list/out = list()
- if(type == "*")
- for(var/datum/d in location)
- out += d
-
- return out
+// If only a single object got returned, wrap it into a list so the for loops run on it.
+ if(!islist(location) && location != world)
+ location = list(location)
type = text2path(type)
var/typecache = typecacheof(type)
@@ -394,21 +361,32 @@
else if(expression[i] == "\[")
var/list/expressions_list = expression[++i]
- var/list/val2 = list()
+ val = list()
for(var/list/expression_list in expressions_list)
- val2[++val2.len] = SDQL_expression(object, expression_list)
- val = val2
+ var/result = SDQL_expression(object, expression_list)
+ var/assoc
+ if(expressions_list[expression_list] != null)
+ assoc = SDQL_expression(object, expressions_list[expression_list])
+ if(assoc != null)
+ // Need to insert the key like this to prevent duplicate keys fucking up.
+ var/list/dummy = list()
+ dummy[result] = assoc
+ result = dummy
+ val += result
else
- val = SDQL_var(object, expression, i)
+ 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)
+/proc/SDQL_var(datum/object, list/expression, start = 1, source)
var/v
- if(expression[start] in object.vars)
- v = object.vars[expression[start]]
- else if(expression[start] == "{" && start < expression.len)
+ var/static/list/exclude = list("usr", "src", "marked", "global")
+ 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
+ else if(expression [start] == "{" && long)
if(lowertext(copytext(expression[start + 1], 1, 3)) != "0x")
to_chat(usr, "Invalid pointer syntax: [expression[start + 1]]")
return null
@@ -417,28 +395,55 @@
to_chat(usr, "Invalid pointer: [expression[start + 1]]")
return null
start++
- else
+ else if((!long || 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 = object
+ 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
else
return null
- if(start < expression.len && expression[start + 1] == ".")
- return SDQL_var(v, expression[start + 2])
- else
- return v
+ else if(object == world) // Shitty ass hack kill me.
+ 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(usr, "Invalid list index: [index]")
+ return null
+ return L[index]
+ return v
+
+/proc/SDQL_function(var/datum/object, var/procname, var/list/arguments, source)
+ set waitfor = FALSE
+ var/list/new_args = list()
+ for(var/arg in arguments)
+ new_args += SDQL_expression(source, arg)
+ if(object == world) // Global proc.
+ procname = "/proc/[procname]"
+ return call(procname)(arglist(new_args))
+ return call(object, procname)(arglist(new_args)) // Spawn in case the function sleeps.
/proc/SDQL2_tokenize(query_text)
var/list/whitespace = list(" ", "\n", "\t")
- var/list/single = list("(", ")", ",", "+", "-", ".", ";", "{", "}", "\[", "]")
+ var/list/single = list("(", ")", ",", "+", "-", ".", ";", "{", "}", "\[", "]", ":")
var/list/multi = list(
"=" = list("", "="),
"<" = list("", "=", ">"),
diff --git a/code/modules/admin/verbs/SDQL2/SDQL_2_parser.dm b/code/modules/admin/verbs/SDQL2/SDQL_2_parser.dm
index 3692b9633df..61945f1dde7 100644
--- a/code/modules/admin/verbs/SDQL2/SDQL_2_parser.dm
+++ b/code/modules/admin/verbs/SDQL2/SDQL_2_parser.dm
@@ -116,18 +116,7 @@
i = select_list(i + 1, select)
node += "select"
node["select"] = select
- var/list/from = list()
- if(tokenl(i) in list("from", "in"))
- i = from_list(i + 1, from)
- else
- from += "world"
- node += "from"
- node["from"] = from
- if(tokenl(i) == "where")
- var/list/where = list()
- i = bool_expression(i + 1, where)
- node += "where"
- node["where"] = where
+ selectors(i, node)
return i
//delete_query: 'DELETE' select_list [('FROM' | 'IN') from_list] ['WHERE' bool_expression]
@@ -136,18 +125,7 @@
i = select_list(i + 1, select)
node += "delete"
node["delete"] = select
- var/list/from = list()
- if(tokenl(i) in list("from", "in"))
- i = from_list(i + 1, from)
- else
- from += "world"
- node += "from"
- node["from"] = from
- if(tokenl(i) == "where")
- var/list/where = list()
- i = bool_expression(i + 1, where)
- node += "where"
- node["where"] = where
+ selectors(i, node)
return i
//update_query: 'UPDATE' select_list [('FROM' | 'IN') from_list] 'SET' assignments ['WHERE' bool_expression]
@@ -156,52 +134,28 @@
i = select_list(i + 1, select)
node += "update"
node["update"] = select
- var/list/from = list()
- if(tokenl(i) in list("from", "in"))
- i = from_list(i + 1, from)
- else
- from += "world"
- node += "from"
- node["from"] = from
if(tokenl(i) != "set")
i = parse_error("UPDATE has misplaced SET")
var/list/set_assignments = list()
i = assignments(i + 1, set_assignments)
node += "set"
node["set"] = set_assignments
- if(tokenl(i) == "where")
- var/list/where = list()
- i = bool_expression(i + 1, where)
- node += "where"
- node["where"] = where
+ selectors(i, node)
return i
//call_query: 'CALL' call_function ['ON' select_list [('FROM' | 'IN') from_list] ['WHERE' bool_expression]]
/datum/SDQL_parser/proc/call_query(i, list/node)
var/list/func = list()
- var/list/arguments = list()
- i = call_function(i + 1, func, arguments)
+ i = variable(i + 1, func) // Yes technically does anything variable() matches but I don't care, if admins fuck up this badly then they shouldn't be allowed near SDQL.
node += "call"
node["call"] = func
- node["args"] = arguments
if(tokenl(i) != "on")
return i
var/list/select = list()
i = select_list(i + 1, select)
node += "on"
node["on"] = select
- var/list/from = list()
- if(tokenl(i) in list("from", "in"))
- i = from_list(i + 1, from)
- else
- from += "world"
- node += "from"
- node["from"] = from
- if(tokenl(i) == "where")
- var/list/where = list()
- i = bool_expression(i + 1, where)
- node += "where"
- node["where"] = where
+ selectors(i, node)
return i
//select_list: select_item [',' select_list]
@@ -211,13 +165,6 @@
i = select_list(i + 1, node)
return i
-//from_list: from_item [',' from_list]
-/datum/SDQL_parser/proc/from_list(i, list/node)
- i = from_item(i, node)
- if(token(i) == ",")
- i = from_list(i + 1, node)
- return i
-
//assignments: assignment, [',' assignments]
/datum/SDQL_parser/proc/assignments(i, list/node)
i = assignment(i, node)
@@ -236,13 +183,33 @@
i = object_type(i, node)
return i
+// Standardized method for handling the IN/FROM and WHERE options.
+/datum/SDQL_parser/proc/selectors(i, list/node)
+ while (token(i))
+ var/tok = tokenl(i)
+ if(tok in list("from", "in"))
+ var/list/from = list()
+ i = from_item(i + 1, from)
+ node["from"] = from
+ continue
+ if(tok == "where")
+ var/list/where = list()
+ i = bool_expression(i + 1, where)
+ node["where"] = where
+ continue
+ parse_error("Expected either FROM, IN or WHERE token, found [token(i)] instead.")
+ return i + 1
+ if(!node.Find("from"))
+ node["from"] = list("world")
+ return i
+
//from_item: 'world' | object_type
/datum/SDQL_parser/proc/from_item(i, list/node)
if(token(i) == "world")
node += "world"
i++
else
- i = object_type(i, node)
+ i = expression(i, node)
return i
//bool_expression: expression [bool_operator bool_expression]
@@ -280,6 +247,19 @@
if(token(i + 1) == ".")
L += "."
i = variable(i + 2, L)
+ else if(token(i + 1) == "(") // OH BOY PROC
+ var/list/arguments = list()
+ i = call_function(i, null, arguments)
+ L += ":"
+ L[++L.len] = arguments
+ else if(token(i + 1) == "\[")
+ var/list/expression = list()
+ i = expression(i + 2, expression)
+ if (token(i) != "]")
+ parse_error("Missing ] at the end of list access.")
+ L += "\["
+ L[++L.len] = expression
+ i++
else
i++
return i
@@ -291,17 +271,36 @@
return i + 1
node += token(i)
var/list/expression_list = list()
- if(token(i + 1) != "]")
+ i++
+ if(token(i) != "]")
var/list/temp_expression_list = list()
+ var/tok
do
- i = expression(i + 1, temp_expression_list)
- if(token(i) == ",")
+ tok = token(i)
+ if(tok == "," || tok == ":")
+ if(temp_expression_list == null)
+ parse_error("Found ',' or ':' without expression in an array.")
+ return i + 1
expression_list[++expression_list.len] = temp_expression_list
- temp_expression_list = list()
+ temp_expression_list = null
+ if(tok == ":")
+ temp_expression_list = list()
+ i = expression(i + 1, temp_expression_list)
+ expression_list[expression_list[expression_list.len]] = temp_expression_list
+ temp_expression_list = null
+ tok = token(i)
+ if(tok != ",")
+ if(tok == "]")
+ break
+ parse_error("Expected ',' or ']' after array assoc value, but found '[token(i)]'")
+ return i
+ i++
+ continue
+ temp_expression_list = list()
+ i = expression(i, temp_expression_list)
while(token(i) && token(i) != "]")
- expression_list[++expression_list.len] = temp_expression_list
- else
- i++
+ if(temp_expression_list)
+ expression_list[++expression_list.len] = temp_expression_list
node[++node.len] = expression_list
return i + 1