TEMPLATE_BUILTINS = a: (text) -> if text.match(/^[aeioAEIO]/) return "an #{text}" return "a #{text}" an: (text) -> return this.a(text) cap: (text) -> return "#{text[0].toUpperCase()}#{text.slice(1)}" A: (text) -> return this.cap(this.a(text)) An: (text) -> return this.A(text) mergeInTag = (tags, tag) -> # Find the matching tag... i = tags.findIndex(t => t[0] == tag[0]) if i == -1 return tags.concat([tag]) # Otherwise: # This is supposed to be a non-destructive operation newTags = tags.concat() newTags[i] = tag return newTags processDirective = (rawDirective, model, cb, generator) -> directive = rawDirective.trim() if directive[0] == directive.slice(-1) && directive[0] == "'" # This is a literal directive. return directive.slice(1, -1) if directive.indexOf(' ') != -1 # The directive contains a space, which means it's a chained directive. funcName = directive.split(' ')[0] rest = directive.slice(directive.indexOf(' ') + 1) if TEMPLATE_BUILTINS.hasOwnProperty(funcName) return "#{TEMPLATE_BUILTINS[funcName](processDirective(rest, model, cb, generator))}" if generator && generator.builtins && generator.builtins[funcName] return "#{generator.builtins[funcName](processDirective(rest, model, cb, generator))}" if typeof model[funcName] != 'function' throw new Error("Builtin or model property \"#{funcName}\" is not a function.") return "#{model[funcName](processDirective(rest, model, cb, generator))}" if directive[0] == '|' [tagStr, snippet] = directive.split(':') # Disregard the first | newTag = tagStr.slice(1).split('|') newModel = Object.create(model) newModel.tags = mergeInTag(model.tags, newTag) return cb(snippet, newModel) if directive[0] == ':' return cb(directive.slice(1), model) if directive[0] == '#' args = directive.slice(1).split('-') return salet.rnd.randRange(parseInt(args[0], 10), parseInt(args[1], 10)) if directive.indexOf('.') != -1 propChain = directive.split('.') return propChain.reduce((obj, prop) => return [obj[prop], model] ## TODO ) return "#{model[directive]}" template = (phrase, model, cb, generator) -> [openBracket, closeBracket] = [phrase.indexOf('['), phrase.indexOf(']')] if openBracket == -1 return phrase if closeBracket == -1 throw new Error("Missing close bracket in phrase: #{phrase}") before = phrase.slice(0, openBracket) after = phrase.slice(closeBracket + 1) directive = phrase.substring(openBracket + 1, closeBracket) return template( "#{before}#{processDirective(directive, model, cb, generator)}#{after}", model, cb, generator )