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}