階層的CSSコンバータ
CSSの記述テクニック 階層宣言コーディング より.エントリ自体は,エイプリルフールネタなのですが,意外と便利そうな記述方法でしたので, 通常のCSSへのコンバータを記述してみました.
階層的CSSクラス
以下が,階層的CSSを解析するためのクラスのプロトタイプ宣言です(名前空間は,clx).
template <
class CharT,
class Traits = std::char_traits<CharT>
>
class basic_hcss {
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 cssdecl<CharT, Traits> declaration_type;
typedef std::vector<string_type> declaration_list;
basic_hcss();
template <class InputIterator>
explicit basic_hcss(InputIterator first, InputIterator last,
const std::locale& loc = std::locale());
explicit basic_hcss(const char_type* filename,
const std::locale& loc = std::locale());
explicit basic_hcss(const string_type& filename,
const std::locale& loc = std::locale());
template <class InputIterator>
basic_hcss& assign(InputIterator first, InputIterator last,
const std::locale& loc = std::locale());
basic_hcss& assign(const char_type* filename,
const std::locale& loc = std::locale());
basic_hcss& assign(const string_type& filename,
const std::locale& loc = std::locale());
size_type size() const;
const string_type& selector(size_type i) const;
const declaration_list& declarations(size_type i) const;
};
typedef basic_hcss<char> hcss;
コンストラクタ,またはassign()メソッドに,ファイル名,または入力ストリームバッファの イテレータ(istreambuf_iterator<char>)を指定すると階層的CSSの解析を行い, 結果を配列(vector)に格納します.解析結果にアクセスするためのメソッドは, selector()およびdeclarations()で,それぞれ添え字に対応するセレクタおよび宣言リストを返します. declarations()で返されるのは配列(vector)なので,さらにat()メソッドや[]演算子などを用いて それぞれの宣言にアクセスします.
例外処理
現在の実装では,以下の場合に例外をthrowします.
- 指定されたファイルが存在しない場合.
- 中括弧の対応("{","}")がおかしい場合.
- コメント記号の対応("/*","*/")がおかしい場合.
- セレクタが指定されていない場合(セレクタの記述の前に"{"が確認された場合).
- プロパティが指定されていない場合(プロパティの記述の前に":"が確認された場合).
- プロパティのみで値が指定されていない場合.
例外クラスのプロトタイプ宣言は,以下の通りです.
class css_syntax_error : public std::runtime_error {
public:
typedef unsigned int size_type;
typedef char char_type;
typedef std::string string_type;
explicit css_syntax_error(size_type n, const string_type& what_arg);
size_type line();
};
css_syntax_errorの例外オブジェクトをcatchした場合,line()メソッドで例外を検知した行数を 取得することができます.
制限事項
制限,その他注意事項としては,以下の通りです.
- @規則が存在すると解析に失敗する.
- コメントは全て読み捨てる.
サンプルプログラム
最後に,このクラスを使用した簡単なコンバータのプログラム例を書いてみました.
#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 < hc.declarations(i).size(); j++) {
std::cout << " " << hc.declarations(i).at(j).property << ": "
<< hc.declarations(i).at(j).value << ';' << std::endl;
}
std::cout << '}' << std::endl;
std::cout << std::endl;
}
}
catch (clx::css_syntax_error& e) {
std::cerr << argv[1] << ':' << e.line()
<< ": error: " << e.what() << std::endl;
std::exit(-1);
}
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_20070427.tar.gz