From 1cf1df847c64282929618e785b162f725cc52d34 Mon Sep 17 00:00:00 2001 From: JJRcop Date: Mon, 15 Apr 2019 03:22:30 -0400 Subject: [PATCH] [READY] Adds selector lists to SDQL (#43383) * [SDQL2] Adds selector arrays * SDQL2 documentation spec updated slightly * SDQL2 selector array docs + tweaks If a single list is the only result of a MAP statement, meaning objs.len == 1, AND it's a list, it will unwrap the result from its containing list and return it directly. The selector array logic keeps track of the state of the query when it starts the first Search. It won't change the state back to Execute until the top-level is reached, which sets a var to keep track. * SDQL2 MAP change from objs.len to length(objs) * [SDQL2] Reverts MAP list unwrap * [SDQL] Move (expr) from expr, unary_expr to var This should result in the same functionality but instead of expressions and unary_expressions processing sub expressions, variables do This means that expression->value->variable is now completely recursable * [SDQL2] Update selector list documentation --- code/modules/admin/verbs/SDQL2/SDQL_2.dm | 27 +++++- .../admin/verbs/SDQL2/SDQL_2_parser.dm | 82 +++++++++++-------- 2 files changed, 72 insertions(+), 37 deletions(-) diff --git a/code/modules/admin/verbs/SDQL2/SDQL_2.dm b/code/modules/admin/verbs/SDQL2/SDQL_2.dm index d2a106f9179..b470d2962a8 100644 --- a/code/modules/admin/verbs/SDQL2/SDQL_2.dm +++ b/code/modules/admin/verbs/SDQL2/SDQL_2.dm @@ -61,6 +61,10 @@ "SELECT /mob WHERE client MAP client WHERE holder MAP holder" + You can also generate a new list on the fly using a selector array. @[] will generate a list of objects based off the selector provided. + + "SELECT /mob/living IN (@[/area/crew_quarters/bar MAP contents])[1]" + What if some dumbass admin spawned a bajillion spiders and you need to kill them all? Oh yeah you'd rather not delete all the spiders in maintenace. Only that one room the spiders were spawned in. @@ -879,6 +883,22 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/SDQL2_VV_all, new(null dummy[result] = assoc result = dummy val += result + + else if(expression[i] == "@\[") + var/list/search_tree = expression[++i] + var/already_searching = (state == SDQL2_STATE_SEARCHING) //In case we nest, don't want to break out of the searching state until we're all done. + + if(!already_searching) + state = SDQL2_STATE_SEARCHING + + val = Search(search_tree) + SDQL2_STAGE_SWITCH_CHECK + + if(!already_searching) + state = SDQL2_STATE_EXECUTING + else + state = SDQL2_STATE_SEARCHING + else val = world.SDQL_var(object, expression, i, object, superuser, src) i = expression.len @@ -968,6 +988,10 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/SDQL2_VV_all, new(null return null start++ long = start < expression.len + else if(expression[start] == "(" && long) + v = query.SDQL_expression(source, expression[start + 1]) + start++ + long = start < expression.len else if(D != null && (!long || expression[start + 1] == ".") && (expression[start] in D.vars)) if(D.can_vv_get(expression[start]) || superuser) v = D.vars[expression[start]] @@ -1060,7 +1084,8 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/SDQL2_VV_all, new(null "=" = list("", "="), "<" = list("", "=", ">"), ">" = list("", "="), - "!" = list("", "=")) + "!" = list("", "="), + "@" = list("\[")) var/word = "" var/list/query_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 272bf83ca47..83592d27681 100644 --- a/code/modules/admin/verbs/SDQL2/SDQL_2_parser.dm +++ b/code/modules/admin/verbs/SDQL2/SDQL_2_parser.dm @@ -19,24 +19,28 @@ // // from_item : 'world' | expression // -// call_function : '(' [arguments] ')' -// arguments : expression [',' arguments] +// call_function : '(' [expression_list] ')' // // object_type : // // assignments : assignment [',' assignments] // assignment : '=' expression -// variable : | '.' variable | '[' ']' | '[' ']' '.' variable +// variable : | variable '.' variable | variable '[' ']' | '{' '}' | '(' expression ')' | call_function // // bool_expression : expression comparitor expression [bool_operator bool_expression] // expression : ( unary_expression | '(' expression ')' | value ) [binary_operator expression] -// unary_expression : unary_operator ( unary_expression | value | '(' expression ')' ) +// expression_list : expression [',' expression_list] +// unary_expression : unary_operator ( unary_expression | value ) +// // comparitor : '=' | '==' | '!=' | '<>' | '<' | '<=' | '>' | '>=' -// value : variable | string | number | 'null' | object_type +// value : variable | string | number | 'null' | object_type | array | selectors_array // unary_operator : '!' | '-' | '~' // binary_operator : comparitor | '+' | '-' | '/' | '*' | '&' | '|' | '^' | '%' // bool_operator : 'AND' | '&&' | 'OR' | '||' // +// array : '[' expression_list ']' +// selectors_array : '@[' object_selectors ']' +// // string : ''' ''' | '"' '"' // number : // @@ -336,7 +340,7 @@ return i -//variable: | '.' variable | '[' ']' | '[' ']' '.' variable +//variable: | variable '.' variable | variable '[' ']' | '{' '}' | '(' expression ')' | call_function /datum/SDQL_parser/proc/variable(i, list/node) var/list/L = list(token(i)) node[++node.len] = L @@ -348,6 +352,16 @@ if(token(i) != "}") parse_error("Missing } at end of pointer.") + else if(token(i) == "(") // not a proc but an expression + var/list/sub_expression = list() + + i = expression(i + 1, sub_expression) + + if(token(i) != ")") + parse_error("Missing ) at end of expression.") + + L[++L.len] = sub_expression + if(token(i + 1) == ".") L += "." i = variable(i + 2, L) @@ -424,7 +438,7 @@ return i + 1 -//array: '[' expression, expression, ... ']' +//array: '[' expression_list ']' /datum/SDQL_parser/proc/array(var/i, var/list/node) // Arrays get turned into this: list("[", list(exp_1a = exp_1b, ...), ...), "[" is to mark the next node as an array. if(copytext(token(i), 1, 2) != "\[") @@ -490,6 +504,23 @@ return i + 1 +//selectors_array: '@[' object_selectors ']' +/datum/SDQL_parser/proc/selectors_array(var/i, var/list/node) + if(token(i) == "@\[") + node += token(i++) + if(token(i) != "]") + var/list/select = list() + i = object_selectors(i, select) + node[++node.len] = select + if(token(i) != "]") + parse_error("Expected ']' to close selector array, but found '[token(i)]'") + else + parse_error("Selector array expected a selector, but found nothing") + else + parse_error("Expected '@\[' but found '[token(i)]'") + + return i + 1 + //call_function: ['(' [arguments] ')'] /datum/SDQL_parser/proc/call_function(i, list/node, list/arguments) if(length(tokenl(i))) @@ -520,25 +551,12 @@ return i + 1 -//expression: ( unary_expression | '(' expression ')' | value ) [binary_operator expression] +//expression: ( unary_expression | value ) [binary_operator expression] /datum/SDQL_parser/proc/expression(i, list/node) if(token(i) in unary_operators) i = unary_expression(i, node) - else if(token(i) == "(") - var/list/expr = list() - - i = expression(i + 1, expr) - - if(token(i) != ")") - parse_error("Missing ) at end of expression.") - - else - i++ - - node[++node.len] = expr - else i = value(i, node) @@ -558,7 +576,7 @@ return i -//unary_expression: unary_operator ( unary_expression | value | '(' expression ')' ) +//unary_expression: unary_operator ( unary_expression | value ) /datum/SDQL_parser/proc/unary_expression(i, list/node) if(token(i) in unary_operators) @@ -570,19 +588,6 @@ if(token(i) in unary_operators) i = unary_expression(i, unary_exp) - else if(token(i) == "(") - var/list/expr = list() - - i = expression(i + 1, expr) - - if(token(i) != ")") - parse_error("Missing ) at end of expression.") - - else - i++ - - unary_exp[++unary_exp.len] = expr - else i = value(i, unary_exp) @@ -607,7 +612,7 @@ return i + 1 -//value: variable | string | number | 'null' | object_type +//value: variable | string | number | 'null' | object_type | array | selectors_array /datum/SDQL_parser/proc/value(i, list/node) if(token(i) == "null") node += "null" @@ -626,8 +631,13 @@ else if(copytext(token(i), 1, 2) == "\[") // Start a list. i = array(i, node) + + else if(copytext(token(i), 1, 3) == "@\[") + i = selectors_array(i, node) + else if(copytext(token(i), 1, 2) == "/") i = object_type(i, node) + else i = variable(i, node)