Doge log

Abby CTO 雑賀 力王のオフィシャルサイトです

KVMで無線LANブリッジ接続(DHCP)

最近ではVMが立ち上げれるぐらいのノートパソコンも安価に手に入るようになりました。
私のマシンもi7搭載のノートでKVMも入れてるのですが、ブリッジ接続の例がeth0のばっかで無線LANでブリッジをやる方法がよくわかりませんでした。
(NATで使ってた)
で最近マジメに設定してみようと思い調べたのでメモっておきたいと思います。
ちなみに環境はUbuntuです。RedHat系だと簡単にできるのかも知れません。

ちなみに適当にeth0と同じように書いてもwlan0ははじかれたり、それっぽい設定をしてもDHCPでうまくつながらなかったりします。
この方法だとゲスト、ホストともにDHCPでつながります。
あっちこっち持ち運んで作業するノートなどではDHCPの方が楽だと思います。

#!/bin/sh

/sbin/brctl addbr br0
/usr/sbin/tunctl -t tap0
/sbin/brctl addif br0 tap0
/sbin/ip addr add 192.168.2.200 dev br0
/sbin/ip link set br0 up

/bin/echo 1 > /proc/sys/net/ipv4/conf/wlan0/proxy_arp
/bin/echo 1 > /proc/sys/net/ipv4/conf/br0/proxy_arp
/bin/echo 1 > /proc/sys/net/ipv4/conf/tap0/proxy_arp

/usr/sbin/parprouted wlan0 br0
/usr/sbin/bcrelay -d -i br0 -o wlan0

ip addr addのとこはDHCPレンジ外を指定します。
このシェルをrootで実行後、virt-managerでゲストのネットワークデバイスで br0を指定すればつながります。
rc.localなどに書いてもいいかもしれませんね。

protocolを使って既存の関数の振る舞いを変える

こんにちわ、高校生です。
今回はprotocolを使った話です。

通常の場合

user=> (bit-and "生" "死")
IllegalArgumentException bit operation not supported for: class java.lang.String  clojure.lang.Numbers.bitOpsCast (Numbers.java:994)

bit-andはNumbersしか受け付けない関数なので当たり前のごとくうまくいきません。
異なる型でもいい感じに処理をして欲しい場合にはprotocolで既存関数も拡張ができます。

(defprotocol bit-protocol
  (bit-and [x y]))

(extend-protocol bit-protocol
  java.lang.String
  (bit-and [x y]
    (let [a (Character/codePointAt x 0)
          b (Character/codePointAt y 0)
          c (bit-and a b)]
      (apply str (Character/toChars c))))

  java.lang.Object
  (bit-and [x y]
    (clojure.core/bit-and x y)))

(bit-and "生" "死")

上記のように型ごとに処理が書けます。

Warning: protocol #'user/bit-protocol is overwriting function bit-and
WARNING: bit-and already refers to: #'clojure.core/bit-and in namespace: user, being replaced by: #'user/bit-and

Warningが出るものの既存の関数をoverwriteすることができます。
もちとんns上も置き換わるのでそちらの警告も出ますが。

Parsecを使ってjinja2風テンプレートをパースする。

http://github.com/mopemope/clarsec2を使って以前のjinja2風のテンプレを解析する。
まずはおおまかなブロックを検出するだけだけど。

input

<html>
  <body>
    {% foo %}
      {{ variable }}
    {% endfoo %}
    <div>
      {#_
      Comment
      Comment
      #}
    </div>
  </body>
</html>

コード

(use '[clarsec])
(use '[clarsec.monad])
(require '[clojure.string :as string])
(import '(java.util.regex Pattern))

(def sp-chars #"([\\\\*+\\[\\](){}\\$.?\\^|])")

(defn- esc-regex [in]
  (string/replace in sp-chars "\\\\$1"))

(defn- build-reg [args]
  (string/join "|" (map esc-regex args)))

(defn- create-regex [tags]
  (Pattern/compile (str "^(.*?)(" (build-reg tags) ")") Pattern/DOTALL))

(defmacro make-data [k]
  `(fn [x#] {~k x#}))

(defn text-data-p [regex conv-fn]
  (make-parser
    (fn p-text-data [^String strn]
      (let [found (re-find (re-matcher (create-regex regex) strn))
            [pair text token] (or found [nil nil nil])]
        (if (and token (not= token pair))
          (consumed (conv-fn text) (.substring strn (- (.length pair) (.length token))))
          (failed))))))

(defmacro surround2 [st ed]
  `(fn [p#] (between (symb ~st) (symb ~ed) p#)))

(defn combine [p last]
   (let-bind [r p
              l last]
            (result (concat [r] l))))

;; -------------------------------------

(def tag-block ((surround2 "{%" "%}")
                  (<$> (make-data :tag-block) identifier)))

(def variant-block ((surround2 "{{" "}}")
                  (<$> (make-data :variant-block) identifier)))

(def comment-block ((surround2 "{#" "#}")
                  (<$> (make-data :comment-block) (text-data-p ["#}"] #(str %)))))


(def text-data (text-data-p ["{{" "{%" "{#"] (make-data :text-data)))

(def all
  (<$> (make-data :text-data)
     (make-parser
       (fn p-any-token [strn]
         (consumed strn "")))))

(def blocks (many1 (<|> text-data tag-block variant-block comment-block)))

(def content (slurp "/tmp/index.html"))
(parse$ (combine blocks all) content)

最後の部分はまあ適当なんですけど。
上半分はutilとかキモいパーサーとかです。
正規表現とミックスしたものが使えるので便利ですね。

肝心はココです。
なんとなくどんなものの組み合わせ構成されてるかわかりますね。

(def tag-block ((surround2 "{%" "%}")
                  (<$> (make-data :tag-block) identifier)))

(def variant-block ((surround2 "{{" "}}")
                  (<$> (make-data :variant-block) identifier)))

(def comment-block ((surround2 "{#" "#}")
                  (<$> (make-data :comment-block) (text-data-p ["#}"] #(str %)))))


(def text-data (text-data-p ["{{" "{%" "{#"] (make-data :text-data)))

(def all
  (<$> (make-data :text-data)
     (make-parser
       (fn p-any-token [strn]
         (consumed strn "")))))

(def blocks (many1 (<|> text-data tag-block variant-block comment-block)))

(def content (slurp "/tmp/index.html"))
(parse$ (combine blocks all) content)

結果:

{:type :consumed, :value (({:text-data "\n \n "} {:tag-block "foo"} {:variant-block "variable"} {:tag-block "endfoo"} {:text-data "

\n "} {:comment-block "_\n Comment\n Comment\n "}) [:text-data "
\n \n\n\n"]), :rest ""}

一応パースできてるっぽい。
正規表現エスケープまみれになったコードよりわかりやすく(?)スッキリ(?)書けます。
速度はもちのろん出ません!!!
(<|>でtry-errorを繰り返しまくるので)

Parsecを使ってパースする

こんにちわ、高校生です。
にわかLisperです。みなさん、本当にClojure書いてますか?
正規表現とかしんどいし関数型言語ならParsec使えよ!という話があると思うので適当に書いてみました。

まずはcsvですよね。

(use '[clarsec])

(def input
       "Year,Make,Model,Length
       1997,Ford,Model-350,234
       2000,Mercury,\"Model 800\",238")

(def cell (lexeme (<|> string-literal
             (stringify (many (none-of ",\n"))))))

(def line (sep-by-1 cell comma))

(def csv (sep-by line eol))

(:value (parse$ csv input))

まあこんな感じです。分かる人にはまあなんとなくわかると思います。
結果:

 (("Year" "Make" "Model" "Length") ("1997" "Ford" "Model-350" "234") ("2000" "Mercury" "Model 800" "238"))

とまあパースできてますね。

次に簡易でxmlをパースしてみます。

(use '[clarsec])
(use '[clarsec.monad])


(def input
     "    <library>
              <book>
                  <title>Joy of Clojure</title>
                  <author>Fogus</author>
              </book>
              <book>
                  <title>Structured and Interpretation of Computer Programs</title>
                  <author>MIT</author>
              </book>
          </library>")

(defn arrows [p] (between (symb "<") (symb ">") p))
(def open-tag (arrows identifier))
(defn close-tag [expect-name] (arrows (symb (str "/" expect-name))))

(defn element [p]
  (let-bind [tag-name open-tag
             contents p
             (close-tag tag-name)]
            {(keyword tag-name) contents}))

(def xml
     (let [list$ #(flatten (list %&))]
       (element
        (<|> (<$> #(apply merge-with list$ %) (many1 (lazy xml)))
             (stringify (many (<|> letter space)))))))

(:value (parse$ xml input))

betweenとか便利ですよね。
えーlet-bindとかありますけど。。。モナド周りとかまあなんとなくといった感じです。
要素は辞書形式で表しています。
結果をflattenで綺麗にしています。
xmlの定義で自身を参照していますが、lazyにしておかないとStackが溢れます。
(要はdelayで必要になったときに計算を始めます)

結果はこんな感じ。

{:library {:book ({:author "Fogus", :title "Joy of Clojure"} {:author "MIT", :title "Structured and Interpretation of Computer Programs"})}}

エラーになる場合

;; 数値を許可していないので失敗する
(parse$ xml "<test>ABC123</test>")

結果

{:error-line <test>ABC1, :line-number 1, :type :failed}

とある程度どこで失敗したかわかります。

clarsecあれこれ

元々、clarsecはvimclojureの人が公開していたのですが闇に消えています。

他の開発者の人がそれらをgithubとかにあげていたのですが、エラー処理がなかったりエラー処理を実装しようとしていて放置されていたりと散々な感じでした。
仕方ないのである程度みんなのコードを寄せ集めたりして色々やって少し動くとこまでできました。
リポジトリは以下です。複雑なものはまだ試してないので自己責任で。
いろいろ手直ししてくれる人も募集しています。
https://github.com/mopemope/clarsec2

jinja2風なテンプレートをパースする

こんにちは、高校生です。
Parserはいつも人の使ったり、ragel先輩に吐かせたり楽してたのですがシンプルなものなら書けるかなと思い書いてみました。

(import '(java.util.regex Pattern))
(require '[clojure.string :as string])

(def sp-chars #"([\\\\*+\\[\\](){}\\$.?\\^|])")

(def ^:dynamic *bt* "{%")
(def ^:dynamic *bv* "{{")
(def ^:dynamic *bc* "{#")

(def ^:dynamic *et* "%}")
(def ^:dynamic *ev* "}}")
(def ^:dynamic *ec* "#}")

(defrecord Text [data])
(defrecord Tag [data])
(defrecord Variable [data])
(defrecord Comment [data])

(defn- esc [hin]
  (string/replace in sp-chars "\\\\$1"))

(defn- build-reg [args]
  (string/join "|" (map esc args)))


(defn- create-regex [tags]
  (Pattern/compile (str "^(.*?)(" (build-reg tags) ")") Pattern/DOTALL))

(def bregex (create-regex [*bt* *bv* *bc*]))
(def eregex (create-regex [*et* *ev* *ec*]))
(def reg {true bregex false eregex})

(def content (slurp "/tmp/index.html"))

(defn- get-node [token text]
  (condp = token
    *bt* (Tag. (string/trim text))
    *bv* (Variable. (string/trim text))
    *bc* (Comment. text)
    (Text. text)))

(defn- parse-tmpl [input]
  (loop [input input mode true next-token nil ast []]
    (let [found (re-find (re-matcher (reg mode) input))
          [pair text token] (or found [input input nil])
          ast (if (empty? text) ast (conj ast (get-node next-token text)))]
      ; (println "pair" pair)
      ; (println "text" text )
      ; (println "token" token)
      (if token
         (let [rest (.substring input (.length pair))
               mode (not mode)]
           (recur rest mode token ast))
        ast))))

(parse-tmpl content)

思ったより普通ですね。

テンプレート

<html>
  <body>
    {% foo %}
      {{ variable }}
    {% endfoo %}
    <div>
      {#_
      Comment
      Comment
      #}
    </div>
  </body>
</html>

結果

[#user.Text{:data "\n \n "} #user.Tag{:data "foo"} #user.Text{:data "\n "} #user.Variable{:data "variable"} #user.Text{:data "\n "} #user.Tag{:data "endfoo"} #user.Text{:data "\n

\n "} #user.Comment{:data " \n Comment \n Comment\n "} #user.Text{:data "\n
\n \n\n"}]

とまあシンプルなASTができたっぽい。
Nodeをdefrecordで作ってるので組み立てる際にprotocolを定義してなめればそれなりのテンプレートエンジンもすぐ作れそうですね。

継続的にタスクを実行するleiningen plugin

こんにちわ、高校生です。
leiningenも2.0-previewが出てきていろいろ機能強化されてきていますね。
ただleiningen 2.0ではinteractiveタスクがなくなっています。
ビルド毎にleiningenを起動をするのもだるいなあと思い、プロジェクト内のファイル変更を監視し
タスクを起動するpluginを書きました。
もちろんleiningen 2.0じゃないと動きません。

(ns leiningen.cont
  "Continuous target task"
  (:use [clojure.string :only (split)]
        [clojure.repl :only (pst)])
  (:require [leiningen.help :as help]
            [watchdog :as watchdog]))

(defn- create-taskmap [task x]
  (let [func-name (second (split (str task) #"\."))]
    (assoc x func-name (str task))))

(defmacro import-task []
  (loop [task help/tasks x {}]
    (if (nil? task)
      `(def ^:dynamic *task* ~x)
      (do
        (require (first task))
        (recur (next task) (create-taskmap (first task) x))))))

(import-task)

(defn- get-lein-tasks [tasks]
  (loop [args tasks result nil]
    (if (nil? args)
      (reverse result)
      (recur (next args)
             (conj result [(get *task* (first args)) (first args)])))))

(defn- call-task [[nm k] project]
  (let [nm-map (ns-interns (symbol nm))
        fun (get nm-map (symbol k))]
    (fn [x]
      (apply fun [project]))))

(defn- create-func [tasks project]
  (fn [x]
    (try
      (println "Change Files" x)
      (loop [nms tasks]
        (if (nil? nms)
          nil
          (do
            ((call-task (first nms) project) x)
            (recur (next nms)))))
      (catch Exception _ ))))

(defn cont [project & args]
  (let [paths (concat [(str (:root project) "/project.clj")]
                    (:source-paths project)
                    (:resource-paths project)
                    (:test-pathes project))
        tasks (get-lein-tasks args)
        task-func (create-func tasks project)]
    (watchdog/set-interval (* 1000))
    (binding [leiningen.test/*exit-after-tests* false]
      (watchdog/watch-start paths task-func))))

恐ろしくベタですね。
監視してるファイルはproject.cljも含み、source, resource, testです。
既にClojarsにあげているのでlein-contで検索すれば出てくると思います。
使い方としてはpluginをインストールし、

lein cont

でファイル監視を行い継続的にタスクを実行します。

autotest風にするならば

lein cont test

定期的に文法などのチェックを行うなら

lein cont check

複数のタスクを実行したりすることもできます。

lein cont check test

ソースなどはgithubにあがってるので何かあればそちらへ。