Pārlūkot izejas kodu

simplified new directive parser

Evan You 10 gadi atpakaļ
vecāks
revīzija
580679e038

+ 129 - 0
src/parsers/directive-new.js

@@ -0,0 +1,129 @@
+var _ = require('../util')
+var Cache = require('../cache')
+var cache = new Cache(1000)
+var filterTokenRE = /[^\s'"]+|'[^']*'|"[^"]*"/g
+var reservedArgRE = /^in$|^-?\d+/
+
+/**
+ * Parser state
+ */
+
+var str, dir
+var c, i, l, lastFilterIndex
+var inSingle, inDouble, curly, square, paren
+
+/**
+ * Push a filter to the current directive object
+ */
+
+function pushFilter () {
+  var exp = str.slice(lastFilterIndex, i).trim()
+  var filter
+  if (exp) {
+    filter = {}
+    var tokens = exp.match(filterTokenRE)
+    filter.name = tokens[0]
+    if (tokens.length > 1) {
+      filter.args = tokens.slice(1).map(processFilterArg)
+    }
+  }
+  if (filter) {
+    (dir.filters = dir.filters || []).push(filter)
+  }
+  lastFilterIndex = i + 1
+}
+
+/**
+ * Check if an argument is dynamic and strip quotes.
+ *
+ * @param {String} arg
+ * @return {Object}
+ */
+
+function processFilterArg (arg) {
+  var stripped = reservedArgRE.test(arg)
+    ? arg
+    : _.stripQuotes(arg)
+  var dynamic = stripped === false
+  return {
+    value: dynamic ? arg : stripped,
+    dynamic: dynamic
+  }
+}
+
+/**
+ * Parse a directive value and extract the expression
+ * and its filters into a descriptor.
+ *
+ * Example:
+ *
+ * "a + 1 | uppercase" will yield:
+ * {
+ *   expression: 'a + 1',
+ *   filters: [
+ *     { name: 'uppercase', args: null }
+ *   ]
+ * }
+ *
+ * @param {String} str
+ * @return {Object}
+ */
+
+exports.parse = function (s) {
+
+  var hit = cache.get(s)
+  if (hit) {
+    return hit
+  }
+
+  // reset parser state
+  str = s
+  inSingle = inDouble = false
+  curly = square = paren = 0
+  lastFilterIndex = 0
+  dir = { raw: str }
+
+  for (i = 0, l = str.length; i < l; i++) {
+    c = str.charCodeAt(i)
+    if (inSingle) {
+      // check single quote
+      if (c === 0x27) inSingle = !inSingle
+    } else if (inDouble) {
+      // check double quote
+      if (c === 0x22) inDouble = !inDouble
+    } else if (
+      c === 0x7C && // pipe
+      str.charCodeAt(i + 1) !== 0x7C &&
+      str.charCodeAt(i - 1) !== 0x7C
+    ) {
+      if (dir.expression == null) {
+        // first filter, end of expression
+        lastFilterIndex = i + 1
+        dir.expression = str.slice(0, i).trim()
+      } else {
+        // already has filter
+        pushFilter()
+      }
+    } else {
+      switch (c) {
+        case 0x22: inDouble = true; break // "
+        case 0x27: inSingle = true; break // '
+        case 0x28: paren++; break         // (
+        case 0x29: paren--; break         // )
+        case 0x5B: square++; break        // [
+        case 0x5D: square--; break        // ]
+        case 0x7B: curly++; break         // {
+        case 0x7D: curly--; break         // }
+      }
+    }
+  }
+
+  if (dir.expression == null) {
+    dir.expression = str.slice(0, i).trim()
+  } else if (lastFilterIndex !== 0) {
+    pushFilter()
+  }
+
+  cache.put(s, dir)
+  return dir
+}

+ 74 - 0
test/unit/specs/parsers/directive-new_spec.js

@@ -0,0 +1,74 @@
+var parse = require('../../../../src/parsers/directive-new').parse
+
+describe('New Directive Parser', function () {
+
+  it('simple', function () {
+    var res = parse('exp')
+    expect(res.expression).toBe('exp')
+    expect(res.raw).toBe('exp')
+  })
+
+  it('with filters', function () {
+    var res = parse('exp | abc de \'ok\' \'\' | bcd')
+    expect(res.expression).toBe('exp')
+    expect(res.raw).toBe('exp | abc de \'ok\' \'\' | bcd')
+    expect(res.filters.length).toBe(2)
+    expect(res.filters[0].name).toBe('abc')
+    expect(res.filters[0].args.length).toBe(3)
+    expect(res.filters[0].args[0].value).toBe('de')
+    expect(res.filters[0].args[0].dynamic).toBe(true)
+    expect(res.filters[0].args[1].value).toBe('ok')
+    expect(res.filters[0].args[1].dynamic).toBe(false)
+    expect(res.filters[0].args[2].value).toBe('')
+    expect(res.filters[0].args[2].dynamic).toBe(false)
+    expect(res.filters[1].name).toBe('bcd')
+    expect(res.filters[1].args).toBeUndefined()
+  })
+
+  it('double pipe', function () {
+    var res = parse('a || b | c')
+    expect(res.expression).toBe('a || b')
+    expect(res.raw).toBe('a || b | c')
+    expect(res.filters.length).toBe(1)
+    expect(res.filters[0].name).toBe('c')
+    expect(res.filters[0].args).toBeUndefined()
+  })
+
+  it('single quote + boolean', function () {
+    var res = parse('a ? \'b\' : c')
+    expect(res.expression).toBe('a ? \'b\' : c')
+    expect(res.filters).toBeUndefined()
+  })
+
+  it('double quote + boolean', function () {
+    var res = parse('"a:b:c||d|e|f" || d ? a : b')
+    expect(res.expression).toBe('"a:b:c||d|e|f" || d ? a : b')
+    expect(res.filters).toBeUndefined()
+    expect(res.arg).toBeUndefined()
+  })
+
+  it('nested function calls + array/object literals', function () {
+    var res = parse('test(c.indexOf(d,f),"e,f")')
+    expect(res.expression).toBe('test(c.indexOf(d,f),"e,f")')
+  })
+
+  it('array literal', function () {
+    var res = parse('d || [e,f]')
+    expect(res.expression).toBe('d || [e,f]')
+    expect(res.filters).toBeUndefined()
+  })
+
+  it('object literal', function () {
+    var res = parse('{a: 1, b: 2} | p')
+    expect(res.expression).toBe('{a: 1, b: 2}')
+    expect(res.filters.length).toBe(1)
+    expect(res.filters[0].name).toBe('p')
+    expect(res.filters[0].args).toBeUndefined()
+  })
+
+  it('cache', function () {
+    var res1 = parse('a || b | c')
+    var res2 = parse('a || b | c')
+    expect(res1).toBe(res2)
+  })
+})