flashコンパイラ

また性懲りもなく、美しい日本のMLコンパイラのソースを眺めてしまった。
で、ようやく、慣れてきたらしいので、四則演算の結果をswfで出力するだけの
何の役にも立たないコンパイラが出来上がりました。


ようやく、意味解析とか、そこら辺の領域にやってきたぞと、いった感じです。


syntax.ml

type t =
  | Int of int
  | Add of t * t
  | Sub of t * t
  | Mul of t * t
  | Div of t * t

parser.ml

%{
open Syntax
%}

%token <int> INT
%token MINUS
%token PLUS
%token AST
%token SLASH
%token EOF
%token LPAREN
%token RPAREN

%left PLUS MINUS
%left AST SLASH

%type <Syntax.t> exp
%start exp

%%
exp:
| LPAREN exp RPAREN { $2 }
| INT               { Int($1) }
| exp PLUS  exp { Add($1, $3) }
| exp MINUS exp { Sub($1, $3) }
| exp AST   exp { Mul($1, $3) }
| exp SLASH exp { Div($1, $3) }

lexer.mll

{
open Parser
}

let space = [' ' '\t' '\n' '\r']
let digit = ['0'-'9']

rule token = parse
| space+ { token lexbuf }
| '-'? digit+ { INT(int_of_string (Lexing.lexeme lexbuf)) }
| '(' { LPAREN }
| ')' { RPAREN }
| '-' { MINUS }
| '+' { PLUS }
| "*" { AST }
| "/" { SLASH }
| eof { EOF }
| _
    { failwith
	(Printf.sprintf "unknown token %s near characters %d-%d"
	   (Lexing.lexeme lexbuf)
	   (Lexing.lexeme_start lexbuf)
	   (Lexing.lexeme_end lexbuf)) }

test.ml

open Syntax
open Parser
open Lexer

open Swf
open SwfParser
open SwfZip

let read_all filename =
  let f = open_in filename in      (* ファイルオープン *)
  let len = in_channel_length f in (* ファイルの長さを取得 *)
  let buf = String.create len in   (* バッファの文字列を作成 *)
  really_input f buf 0 len;        (* バッファにデータをすべて読み込む *)
  close_in f;                      (* ファイルをクローズ *)
  buf                              (* 取得した文字列を返す *)

let argv = Sys.argv;;

let endprg () =
    print_string "usage: o src.txt out.swf\n";
    exit(0);;

if (Array.length(argv) < 2) then endprg ()
  else ();;
let out = argv.(2);;

let prg = read_all argv.(1);;


(* タグ生成 *)
let tag ?(ext=false) d = {
	tid = 0;
	textended = ext;
	tdata = d;
	}

(* 領域データ生成 *)
let bounds x y w h = {
		rect_nbits = if (max w h) >= 820 then 16 else 15;
		left = x;
		top = y;
		right = w * 20;
		bottom = h * 20;
	}

let w = 640
let h = 480
let fps = 40.0
let bgcolor = ref 0xffffff


let header = {
		h_version = 8;
		h_size = bounds 0 0 w h;
		h_frame_count = 1;
		h_fps = to_float16 fps;
		h_compressed = false;
	}
;;

let ass : Swf.action DynArray.t = DynArray.create();;
let a1 = (Parser.exp Lexer.token (Lexing.from_string prg));;

let rec calc = function
| Int(i) -> DynArray.add ass (APush [PInt (Int32.of_int i)])
| Add(i,j) -> calc(i); calc(j); DynArray.add ass AAdd;
| Sub(i,j) -> calc(i); calc(j); DynArray.add ass ASubtract;
| Mul(i,j) -> calc(i); calc(j); DynArray.add ass AMultiply;
| Div(i,j) -> calc(i); calc(j); DynArray.add ass ADivide;;

DynArray.add ass (AStringPool ["tf"; "createTextField"; "text"; prg ^ " = "]);;

DynArray.add ass (APush [
	(PInt (Int32.of_int 480));
	(PInt (Int32.of_int 640));
	(PInt (Int32.of_int 0));
	(PInt (Int32.of_int 0));
	(PInt (Int32.of_int 0));
	(PStack 0); (* tf *)
	(PInt (Int32.of_int 6));
	(PStack 1); (* createTextField *)
	]);;
DynArray.add ass (ACall);;
DynArray.add ass (APop);;

DynArray.add ass (APush [
	(PStack 0); (* tf *)
	]);;
DynArray.add ass (AEval);;
DynArray.add ass (APush [
	(PStack 2); (* text *)
	(PStack 3); (* prg *)
	]);;
calc a1;;
DynArray.add ass (AStringAdd);;
DynArray.add ass (AObjSet);;

let data = [
	tag (TSetBgColor { cr = 0xFF; cg =  0xFF; cb =  0xFF });
	tag (TDoAction ass);
	tag (TShowFrame)];;

SwfParser.init SwfZip.inflate SwfZip.deflate;;
Swf.warnings := true;;
let ch = IO.output_channel (open_out_bin out);;
Swf.write ch (header, data);;
IO.close_out ch;;
print_string out;;

ビルドはこんなかんじ

ocamlyacc parser.mly
del parser.mli
ocamllex lexer.mll
ocamlopt -o test.exe -I .. -I ../extc -I ../swflib ../extLib.cmxa ../extc/extc.cmxa ../swflib/swflib.cmxa syntax.ml parser.ml lexer.ml test.ml
del *.cm* *.o

mtascとかコンパイルできればコンパイルできると思います。

test test.txt test.swf
とかやりますと、
test.txt

((1+2)*3+4*2)/2

なんてあると、

((1+2)*3+4*2)/2 = 8.5

と出るだけです。