summaryrefslogtreecommitdiff
path: root/app/Core
diff options
context:
space:
mode:
authorFrederic Guillot <fred@kanboard.net>2015-06-28 18:52:01 -0400
committerFrederic Guillot <fred@kanboard.net>2015-06-28 18:52:01 -0400
commite22985df50d3a0a2ac883c43749542e41e425927 (patch)
tree784830150173ad36ad40037b9c938f09f6c77025 /app/Core
parent0fa64fc9bd947e2f82f60d63d57479fa4189ef68 (diff)
Start to implement advanced search query language
Diffstat (limited to 'app/Core')
-rw-r--r--app/Core/Lexer.php142
1 files changed, 142 insertions, 0 deletions
diff --git a/app/Core/Lexer.php b/app/Core/Lexer.php
new file mode 100644
index 00000000..a81965c5
--- /dev/null
+++ b/app/Core/Lexer.php
@@ -0,0 +1,142 @@
+<?php
+
+namespace Core;
+
+/**
+ * Lexer
+ *
+ * @package core
+ * @author Frederic Guillot
+ */
+class Lexer
+{
+ /**
+ * Current position
+ *
+ * @access private
+ * @var integer
+ */
+ private $offset = 0;
+
+ /**
+ * Token map
+ *
+ * @access private
+ * @var array
+ */
+ private $tokenMap = array(
+ "/^(assignee:)/" => 'T_ASSIGNEE',
+ "/^(color:)/" => 'T_COLOR',
+ "/^(due:)/" => 'T_DUE',
+ "/^(title:)/" => 'T_TITLE',
+ "/^(\s+)/" => 'T_WHITESPACE',
+ '/^([<=>]{0,2}[0-9]{4}-[0-9]{2}-[0-9]{2})/' => 'T_DATE',
+ '/^(yesterday|tomorrow|today)/' => 'T_DATE',
+ '/^("(.*?)")/' => 'T_STRING',
+ "/^(\w+)/" => 'T_STRING',
+ );
+
+ /**
+ * Tokenize input string
+ *
+ * @access public
+ * @param string $input
+ * @return array
+ */
+ public function tokenize($input)
+ {
+ $tokens = array();
+ $this->offset = 0;
+
+ while (isset($input[$this->offset])) {
+
+ $result = $this->match(substr($input, $this->offset));
+
+ if ($result === false) {
+ return array();
+ }
+
+ $tokens[] = $result;
+ }
+
+ return $tokens;
+ }
+
+ /**
+ * Find a token that match and move the offset
+ *
+ * @access public
+ * @param string $string
+ * @return array|boolean
+ */
+ public function match($string)
+ {
+ foreach ($this->tokenMap as $pattern => $name) {
+ if (preg_match($pattern, $string, $matches)) {
+
+ $this->offset += strlen($matches[1]);
+
+ return array(
+ 'match' => trim($matches[1], '"'),
+ 'token' => $name,
+ );
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Change the output of tokenizer to be easily parsed by the database filter
+ *
+ * Example: ['T_ASSIGNEE' => ['user1', 'user2'], 'T_TITLE' => 'task title']
+ *
+ * @access public
+ * @param array $tokens
+ * @return array
+ */
+ public function map(array $tokens)
+ {
+ $map = array(
+ 'T_TITLE' => '',
+ );
+
+ while (false !== ($token = current($tokens))) {
+
+ switch ($token['token']) {
+ case 'T_ASSIGNEE':
+ case 'T_COLOR':
+ $next = next($tokens);
+
+ if ($next !== false && $next['token'] === 'T_STRING') {
+ $map[$token['token']][] = $next['match'];
+ }
+
+ break;
+
+ case 'T_DUE':
+ $next = next($tokens);
+
+ if ($next !== false && $next['token'] === 'T_DATE') {
+ $map[$token['token']] = $next['match'];
+ }
+
+ break;
+
+ default:
+ $map['T_TITLE'] .= $token['match'];
+ break;
+ }
+
+ next($tokens);
+ }
+
+ $map['T_TITLE'] = trim($map['T_TITLE']);
+
+ if (empty($map['T_TITLE'])) {
+ unset($map['T_TITLE']);
+ }
+
+ return $map;
+ }
+}