-
Notifications
You must be signed in to change notification settings - Fork 35
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
1 changed file
with
390 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,390 @@ | ||
// -*- mode: js; indent-tabs-mode: nil; js-basic-offset: 4 -*- | ||
// | ||
// This file is part of ThingTalk | ||
// | ||
// Copyright 2017-2018 The Board of Trustees of the Leland Stanford Junior University | ||
// | ||
// Author: Giovanni Campagna <gcampagn@cs.stanford.edu> | ||
// | ||
// See COPYING for details | ||
|
||
{ | ||
const assert = require('assert'); | ||
|
||
const ThingTalk = require('thingtalk'); | ||
const Ast = ThingTalk.Ast; | ||
const Type = ThingTalk.Type; | ||
|
||
// import the combinator library | ||
const C = require('../../lib/sentence-generator/ast_manip'); | ||
|
||
} | ||
|
||
import './constants'; | ||
import './timers'; | ||
import './filters'; | ||
import './parameters'; | ||
import './aggregation'; | ||
|
||
complete_table = { | ||
thingpedia_query if complete; | ||
table_join_replace_placeholder if complete; | ||
} | ||
complete_get_command = { | ||
thingpedia_get_command if complete; | ||
} | ||
if_filtered_table = { | ||
complete_table; | ||
one_filter_table; | ||
!turking two_filter_table; | ||
} | ||
|
||
one_filter_table = { | ||
table:complete_table '其中' filter:atom_filter => { | ||
if (!C.checkFilter(table, filter)) | ||
return null; | ||
return C.addFilter(table, filter, $options); | ||
}; | ||
} | ||
two_filter_table = { | ||
table:one_filter_table '而且' filter:atom_filter => { | ||
if (!C.checkFilter(table, filter)) | ||
return null; | ||
return C.addFilter(table, filter, $options); | ||
}; | ||
} | ||
|
||
// FIXME delete all with_filtered table in this file | ||
with_filtered_table = { | ||
|
||
} | ||
|
||
edge_stream = { | ||
!turking ('當' | '如果' | '等') p:projection_Any ('變成' | '變為' | '是' | '為') x:constant_Any => C.makeEdgeFilterStream(p, '==', x, $options); | ||
('當' | '如果' | '等') 'the' p:projection_Numeric ('變得' | '變成') '比' x:constant_Numeric ('大' | '多') => C.makeEdgeFilterStream(p, '>=', x, $options); | ||
('當' | '如果' | '等') 'the' p:projection_Numeric ('變得' | '變成') '比' x:constant_Numeric ('小' | '少') => C.makeEdgeFilterStream(p, '<=', x, $options); | ||
} | ||
|
||
stream = { | ||
thingpedia_stream if complete; | ||
|
||
!turking { | ||
'當' proj:projection_Any ('改變' | '變化') ('時' | '的 時候') => { | ||
if (proj.args[0] === 'picture_url') | ||
return null; | ||
if (proj.table.isAggregation) | ||
return null; | ||
let outParams = Object.keys(proj.table.schema.out); | ||
let stream; | ||
if (outParams.length === 1 && $options.flags.turking) | ||
return null; | ||
if (outParams.length === 1) | ||
stream = C.tableToStream(proj.table, null); | ||
else | ||
stream = C.tableToStream(proj.table, proj.args); | ||
return stream; | ||
}; | ||
('假如' | '如果') proj:projection_Any ('改變' | '變' | '不一樣') '了' => { | ||
if (proj.args[0] === 'picture_url') | ||
return null; | ||
if (proj.table.isAggregation) | ||
return null; | ||
let outParams = Object.keys(proj.table.schema.out); | ||
let stream; | ||
if (outParams.length === 1 && $options.flags.turking) | ||
return null; | ||
if (outParams.length === 1) | ||
stream = C.tableToStream(proj.table, null); | ||
else | ||
stream = C.tableToStream(proj.table, proj.args); | ||
return stream; | ||
}; | ||
('假如' | '如果') proj:projection_Any '變得' '不一樣 了' => { | ||
if (proj.args[0] === 'picture_url') | ||
return null; | ||
if (proj.table.isAggregation) | ||
return null; | ||
let outParams = Object.keys(proj.table.schema.out); | ||
let stream; | ||
if (outParams.length === 1 && $options.flags.turking) | ||
return null; | ||
if (outParams.length === 1) | ||
stream = C.tableToStream(proj.table, null); | ||
else | ||
stream = C.tableToStream(proj.table, proj.args); | ||
return stream; | ||
}; | ||
} | ||
'當' table:complete_table ('變' | '改變' | '不一樣') '了' ('而且' | '且') filter:edge_filter ('時' | '的 時候') => { | ||
if (!table.schema.is_monitorable || !C.checkFilter(table, filter) || table.schema.is_list) | ||
return null; | ||
table = C.addFilter(table, filter, $options); | ||
if (!table) | ||
return null; | ||
return C.tableToStream(table, null); | ||
}; | ||
'當' table:complete_table ('變' | '改變' | '不一樣') '了' ('而且' | '且') filter:atom_filter ('時' | '的 時候') => { | ||
if (!table.schema.is_monitorable || !C.checkFilter(table, filter)) | ||
return null; | ||
if ($options.flags.turking && table.schema.is_list) | ||
return null; | ||
table = C.addFilter(table, filter, $options); | ||
if (!table) | ||
return null; | ||
return C.tableToStream(table, null); | ||
}; | ||
edge_stream; | ||
timer; | ||
} | ||
|
||
// FIXME add more... | ||
|
||
forward_get_do_command = { | ||
('給我' | '拿' | '取得') table:if_filtered_table ('然後' | '接著') action:thingpedia_action if !complete => new Ast.Statement.Command(table, [action]); | ||
table:complete_get_command ',' ('然後' | '接著') action:thingpedia_action if !complete => new Ast.Statement.Command(table, [action]); | ||
table:complete_get_command ('然後' | '接著') action:thingpedia_action if !complete => new Ast.Statement.Command(table, [action]); | ||
table:complete_get_command ',' action:thingpedia_action if !complete => new Ast.Statement.Command(table, [action]); | ||
} | ||
|
||
complete_forward_get_do_command = { | ||
forward_get_do_command if complete; | ||
} | ||
|
||
backward_get_do_command = { | ||
action:thingpedia_action '當' ('拿到' | '取得' | '得到') table:complete_get_command ('時' | '的 時候') if !complete => new Ast.Statement.Command(table, [action]); | ||
} | ||
|
||
complete_get_do_command = { | ||
complete_forward_get_do_command; | ||
!turking backward_get_do_command if complete; | ||
action_replace_param_with_table if complete; | ||
} | ||
|
||
forward_when_do_rule = { | ||
// pp from when to do (optional) | ||
stream:stream action:thingpedia_action => new Ast.Statement.Rule(stream, [action]); | ||
|
||
('如果' | '假如') ('有' | '出現') ('新' | '新的') table:complete_table ('' | ',' | '就') action:thingpedia_action if !complete => { | ||
if (!table.schema.is_monitorable) | ||
return null; | ||
return new Ast.Statement.Rule(new Ast.Stream.Monitor(table, null, table.schema), [action]); | ||
}; | ||
} | ||
|
||
complete_forward_when_do_rule = { | ||
forward_when_do_rule if complete; | ||
} | ||
|
||
backward_when_do_rule = { | ||
action:thingpedia_action stream:stream => new Ast.Statement.Rule(stream, [action]); | ||
|
||
action:thingpedia_action ('如果' | '假如') ('有' | '出現') ('新' | '新的') table:complete_table if !complete => { | ||
if (!table.schema.is_monitorable) | ||
return null; | ||
return new Ast.Statement.Rule(new Ast.Stream.Monitor(table, null, table.schema), [action]); | ||
}; | ||
} | ||
complete_when_do_rule = { | ||
complete_forward_when_do_rule; | ||
backward_when_do_rule if complete; | ||
} | ||
|
||
when_get_stream = { | ||
// pp from when to get (optional) | ||
// NOTE: the schema is not quite right but it's ok because the stream is complete | ||
// and the table is what we care about | ||
stream:stream table:thingpedia_get_command => C.checkNotSelfJoinStream(new Ast.Stream.Join(stream, table, [], table.schema)); | ||
stream:stream ('給我' | '取得' | '下載' | '傳給 我') table:thingpedia_query => C.checkNotSelfJoinStream(new Ast.Stream.Join(stream, table, [], table.schema)); | ||
stream:stream ('給我' | '取得' | '下載' | '傳給 我') proj:projection_Any => { | ||
if (proj.args[0] === 'picture_url') | ||
return null; | ||
let outParams = Object.keys(proj.table.schema.out); | ||
if (outParams.length === 1 && $options.flags.turking && !proj.table.isAggregation) | ||
return null; | ||
|
||
return C.checkNotSelfJoinStream(new Ast.Stream.Join(stream, proj.table, [], proj.table.schema)); | ||
}; | ||
} | ||
complete_when_get_stream = { | ||
when_get_stream if complete; | ||
} | ||
|
||
when_get_do_rule = { | ||
stream:stream command:complete_get_do_command => C.combineStreamCommand(stream, command); | ||
} | ||
|
||
$root = { | ||
// when => notify | ||
stream:stream ('通知 我' | '告知 我' | '讓 我 知道' | '提醒 我' | '跟 我 說') => C.makeProgram(new Ast.Statement.Rule(stream, [stream.isTimer || stream.isAtTimer ? C.builtinSayAction($options) : C.notifyAction()])); | ||
stream:timer ('通知 我' | '告知 我' | '讓 我 知道' | '提醒 我' | '跟 我 說') => C.makeProgram(new Ast.Statement.Rule(stream, [C.builtinSayAction($options)])); | ||
stream:timer ('傳' | '送' | '傳送') constant:constant_String ('' | '給 我') => C.makeProgram(new Ast.Statement.Rule(stream, [C.builtinSayAction($options, constant)])); | ||
|
||
'當' table:if_filtered_table ('變' | '改變' | '不一樣') '了' ('時' | '的 時候' | '') (',' | '') ('通知 我' | '告知 我' | '讓 我 知道' | '提醒 我' | '跟 我 說') => { | ||
let stream = C.tableToStream(table, null); | ||
if (!stream) | ||
return null; | ||
return C.makeProgram(new Ast.Statement.Rule(stream, [C.notifyAction()])); | ||
}; | ||
|
||
table:if_filtered_table ('有變化' | '改變' | '不一樣') ('時' | '的 時候' | '') (',' | '') ('通知 我' | '告知 我' | '讓 我 知道' | '提醒 我' | '跟 我 說') => { | ||
let stream = C.tableToStream(table, null); | ||
if (!stream) | ||
return null; | ||
return C.makeProgram(new Ast.Statement.Rule(stream, [C.notifyAction()])); | ||
}; | ||
|
||
!turking '當' proj:projection_Any ('變' | '改變' | '不一樣') '了' ('時' | '的 時候' | '') (',' | '') ('通知 我' | '告知 我' | '讓 我 知道' | '提醒 我' | '跟 我 說')=> { | ||
if (!proj.schema.is_monitorable) | ||
return null; | ||
if (proj.table.isAggregation) | ||
return null; | ||
let outParams = Object.keys(proj.table.schema.out); | ||
if (outParams.length === 1 && $options.flags.turking) | ||
return null; | ||
// note that we intentionally don't use tableToStream here, because we don't want to introduce edge filters | ||
return C.makeProgram(new Ast.Statement.Rule(new Ast.Stream.Monitor(proj.table, null, proj.table.schema), [C.notifyAction()])); | ||
}; | ||
|
||
!turking proj:projection_Any ('有變化' | '改變' | '不一樣') ('時' | '的 時候' | '') (',' | '') ('通知 我' | '告知 我' | '讓 我 知道' | '提醒 我' | '跟 我 說')=> { | ||
if (!proj.schema.is_monitorable) | ||
return null; | ||
if (proj.table.isAggregation) | ||
return null; | ||
let outParams = Object.keys(proj.table.schema.out); | ||
if (outParams.length === 1 && $options.flags.turking) | ||
return null; | ||
// note that we intentionally don't use tableToStream here, because we don't want to introduce edge filters | ||
return C.makeProgram(new Ast.Statement.Rule(new Ast.Stream.Monitor(proj.table, null, proj.table.schema), [C.notifyAction()])); | ||
}; | ||
|
||
('當' | '') table:complete_table filter:atom_filter ('時' | '的 時候') ('通知 我' | '告知 我' | '讓 我 知道' | '提醒 我' | '跟 我 說') => { | ||
if (C.hasGetPredicate(filter)) | ||
return null; | ||
if (!table.schema.is_monitorable || !C.checkFilter(table, filter)) | ||
return null; | ||
if ($options.flags.turking && table.schema.is_list) | ||
return null; | ||
table = C.addFilter(table, filter, $options); | ||
if (!table) | ||
return null; | ||
let stream = C.tableToStream(table, null); | ||
if (!stream) | ||
return null; | ||
return C.makeProgram(new Ast.Statement.Rule(stream, [C.notifyAction()])); | ||
}; | ||
('當' | '') table:complete_table filter:edge_filter ('時' | '的 時候') ('通知 我' | '告知 我' | '讓 我 知道' | '提醒 我' | '跟 我 說') => { | ||
if (C.hasGetPredicate(filter)) | ||
return null; | ||
if (!table.schema.is_monitorable || table.schema.is_list || !C.checkFilter(table, filter)) | ||
return null; | ||
table = C.addFilter(table, filter, $options); | ||
if (!table) | ||
return null; | ||
let stream = C.tableToStream(table, null); | ||
if (!stream) | ||
return null; | ||
return C.makeProgram(new Ast.Statement.Rule(stream, [C.notifyAction()])); | ||
}; | ||
|
||
// now => get => notify | ||
( | ||
table:complete_get_command | ||
| ('給我' | '取得' | '下載' | '傳給 我') table:complete_table | ||
| ('嗨 Almond' | '哈囉 Almond' | '') ('給我' | '我要' | '我 需要' | ''傳給 我'') table:with_filtered_table | ||
) => C.makeProgram(new Ast.Statement.Command(table, [C.notifyAction()])); | ||
// now => get => say(...) | ||
// don't merge these, the output sizes are too small | ||
( | ||
('嗨 Almond' | '哈囉 Almond' | '') ('給我' | '我要' | '我 需要' | ''傳給 我'') proj:projection_Any | ||
| ('嗨 Almond' | '哈囉 Almond' | '') proj:projection_Any ('是 什麼 ?' | '?') | ||
| ('告訴 我' | '跟 我 說') '什麼是' proj:projection_Any | ||
| '誰 是' proj:projection_Entity__tt__username ('' | '?') | ||
| proj:projection_Entity__tt__username '是 誰' ('' | '?') | ||
| '誰 的 信箱 是' proj:projection_Entity__tt__email_address ('' | '?') | ||
| proj:projection_Entity__tt__email_address '是 誰 的 信箱' ('' | '?') | ||
| proj:projection_Any | ||
) => C.sayProjectionProgram($options, proj); | ||
|
||
// now => do | ||
( | ||
action:thingpedia_action | ||
| ('請' | '請你') action:thingpedia_action | ||
| ('我要' | '我 想 要') action:thingpedia_action | ||
| ('嗨 Almond' | '哈囉 Almond') ('請' | '請你' | '') action:thingpedia_action | ||
) if complete => C.makeProgram(new Ast.Statement.Command(null, [action])); | ||
|
||
// now => get => do | ||
command:complete_get_do_command => C.makeProgram(command); | ||
|
||
// when join get => notify/say(...) | ||
stream:complete_when_get_stream => { | ||
assert(stream.isJoin, `unexpected stream in when_get, found ${stream}`); | ||
if (stream.table.isProjection) | ||
return C.makeProgram(new Ast.Statement.Rule(new Ast.Stream.Join(stream.stream, stream.table.table, stream.in_params, stream.schema), [C.notifyAction()])); | ||
else | ||
return C.makeProgram(new Ast.Statement.Rule(stream, [C.notifyAction()])); | ||
}; | ||
|
||
// when => do | ||
rule:complete_when_do_rule => C.makeProgram(rule); | ||
|
||
// when => get => do | ||
rule:when_get_do_rule => C.makeProgram(rule); | ||
|
||
// setup commands | ||
('叫' | '請' | '要') principal:constant_Entity__tt__username ('去' | '') action:thingpedia_action if complete => C.makeProgram(new Ast.Statement.Command(null, [action]), principal); | ||
( | ||
('叫' | '請' | '要') principal:constant_Entity__tt__username '把' table:complete_table ('傳給 我' | '給我' | '寄給 我') | ||
| ('叫' | '請' | '要') principal:constant_Entity__tt__username ('傳給 我' | '給我' | '寄給 我') table:complete_table | ||
| ('給我' | '我要') principal:constant_Entity__tt__username ('他' | 她') '的' table:complete_table | ||
| ('給我' | '我要') principal:constant_Entity__tt__username '的' table:complete_table | ||
) => C.makeProgram(new Ast.Statement.Command(table, [C.notifyAction('return')]), principal); | ||
('叫' | '請' | '要') principal:constant_Entity__tt__username stream:stream ('通知 我' | '告知 我' | '讓 我 知道' | '提醒 我' | '跟 我 說') => C.makeProgram(new Ast.Statement.Rule(stream, [C.notifyAction('return')]), principal); | ||
stream:stream ('叫' | '請' | '要') principal:constant_Entity__tt__username ('通知 我' | '告知 我' | '讓 我 知道' | '提醒 我' | '跟 我 說') => C.makeProgram(new Ast.Statement.Rule(stream, [C.notifyAction('return')]), principal); | ||
|
||
// policies | ||
('每 個 人' | '所有人' | '全部 的 人' | '任何 人') '都 可以' action:thingpedia_action if complete => C.makePolicy(null, null, action); | ||
('允許' | '授權' | '讓') ('每 個 人' | '所有人' | '全部 的 人' | '任何 人') '都 可以' action:thingpedia_action if complete => C.makePolicy(null, null, action); | ||
('每個' | '所有' | '全部 的' | '任何') filter:atom_filter '的 人' '都 可以' action:thingpedia_action if complete => { | ||
if (!filter.isExternal) | ||
return null; | ||
let policy = C.makePolicy(null, null, action); | ||
if (!policy) | ||
return null; | ||
policy.action.filter = Ast.BooleanExpression.And([policy.action.filter, filter]); | ||
return policy; | ||
}; | ||
('允許' | '授權' | '讓') ('每個' | '所有' | '全部 的' | '任何') filter:atom_filter '的 人' '都 可以' action:thingpedia_action if complete => { | ||
if (!filter.isExternal) | ||
return null; | ||
let policy = C.makePolicy(null, null, action); | ||
if (!policy) | ||
return null; | ||
policy.action.filter = Ast.BooleanExpression.And([policy.action.filter, filter]); | ||
return policy; | ||
}; | ||
('每 個 人' | '所有人' | '全部 的 人' | '任何 人') '都 可以' ('看' | '查看' | '存取' | '取得' | '讀取' | '讀' | '拿') table:if_filtered_table => C.makePolicy(null, table, null); | ||
source:constant_Entity__tt__username '可以' action:thingpedia_action if complete => C.makePolicy(source, null, action); | ||
('允許' | '授權' | '讓') source:constant_Entity__tt__username ('可以' | '') action:thingpedia_action if complete => C.makePolicy(source, null, action); | ||
('允許' | '授權' | '讓') source:constant_Entity__tt__username ('可以' | '') action:thingpedia_action '如果' filter:atom_filter if complete => { | ||
if (!filter.isExternal) | ||
return null; | ||
let policy = C.makePolicy(source, null, action); | ||
if (!policy) | ||
return null; | ||
policy.action.filter = Ast.BooleanExpression.And([policy.action.filter, filter]); | ||
return policy; | ||
}; | ||
'如果' filter:atom_filter ('允許' | '授權' | '讓') source:constant_Entity__tt__username ('可以' | '') action:thingpedia_action if complete => { | ||
if (!filter.isExternal) | ||
return null; | ||
let policy = C.makePolicy(source, null, action); | ||
if (!policy) | ||
return null; | ||
policy.action.filter = Ast.BooleanExpression.And([policy.action.filter, filter]); | ||
return policy; | ||
}; | ||
('允許' | '授權' | '讓') source:constant_Entity__tt__username ('可以' | '') ('看' | '查看' | '存取' | '取得' | '讀取' | '讀' | '拿') table:if_filtered_table => C.makePolicy(source, table, null); | ||
} | ||
|