| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170 |
- var _ = require('../util')
- var Cache = require('../cache')
- var cache = new Cache(1000)
- var argRE = /^[^\{\?]+$|^'[^']*'$|^"[^"]*"$/
- var filterTokenRE = /[^\s'"]+|'[^']+'|"[^"]+"/g
- var reservedArgRE = /^in$|^-?\d+/
- /**
- * Parser state
- */
- var str
- var c, i, l
- var inSingle
- var inDouble
- var curly
- var square
- var paren
- var begin
- var argIndex
- var dirs
- var dir
- var lastFilterIndex
- var arg
- /**
- * Push a directive object into the result Array
- */
- function pushDir () {
- dir.raw = str.slice(begin, i).trim()
- if (dir.expression === undefined) {
- dir.expression = str.slice(argIndex, i).trim()
- } else if (lastFilterIndex !== begin) {
- pushFilter()
- }
- if (i === 0 || dir.expression) {
- dirs.push(dir)
- }
- }
- /**
- * 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(function (arg) {
- var stripped = reservedArgRE.test(arg)
- ? arg
- : _.stripQuotes(arg)
- return {
- value: stripped || arg,
- dynamic: !stripped
- }
- })
- }
- }
- if (filter) {
- (dir.filters = dir.filters || []).push(filter)
- }
- lastFilterIndex = i + 1
- }
- /**
- * Parse a directive string into an Array of AST-like
- * objects representing directives.
- *
- * Example:
- *
- * "click: a = a + 1 | uppercase" will yield:
- * {
- * arg: 'click',
- * expression: 'a = a + 1',
- * filters: [
- * { name: 'uppercase', args: null }
- * ]
- * }
- *
- * @param {String} str
- * @return {Array<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 = begin = argIndex = 0
- lastFilterIndex = 0
- dirs = []
- dir = {}
- arg = null
- 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 === 0x2C && // comma
- !paren && !curly && !square
- ) {
- // reached the end of a directive
- pushDir()
- // reset & skip the comma
- dir = {}
- begin = argIndex = lastFilterIndex = i + 1
- } else if (
- c === 0x3A && // colon
- !dir.expression &&
- !dir.arg
- ) {
- // argument
- arg = str.slice(begin, i).trim()
- // test for valid argument here
- // since we may have caught stuff like first half of
- // an object literal or a ternary expression.
- if (argRE.test(arg)) {
- argIndex = i + 1
- dir.arg = _.stripQuotes(arg) || arg
- }
- } else if (
- c === 0x7C && // pipe
- str.charCodeAt(i + 1) !== 0x7C &&
- str.charCodeAt(i - 1) !== 0x7C
- ) {
- if (dir.expression === undefined) {
- // first filter, end of expression
- lastFilterIndex = i + 1
- dir.expression = str.slice(argIndex, 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 (i === 0 || begin !== i) {
- pushDir()
- }
- cache.put(s, dirs)
- return dirs
- }
|