Przeglądaj źródła

working on text parser

Evan You 11 lat temu
rodzic
commit
83749bbb85
2 zmienionych plików z 151 dodań i 2 usunięć
  1. 34 2
      src/config.js
  2. 117 0
      src/parse/text.js

+ 34 - 2
src/config.js

@@ -2,6 +2,7 @@ module.exports = {
 
   /**
    * The prefix to look for when parsing directives.
+   *
    * @type {String}
    */
 
@@ -10,6 +11,7 @@ module.exports = {
   /**
    * Whether to print debug messages.
    * Also enables stack trace for warnings.
+   *
    * @type {Boolean}
    */
 
@@ -17,6 +19,7 @@ module.exports = {
 
   /**
    * Whether to suppress warnings.
+   *
    * @type {Boolean}
    */
 
@@ -24,9 +27,38 @@ module.exports = {
 
   /**
    * Whether to parse mustache tags in templates.
+   *
    * @type {Boolean}
    */
 
-  interpolate: true
+  interpolate: true,
 
-}
+  /**
+   * Internal flag to indicate the delimiters have been
+   * changed.
+   *
+   * @type {Boolean}
+   */
+
+  _delimitersChanged: true
+
+}
+
+/**
+ * Interpolation delimiters.
+ * We need to mark the changed flag so that the text parser
+ * knows it needs to recompile the regex.
+ *
+ * @type {Array<String>}
+ */
+
+var delimiters = ['{{', '}}']
+Object.defineProperty(module.exports, 'delimiters', {
+  get: function () {
+    return delimiters
+  },
+  set: function (val) {
+    delimiters = val
+    this._delimitersChanged = true
+  }
+})

+ 117 - 0
src/parse/text.js

@@ -0,0 +1,117 @@
+var Cache = require('../cache')
+var config = require('../config')
+var regexEscapeRE = /[-.*+?^${}()|[\]\/\\]/g
+var cache, tagRE, htmlRE, firstChar, lastChar
+
+/**
+ * Escape a string so it can be used in a RegExp
+ * constructor.
+ *
+ * @param {String} str
+ */
+
+function escapeRegex (str) {
+  return str.replace(regexEscapeRE, '\\$&')
+}
+
+/**
+ * Compile the interpolation tag regex.
+ *
+ * @return {RegExp}
+ */
+
+function compileRegex () {
+  config._delimitersChanged = false
+  var open = config.delimiters[0]
+  var close = config.delimiters[1]
+  firstChar = open.charAt(0)
+  lastChar = close.charAt(close.length - 1)
+  var firstCharRE = escapeRegex(firstChar)
+  var lastCharRE = escapeRegex(lastChar)
+  var openRE = escapeRegex(open)
+  var closeRE = escapeRegex(close)
+  tagRE = new RegExp(
+    firstCharRE + '?' + openRE +
+    '(.+?)' +
+    closeRE + lastCharRE + '?',
+    'g'
+  )
+  htmlRE = new RegExp(
+    '^' + firstCharRE + openRE +
+    '.*' +
+    closeRE + lastCharRE + '$'
+  )
+  // reset cache
+  cache = new Cache(1000)
+}
+
+/**
+ * Parse a template text string into an array of tokens.
+ *
+ * @param {String} text
+ * @return {Array<Object> | null}
+ *               - {String} type
+ *               - {String} value
+ *               - {Boolean} [html]
+ *               - {Boolean} [oneTime]
+ */
+
+exports.parse = function (text) {
+  if (config._delimitersChanged) {
+    compileRegex()
+  }
+  var hit = cache.get(text)
+  if (hit) {
+    return hit
+  }
+  if (!tagRE.test(text)) {
+    return null
+  }
+  var tokens = []
+  var lastIndex = tagRE.lastIndex = 0
+  var match, index, value, oneTime
+  while (match = tagRE.exec(text)) {
+    index = match.index
+    // push text token
+    if (index > lastIndex) {
+      tokens.push({
+        value: text.slice(lastIndex, index)
+      })
+    }
+    // tag token
+    value = match[1].trim()
+    oneTime = value.charAt(0) === '*'
+    tokens.push({
+      tag: true,
+      value: oneTime ? value.slice(1) : value,
+      html: htmlRE.test(match[0]),
+      oneTime: oneTime
+    })
+    lastIndex = index + match[0].length
+  }
+  if (lastIndex < text.length - 1) {
+    tokens.push({
+      value: text.slice(lastIndex)
+    })
+  }
+  cache.put(text, tokens)
+  return tokens
+}
+
+/**
+ * Parse a template text string into an expression
+ *
+ * @param {String} text
+ * @return {String}
+ */
+
+exports.textToExpression = function (text) {
+  var tokens = exports.parse(text)
+  if (tokens) {
+    return tokens.map(function (token) {
+      return token.tag
+        ? token.value
+        : ('"' + token.value + '"')
+    }).join('+')
+  }
+}