JavaScript has no direct support for keyword arguments, but it supports argument object destructuring. It is standard to use a destructed final argument for keyword arguments. Here is an example:
function f(x, { a = 7, b = 2}){
return [x, a, b];
}
You would then call this function like:
f(9, {}); // ==> [9, 7, 2]
f(9, {a : 3}); // ==> [9, 3, 2]
f(9, {b : 3, a : 1}); // ==> [9, 1, 3]
The object may also supply a separate default if you omit the object entirely:
function g({ a = 7, b = 2} = {b : 3} ){
return [a, b];
}
g() // => [7,3]
g({}) // => [7,2]
Goal
Given an argument specification for a JavaScript function, determine whether it "supports key word arguments". The input must be a valid argument specification for a JavaScript function. It "supports key word arguments" if the last argument does object destructuring. See many examples below.
You may assume that no strings appear in the input (so no single quote '
double quote "
or uptick `
). You may also assume that the spread operator ...
does not appear.
(It would be nice to have the grammar for JavaScript argument specifications here but on a quick google I didn't find a readable reference. This section of the ECMAScript specification looks relevant, but I can't make sense of the grammar they provide. If anyone can suggest a readable link, I will add it.)
Scoring
This is code golf, and the shortest code wins.
Examples
"" // empty params ==> false
"x" // single normal argument ==> false
"x " // test is space insensitive ==> false
"{x}" // ==> true
"x, y, z" // multiple normal arguments ==> false
"x, y, {z}" // ==> true
"x, {y}, z" // ==> false
"x, {y}, {z}" // ==> true
"{}" // ==> true
"{} = {}" ==> true
"[] = {}" ==> false
"{} = []" ==> true
"[] = []" ==> false
"{} = null" ==> true
"[{x}]" // ==> false
"[x, y, z]" // ==> false
"x," // ==> false
"{x}," // ==> true
"x, { y = 2 }" // ==> true
"{ y = 2 }, x" // ==> false, not last argument
"{ x = 2 }, { y = 2 }" // ==> true
"{ a = 7, b = 2}" // ==> true, this is f above
"{ a = 7, b = 2} = {b : 3}" // ==> true this is g above
"{ a = [7, 1], b = { c : 2} } = {}" // ==> true
"{ a = 7, b = 2} = {}" // ==> true
"{ a = 7, b = 2} = null" // ==> true.
"{ x = { y : 2 }}" // ==> true
"x, [y, { z }]" // ==> false
"[x] = {[Symbol.iterator]: [][Symbol.iterator].bind([42])}" // ==> false
"x, y = function ({z}) {}" // ==> false
`x, y = ({z}) => {z}` // ==> false
Unspecified inputs:
","
"x y"
"x = '2, {y}' " // no syntax error but default is a string
...[x, y, { keyword },] // no syntax error but contains illegal ...
"{x"
"{{x}}"
"{[x]}"
"{1}"
"x,,"
"x, { x = 2 }"
"{ x = { y = 2 }}"
"{ x : 2 }"
Here are the tests as a JSON string:
"[[\"\",false],[\"x\",false],[\"x \",false],[\"{x}\",true],[\"x, y, z\",false],[\"x, y, {z}\",true],[\"x, {y}, z\",false],[\"x, {y}, {z}\",true],[\"{}\",true],[\"{} = {}\",true],[\"[] = {}\",false],[\"{} = []\",true],[\"[] = []\",false],[\"{} = null\",true],[\"[{x}]\",false],[\"[x, y, z]\",false],[\"x,\",false],[\"{x},\",true],[\"x, { y = 2 }\",true],[\"{ y = 2 }, x\",false],[\"{ x = 2 }, { y = 2 }\",true],[\"{ a = 7, b = 2}\",true],[\"{ a = 7, b = 2} = {b : 3}\",true],[\"{ a = [7, 1], b = { c : 2} } = {}\",true],[\"{ a = 7, b = 2} = {}\",true],[\"{ a = 7, b = 2} = null\",true],[\"{ x = { y : 2 }}\",true], [\"x, [y, { z }]\", false], [\"[x] = {[Symbol.iterator]: [][Symbol.iterator].bind([42])}\", false], [\"x, y = function ({z}) {}\", false], [\"x, y = ({z}) => {z}\", false] ]"
A reference implementation
Here is an ungolfed reference implementation in JavaScript (it uses nothing specific to JavaScript and could be easily ported to other languages).
function supportsKwargs (funcstr) {
let START_ARG = 1;
let ARG = 2;
let paren_depth = 0;
let arg_is_obj_dest = false;
let state = START_ARG;
for (let i = 0; i < funcstr.length; i++) {
let x = funcstr[i];
// Skip whitespace.
if(x === " " || x === "\n" || x === "\t"){
continue;
}
if(paren_depth === 0){
if(x === ","){
state = START_ARG;
continue;
}
}
if(state === START_ARG){
// Nonwhitespace character in START_ARG so now we're in state arg.
state = ARG;
arg_is_obj_dest = x === "{";
}
switch(x){
case "[": case "{": case "(":
paren_depth ++;
continue;
case "]": case "}": case ")":
paren_depth--;
continue;
}
}
return arg_is_obj_dest;
}
...[x, y, { keyword },]
;x, y, { keyword },
;x, [y, { z }]
;[x] = {[Symbol.iterator]: [][Symbol.iterator].bind([42])}
;x, y = function ({z}) {}
;x, y = ({z}) => {z}
? \$\endgroup\$