Archive for the ‘regex’ Tag

newLISP の組込関数を表示してみる...または、find-all の filter 的使い方

 newLISP の組込関数は 360個前後(OS によって若干違う)。
 よく使う関数名は自然と打ち込めますが、たまにしか使わない関数だと打ち間違えたりします。
 マニュアルを見れば良いのですが、関数名だけを知りたいならば、

> (filter (fn (x) (primitive? (eval x))) (symbols))
(! != $ % & * + ++ - -- / : < <<  >= >> NaN? ^ abs acos acosh add address amb 
 :(途中略)
 xml-type-tags zero? zerop | ~)
> 

 として探すこともできます。
 しかし、このままでは多すぎるので、更に絞り込む関数を作ってみました。

(define (primitives reg flag (predicate primitive?) (cnt MAIN))
  (let (func (filter (letex (_p predicate) (fn (x) ('_p (eval x)))) (symbols cnt)))
    (if-not reg
        func
      (let (pat (if flag reg (string "^" reg ".*")))
        (map sym (find-all pat (map string func) $it (fn (x y) (regex x y 1))))))
  ))

 使い方はこんな感じ、

> (primitives)
(! != $ % & * + ++ - -- / : < <<  >= >> NaN? ^ abs acos acosh add address amb 
 :(途中略)
 xfer-event xml-error xml-parse xml-type-tags zero? zerop | ~)
> (primitives "ap")
(append append-file apply)
> 

 引数に文字列を与えれば、その文字列から始まる組込関数を返します。
 文字列には、正規表現も使えます。

> (primitives ".*file.*" true)
(append-file copy-file delete-file file-info file? read-file rename-file write-file)
> (primitives {.+\?$} true)
(NaN? array? atom? context? directory? empty? file? float? global? inf? integer? 
 lambda? legal? list? macro? nil? null? number? primitive? protected? quote? string? 
 symbol? true? zero?)
> 

 これで、おおかた絞り込めるでしょう(嬉)。
 正規表現を使ったリストの絞り込みには、find-allregex を組み合わせて、filter のように使っています。この使い方はマニュアルにある方法です。
 関数primitives の中をを見れば分かる通り、primitive? も引数ですから、

> (primitives ".+" true lambda?)
($reader-event cadr car cdr consp evenp flat1 fourth gensym mapc maplist mappend 
 module oddp primitives read-integer read-string remove second third)
> (primitives ".+" true context?)
(*gensym* Class MAIN Tree cnt decf defun hayashi incf include labels macro map-mv 
 multi-let psetq reference-inversion rsetq type-of with-open-file)
> 

 こんな風にも使えます。lambda? で $reader-event が出てきているのは、私の場合 macro.lsp を使っているからです。

> $reader-event
(lambda (macro:expr) 
 (if (list? macro:expr) 
  (dolist (macro:pattern macro:macro-list) 
   (if (match macro:pattern macro:expr) 
    (setf macro:expr (eval macro:expr)) 
    (set-ref-all macro:pattern macro:expr (eval $it) match)))) macro:expr)
> 

 さて、もう一つのオプションはコンテキストの指定で、

> (symbols macro)
(macro:body macro:callp macro:delete macro:expr macro:macro macro:macro-list macro:pattern 
 macro:resume macro:rewrite macro:sname macro:suspend)
> (primitives ".+" true lambda? macro)
(macro:resume macro:rewrite macro:suspend)
> (primitives ".+" true macro? macro)
(macro:delete macro:macro)
> 

 こんな感じ。
 ここまでやると、primitives ではないような、、、(汗)

以上、如何でしょうか?

P.S.
 ちなみ、symbols では fnlambdalambda-macro は出て来ません。
 名無し(無名関数と無名マクロ)だからシンボルですら無い!
 たぶん、、、

newLISP で GUI する。。。または、天気予報を表示する。(解説編)

 “newLISP で GUI する。。。または、天気予報を表示する。”を出してからだいぶ経ちますが(汗)、如何だったでしょうか?

 “short short story または 晴耕雨読な日々”の WordPress.com の移動に伴う LISPOn newLISP不具合の修正が終わったので、ようやく、こちらを更新できるようになりました(言い訳ですけどね、汗)。

 さて、解説ですが、今回は、鬼門(笑)の正規表現です。
 スクリプト中の関数button-handler

    (regex {<div class="titleBgLong">(([^v]*)*(v+[^>][^v]*)*)</div>} weather 0)

 という部分があります。変数weather には、tenki.jp のソースが入っていて、天気予報の場所と更新時間を取り出しています。
 "<div class="titleBgLong">~</div>" という部分を切り出そうとして、

{<div class="titleBgLong">.*</div>} 

 とすると、複数の "</div>" がある場合、うまくいきません。
 そこで、ここを

{<div class="titleBgLong">(([^v]*)*(v+[^>][^v]*)*)</div>}

 としているのが、今回の肝です。
 この正規表現の中で、

(([^v]*)*(v+[^>][^v]*)*)

 の部分が、"v>" という文字列を含まない文字列にマッチします。
 なぜそうなるのか? 正直に言いましょう、私には判りません(笑)。
 “ある文字列を含まない正規表現”で、自動生成してもらった正規表現です。
 同様に

    (letn (tmp (regex {(<table(([^e]*)*(e+[^>][^e]*)*)*/table>)} weather (+ 2048 4))

 の部分では、"e>" という文字列を含まない、を使って、"<table~/table>" 部分を切り出し天気予報情報自体を取り出しています。
 このように、~という文字列を含まない、という表現を使ったことで格段に文字列の切り出しが楽になりました(笑)。

 以上、如何でしょうか?

正規表現表記の覚え書4。。。または、改行を含む文字列の扱い方

 前回EPUB Viewer のスクリプトの変更部分:

    (let (book (replace {<\?.+\?>} (read-file file) "" 0))
       (replace {<meta http-equiv[^>]*>} book "" 4)

 の正規表現オプションで 4 を指定しています。
 マニュアルで正規表現オプションは、

PCRE name no description
PCRE_CASELESS 1 大文字を小文字として扱う
treat uppercase like lowercase
PCRE_MULTILINE 2 Perl の /m のように改行で検索が制限されます。
(訳注: 行頭 (^) と行末 ($) が一行毎に使えるようになる )
limit search at a newline like Perl’s /m
PCRE_DOTALL 4 . (dot) が改行にもマッチします。
. (dot) also matches newline
PCRE_EXTENDED 8 文字クラス内以外の空白文字を無視します。
ignore whitespace except inside char class
PCRE_ANCHORED 16 (訳注:検索位置を)文字列の先頭に固定します。
anchor at the start
PCRE_DOLLAR_ENDONLY 32 $ は、改行の前ではなく、文字列の最後でマッチします。
$ matches at end of string, not before newline
PCRE_EXTRA 64 この付加機能は、現在使えません。
additional functionality currently not used
PCRE_NOTBOL 128 最初の文字は、行の先頭でないので、^ で一致しません。
first ch, not start of line; ^ shouldn’t match
PCRE_NOTEOL 256 最後の文字は、行の終わりでないので、$ で一致しません。
last char, not end of line; $ shouldn’t match
PCRE_UNGREEDY 512 数量詞の貪欲さを反転します。
invert greediness of quantifiers
PCRE_NOTEMPTY 1024 空文字列は、無効になります。
empty string considered invalid
PCRE_UTF8 2048 パターンと文字列が、UTF-8文字になります。
pattern and strings as UTF-8 characters
REPLACE_ONCE 0x8000 replaceで使われた時のみ、一回だけ置換されます。
replace only one occurrence only for use in replace
PRECOMPILED 0x10000 パターンは、プリ・コンパイルされ、RREPLACE_ONCE 0x8000とだけ結合できます。
pattern is pre-compiled, can only be combined with RREPLACE_ONCE 0x8000

 となっていて、4 は “. (dot) が改行にもマッチします。” とあります。
 どうやら、改行を含んだ文字列を扱う時は、4 を指定したほうが良さそうです。
 冒頭の正規表現表記では、. (dot) を含んでいないので 4 は要りません。
 でも私の場合、このように blog に書いておいても忘れますから、こうして置いたほうが、無難です。
 後で変更するかもしれませんからね(汗)。

 以上、如何でしょうか?

UTF-8 版 newLISP で日付を抽出する。

 前の blog で紹介した “newLISP で日付を抽出する”と“newLISP で日付を抽出する(再び)”の UTF-8 対応です。
 先ずは、スクリプトから、

(define dateMatchStr 
  {([  ]*[0-90-9]{2,4})?[  ]*[./\-・/-‐年]?([  ]*[0-90-9]{1,2}[  ]*)[./\-・/-‐月]([  ]*[0-90-9]{1,2})日?}
)
(define (number-zen2han str)
  (replace "[  ]" str "" 2048)    ; added 2010/11/ 8
  (replace "[0-9]" str (char (+ (- (char $0) 0xFF10) 0x30)) 2048))

(define (convert-2slash str) 
  (trim 
    (replace "[.\\-・/-‐年月日]" (number-zen2han str) "/" 2048)
    "/"))

(define (divide-with-date str (flag true))
  (let (res (regex dateMatchStr str 2048))
    (if res (list (0 (res 1) str)
                  (if flag (convert-2slash (res 0)) (res 0))
                  ((+ (res 1) (res 2)) str)))))

 前の時のように、正規表現文字列を複数用意せずに済み、コードもすっきり(笑)。
 (2010/ 9/23 区切り文字から全角と半角のスペースを削除。)
 ちなみに、全角には、(マイナス記号)と(ハイホン記号)の両方を用意しました。また、半角の -(ハイホン-マイナス記号)の前には、\(エスケープ文字)が必要です。前の時の文字列では付いていませんが、たまたま動作する配置になっています(汗)。
 全角->半角変換には前回のスクリプトを使ってもいいのですが、数字だけなので専用にしました。漢数字も対応といきたいところですが、それはまた、別の機会に。
 動作は、

> (divide-with-date "出張 3/13 横浜")
("出張" "3/13" " 横浜")
> (divide-with-date "10月11日芋煮会")
("" "10/11" "芋煮会")
> 

 こんな感じ。
 関数divide-with-date の追加した第二引数を nil にすると、

> (divide-with-date "10月11日芋煮会" nil)
("" "10月11日" "芋煮会")
> 

 日付は、入力のまま出力されます。

 以上、如何でしょうか?