silk-gui
Version:
GUI for developers and Node OS
159 lines (146 loc) • 3.48 kB
JavaScript
var _ = require('../util')
var Cache = require('../cache')
var cache = new Cache(1000)
var argRE = /^[^\{\?]+$|^'[^']*'$|^"[^"]*"$/
var filterTokenRE = /[^\s'"]+|'[^']+'|"[^"]+"/g
/**
* 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]
filter.args = tokens.length > 1 ? tokens.slice(1) : null
}
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
}