express-俺-generator作ろうぜ

#express-俺-generator
というわけで、[goldblendの続き](http://qiita.com/mrpepper/items/c6f7aa46402c66f232a2)です。とは言え、今回はgoldblendの改版ではなく、generatorの話。とは言え、function * とか、**Javascriptの新しい言語機能のgeneratorではありません**。express-generatorの方のgeneratorです。まぎらわしいったらありゃしない。そのへんなんか考えて欲しいものですね。

で、そういうgeneratorを作ろうと思うと、どうしてもテンプレートエンジン的なものが欲しくなってきます。ディレクトリ名に変数を展開したり、本文中にプリプロセッサ指令みたいなので分岐したり。

で、まだ、express-goldblend-generatorはできていないのですが（できたら、npmに上げようかと思っています）、とりあえず変数展開をするディレクトリコピーを作ったので、お披露目と行きたいと思います。

使い方は、冒頭のコメントをみてちょんまげ。ファイル内のブロック要素の入れ子とか、まだ充分にテストできていませんのであしからず。ご指摘など頂ければ幸いです。そして、作ろうぜ！　それぞれの夏！　express-俺-generator！

```coffeescript:template.coffee
#!/usr/bin/env coffee

###
下記「式」では、paramで渡した変数が展開され、その演算も可能である。
式の文法は、基本的にJavascriptの文法に準じる。

ディレクトリ名とファイル名
$$$$$$名前:#(式)$$$$$$
$$$$$$名前:!#(式)$$$$$$
$$$$$$#(式)$$$$$$
	各キーワードとも名前全体との一致のみ判定
	1番目は、式の値==trueなら、「名前」という名前でコピー先に複製される
	　式の値==falseなら、コピーされない（ディレクトリなら、中身も無視）
	2番目は、式の値==falseなら、「名前」という名前でコピー先に複製される
	　式の値==trueなら、コピーされない（ディレクトリなら、中身も無視）
	3番目は、式の値が存在すれば、式の値を名前にしてコピー先に複製される
	　式の値==nullなら、コピーされない（ディレクトリなら、中身も無視）


ファイル内
ブロック要素（ブロック要素は入れ子にできる）
$$$$$$switch 式1$$$$$$
$$$$$$when 式2$$$$$$
　記述2
$$$$$$when 式3$$$$$$
　記述3
$$$$$$else$$$$$$
　記述4
$$$$$$endswitch$$$$$$
	式2==式1なら、記述2のみコピー先に複製される
	式3==式1なら、記述3のみコピー先に複製される
	どちらでもなければ、記述4のみコピー先に複製される
	when節はいくつあってもよく、else節はなくてもよい

$$$$$$if 式$$$$$$
　記述1
$$$$$$else$$$$$$
　記述2
$$$$$$endif$$$$$$
	式==trueなら、記述1のみコピー先に複製される
	式==falseなら、記述2のみコピー先に複製される

$$$$$$for 変数名 in 配列$$$$$$
　記述1
$$$$$$next$$$$$$
	記述1の内容が、変数 = 配列要素それぞれとして、
	配列要素数回だけコピー先に複製される。

$$$$$$for 変数名,変数名 of オブジェクト$$$$$$
　記述1
$$$$$$next$$$$$$
	記述1の内容が、変数,変数 = オブジェクト要素それぞれとして、
	オブジェクト要素数回だけコピー先に複製される。

$$$$$$式$$$$$$
	式を実行するが、コピー先には影響を与えない。
	むしろ、上記下記の制御に用いるための変数の演算などを実行する
	Javascriptとして解釈可能なコードが実行できる
	が、これは自由すぎるので消すかも


ファイル内
インライン要素（ブロック要素のローカル変数
$$$$式$$$$
	式の値に置換される


ファイル内
エスケープシーケンス
$$$$dollar n$$$$
	n個の「$」に置換されるが、nは直値での指定のみ
	（まあ、エスケープシーケンスですから）



templatecopydir(__dstpath__,__srcpath__,__param__)
templatecopyfile(__dstpath__,__srcfile__,__param__)
###

fs = require('fs')

templatecopyfile = (__dstpath__, __srcpath__, __dstfile__, __srcfile__, __param__) ->
  __reserved__ =
    __reserved__:true
    __dstpath__:true
    __srcpath__:true
    __dstfile__:true
    __srcfile__:true
    __param__:true
    __detail__:true
    __inputlines__:true
    __key__:true
    __line__:true
    __linenumber__:true
    __result__:true
    __resultlines__:true
    __scanscope__:true
    __script__:true
    __stack__:true
    __usekey__:true
    __usescope__:true
    __usevalue__:true
    __value__:true
    __whenvalue__:true
    i:true
    len:true
  
  __inputlines__ = (fs.readFileSync __srcpath__+'/'+__srcfile__,'utf8').split("\n")
  __resultlines__ = []
  
  for __key__,__value__ of __param__
    if not __key__ in __reserved__
      eval 'var '+__key__+' = __value__;'
  
  __stack__ = []
  __usescope__ = true
  __linenumber__ = 0
  while __linenumber__ < __inputlines__.length
    __line__ = __inputlines__[__linenumber__]
    __linenumber__ += 1
    
    __result__ = __line__.match(/^\s*\$\$\$\$\$\$\s*(\S.*\S)\s*\$\$\$\$\$\$\s*$/)
    if __result__?
      #block
      __script__ = __result__[1]
      #scope end
      if __script__ == 'endswitch' 
        if __stack__.length == 0 or
           __stack__[__stack__.length-1].mode != 'switch'
          throw 'unexpected endswitch '+__srcfile__+'['+(__linenumber__-1)+']'
        else
          __stack__.pop()
      else if __script__ == 'endif'
        if __stack__.length == 0 or
           __stack__[__stack__.length-1].mode != 'if'
          throw 'unexpected endif '+__srcfile__+'['+(__linenumber__-1)+']'
        else
          __stack__.pop()
      else if __script__ == 'next'
        if __stack__.length == 0 or
          (__stack__[__stack__.length-1].mode != 'forin' and
           __stack__[__stack__.length-1].mode != 'forof')
          throw 'unexpected next '+__srcfile__+'['+(__linenumber__-1)+']'
        else 
          if __stack__[__stack__.length-1].mode == 'forin'
            __stack__[__stack__.length-1].count += 1
            if __stack__[__stack__.length-1].num == __stack__[__stack__.length-1].count
              __usescope__ = __stack__[__stack__.length-1].usescope
              __stack__.pop()
            else
              __usevalue__ = __stack__[__stack__.length-1].array[__stack__[__stack__.length-1].count]
              eval 'var '+__stack__[__stack__.length-1].value+' = __usevalue__;'
              __linenumber__ = __stack__[__stack__.length-1].line
          else if __stack__[__stack__.length-1].mode == 'forof'
            __stack__[__stack__.length-1].count += 1
            if __stack__[__stack__.length-1].num == __stack__[__stack__.length-1].count
              __usescope__ = __stack__[__stack__.length-1].usescope
              __stack__.pop()
            else
              __usekey__ = Object.keys(__stack__[__stack__.length-1].array)[__stack__[__stack__.length-1].count]
              eval 'var '+__stack__[__stack__.length-1].key+' = __usekey__;'
              __usevalue__ = __stack__[__stack__.length-1].array[__usekey__]
              eval 'var'+__stack__[__stack__.length-1].value+' = __usevalue__'
              __linenumber__ = __stack__[__stack__.length-1].line
      else if __script__ == 'else'
        if __stack__.length == 0 or
          (__stack__[__stack__.length-1].mode != 'if' and
           __stack__[__stack__.length-1].mode != 'switch')
          throw 'unexpected next '+__srcfile__+'['+(__linenumber__-1)+']'
        else if __stack__[__stack__.length-1].mode == 'if'
          __stack__[__stack__.length-1].usescope = not __stack__[__stack__.length-1].usescope
          __stack__[__stack__.length-1].phase = false
        else if __stack__[__stack__.length-1].mode == 'switch'
          __stack__[__stack__.length-1].usescope = not __stack__[__stack__.length-1].matched
          __stack__[__stack__.length-1].phase = null
      else if /^for\s+([_a-zA-Z]\w*)\s+in\s+(\S.*)$/.test(__script__)
        __detail__ = __script__.match(/^for\s+([_a-zA-Z]\w*)\s+in\s+(\S.*)$/)
        __stack__.push()
        #  .mode = 'forin'
        #  .value = ローカル変数名
        #  .num = 要素数
        #  .count = 処理中要素番号
        #  .array = 配列オブジェクトのコピー
        #  .line = コンテンツ開始行
        #  .usescope = スコープの状態
        __stack__[__stack__.length-1].mode = 'forin'
        __stack__[__stack__.length-1].value = __detail__[1]
        eval 'var '+__detail__[1]+' = ('+__detail__[2]+')[0];'
        __stack__[__stack__.length-1].array = eval __detail__[2]
        __stack__[__stack__.length-1].num = eval '('+__detail__[2]+').length'
        __stack__[__stack__.length-1].count = 0
        __stack__[__stack__.length-1].line = __linenumber__
        __stack__[__stack__.length-1].usescope = true
        
      else if /^for\s+([_a-zA-Z]\w*)\s*,\s*([_a-zA-Z]\w*)\s+of\s+(\S.*)$/.test(__script__)
        __detail__ = __script__.match(/^for\s+([_a-zA-Z]\w*)\s*,\s*([_a-zA-Z]\w*)\s+of\s+(\S.*)$/)
        __stack__.push()
        #  .mode = 'forof'
        #  .key = ローカル変数名
        #  .value = ローカル変数名
        #  .num = 要素数
        #  .count = 処理中要素番号
        #  .array = オブジェクトのコピー
        #  .line = 開始行
        #  .usescope = スコープの状態
        __stack__[__stack__.length-1].mode = 'forin'
        __stack__[__stack__.length-1].key = __detail__[1]
        eval 'var '+__detail__[1]+' = Object.keys('+__detail__[3]+')[0];'
        __stack__[__stack__.length-1].value = __detail__[2]
        eval 'var '+__detail__[2]+' = ('+__detail__[3]+')[Object.keys('+__detail__[3]+')[0]];'
        __stack__[__stack__.length-1].array = eval __detail__[3]
        __stack__[__stack__.length-1].num = eval '('+__detail__[3]+').length'
        __stack__[__stack__.length-1].count = 0
        __stack__[__stack__.length-1].line = __linenumber__
        __stack__[__stack__.length-1].usescope = true
        
      else if /^switch\s+(\S.*)$/.test(__script__)
        __detail__ = __script__.match(/^switch\s+(\S.*)$/)
        __stack__.push()
        #  .mode = 'switch'
        #  .value = 式の値
        #  .phase = 最初のwhenの前とelseの後はnull,whenの後は、その式の値
        #  .matched = どれかのwhenにマッチしたらtrueさもなくばfalse
        #  .usescope = スコープの状態
        __stack__[__stack__.length-1].mode = 'switch'
        __stack__[__stack__.length-1].value = eval __detail__[1]
        __stack__[__stack__.length-1].phase = null
        __stack__[__stack__.length-1].matched = false
        __stack__[__stack__.length-1].usescope = false
        
      else if /^if\s+(\S.*)$/.test(__script__)
        __detail__ = __script__.match(/^if\s+(\S.*)$/)
        __stack__.push()
        #  .mode = 'if'
        #  .value = 式の値
        #  .phase = elseの前ならtrue
        #  .usescope = スコープの状態
        __stack__[__stack__.length-1].mode = 'switch'
        __stack__[__stack__.length-1].value = eval __detail__[1]
        __stack__[__stack__.length-1].phase = true
        __stack__[__stack__.length-1].usescope = __stack__[__stack__.length-1].value
        
      else if /^when\s+(\S.*)$/.test(__script__)
        if __stack__.length == 0 or
           __stack__[__stack__.length-1].mode != 'switch'
          throw 'unexpected next '+__srcfile__+'['+(__linenumber__-1)+']'
        __whenvalue__ = eval (__script__.match(/^when\s+(\S.*)$/))[1]
        __stack__[__stack__.length-1].phase = __whenvalue__
        if __stack__[__stack__.length-1].usescope = (__whenvalue__ == __stack__[__stack__.length-1].value)
          __stack__[__stack__.length-1].matched = true
      else
        eval '('+__script__+')'
        #throw 'undefined token '+__srcfile__+'['+(__linenumber__-1)+']'
      #__usescope__更新
      __usescope__ = true
      for __scanscope__ in __stack__
        if not __scanscope__.usescope
          __usescope__ = false
    else
      if __usescope__
        #通常行
        #inline展開
        __result__ = __line__.match(/\$\$\$\$\s*(\S.*\S)\s*\$\$\$\$/)
        while __result__?
          __line__.replace(__result__[0],(eval __result__[1]).toString())
          __result__ = __line__.match(/\$\$\$\$\s*(\S.*\S)\s*\$\$\$\$/)
        
        __resultlines__.push(__line__)
  
  fs.writeFileSync __dstpath__+'/'+__dstfile__,__resultlines__.join("\n")


convfilename = (__srcfile__, __param__) ->
  for __key__,__value__ of __param__
    eval 'var '+__key__+' = __value__;'

  __dstfile__ = ''
  if /\$\$\$\$\$\$\s*(\w+)\s*:\s*#\((.+)\)\s*\$\$\$\$\$\$/.test(__srcfile__)
    __result__ = __srcfile__.match(/^\$\$\$\$\$\$\s*(\w+)\s*:\s*#\((.+)\)\s*\$\$\$\$\$\$$/)
    if (eval __result__[2])
      __dstfile__ = __fileordir__.replace(__result__[0], __result__[1])
    else return
  else if /\$\$\$\$\$\$\s*(\w+)\s*:\s*!\s*#\((\w+)\)\s*\$\$\$\$\$\$/.test(__srcfile__)
    __result__ = __srcfile__.match(/^\$\$\$\$\$\$\s*(\w+)\s*:\s*!\s*#\((\w+)\)\s*\$\$\$\$\$\$$/)
    if not(eval __result__[2])
      __dstfile__ = __fileordir__.replace(__result__[0], __result__[1])
    else return
  else if /\$\$\$\$\$\$\s*#\((.+)\)\s*\$\$\$\$\$\$/.test(__srcfile__)
    __result__ = __srcfile__.match(/^\$\$\$\$\$\$\s*#\((.+)\)\s*\$\$\$\$\$\$$/)
    __eval__ = (eval __result__[1]).toString()
    if __eval__ != ''
      __dstfile__ = __eval__
    else return
  else
    __dstfile__ = __srcfile__
  return __dstfile__

templatecopydir_inside = (func) ->
  return (__dstpath__, __srcpath__, __param__, __first__ = true) ->
    console.log 'src '+__srcpath__
    if __first__
      if not fs.existsSync __srcpath__
        console.log 'templates are not found.'
        return -1
    __result__ = __srcpath__.match(/\/([^\/]+)$/)
    if not(__result__?)
      return -1
    __srcdir__ = __result__[1]
    __dstdir__ = (convfilename __srcdir__,__param__) ? __srcdir__
    
    process.umask(0)
    __dstpath__ += '/'+__dstdir__
    if not fs.existsSync __dstpath__
      fs.mkdirSync __dstpath__,'0757'
    else
      if __first__
        console.log 'the project has already created.'
        return -1

    console.log 'dst '+__dstpath__
    
    __filearray__ = fs.readdirSync __srcpath__
    for __fileordir__ in __filearray__
      __stat__ = fs.statSync __srcpath__+'/'+__fileordir__
      if __stat__.isFile()
        console.log 'scanfiles '+__fileordir__
        __dstfile__ = convfilename __fileordir__,__param__
        templatecopyfile __dstpath__, __srcpath__, __dstfile__, __fileordir__, __param__
      else if __stat__.isDirectory()
        console.log 'scandirs '+__fileordir__
        func __dstpath__,__srcpath__+'/'+__fileordir__,__param__,false

#所謂Yコンビネータ
____y____ = (func) ->
  return ((g) ->
    return (m,n,o,p) ->
      return func(g(g))(m,n,o,p)
  )((g) ->
    return (m,n,o,p) ->
      return func(g(g))(m,n,o,p)
  )


templatecopydir = ____y____(templatecopydir_inside)

templatecopydir '/path/to/products','/path/to/generator-goldblend-generator/$$$$$$#(project)$$$$$$',{project: 'testtest', test:12}
```