private static function sentence_respond(&$in_input) { /* find a category for the input */ $start_time = microtime(true); $match = JxBotEngine::match($in_input, JxBotConverse::history_response(0), JxBotConverse::predicate('topic')); $end_time = microtime(true); JxBotConverse::$match_time += $end_time - $start_time; /* check recursion */ $category = $match === false ? -1 : $match->matched_category(); $count = 0; foreach (JxBotConverse::$category_stack as $nested_category) { if ($nested_category == $category) { $count++; } } if ($count >= JxBotConverse::MAX_CATEGORY_NESTING) { //print 'CATEGORY LIMIT<br>'; return ''; } JxBotConverse::$category_stack[] = $category; if ($match === false) { /* no match was found; the input was not understood and no default category is available */ $output = '???'; } else { /* select a template at random */ //print 'MATCHED CATEGORY '.$category.'<br>'; $template = JxBotNLData::fetch_templates($category); $count = count($template); if ($count == 0) { $output = '???'; } else { if ($count == 1) { $index = 0; } else { $index = mt_rand(1, $count) - 1; } $output = $template[$index][1]; } if (JxBotConverse::$srai_level == 1) { JxBotConverse::$iq_score += $match->iq_score(); } } /* generate the template */ $template = JxBotAiml::parse_template($output); $output = $template->generate($match); /* track recursion */ array_pop(JxBotConverse::$category_stack); return $output; }
public function generate($in_context) { switch ($this->name) { case 'system': /* security implications; will not implement until reasonable controls are developed and an on/off switch */ /* security implications; will not implement until reasonable controls are developed and an on/off switch */ case 'javascript': /* server-side javascript is not implemented */ break; case 'think': $this->text_value($in_context); break; case 'template': case 'gossip': /* doesn't do anything in this AIML interpreter and is removed in AIML 2.0 */ /* doesn't do anything in this AIML interpreter and is removed in AIML 2.0 */ case 'x-learn-1': /* AIML 1 learn tag to be filtered on import and contents passed through without further action */ return $this->text_value($in_context); case 'random': $count = $this->child_element_count(); $index = mt_rand(1, $count) - 1; return $this->child_element($index)->text_value($in_context); case 'condition': $loop = false; // preparation for AIML 2 loop; will need to keep a stack & check the stack so it can be toggled // by a <loop/> element anywhere in the depth of the <li> do { $count = $this->child_element_count('li'); if ($count == 0) { $predicate = $this->child_or_attr_named($in_context, 'name'); $value = JxBotConverse::predicate($predicate); $pattern = $this->child_or_attr_named($in_context, 'value'); if (JxBotElement::matches_simple_pattern($value, $pattern)) { return $this->text_value($in_context, array('name', 'value')); } } else { $count = $this->child_element_count(); $predicate = $this->child_or_attr_named($in_context, 'name', null); if ($predicate !== null) { $value = JxBotConverse::predicate($predicate); for ($i = 0; $i < $count; $i++) { $item = $this->child_element($i); $pattern = $item->child_or_attr_named($in_context, 'value', null); if ($pattern === null) { return $item->text_value($in_context, array('value')); } if (JxBotElement::matches_simple_pattern($value, $pattern)) { return $item->text_value($in_context, array('value')); } } } else { for ($i = 0; $i < $count; $i++) { $item = $this->child_element($i); $predicate = $item->child_or_attr_named($in_context, 'name'); $value = JxBotConverse::predicate($predicate); $pattern = $item->child_or_attr_named($in_context, 'value', null); if ($pattern === null) { return $item->text_value($in_context, array('value', 'name')); } if (JxBotElement::matches_simple_pattern($value, $pattern)) { return $item->text_value($in_context, array('value', 'name')); } } } } } while ($loop); break; case 'star': case 'thatstar': case 'topicstar': $index = intval($this->child_or_attr_named($in_context, 'index', 1)); return $this->get_capture($in_context, $this->name, $index); case 'srai': return JxBotConverse::srai($this->text_value($in_context)); case 'sr': /* srai star /srai */ return JxBotConverse::srai($this->get_capture($in_context, 'star', 1)); case 'bot': case 'get': $name = trim($this->child_or_attr_named($in_context, 'name')); if ($name !== '') { if ($this->name == 'get') { return JxBotConverse::predicate($name); } else { if ($name == 'size') { return JxBotNLData::pattern_count(); } else { if ($name == 'age') { return JxBotElement::compute_age(); } else { if ($name == 'birthday') { return JxBotElement::birthday(); } else { return JxBotConfig::predicate($name); } } } } } break; case 'tag': /* non-standard Tag feature */ $name = trim($this->child_or_attr_named($in_context, 'name')); return $in_context->tag_value($name); case 'id': return JxBotConverse::predicate('id'); case 'size': /* we return the number of patterns, which is equivalent to AIML standard category count; since in JxBot one category != one pattern. */ return JxBotNLData::pattern_count(); case 'vocabulary': return JxBotNLData::word_count(); case 'version': return JxBot::VERSION; case 'program': return JxBot::PROGRAM . ' ' . JxBot::VERSION; case 'set': $name = trim($this->child_or_attr_named($in_context, 'name')); if ($name != '') { $value = $this->text_value($in_context, array('name')); JxBotConverse::set_predicate($name, $value); return $value; } break; case 'date': $php_format = 'r'; /* default - AIML 1.0 - we specify date & time format */ return date($php_format); case 'uppercase': return JxBotNL::upper($this->text_value($in_context)); case 'lowercase': return JxBotNL::lower($this->text_value($in_context)); case 'formal': return JxBotNL::formal($this->text_value($in_context)); case 'sentence': return JxBotNL::sentence($this->text_value($in_context)); case 'explode': return JxBotNL::explode($this->text_value($in_context)); case 'normalize': return JxBotNL::template_normalize($this->text_value($in_context)); case 'denormalize': return JxBotNL::template_denormalize($this->text_value($in_context)); case 'gender': if (count($this->children) == 0) { return JxBotNL::remap('gender', $this->get_capture($in_context, 'star', 1)); } return JxBotNL::remap('gender', $this->text_value($in_context)); case 'person': if (count($this->children) == 0) { return JxBotNL::remap('person', $this->get_capture($in_context, 'star', 1)); } return JxBotNL::remap('person', $this->text_value($in_context)); case 'person2': if (count($this->children) == 0) { return JxBotNL::remap('person2', $this->get_capture($in_context, 'star', 1)); } return JxBotNL::remap('person2', $this->text_value($in_context)); case 'map': $map_name = $this->child_or_attr_named($in_context, 'name'); return JxBotNL::remap($map_name, $this->text_value($in_context, array('name'))); case 'that': $indicies = JxBotElement::indicies($this->child_or_attr_named($in_context, 'index')); $in_response = count($indicies) >= 1 ? $indicies[0] : 1; $in_sentence = count($indicies) >= 2 ? $indicies[1] : 1; $response = JxBotConverse::history_response($in_response - 1); $sentences = JxBotNL::split_sentences($response); if ($in_sentence < 1 || $in_sentence > count($sentences)) { return ''; } return $sentences[$in_sentence - 1]; case 'input': $indicies = JxBotElement::indicies($this->child_or_attr_named($in_context, 'index')); $in_request = count($indicies) >= 1 ? $indicies[0] : 1; $in_sentence = count($indicies) >= 2 ? $indicies[1] : 1; $request = JxBotConverse::history_request($in_request - 1); $sentences = JxBotNL::split_sentences($request); if ($in_sentence < 1 || $in_sentence > count($sentences)) { return ''; } return $sentences[$in_sentence - 1]; case 'request': $index = intval($this->child_or_attr_named($in_context, 'index', 1)); return JxBotConverse::history_request($index - 1); case 'response': $index = intval($this->child_or_attr_named($in_context, 'index', 1)); return JxBotConverse::history_response($index - 1); default: /* push unknown content & tags through to output */ // ! REVIEW: A better policy might be to tightly control which tags are // allowed through, or provide an appropriate system option. return $this->flatten(); } }