all files / liquidjs/src/ parser.js

100% Statements 63/63
100% Branches 20/20
100% Functions 11/11
100% Lines 60/60
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106   116×   102× 102× 102×     444× 444×     652× 652× 344× 332×       102× 102× 102× 273× 258×   121×   137× 137×   102× 90×     90× 90×       282× 282× 282× 419×   256×     556× 556× 556× 190× 366× 177×   189×   530× 530×   26×       190× 188×     209× 209×   208× 208×   208× 208× 124×     208×     124×       102× 102×     116×              
const lexical = require('./lexical.js')
const ParseError = require('./util/error.js').ParseError
const assert = require('./util/assert.js')
 
module.exports = function (Tag, Filter) {
  var stream = {
    init: function (tokens) {
      this.tokens = tokens
      this.handlers = {}
      return this
    },
    on: function (name, cb) {
      this.handlers[name] = cb
      return this
    },
    trigger: function (event, arg) {
      var h = this.handlers[event]
      if (typeof h === 'function') {
        h(arg)
        return true
      }
    },
    start: function () {
      this.trigger('start')
      var token
      while (!this.stopRequested && (token = this.tokens.shift())) {
        if (this.trigger('token', token)) continue
        if (token.type === 'tag' &&
            this.trigger(`tag:${token.name}`, token)) {
          continue
        }
        var template = parseToken(token, this.tokens)
        this.trigger('template', template)
      }
      if (!this.stopRequested) this.trigger('end')
      return this
    },
    stop: function () {
      this.stopRequested = true
      return this
    }
  }
 
  function parse (tokens) {
    var token
    var templates = []
    while ((token = tokens.shift())) {
      templates.push(parseToken(token, tokens))
    }
    return templates
  }
 
  function parseToken (token, tokens) {
    try {
      var tpl = null
      if (token.type === 'tag') {
        tpl = parseTag(token, tokens)
      } else if (token.type === 'output') {
        tpl = parseOutput(token.value)
      } else { // token.type === 'html'
        tpl = token
      }
      tpl.token = token
      return tpl
    } catch (e) {
      throw new ParseError(e, token)
    }
  }
 
  function parseTag (token, tokens) {
    if (token.name === 'continue' || token.name === 'break') return token
    return Tag.construct(token, tokens)
  }
 
  function parseOutput (str) {
    var match = lexical.matchValue(str)
    assert(match, `illegal output string: ${str}`)
 
    var initial = match[0]
    str = str.substr(match.index + match[0].length)
 
    var filters = []
    while ((match = lexical.filter.exec(str))) {
      filters.push([match[0].trim()])
    }
 
    return {
      type: 'output',
      initial: initial,
      filters: filters.map(str => Filter.construct(str))
    }
  }
 
  function parseStream (tokens) {
    var s = Object.create(stream)
    return s.init(tokens)
  }
 
  return {
    parse,
    parseTag,
    parseStream,
    parseOutput
  }
}