Hatena::ブログ(Diary)

Life like a clown このページをアンテナに追加 RSSフィード Twitter

2007-04-05

階層的CSSコンバータ

CSSの記述テクニック 階層宣言コーディングから.エイプリルフールネタを掘り起こすのはどうかとも思いましたが,意外と便利そうな記述方法でしたので,コンバータを記述してみました.以下が,階層的CSSを解析するためのクラスのプロトタイプ宣言.

namespace clx {
    template <
        class CharT,
        class Traits = std::char_traits<CharT>
    >
    class basic_hierarchical_css {
    public:
        typedef CharT char_type;
        typedef unsigned int size_type;
        typedef std::basic_string<CharT, Traits> string_type;
        typedef std::vector<string_type> selector_list;
        typedef std::vector<string_type> declaration_list;
        
        basic_hierarchical_css();
                
        template <class InputIterator>
        explicit basic_hierarchical_css(InputIterator first, InputIterator last);
        explicit basic_hierarchical_css(const char_type* filename);
        explicit basic_hierarchical_css(const string_type& filename);
        
        template <class InputIterator>
        basic_hierarchical_css& assign(InputIterator first, InputIterator last);
        basic_hierarchical_css& assign(const char_type* filename);        
        basic_hierarchical_css& assign(const string_type& filename);
        
        size_type size() const;
        const string_type& selector(size_type i) const;
        const declaration_list& declarations(size_type i) const;
    };
    
    typedef basic_hierarchical_css<char> hcss;
}

コンストラクタ,またはassign()メソッドに,ファイル名,または入力ストリームのイテレータを渡すと階層的CSSの解析を行い,結果を配列(vector)に格納します.解析結果にアクセスするためのメソッドは,selector()およびdeclarations()メソッドで,それぞれ添え字に対応するセレクタおよび宣言リストを返します.declarations()で返されるのは配列(vector)であるため,さらにat()メソッドでそれぞれの宣言にアクセスします.

例外処理は今の所ごく簡易なもので,以下の場合のみ例外を投げます.

  • 指定されたファイルが存在しない場合.
  • コメント記号の対("/*","*/")の数が合わない場合.
  • 中括弧の対("{","}")の数が合わない場合.

制限,その他注意事項としては,以下の通り(いくつかは修正するかも).

  • @規則が存在すると解析に失敗する.
  • コメントは全て読み捨てる.
  • 中括弧(“{”,“}”),セミコロン(“;”),コメント記号(“/*”,“*/”)の直後に空白や改行文字が挿入されていないと,解析に失敗する.

さて,このクラスを使用した簡単なコンバータのプログラム例を書いてみました.

#include <cstdlib>
#include <iostream>
#include "hcss.h"

int main(int argc, char* argv[]) {
    if (argc < 2) {
        std::cerr << "usage: hcssconv filename" << std::endl;
        std::exit(-1);
    }
    
    try {
        clx::hcss hc;
        hc.assign(argv[1]);
        
        // print
        for (size_t i = 0; i < hc.size(); i++) {
            if (hc.declarations(i).empty()) continue;
            std::cout << hc.selector(i) << " {" << std::endl;
            for (size_t j = 0; j &lt; hc.declarations(i).size(); j++) {
                std::cout << "  " << hc.declarations(i).at(j) << std::endl;
            }
            std::cout << '}' << std::endl;
            std::cout << std::endl;
        }
    }
    catch (std::runtime_error& e) {
        std::cerr << e.what() << std::endl;
        std::exit(-1);
    }
    
    return 0;
}

このプログラムは引数としてファイル名を渡すと,そのファイル中に記述されている階層的CSSを解析し,結果を標準出力へ出力します.以下のサンプルで試してみます.尚,現在のところ,コンパイルgccでのみ確認.

body {
	p.caution {
 		color: red;
 		font-weight: bold;
	}

	div.entry {
 		background-color: #eee;
		p.caution {
			border: 1px solid green;
			margin: 1em;
		}
	}
}

実行結果は,以下の通り.

p.caution {
  color: red;
  font-weight: bold;
}

div.entry {
  background-color: #eee;
}

div.entry p.caution {
  border: 1px solid green;
  margin: 1em;
}

なかなか面白いかもしれません.hcss_20070406.tar.gz

追記

hcssクラスをもうすこし真面目に書き直したバージョン(with clx).最新の説明は,こちら