Skip to content

Commit

Permalink
Add iteration APIs for device attributes
Browse files Browse the repository at this point in the history
This allows Genie to do beta-reduction of device attributes
in primitive templates
  • Loading branch information
gcampax committed Nov 1, 2019
1 parent 7b5037e commit a9cb280
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 4 deletions.
13 changes: 11 additions & 2 deletions lib/ast/api.js
Expand Up @@ -34,9 +34,10 @@ const {
recursiveYieldArraySlots,
makeScope,
InputParamSlot,
DeviceAttributeSlot,
FilterSlot,
ArrayIndexSlot,
FieldSlot
FieldSlot,
} = require('./slots');

const InvocationProto = Object.getPrototypeOf(new Ast.Invocation(Ast.Selector.Builtin, 'notify', [], null));
Expand Down Expand Up @@ -464,8 +465,16 @@ function* iterateSlots2InputParams(prim, scope) {
* @yields {Ast~AbstractSlot}
*/
InvocationProto.iterateSlots2 = function* iterateSlots2(scope) {
if (this.selector.isDevice)
if (this.selector.isDevice) {
for (let attr of this.selector.attributes)
yield new DeviceAttributeSlot(this, attr);

// note that we yield the selector after the device attributes
// this way, almond-dialog-agent will first ask any question to slot-fill
// the device attributes (if somehow it needs to) and then use the chosen
// device attributes to choose the device
yield this.selector;
}
return yield* iterateSlots2InputParams(this, scope);
};

Expand Down
2 changes: 1 addition & 1 deletion lib/ast/program.js
Expand Up @@ -77,7 +77,7 @@ class DeviceSelector extends Selector {
}

toString() {

return `Device(${this.kind}, ${this.id ? this.id : ''}, )`;
}
}
DeviceSelector.prototype.isDevice = true;
Expand Down
34 changes: 33 additions & 1 deletion lib/ast/slots.js
Expand Up @@ -144,6 +144,37 @@ class InputParamSlot extends AbstractSlot {
}
}

class DeviceAttributeSlot extends AbstractSlot {
constructor(prim, attr) {
super(prim, {});
this._slot = attr;
assert(this._slot.name === 'name');
}

toString() {
return `DeviceAttributeSlot(${this._slot.name} : ${this.type})`;
}

get type() {
return Type.String;
}
get tag() {
return `attribute.${this._slot.name}`;
}
getPrompt(locale) {
// this method should never be used, because $? does not typecheck in a device
// attribute, but we include for completeness, and just in case
const gettext = I18n.get(locale);
return gettext.dgettext('thingtalk', "Please tell me the name of the device you would like to use.");
}
get() {
return this._slot.value;
}
set(value) {
this._slot.value = value;
}
}

class FilterSlot extends AbstractSlot {
constructor(prim, scope, arg, filter) {
super(prim && prim.isPermissionRule ? null : prim, scope);
Expand Down Expand Up @@ -456,7 +487,8 @@ module.exports = {
recursiveYieldArraySlots,
makeScope,
InputParamSlot,
DeviceAttributeSlot,
FilterSlot,
ArrayIndexSlot,
FieldSlot
FieldSlot,
};
13 changes: 13 additions & 0 deletions test/test_iteration_apis.js
Expand Up @@ -429,6 +429,19 @@ var TEST_CASES = [
['Selector(@org.schema)',
'FilterSlot(author =~ : String) filter.=~.author What should the author contain?']
],

[`now => @light-bulb(name="bedroom").set_power(power=enum(off));`,
['action: Invocation(Device(light-bulb, , ), set_power, InputParam(power, Enum(off)), )'],
[
'Device(light-bulb, , ) light-bulb:set_power',
'InputParam(power, Enum(off)) light-bulb:set_power',
],
[
'DeviceAttributeSlot(name : String) attribute.name Please tell me the name of the device you would like to use.',
'Selector(@light-bulb)',
'InputParamSlot(power : Enum(on,off)) in_param.power Do you want to turn it on or off?',
]
]
];

async function test(i) {
Expand Down

0 comments on commit a9cb280

Please sign in to comment.