LoginSignup
0
0

More than 5 years have passed since last update.

express-俺-generator作ろうぜ

Last updated at Posted at 2015-05-25

express-俺-generator

というわけで、goldblendの続きです。とは言え、今回はgoldblendの改版ではなく、generatorの話。とは言え、function * とか、Javascriptの新しい言語機能のgeneratorではありません。express-generatorの方のgeneratorです。まぎらわしいったらありゃしない。そのへんなんか考えて欲しいものですね。

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

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

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

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}
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0