diff --git a/src/Driver.ml b/src/Driver.ml index 5c8e0b34..c42cf556 100644 --- a/src/Driver.ml +++ b/src/Driver.ml @@ -8,7 +8,7 @@ let parse filename = Util.parse (object inherit Matcher.t s - inherit Util.Lexers.ident ["read"; "write"; "skip"] s + inherit Util.Lexers.ident ["read"; "write"; "skip"; "if"; "then"; "else"; "fi"; "while"; "do"; "od"; "repeat"; "until"; "for"] s inherit Util.Lexers.decimal s inherit Util.Lexers.skip [ Matcher.Skip.whitespaces " \t\n"; diff --git a/src/Interpret.ml b/src/Interpret.ml index 9f863d55..5fd9951a 100644 --- a/src/Interpret.ml +++ b/src/Interpret.ml @@ -2,48 +2,69 @@ open Language (* Interpreter for expressions *) module Expr = - struct +struct - open Expr + open Expr - let rec eval expr st = - let eval' e = eval e st in - match expr with - | Var x -> st x - | Const z -> z - | Add (x, y) -> eval' x + eval' y - | Mul (x, y) -> eval' x * eval' y + let calc_op op x y = + match op with + | "+" -> x + y + | "-" -> x - y + | "*" -> x * y + | "/" -> x / y + | "%" -> x mod y + | "==" -> if(x == y) then 1 else 0 + | "!=" -> if(x != y) then 1 else 0 + | ">" -> if(x > y) then 1 else 0 + | ">=" -> if(x >= y) then 1 else 0 + | "<" -> if(x < y) then 1 else 0 + | "<=" -> if(x <= y) then 1 else 0 + | "!!" -> if((x != 0) || (y != 0)) then 1 else 0 + | "&&" -> if((x != 0) && (y != 0)) then 1 else 0 - end + + let rec eval expr st = + let eval' e = eval e st in + match expr with + | Var x -> st x + | Const z -> z + | BinOp (op, x, y) -> calc_op op (eval' x ) (eval' y ) +end (* Interpreter for statements *) module Stmt = - struct +struct - open Stmt + open Stmt - (* State update primitive *) - let update st x v = fun y -> if y = x then v else st y + (* State update primitive *) + let update st x v = fun y -> if y = x then v else st y - let rec eval stmt ((st, input, output) as conf) = - match stmt with - | Skip -> conf - | Assign (x, e) -> (update st x (Expr.eval e st), input, output) - | Read x -> - let z :: input' = input in - (update st x z, input', output) - | Write e -> (st, input, output @ [Expr.eval e st]) - | Seq (s1, s2) -> eval s1 conf |> eval s2 - - end + let rec eval stmt ((st, input, output) as conf) = + match stmt with + | Skip -> conf + | Assign (x, e) -> (update st x (Expr.eval e st), input, output) + | Read x -> + let z :: input' = input in + (update st x z, input', output) + | Write e -> (st, input, output @ [Expr.eval e st]) + | Seq (s1, s2) -> eval s1 conf |> eval s2 + | If (e, s1, s2) -> if((Expr.eval e st) != 0) + then (eval s1 conf) + else (eval s2 conf) + | While(e, s) -> if((Expr.eval e st) != 0) + then eval stmt (eval s conf) + else conf + | Repeat(e, s) -> eval s conf |> eval (While(BinOp("==",e,Const 0),s)) + +end module Program = - struct +struct - let eval p input = - let (_, _, output) = + let eval p input = + let (_, _, output) = Stmt.eval p ((fun _ -> failwith "undefined variable"), input, []) - in - output - - end + in + output +end diff --git a/src/Language.ml b/src/Language.ml index 96ac3e01..1befba6b 100644 --- a/src/Language.ml +++ b/src/Language.ml @@ -1,54 +1,93 @@ +open Ostap +open Matcher (* AST for expressions *) module Expr = - struct +struct - type t = - | Var of string - | Const of int - | Add of t * t - | Mul of t * t + type t = + | Var of string + | Const of int + | BinOp of string * t * t - ostap ( - parse: x:mull "+" y:parse {Add (x,y)} | mull; - mull : x:prim "*" y:mull {Mul (x,y)} | prim; - prim : - n:DECIMAL {Const n} - | e:IDENT {Var e} - | -"(" parse -")" - ) - - end + ostap ( + parse: + head:and_op tail:("!!" and_op)*{ + List.fold_left(fun left (op, right) -> BinOp(Token.repr op, left, right)) head tail} + | and_op; + + and_op: + head:comp_op tail:("&&" comp_op)*{ + List.fold_left(fun left (op, right) -> BinOp(Token.repr op, left, right)) head tail} + | comp_op; + + comp_op: + head:addsub_op tail:((">=" | ">" | "<=" | "<" | "==" | "!=") addsub_op)*{ + List.fold_left(fun left (op, right) -> BinOp(Token.repr op, left, right)) head tail} + | addsub_op; + + addsub_op: + head:muldiv_op tail:(("+" | "-") muldiv_op)*{ + List.fold_left(fun left (op, right) -> BinOp(Token.repr op, left, right)) head tail} + | muldiv_op; + + muldiv_op: + head:primary tail:(("*" | "/" | "%") primary)*{ + List.fold_left(fun left (op, right) -> BinOp(Token.repr op, left, right)) head tail} + | primary; + + primary: + n:DECIMAL {Const n} + | x:IDENT {Var x} + | -"(" parse -")") +end (* AST statements/commands *) module Stmt = - struct - - type t = - | Skip - | Assign of string * Expr.t - | Read of string - | Write of Expr.t - | Seq of t * t - - let expr = Expr.parse - - ostap ( - simp: x:IDENT ":=" e:expr {Assign (x, e)} - | %"read" "(" x:IDENT ")" {Read x} - | %"write" "(" e:expr ")" {Write e} - | %"skip" {Skip}; - - parse: s:simp ";" d:parse {Seq (s,d)} | simp - ) +struct - end + type t = + | Skip + | Assign of string * Expr.t + | Read of string + | Write of Expr.t + | Seq of t * t + | If of Expr.t * t * t + | While of Expr.t * t + | Repeat of Expr.t * t + + ostap ( + parse: + s:simp ";" d:parse {Seq(s,d)} + |simp; + expr: + !(Expr.parse); + simp: + x:IDENT ":=" e:expr {Assign (x, e)} + | %"read" "(" x:IDENT ")" {Read x} + | %"write" "(" e:expr ")" {Write e} + | %"skip" {Skip} + | %"if" e:expr + %"then" s:parse + %"fi" {If(e,s,Skip)} + | %"if" e:expr + "then" s1:parse + "else" s2:parse + "fi" {If(e,s1,s2)} + | %"while" e:expr + "do" s:parse + "od" {While(e,s)} + | %"repeat" s:parse + "until" e:expr {Repeat(e,s)} + | %"for" s1:parse + "," e:expr + "," s2:parse + "do" s3:parse + "od" {Seq(s1,While(e,Seq(s3,s2)))}) +end module Program = - struct - - type t = Stmt.t - - let parse = Stmt.parse - - end +struct + type t = Stmt.t + let parse = Stmt.parse +end diff --git a/src/StackMachine.ml b/src/StackMachine.ml index 870537a4..9eafff51 100644 --- a/src/StackMachine.ml +++ b/src/StackMachine.ml @@ -1,102 +1,187 @@ (* Stack Machine *) module Instr = - struct - - type t = - | READ - | WRITE - | PUSH of int - | LD of string - | ST of string - | ADD - | MUL - - end +struct + + type t = + | READ + | WRITE + | PUSH of int + | LD of string + | ST of string + | ADD + | SUB + | MUL + | DIV + | MOD + | OR + | AND + | EQU + | NEQU + | LESS + | GREAT + | LEQU + | GEQU + | LABEL of int + | CJMP of int * string + | JMP of int +end module Program = - struct - - type t = Instr.t list - - end +struct + type t = Instr.t list +end module Interpret = - struct - - open Instr - open Interpret.Stmt - - let run prg input = - let rec run' prg ((stack, st, input, output) as conf) = - match prg with - | [] -> conf - | i :: prg' -> - run' prg' ( - match i with - | READ -> let z :: input' = input in - (z :: stack, st, input', output) - | WRITE -> let z :: stack' = stack in - (stack', st, input, output @ [z]) - | PUSH n -> (n :: stack, st, input, output) - | LD x -> (st x :: stack, st, input, output) - | ST x -> let z :: stack' = stack in - (stack', update st x z, input, output) - | _ -> let y :: x :: stack' = stack in - ((match i with ADD -> (+) | _ -> ( * )) x y :: stack', - st, - input, - output - ) - ) - in - let (_, _, _, output) = - run' prg ([], - (fun _ -> failwith "undefined variable"), - input, - [] - ) - in - output - end +struct + + open Instr + open Interpret.Stmt + + let rec go_to prg label = + let i::prg' = prg in + if(i = label) + then prg' + else go_to prg' label + + let run prg input = + let prg_copy = prg in + let rec run' ((prg, stack, st, input, output) as conf) = + match prg with + | [] -> conf + | i :: prg' -> + run'( + match i with + | READ -> let z :: input' = input in + (prg', z :: stack, st, input', output) + | WRITE -> let z :: stack' = stack in + (prg', stack', st, input, output @ [z]) + | PUSH n -> + (prg', n :: stack, st, input, output) + | LD x -> + (prg', st x :: stack, st, input, output) + | ST x -> let z :: stack' = stack in + (prg', stack', update st x z, input, output) + | LABEL s -> + (prg', stack, st, input, output) + | CJMP (s,c) -> + let z :: stack' = stack in + if( + match c with + | "z" -> (z = 0) + | "nz" -> (z != 0)) + then (go_to prg_copy (LABEL s), stack', st, input, output) + else (prg', stack', st, input, output) + | JMP s -> + (go_to prg_copy (LABEL s), stack, st, input, output) + | _ -> + let y :: x :: stack' = stack in + ( prg', + ( match i with + | ADD -> ( + ) + | SUB -> ( - ) + | MUL -> ( * ) + | DIV -> ( / ) + | MOD -> ( mod ) + | OR -> (fun l r -> if((l != 0) || (r != 0)) then 1 else 0) + | AND -> (fun l r -> if((l != 0) && (r != 0)) then 1 else 0) + | EQU -> (fun l r -> if(l == r) then 1 else 0) + | NEQU -> (fun l r -> if(l == r) then 0 else 1) + | LESS -> (fun l r -> if(l < r) then 1 else 0) + | GREAT -> (fun l r -> if(l > r) then 1 else 0) + | LEQU -> (fun l r -> if(l <= r) then 1 else 0) + | GEQU -> (fun l r -> if(l >= r) then 1 else 0) + ) x y :: stack', + st, + input, + output) ) + in + let (_, _, _, _, output) = + run' (prg, [], + (fun _ -> failwith "undefined variable"), + input, []) + in + output +end module Compile = - struct - - open Instr - - module Expr = - struct - - open Language.Expr - - let rec compile = function - | Var x -> [LD x] - | Const n -> [PUSH n] - | Add (x, y) -> (compile x) @ (compile y) @ [ADD] - | Mul (x, y) -> (compile x) @ (compile y) @ [MUL] - - end - - module Stmt = - struct - - open Language.Stmt - - let rec compile = function - | Skip -> [] - | Assign (x, e) -> Expr.compile e @ [ST x] - | Read x -> [READ; ST x] - | Write e -> Expr.compile e @ [WRITE] - | Seq (l, r) -> compile l @ compile r - - end - - module Program = - struct - - let compile = Stmt.compile - - end - - end +struct + + open Instr + + class lbl_gen = + object(this) + val mutable num = 0 + method next_lbl = num <- (num+1); num + end + + module Expr = + struct + + open Language.Expr + + let rec compile = + function + | Var x -> [LD x] + | Const n -> [PUSH n] + | BinOp (o,x,y) -> (compile x) @ (compile y) @ + (match o with + |"+" -> [ADD] + |"-" -> [SUB] + |"*" -> [MUL] + |"/" -> [DIV] + |"%" -> [MOD] + |"==" -> [EQU] + |"!=" -> [NEQU] + |"<" -> [LESS] + |"<=" -> [LEQU] + |">" -> [GREAT] + |">=" -> [GEQU] + |"!!" -> [OR] + |"&&" -> [AND]) + end + + module Stmt = + struct + + open Language.Stmt + + let rec compile lblgen = function + | Skip -> [] + | Assign (x, e) -> Expr.compile e @ [ST x] + | Read x -> [READ; ST x] + | Write e -> Expr.compile e @ [WRITE] + | Seq (l, r) -> compile lblgen l @ compile lblgen r + | If (e, s1, s2) -> + let l1 = lblgen#next_lbl in + let l2 = lblgen#next_lbl in + Expr.compile e @ + [CJMP (l1,"z")] @ + compile lblgen s1 @ + [JMP(l2)] @ + [LABEL(l1)] @ + compile lblgen s2 @ + [LABEL(l2)] + | While (e,s) -> + let l1 = lblgen#next_lbl in + let l2 = lblgen#next_lbl in + [JMP(l2)] @ + [LABEL(l1)] @ + compile lblgen s @ + [LABEL(l2)] @ + Expr.compile e @ + [CJMP(l1,"nz")] + | Repeat(e,s) -> + let l = lblgen#next_lbl in + [LABEL(l)] @ + compile lblgen s @ + Expr.compile e @ + [CJMP(l,"z")] + end + + module Program = + struct + let compile = Stmt.compile (new lbl_gen) + end + +end diff --git a/src/X86.ml b/src/X86.ml index 9f0544af..ea57cf96 100644 --- a/src/X86.ml +++ b/src/X86.ml @@ -1,122 +1,185 @@ open StackMachine open Instr -type opnd = R of int | S of int | L of int | M of string +type opnd = R of int | S of int | L of int | M of string| R_8 of int let regs = [|"%eax"; "%ebx"; "%ecx"; "%esi"; "%edi"; "%edx"; "%esp"; "%ebp"|] +let [|eax; ebx; ecx; esi; edi; edx; esp; ebp|] = Array.mapi (fun i _ -> R i) regs + +let regs_8bit = [|"%al"|] +let [|al|] = Array.mapi (fun i _ -> R_8 i) regs_8bit + let nregs = Array.length regs - 3 -let [|eax; ebx; ecx; esi; edi; edx; esp; ebp|] = Array.mapi (fun i _ -> R i) regs type instr = -| Add of opnd * opnd -| Mul of opnd * opnd -| Mov of opnd * opnd -| Push of opnd -| Pop of opnd -| Call of string -| Ret + | Cltd + | Add of opnd * opnd + | Sub of opnd * opnd + | Mul of opnd * opnd + | Div of opnd + | And of opnd * opnd + | Or of opnd * opnd + | Equ of opnd * opnd + | Mov of opnd * opnd + | Xor of opnd * opnd + | Label of int + | Jmp of int + | CJmp of string * int + | Push of opnd + | Pop of opnd + | Call of string + | Set of string * opnd + | Ret let to_string buf code = - let instr = - let opnd = function - | R i -> regs.(i) - | S i -> Printf.sprintf "%d(%%ebp)" (-i * 4) - | L i -> Printf.sprintf "$%d" i - | M s -> s - in - function - | Add (x, y) -> Printf.sprintf "addl\t%s,%s" (opnd x) (opnd y) - | Mul (x, y) -> Printf.sprintf "imull\t%s,%s" (opnd x) (opnd y) - | Mov (x, y) -> Printf.sprintf "movl\t%s,%s" (opnd x) (opnd y) - | Push x -> Printf.sprintf "pushl\t%s" (opnd x) - | Pop x -> Printf.sprintf "popl\t%s" (opnd x) - | Call x -> Printf.sprintf "call\t%s" x - | Ret -> "ret" - in - let out s = - Buffer.add_string buf "\t"; - Buffer.add_string buf s; - Buffer.add_string buf "\n" - in - List.iter (fun i -> out @@ instr i) code +let instr = +let opnd = function + | R i -> regs.(i) + | R_8 i -> regs_8bit.(i) + | S i -> Printf.sprintf "%d(%%ebp)" (-i * 4) + | L i -> Printf.sprintf "$%d" i + | M s -> s + in + function + | Cltd -> Printf.sprintf "cltd\t" + | Add (x,y) -> Printf.sprintf "addl\t%s,\t%s" (opnd x) (opnd y) + | Sub (x,y) -> Printf.sprintf "subl\t%s,\t%s" (opnd x) (opnd y) + | Mul (x,y) -> Printf.sprintf "imull\t%s,\t%s" (opnd x) (opnd y) + | Div x -> Printf.sprintf "idiv\t%s" (opnd x) + | And (x,y) -> Printf.sprintf "andl\t%s,\t%s" (opnd x) (opnd y) + | Or (x,y) -> Printf.sprintf "orl\t%s,\t%s" (opnd x) (opnd y) + | Equ (x,y) -> Printf.sprintf "cmpl\t%s,\t%s" (opnd x) (opnd y) + | Mov (x,y) -> Printf.sprintf "movl\t%s,\t%s" (opnd x) (opnd y) + | Xor (x,y) -> Printf.sprintf "xor\t%s,\t%s" (opnd x) (opnd y) + | Label i -> Printf.sprintf "lbl%d:" i + | Jmp i -> Printf.sprintf "jmp\tlbl%d" i + | CJmp (s,i) -> Printf.sprintf "j%s\tlbl%d" s i + | Push x -> Printf.sprintf "pushl\t%s" (opnd x) + | Pop x -> Printf.sprintf "popl\t%s" (opnd x) + | Call s -> Printf.sprintf "call\t%s" s + | Set (s,x) -> Printf.sprintf "set%s\t%s" s (opnd x) + | Ret -> Printf.sprintf "ret" + in + let out s = + Buffer.add_string buf "\t"; + Buffer.add_string buf s; + Buffer.add_string buf "\n" + in + List.iter (fun i -> out @@ instr i) code module S = Set.Make (String) class env = - object (this) - val locals = S.empty - val depth = 0 +object (this) + val locals = S.empty + val depth = 0 - method allocate = function - | [] -> this, R 0 - | R i :: _ when i < nregs - 1 -> this, R (i+1) - | S i :: _ -> {< depth = max depth (i+1) >}, S (i+1) - | _ -> {< depth = max depth 1 >}, S 1 + method allocate = function + | [] -> this, R 1 + | R i :: _ when i < nregs - 1 -> this, R (i+1) + | S i :: _ -> {< depth = max depth (i+1) >}, S (i+1) + | _ -> {< depth = max depth 1 >}, S 1 - method local x = {< locals = S.add x locals >} - method get_locals = S.elements locals - method get_depth = depth - end + method local x = {< locals = S.add x locals >} + method get_locals = S.elements locals + method get_depth = depth +end + +let save_opnd opnd x = + [Push opnd] @ x @ [Pop opnd] + +let compare comp_type x y = + [Xor(eax,eax); + Equ(x,y); + Set(comp_type,al); + Mov(eax, y)] + +let div_command x y action = + [Mov(y, eax); + Cltd; + Push(esi); + Mov(x,esi); + Div(esi); + Mov(esi,x); + Pop(esi)] @ action let rec sint env prg sstack = - match prg with - | [] -> env, [], [] - | i :: prg' -> - let env, code, sstack' = - match i with - | PUSH n -> - let env', s = env#allocate sstack in - env', [Mov (L n, s)], s :: sstack - | LD x -> - let env' = env#local x in - let env'', s = env'#allocate sstack in - env'', [Mov (M x, s)], s :: sstack - | ST x -> - let env' = env#local x in - let s :: sstack' = sstack in - env', [Mov (s, M x)], sstack' - | READ -> - env, [Call "lread"], [eax] - | WRITE -> - env, [Push eax; Call "lwrite"; Pop edx], [] - | _ -> - let x::(y::_ as sstack') = sstack in - (fun op -> - match x, y with - | S _, S _ -> env, [Mov (y, edx); op x edx; Mov (edx, y)], sstack' - | _ -> env, [op x y], sstack' - ) - (match i with - | MUL -> fun x y -> Mul (x, y) - | ADD -> fun x y -> Add (x, y) - ) - in - let env, code', sstack'' = sint env prg' sstack' in - env, code @ code', sstack'' + match prg with + | [] -> env, [], [] + | i :: prg' -> + let env, code, sstack' = + match i with + | PUSH n -> + let env', s = env#allocate sstack in + env', [Mov (L n, s)], s :: sstack + | LD x -> + let env' = env#local x in + let env'', s = env'#allocate sstack in + (match s with + | S _ -> env'', [Mov (M x, edx); Mov (edx, s)], s :: sstack + | _ -> env'', [Mov (M x, s)], s :: sstack) + | ST x -> + let env' = env#local x in + let s :: sstack' = sstack in + env', [Mov (s, M x)], sstack' + | READ -> env, [Call "lread"], [eax] + | WRITE -> env, [Push ebx; Call "lwrite"; Pop edx], [] + | LABEL n -> env, [Label n], [] + | JMP n -> env, [Jmp n], [] + | CJMP (x,s) -> + let z :: sstack' = sstack in + (env, [Xor(eax,eax);Equ(L 0, z); + (match s with + | "z" -> CJmp("e",x) + | "nz" -> CJmp("ne", x))], sstack') + | _ -> + let x::(y::_ as sstack') = sstack in + let getCommand x y = + (match i with + | ADD -> [Add(x,y)] + | SUB -> [Sub(x,y)] + | MUL -> [Mul(x,y)] + | DIV -> save_opnd eax (div_command x y [Mov(eax,y)]) + | MOD -> save_opnd edx (div_command x y [Mov(edx,y)]) + | OR -> save_opnd eax (compare "ne" (L 0) y @ compare "ne" (L 0) x @ [Or(x,y)]) + | AND -> save_opnd eax (compare "ne" (L 0) y @ compare "ne" (L 0) x @ [And(x,y)]) + | EQU -> save_opnd eax (compare "e" x y) + | NEQU -> save_opnd eax (compare "ne" x y) + | LESS -> save_opnd eax (compare "l" x y) + | GREAT -> save_opnd eax (compare "g" x y) + | LEQU -> save_opnd eax (compare "le" x y) + | GEQU -> save_opnd eax (compare "ge" x y)) + in + match x, y with + | S _, S _ -> env, [Mov (y, edx)] @ getCommand x edx @ [Mov (edx, y)], sstack' + | _ -> env, getCommand x y, sstack' + in + let env, code', sstack'' = sint env prg' sstack' in + env, code @ code', sstack'' let compile p = - let env, code, [] = sint (new env) (Compile.Program.compile p) [] in - let buf = Buffer.create 1024 in - let out s = Buffer.add_string buf s in - out "\t.data\n"; - List.iter (fun x -> out (Printf.sprintf "%s:\t.int 0\n" x)) - env#get_locals; - out "\t.text\n"; - out "\t.globl\tmain\n"; - out "main:\n"; - out "\tpushl\t%ebp\n"; - out "\tmovl\t%esp,%ebp\n"; - out (Printf.sprintf "\tsubl\t$%d,%%esp\n" (env#get_depth * 4)); - to_string buf code; - out "\tmovl\t%ebp,%esp\n"; - out "\tpopl\t%ebp\n"; - out "\tret\n"; - Buffer.contents buf +let env, code, [] = sint (new env) (Compile.Program.compile p) [] in +let buf = Buffer.create 1024 in +let out s = Buffer.add_string buf s in + out "\t.data\n"; + List.iter (fun x -> out (Printf.sprintf "%s:\t.int 0\n" x)) + env#get_locals; + out "\t.text\n"; + out "\t.globl\tmain\n"; + out "main:\n"; + out "\tpushl\t%ebp\n"; + out "\tmovl\t%esp,%ebp\n"; + out (Printf.sprintf "\tsubl\t$%d,%%esp\n" (env#get_depth * 4)); + to_string buf code; + out "\tmovl\t%ebp,%esp\n"; + out "\tpopl\t%ebp\n"; + out "\tret\n"; + Buffer.contents buf let build stmt name = - let outf = open_out (Printf.sprintf "%s.s" name) in - Printf.fprintf outf "%s" (compile stmt); - close_out outf; - let inc = try Sys.getenv "RC_RUNTIME" with _ -> "../runtime" in - Sys.command (Printf.sprintf "gcc -m32 -o %s %s/runtime.o %s.s" name inc name) +let outf = open_out (Printf.sprintf "%s.s" name) in + Printf.fprintf outf "%s" (compile stmt); + close_out outf; +let inc = try Sys.getenv "RC_RUNTIME" with _ -> "../runtime" in + Sys.command (Printf.sprintf "gcc -m32 -o %s %s/runtime.o %s.s" name inc name)