がぶちゃんの日記

札幌からフルリモートCTO

「第3回 コンパイラを作ろう」に行ってきた。

コンパイラを作ろう」第3回にして初エントリーを書くとか、相当ブログばなれが進んでいるなぁと反省。

どうでもいいけど、Wordpressのビジュアルモードが激しくウザいので、はてなに乗り換えたいと考え中。ました。

ってことで、知らない人のためにも書くと先月から、CSNAGOYAの「コンパイラを作ろう」という勉強会に参加しています。冨沢高明さんの「コンパイラ入門」という最高の参考書をもとに全員が好きな言語でコンパイラを作るという勉強会です。参考書がPascalに似てる著者の独自言語のコンパイラを作るため、僕らも著者の独自言語の構文を解析することになりました。

好きな言語でコンパイラを作る。ということで、かなり悩んだあげくSICPを読むためにSchemeを覚えたいなぁと思い、Gaucheで著者の独自言語のコンパイラを作ることに。この組み合わせが何とも言えません。

ちなみに、どこまで勉強会が進んでいるかというと

  • 前回は、字句解析の実装。
  • 今回は、構文解析の前置き。(実装なし)
  • 次回は、パーサの実装(の予定?)

という感じです。

さて、前置きが長くなりましたが今日までの宿題になっていた、字句解析部分の実装を晒すことになっているんですが、Scheme を合計1週間も勉強していない人間が作ったソースコードだと思って読んでください。(言い訳)

main も、用意していないので lexer 関数に string を渡すと、それをTokenに分解して print する所までしか出来ていません。(print は本質的には必要ないんですがデバッグ用です。)

(use srfi-1)

(define-class <token> ()
  ((def  :init-keyword :def  :init-value "")
   (str  :init-keyword :str  :init-value "")
   (type :init-keyword :type :init-value "")))

(define (make-token def str type)
  (make <token> :def def :str str :type type))

(define reserved
  (hash-table 'string=?
	      '("MODULE" . 0)
	      '("BEGIN" .0)
	      '("END" .0)
	      '("VAR" .0)
	      '("INTEGER" .0)
	      '("STRING" .0)
	      '("IF" .0)
	      '("THEN" .0)
	      '("ELSE" .0)
	      '("WHILE" .0)
	      '("DO" .0)))

(define symbols
  (hash-table 'string=?
	      '("+" . "PLUS")
	      '("-" . "MINUS")
	      '("*" . "MULT")
	      '("/" . "DIV")
	      '("=" . "EQ")
	      '("<" . "LT")
	      '("<=" . "LE")
	      '(">" . "GT")
	      '(">=" . "GE")
	      '("<>" . "NE")
	      '(":=" . "ASSIGN")
	      '(";" . "COLON")
	      '(":" . "SEMICOLON")
	      '("," . "COMMA")
	      '("(" . "OPEN")
	      '(")" . "CLOSE")
	      '("." . "PIRIOD")))

(define (add lis e)
  (append lis (list e)))

(define (print-token-list lis)
  (map (lambda (e) (print #`"DEF[,(ref e 'def)]\t\tSTR[,(ref e 'str)]")) lis))

(define (lexer str)
  (print-token-list (scanner (string->list str) '())))
;;  (d (scanner (string->list str) '())))

(define (scanner char-list token-list)
  (if (null? char-list)
      token-list
      (let ((c (car char-list))
	    (cs (cdr char-list)))
	(cond [(char-whitespace? c) (scanner cs token-list)]
	      [(char-alphabetic? c)
	       (pred-scanner cs
			     token-list
			     (list c)
			     (lambda (char) (or (char-alphabetic? char) (char-numeric? char)))
			     (lambda (char-list)
			       (let* ((str (list->string char-list))
				      (def (if (hash-table-exists? reserved str)
					       str
					       "IDENT")))
				 (make-token def str ""))))]
	      [(char-numeric? c)
	       (pred-scanner cs
			     token-list
			     (list c)
			     (lambda (char) (char-numeric? char))
			     (lambda (char-list)
			       (make-token "NUMBER" (list->string char-list) "")))]
	      [(char-numeric? c) (scan-of-pred cs (list c) token-list char-numeric?)]
	      [(or (equal? c #\=)
		   (equal? c #\+)
		   (equal? c #\-)
		   (equal? c #\*)
		   (equal? c #\/)
		   (equal? c #\;)
		   (equal? c #\,)
		   (equal? c #\()
		   (equal? c #\))
		   (equal? c #\.))
	       (let ((str (make-string 1 c)))
		 (scanner cs (add token-list (make-token (hash-table-get symbols str) str ""))))]
	      [(or (equal? c #\<)
		   (equal? c #\>)
		   (equal? c #\:))
	       (let ((pred (cond [(equal? c #\<)
				  (lambda (e) (or (equal? e #\=)
						  (equal? e #\>)))]
				 [(equal? c #\>)
				  (lambda (e) (equal? e #\=))]
				 [(equal? c #\:)
				  (lambda (e) (equal? e #\=))])))
		 (pred-scanner cs
			     token-list
			     (list c)
			     pred
			     (lambda (char-list)
			       (let ((str (list->string char-list)))
				 (make-token (hash-table-get symbols str) str "")))))]
	      [(equal? c #\")
	       (let ((index (list-index (lambda (c) (equal? c #\")) cs)))
		 (scanner (drop cs (+ 1 index)) (add token-list (make-token "STR" (take cs index) ""))))]
	      ))))

(define (pred-scanner char-list token-list buffered-char-list pred maker)
  (if (null? char-list)
      (scanner char-list (add token-list (maker buffered-char-list)))
      (let ((c (car char-list))
	    (cs (cdr char-list)))
	(cond [(pred c) (pred-scanner cs token-list (add buffered-char-list c) pred maker)]
	      [else (scanner (cons c cs) (add token-list (maker buffered-char-list)))]))))

こんなソースコード生んで本当にすみません。

でも、綺麗に書けるようになりたいです。コメントお願いします><