特許拒絶査定不服審判
2024-007132
解析装置、解析方法及び解析プログラム
- 審判番号
- 2024-007132
- 法域
- 特許
- 審判種別
- 拒絶査定不服審判(拒絶査定不服の審決)
- 出願番号
- 特願2022-501600
- 請求日
- 2024年04月25日
- 審決日
- 2025年06月03日
- 公報発行日
- 2025年06月27日
- 結論
- 原査定取消(特許/登録すべき)
- 当事者
- 【請求人】日本電信電話株式会社
- 【請求人代理人】弁理士法人酒井国際特許事務所
- 審判長/審判官
- 審判長 吉田 美彦
- 審判官 福西 章人
- 審判官 脇岡 剛
- 分類
- P18 .113-WY (G06F) / P18 .121-WY (G06F)
- 条文(本文から自動抽出・参考)
- 特許法第29条第1項第3号、特許法第29条第2項
- データ出所
- 審決公報(XML)
事件の表示
特願2022-501600「解析装置、解析方法及び解析プログラム」拒絶査定不服審判事件〔令和 3年 8月26日国際公開、WO2021/166271、請求項の数(6)〕について、次のとおり審決する。
結論
結 論
原査定を取り消す。
本願の発明は、特許すべきものとする。
理由
理 由
第1.手続の経緯
本願は、令和2年2月21日を国際出願日とする特許出願であって、その手続きの経緯は、概略、以下のとおりである。
令和5年 6月 5日付け :拒絶理由通知
令和5年 8月 9日 :意見書、手続補正書の提出
令和5年 9月25日付け :拒絶理由通知
令和5年12月 4日 :意見書、手続補正書の提出
令和5年12月 8日付け :拒絶査定
令和6年 4月25日 :審判請求書の提出
令和7年 1月28日付け :審尋
なお、請求人は、令和7年1月28日付けの審尋に対して、その指定期間内に何らの応答もしなかった。
第2.原査定の概要
原査定(令和5年12月8日付け拒絶査定)の概要は以下のとおりである。
理由1(新規性)この出願の請求項1ないし6に係る発明は、以下の引用文献等1に記載された発明であるから、特許法第29条第1項第3号に該当し、特許を受けることができない。
理由2(進歩性)この出願の請求項1ないし6に係る発明は、以下の引用文献等1の記載に基いて、その出願前にその発明の属する技術の分野における通常の知識を有する者が容易に発明をすることができたものであるから、特許法第29条第2項の規定により特許を受けることができない。
引用文献等1:Chida Nariyoshi,"PoC Implementation of parser generators based on existing/SG/CM-stateful packrat parsing", zenodo [online], 2020年1月21日,[検索日 2023.09.25],ZIPファイル "artifact.zip",インターネット <URL: https://zenodo.org/record/3607141>
第3.本願発明
本願請求項1ないし6に係る発明(以下、「本願発明1」ないし「本願発明6」という。)は、令和5年12月4日に提出された手続補正書により補正された以下の特許請求の範囲の請求項1ないし6に記載された事項により特定されるものである。
「 【請求項1】
文字列をマッチさせるための表現であって、終端記号と非終端記号により属性に対応する部分が示された表現に、第1の文字列がマッチするか否かを解析する解析部と、
前記第1の文字列が前記表現にマッチした場合、前記第1の文字列の前記属性に対応する部分のうちの非終端記号にマッチする第2の文字列に前記属性を付与した要素を、変数に追加する追加部と、
前記変数から、各属性の最新の要素を抽出する抽出部と、
前記抽出部によって抽出された要素が文脈に関する所定の条件に合致するか否かを判定する判定部と、
を有することを特徴とする解析装置。
【請求項2】
前記追加部は、前記属性をキーとし、前記第2の文字列をバリューとするキーバリュー形式の要素を、配列である前記変数の右端に追加し、
前記抽出部は、前記配列のキーが同じである要素のうち、最も右側にある要素を抽出することを特徴とする請求項1に記載の解析装置。
【請求項3】
前記追加部は、前記属性に対応するスタックに、前記第2の文字列をpushし、
前記抽出部は、前記スタックのtopをpopすることによって抽出することを特徴とする請求項1に記載の解析装置。
【請求項4】
前記解析部は、HTMLの文法を基に前記第1の文字列の構文解析を行い、
前記追加部は、前記第1の文字列における開きタグの中の文字列に、開きタグを示す属性を付与した要素を前記変数に追加し、
前記抽出部は、開きタグを示す属性の要素のうち最新の要素を抽出し、
前記判定部は、前記抽出部によって抽出された要素の文字列と、前記第1の文字列における閉じタグの中の文字列が一致するか否かを判定することを特徴とする請求項1から3のいずれか1項に記載の解析装置。
【請求項5】
解析装置によって実行される解析方法であって、
文字列をマッチさせるための表現であって、終端記号と非終端記号により属性に対応する部分が示された表現に、第1の文字列がマッチするか否かを解析する解析工程と、
前記第1の文字列が前記表現にマッチした場合、前記第1の文字列の前記属性に対応する部分のうちの非終端記号にマッチする第2の文字列に前記属性を付与した要素を、変数に追加する追加工程と、
前記変数から、各属性の最新の要素を抽出する抽出工程と、
前記抽出工程によって抽出された要素が文脈に関する所定の条件に合致するか否かを判定する判定工程と、
を含むことを特徴とする解析方法。
【請求項6】
コンピュータを、請求項1から4のいずれか1項に記載の解析装置として機能させるための解析プログラム。」
第4.当審の判断
1.引用文献等1の公知日について
ア.インターネットアーカイブの記載事項
令和7年1月28日付け審尋において通知したウェブページ(URL:https://web.archive.org/web/20200212092736/https:/zenodo.org/record/3607141#.XkPFDnbRrtM、以下、「アーカイブ」という。)には、次の事項が記載されている。
「
[図:0001423229000001.jpg]
」
イ.引用文献等1が掲載されたウェブページの記載事項
引用文献等1が掲載されたウェブページ(URL:https://zenodo.org/record/3607141、以下、「ウェブページ1」という。)には、次の事項が記載されている。
「
[図:0001423229000002.jpg]
」
ウ.検討
上記アによると、上記アーカイブは、ウェイバックマシン(Wayback Machine)によって、上記ウェブページ1のURLに対応する過去のウェブページ(以下、「過去ページ」という。)を、アメリカ合衆国カリフォルニア州サンフランシスコの時間で2020年2月12日午前9時27分36秒(日本時間では、2020年2月13日午前2時27分36秒)に取得したものであると認められるから、上記過去ページは、遅くとも2020年2月13日には公知であったといえる。
そして、上記アの「ZIPファイル"artifact.zip"」の箇所に注目すると、上記過去ページは、ZIPファイル"artifact.zip"を掲載したものであって、当該ファイルのサイズは476.1kBであり、当該ファイルのハッシュ値は、「md5:f9a7d4f6ea84488fa764b7b22639a18a」であるといえる。また、上記アには、“Preview”と“Download”のアイコンが表示され、ページの右上には、“Software”の右側に“Open Access”のアイコンが表示されていることからすると、上記過去ページに掲載されたZIPファイル"artifact.zip"は、不特定多数の者が制限なくアクセス可能なものであったと認められる。
ここで、上記イによると、上記ウェブページ1に掲載されたZIPファイル"artifact.zip"のサイズは476.1kBであり、そのハッシュ値は、「md5:f9a7d4f6ea84488fa764b7b22639a18a」であるといえるところ、上記過去ページと上記ウェブページ1とに掲載されたそれぞのZIPファイル"artifact.zip"は、同じURLで特定されるウェブページに掲載されたファイルであって、その名称、サイズ及びハッシュ値が同一であるから、同一のファイルであることが推認される。
そうすると、引用文献等1として示されたZIPファイル"artifact.zip"は、遅くとも2020年2月13日には、上記過去ページにおいて、不特定多数の者が制限なくアクセス可能に掲載されたものであり、本願出願前には公知であったと認められる。
エ.まとめ
以上のとおりであるから、原査定の拒絶理由で引用された引用文献等1(ZIPファイル"artifact.zip")に含まれるファイル(C++言語で記述されたソースコード)の記載から把握される発明は、本願出願前に公知であったと認められる。
2.新規性、進歩性について
(1)引用文献等1の記載事項
引用文献等1に含まれるファイルには、以下の事項が記載されている。(下線は当審で付加した。以下、同様。)
ア.「
int main(int argc, char *argv[]) {
bool ok = parse(argc, argv);
if( !ok ) {
return 0;
}
Grammar *g = read_grammar(gpath);
cout << "[GRAMMAR INFO]" << endl;
cout << g->toString() << endl;
cout << "--" << endl << endl;
if( itype == SINGLE ) {
string x = read_input(ipath);
if( 0 ) cout << "x = " << x << endl;
CMStatefulPackratParser parser = CMStatefulPackratParser(g,x);
auto st = chrono::system_clock::now();
int res = parser.parse();
auto ed = chrono::system_clock::now();
cout << "[SIZE] Memo : " << parser.getMemoSize() << endl;
cout << "[CACHE COUNTER] : " << parser.cache_counter << endl;
cout << "[SIZE] Memo : " << parser.getMemoSize() << endl;
//cout << "HINT:" << x.substr(res,100) << endl;
//parser.showMemoInfo();
if( res == -1 ) {
puts("Matching failed...");
} else {
puts("Matching succeeded!!!");
cout << "consumed :: " << res << endl;
}
cout << "matching time :: " << chrono::duration_cast<chrono::milliseconds>(ed-st).count() << "msec" << endl;
} else if( itype == DIRECTORY ) {
vector<string> files = getFileNames(ipath);
cout << "* Number of files : " << files.size() << endl;
string path = ipath;
if( !path.empty() && path.back() != '/' ) path += "/";
int m = files.size();
chrono::milliseconds total_times = chrono::milliseconds(0);
long long total_spaces = 0;
for(int i=0;i<m;++i) {
cout << "* progress... [" << i + 1 << "/" << m << "] : file name = " << files[i] << endl;
string x = read_input(path+files[i]);
if( 0 ) cout << "x = " << x << endl;
CMStatefulPackratParser parser = CMStatefulPackratParser(g,x);
auto st = chrono::system_clock::now();
int res = parser.parse();
auto ed = chrono::system_clock::now();
cout << "[SIZE] Memo : " << parser.getMemoSize() << endl;
cout << "[CACHE COUNTER] : " << parser.cache_counter << endl;
cout << "[SIZE] Memo : " << parser.getMemoSize() << endl;
total_spaces += parser.getMemoSize();
if( res == -1 ) {
puts("Matching failed...");
} else {
puts("Matching succeeded!!!");
cout << "consumed :: " << res << endl;
}
auto diff = chrono::duration_cast<chrono::milliseconds>(ed-st);
cout << "matching time :: " << diff.count() << "msec" << endl;
total_times += chrono::duration_cast<chrono::milliseconds>(ed-st);
puts("");
}
cout << "[SUMMARY]---" << endl;
cout << " * SCAPE CONSUMPTION (TOTAL) : " << total_spaces << endl;
cout << " * RUNNING TIME (TOTAL) : " << total_times.count() << "[msec]" << endl;
} else assert(false);
return 0;
}」(CM_SPP\main.ccの106行ないし176行)
イ.「
int CMStatefulPackratParser::parse(Expression* e,int pos) {
if( e->getTypeInfo()->name() == typeid(Nonterminal*).name() ) {
Nonterminal* tmp = e->get<Nonterminal*>();
return parse(tmp, pos);
} else if( e->getTypeInfo()->name() == typeid(Slash*).name() ) {
Slash* tmp = e->get<Slash*>();
return parse(tmp, pos);
} else if( e->getTypeInfo()->name() == typeid(Char*).name() ) {
Char* tmp = e->get<Char*>();
return parse(tmp, pos);
} else if( e->getTypeInfo()->name() == typeid(And*).name() ) {
And* tmp = e->get<And*>();
return parse(tmp, pos);
} else if( e->getTypeInfo()->name() == typeid(Not*).name() ) {
Not* tmp = e->get<Not*>();
return parse(tmp, pos);
} else if( e->getTypeInfo()->name() == typeid(Sequence*).name() ) {
Sequence* tmp = e->get<Sequence*>();
return parse(tmp, pos);
} else if( e->getTypeInfo()->name() == typeid(Question*).name() ) {
Question* tmp = e->get<Question*>();
return parse(tmp, pos);
} else if( e->getTypeInfo()->name() == typeid(Star*).name() ) {
Star* tmp = e->get<Star*>();
return parse(tmp, pos);
} else if( e->getTypeInfo()->name() == typeid(Plus*).name() ) {
Plus* tmp = e->get<Plus*>();
return parse(tmp, pos);
} else if( e->getTypeInfo()->name() == typeid(Any*).name() ) {
Any* tmp = e->get<Any*>();
return parse(tmp, pos);
} else if( e->getTypeInfo()->name() == typeid(Range*).name() ) {
Range* tmp = e->get<Range*>();
return parse(tmp, pos);
} else if( e->getTypeInfo()->name() == typeid(Pstring*).name() ) {
Pstring* tmp = e->get<Pstring*>();
return parse(tmp, pos);
} else if( e->getTypeInfo()->name() == typeid(Bind_M*).name() ) {
Bind_M* tmp = e->get<Bind_M*>();
return parse(tmp, pos);
} else if( e->getTypeInfo()->name() == typeid(Bind_E*).name() ) {
Bind_E* tmp = e->get<Bind_E*>();
return parse(tmp, pos);
} else if( e->getTypeInfo()->name() == typeid(Match*).name() ) {
Match* tmp = e->get<Match*>();
return parse(tmp, pos);
} else if( e->getTypeInfo()->name() == typeid(Exists*).name() ) {
Exists* tmp = e->get<Exists*>();
return parse(tmp, pos);
} else if( e->getTypeInfo()->name() == typeid(Scope*).name() ) {
Scope* tmp = e->get<Scope*>();
return parse(tmp, pos);
} else assert(false && "[ERRROR] UNKNOWN TYPE");
}」(CM_SPP\CM_stateful_packrat_parser.ccの199行ないし252行)
ウ.「
int CMStatefulPackratParser::parse(Bind_M* e,int pos) {
if( show_trace ) {
std::cout << "[Bind_M] pos = " << pos << std::endl;
}
int next_pos = parse(e->getE(), pos);
if( next_pos == FAIL ) {
// e->getE() does not change environment!
return FAIL;
}
std::string v = e->getV();
Phi_m[v].push_back(++bind_counter.first);
unsigned long long hash = getHash(pos, next_pos-1);
Em.append(v, hash);
return next_pos;
}」(CM_SPP\CM_stateful_packrat_parser.ccの505行ないし519行)
エ.「
int CMStatefulPackratParser::parse(Match* e,int pos) {
if( show_trace ) {
std::cout << "[Match] pos = " << pos << std::endl;
}
int next_pos = parse(e->getE(), pos);
if( next_pos == FAIL ) {
// e->getE() does not change environment!
return FAIL;
}
unsigned long long hash = getHash(pos, next_pos-1);
bool b;
if( Em.hasTheVariable(e->getV()) ) {
b = ( hash == Em.get(e->getV()) );
} else {
b = false;
}
{
Conditions cs;
cs.push_back((Condition){MATCH, std::make_pair(e->getV(), hash), b});
propagate(cs);
}
return ( b ? next_pos : FAIL );
}」(CM_SPP\CM_stateful_packrat_parser.ccの537行ないし559行)
オ.「
class Environment_M { // environment for match operators
private:
const unsigned long long p = 1000000007ULL;
std::deque<kv_pair> table;
std::map<std::string,std::stack<unsigned long long> > env_map;
public:
Environment_M() {
}
void append(std::string, unsigned long long);
void append(kv_pair);
void update(std::deque<kv_pair>&);
void update(std::deque<kv_pair>&, std::map<std::string, std::deque<int>>&,ii&);
void rollback(int);
kv_pair get(int);
bool hasTheVariable(std::string);
unsigned long long get(std::string);
int size();
};」(CM_SPP\environment_m.hの16行ないし39行)
カ.「
void Environment_M::append(std::string k, unsigned long long v) {
table.push_back(std::make_pair(k,v));
env_map[k].push(v);
}
void Environment_M::append(kv_pair tmp) {
table.push_back(tmp);
env_map[tmp.first].push(tmp.second);
}
void Environment_M::rollback(int anchor) {
assert( anchor <= (int)table.size() ); // ADDED
while( anchor < (int)table.size() ) {
kv_pair &kv = table.back();
env_map[kv.first].pop();
table.pop_back();
}
}
kv_pair Environment_M::get(int v) {
assert( v < (int)table.size() );
return table[v];
}
bool Environment_M::hasTheVariable(std::string v) {
return ( env_map.count(v) && !env_map[v].empty() );
}
unsigned long long Environment_M::get(std::string v) {
assert( hasTheVariable(v) );
return env_map[v].top();
}」(CM_SPP\environment_m.ccの3行ないし34行)
(2)引用発明
上記(1)アないしカによると、引用文献等1は、PEG(Parsing Expression Grammar)に係る構文解析処理をC++言語によって記述したものであって、次の発明(以下、「引用発明」という。)が記載されていると認められる。
「コンピュータによって、入力された文字列が所定の文法表現に一致するか否かを解析する方法であって、
入力文字列を取得し(上記(1)ア)、
終端記号と非終端記号により属性に対応する部分が示された文法表現に、入力文字列に含まれる所定の文字列がマッチするか否かを解析し(上記(1)イ、ウ)、
前記所定の文字列が前記文法表現にマッチした場合、前記所定の文字列の前記属性に対応する部分のうちの非終端記号にマッチする文字列のハッシュ値(上記(1)ウにおける、入力文字列においてbind表現に一致する位置posから位置nextpos-1までの範囲の文字列のハッシュ値hash = getHash(pos, next_pos-1))に前記属性(v = e->getV())を付与した要素を、スタックに追加し(上記(1)ウ、カ)、
前記スタックから、各属性に対応する要素を抽出し(上記(1)エ、カ)、
入力文字列に含まれる前記所定の文字列とは異なる位置の文字列のハッシュ値(上記(1)エにおいて、入力文字列においてmatch表現に一致する位置posから位置nextpos-1までの範囲の文字列のハッシュ値hash = getHash(pos, next_pos-1))が、上記スタックから取り出した要素のハッシュ値(上記(1)エにおけるEm.get(e->getV()))と一致するか否かを判定する(上記(1)エ)、
方法。」
(3)本願発明5について
事案に鑑みて、まず、本願発明5について検討する。
ア.対比
(ア)引用発明の「コンピュータによって、入力された文字列が所定の文法表現に一致するか否かを解析する方法」は、本願発明5の「解析装置によって実行される解析方法」に相当する。
(イ)引用発明の「終端記号と非終端記号により属性に対応する部分が示された文法表現に、入力文字列に含まれる所定の文字列がマッチするか否かを解析」することは、本願発明5の「文字列をマッチさせるための表現であって、終端記号と非終端記号により属性に対応する部分が示された表現に、第1の文字列がマッチするか否かを解析する解析工程」に相当する。
(ウ)引用発明の「前記所定の文字列の前記属性に対応する部分のうちの非終端記号にマッチする文字列のハッシュ値」と本願発明5の「前記第1の文字列の前記属性に対応する部分のうちの非終端記号にマッチする第2の文字列」とは、「前記第1の文字列の前記属性に対応する部分のうちの非終端記号にマッチする第2の文字列に関する情報」である点で共通する。
(エ)スタックとは、後から入れた要素が先に取り出される性質を有する変数であることが、本願出願時の技術常識であることからすると、引用発明の「スタック」は、本願発明5の「変数」に相当する。
(オ)上記(ウ)、(エ)での説示をふまえると、引用発明の「前記所定の文字列が前記文法表現にマッチした場合、前記所定の文字列の前記属性に対応する部分のうちの非終端記号にマッチする文字列のハッシュ値に前記属性を付与した要素を、スタックに追加」することと、本願発明5の「前記第1の文字列が前記表現にマッチした場合、前記第1の文字列の前記属性に対応する部分のうちの非終端記号にマッチする第2の文字列に前記属性を付与した要素を、変数に追加する追加工程」とは、「前記第1の文字列が前記表現にマッチした場合、前記第1の文字列の前記属性に対応する部分のうちの非終端記号にマッチする第2の文字列に関する情報に前記属性を付与した要素を、変数に追加する追加工程」の点で共通する。
(カ)上記(エ)での説示をふまえると、引用発明において「前記スタックから、各属性に対応する要素を抽出」することにおいて、当該抽出される要素は、最も後に追加された要素である、つまり、最新の要素であるといえる。
(キ)本願発明5の「前記変数から、各属性の最新の要素を抽出する抽出工程」は、上記(カ)のとおりであるから、引用発明の「前記スタックから、各属性に対応する要素を抽出」することは、本願発明5の「前記変数から、各属性の最新の要素を抽出する抽出工程」に相当する。
(ク)引用発明の「入力文字列に含まれる前記所定の文字列とは異なる位置の文字列のハッシュ値が、上記スタックから取り出した要素のハッシュ値と一致するか否かを判定する」ことは、所定の属性と上記「入力文字列に含まれる前記所定の文字列とは異なる位置の文字列」との組み合わせが、文法表現に適合するか否かを判定するものであると解されるから、当該事項は、本願発明5の「前記抽出工程によって抽出された要素が文脈に関する所定の条件に合致するか否かを判定する判定工程」に相当する。
(ケ)以上によると、本願発明5と引用発明とは、次の点で一致し、また、相違する。
【一致点】
「 解析装置によって実行される解析方法であって、
文字列をマッチさせるための表現であって、終端記号と非終端記号により属性に対応する部分が示された表現に、第1の文字列がマッチするか否かを解析する解析工程と、
前記第1の文字列が前記表現にマッチした場合、前記第1の文字列の前記属性に対応する部分のうちの非終端記号にマッチする第2の文字列に関する情報に前記属性を付与した要素を、変数に追加する追加工程と、
前記変数から、各属性の最新の要素を抽出する抽出工程と、
前記抽出工程によって抽出された要素が文脈に関する所定の条件に合致するか否かを判定する判定工程と、
を含むことを特徴とする解析方法。」
【相違点】
上記一致点の「第2の文字列に関する情報」について、本願発明5が「第2の文字列」であるのに対して、引用発明では、「第2の文字列のハッシュ値」である点。
イ.判断
上記相違点について検討する。
引用発明において、「前記入力文字列における所定の位置の文字列のハッシュ値」を変数に格納することに代えて、「前記入力文字列における所定の位置の文字列」を変数に格納する構成を採用するべき動機付けがあるとはいえない。また、記憶領域の低減や処理の効率化のために、文字列を変数に格納することに代えて、文字列のハッシュ値を変数に格納する構成を採用することは、本願出願時における周知技術といえるところ、引用発明において変数に格納する値として、上記「…文字列のハッシュ値」に代えて、上記「…文字列」を採用することは、記憶領域の低減や処理の効率化という当該技術分野における普遍的な技術課題に反するものである。
そうすると、上記相違点に係る本願発明5の構成は、引用発明に基づいて当業者が容易に想到できたことであるとはいえない。
よって、本願発明5は、引用発明に基づいて当業者が容易に発明をすることができたものではない。
ウ.まとめ
上記のとおり、上記アで説示した相違点は実質的な相違点であるから、本願発明5は、引用発明ではない。そして、本願発明5は、引用発明に基づいて、当業者が容易に発明をすることができたものではない。
(4)本願発明1について
本願発明1は、本願発明5を「解析装置」の発明として特定したものであるから、本願発明5と同様の理由により、引用発明に基づいて当業者が容易に発明をすることができたものであるとはいえない。
したがって、本願発明1は、引用発明ではない。そして、本願発明1は、引用発明に基づいて、当業者が容易に発明をすることができたものではない。
(5)本願発明2ないし4、6について
本願発明2ないし4、6は、本願発明1を包含するものであって、上記相違点に係る本願発明1の構成を備えるものであるから、本願発明1と同様の理由により、引用発明に基づいて当業者が容易に発明をすることができたものであるとはいえない。
したがって、本願発明2ないし4、6は、引用発明ではない。そして、本願発明2ないし4、6は、引用発明に基づいて、当業者が容易に発明をすることができたものではない。
第5.まとめ
以上のとおり、原査定の理由によっては、本願を拒絶することはできない。
また、他に本願を拒絶すべき理由を発見しない。
よって、結論のとおり審決する。
審決本文(J-PlatPat)
J-PlatPatの審決検索トップを開きます。番号入力で「審判番号」を選び、下の番号を貼り付けて検索してください。
J-PlatPat 審決検索を開く
2024-007132