improv-salet/template.coffee
2017-11-10 16:11:35 +07:00

92 lines
2.7 KiB
CoffeeScript

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
)