diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..f9ae5737 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +* +!src/*.lama +!.gitignore \ No newline at end of file diff --git a/README.md b/README.md index d212fdfc..5dd827e1 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,7 @@ # compiler-2020 -A supplementary repository for the course on compilers. + +ДЗ 4 (Конструкции управления): + +* Написать синтаксический анализатор для- и поддержать в режими интерпретации конструкции потока управления `if`, `while`, `for`, `repeat ... until` +* `if...elif...[else ...]?` и `for` должны быть реализованы как синтасический сахар +* `repeat ... until` необходимо поддержать на уровне абстрактного синтаксиса diff --git a/lectures/control_flow_interpr.pdf b/lectures/control_flow_interpr.pdf new file mode 100644 index 00000000..62c24694 Binary files /dev/null and b/lectures/control_flow_interpr.pdf differ diff --git a/regression/Embedding.meta b/regression/Embedding.meta new file mode 100644 index 00000000..3d52ec9f --- /dev/null +++ b/regression/Embedding.meta @@ -0,0 +1,80 @@ +-- A deep embedding of L0 into Lama +import List; +import Array; +import Fun; +import World; +import Stmt; +import State; +import SM; +import X86; + +-- Embeds expression operands: strings are +-- embedded into identifiers, integer constants --- into +-- constants; non-operand expressions are left intact + +# define HASH # + +fun opnd (x) { + case x of + HASH string -> Var (x) + | HASH unboxed -> Const (x) + | _ -> x + esac +} + +-- Redefinition of standard infix operators +infix + at + (l, r) {Binop ("+", opnd (l), opnd (r))} +infix - at - (l, r) {Binop ("-", opnd (l), opnd (r))} +infix * at * (l, r) {Binop ("*", opnd (l), opnd (r))} +infix / at / (l, r) {Binop ("/", opnd (l), opnd (r))} +infix % at % (l, r) {Binop ("%", opnd (l), opnd (r))} +infix == at == (l, r) {Binop ("==", opnd (l), opnd (r))} +infix != at != (l, r) {Binop ("!=", opnd (l), opnd (r))} +infix < at < (l, r) {Binop ("<", opnd (l), opnd (r))} +infix <= at <= (l, r) {Binop ("<=", opnd (l), opnd (r))} +infix > at > (l, r) {Binop (">", opnd (l), opnd (r))} +infix >= at >= (l, r) {Binop (">=", opnd (l), opnd (r))} +infix && at && (l, r) {Binop ("&&", opnd (l), opnd (r))} +infix !! at !! (l, r) {Binop ("!!", opnd (l), opnd (r))} + +-- Embeds "read" construct; x is expected to be a string (not a "Var") +fun read (x) { + Read (x) +} + +-- Embeds "write" construct; note, e is expression, thus it +-- is embedded using "opnd" +fun write (e) { + Write (opnd (e)) +} + +-- Embeds assignment operator; x is expected to be string, e is expression, this +-- it is embeeded using "opnd" +infix ::= before := (x, e) { + Assn (x, opnd (e)) +} + +-- Embeds sequential composition +infixr >> before ::= (s1, s2) { + Seq (s1, s2) +} + +-- Returns embedded program (thus, the file has to be recompiled as the +-- program changes) +fun program () { + PROGRAM_BODY +} + +case sysargs of + [_] -> printf ("%s\n", compileX86 (compileSM (program ()))) +| _ -> + local input = + reverse (fix (fun (f) {fun (acc) {case readLine () of HASH unboxed -> acc | arg -> f (stringInt (arg) : acc) esac}}) ({})); + + iter (fun (x) {printf ("%d\n", x)}, + case sysargs[1] of + "-i" -> evalStmt (input, program ()) + | "-s" -> evalSM (input, compileSM (program ())) + esac + ) +esac \ No newline at end of file diff --git a/regression/Makefile b/regression/Makefile index 682ee661..9adafc08 100644 --- a/regression/Makefile +++ b/regression/Makefile @@ -21,4 +21,4 @@ expr_tests: clean: $(RM) *.s *.i *~ $(LOGS) $(TESTS) *.run make -C expressions clean - make -C deep-expressions clean + make -C deep-expressions clean \ No newline at end of file diff --git a/src/Driver.lama b/src/Driver.lama index 978bac91..3001e18a 100644 --- a/src/Driver.lama +++ b/src/Driver.lama @@ -18,15 +18,8 @@ import Manifest; -- defined in the unit Manifest fun parseArgs (args) { local mode = ref (Comp), - infile = ref ({}), - smDump = ref (false); + infile = ref ({}); - fun setDump (m) { - case m of - SM -> smDump ::= true - esac - } - fun setMode (m) { case deref (mode) of Comp -> mode ::= m @@ -47,28 +40,21 @@ fun parseArgs (args) { {} -> skip | h : t -> case h of - "-i" -> setMode (Int) - | "-s" -> setMode (SM) - | "-ds" -> setDump (SM) - | fn -> setInFile (fn) + "-i" -> setMode (Int) + | "-s" -> setMode (SM) + | fn -> setInFile (fn) esac; rec (t) esac } })(args); + [fun () {deref (mode)}, - fun () {case deref(infile) of #unboxed -> failure ("input file name not set\n") | fn -> fn esac}, - fun () {deref (smDump)} + fun () {case deref(infile) of #unboxed -> failure ("input file name not set\n") | fn -> fn esac} ] } --- Utility function to peeping to SM code -fun peepSM (args, smCode) { - dumpSM (args, lazy (showSM (smCode))); - smCode -} - local args = parseArgs (arrayList (sysargs).tl); -- The main part: parses input file, invokes interpreter/stack machine interpreter/x86 @@ -76,7 +62,7 @@ local args = parseArgs (arrayList (sysargs).tl); case parseString (parse |> bypass (end), fread (args.getInFile)) of Succ (program) -> case args.getMode of - Comp -> compileX86 (args, peepSM (args, compileSM (program))) + Comp -> compileX86 (args, compileSM (program)) | mode -> local input = reverse (fix (fun (f) { @@ -90,7 +76,7 @@ case parseString (parse |> bypass (end), fread (args.getInFile)) of iter (fun (x) {printf ("%d\n", x)}, case mode of Int -> evalStmt (input, program) - | SM -> evalSM (input, peepSM (args, compileSM (program))) + | SM -> evalSM (input, compileSM (program)) esac) esac | x@Fail (err, line, col) -> diff --git a/src/Expr.lama b/src/Expr.lama index b7580356..b3df7af4 100644 --- a/src/Expr.lama +++ b/src/Expr.lama @@ -13,6 +13,29 @@ import State; -- Const (int) | -- Binop (string, expr, expr) -public fun evalExpr (st, expr) { - failure ("evalExpr not implemented\n") +public fun evalOp (op, expr1, expr2) { + case op of + "+" -> expr1 + expr2 | + "-" -> expr1 - expr2 | + "*" -> expr1 * expr2 | + "/" -> expr1 / expr2 | + "%" -> expr1 % expr2 | + "==" -> expr1 == expr2 | + "!=" -> expr1 != expr2 | + "<" -> expr1 < expr2 | + "<=" -> expr1 <= expr2 | + ">" -> expr1 > expr2 | + ">=" -> expr1 >= expr2 | + "&&" -> expr1 && expr2 | + "!!" -> expr1 !! expr2 + esac } + + +public fun evalExpr (st, expr) { + case expr of + Var (x) -> st(x) | + Const (n) -> n | + Binop (op, expr1, expr2) -> evalOp(op, evalExpr(st, expr1), evalExpr(st, expr2)) + esac +} \ No newline at end of file diff --git a/src/Lexer.lama b/src/Lexer.lama index 6d3520ae..a7dd94f4 100644 --- a/src/Lexer.lama +++ b/src/Lexer.lama @@ -78,5 +78,4 @@ public kRead = s (rRead), | a -> a esac) } - }); - + }); \ No newline at end of file diff --git a/src/Makefile b/src/Makefile index e6b82a79..7476ae55 100644 --- a/src/Makefile +++ b/src/Makefile @@ -23,5 +23,4 @@ X86.o: SM.o Manifest.o $(LAMAC) -I . -c $< clean: - rm -Rf *.s *.o *.i *~ *.html *.sm lama-impl - + rm -Rf *.s *.o *.i *~ *.html *.sm lama-impl \ No newline at end of file diff --git a/src/Manifest.lama b/src/Manifest.lama index 98dca88e..68005a51 100644 --- a/src/Manifest.lama +++ b/src/Manifest.lama @@ -1,6 +1,4 @@ -- Manifests a top-level environment -import Lazy; -import Fun; public fun getMode (args) { args [0] () @@ -10,12 +8,6 @@ public fun getInFile (args) { args [1] () } -public fun dumpSM (args, smCode) { - if args [2] () then - fwrite (args.getBaseName ++ ".sm", force $ smCode) - fi -} - public fun getBaseName (args) { force (lazy ({local name = args.getInFile; if (matchSubString (name, ".lama", name.length - 5)) diff --git a/src/Parser.lama b/src/Parser.lama index 4796fec6..e7a86e57 100644 --- a/src/Parser.lama +++ b/src/Parser.lama @@ -13,13 +13,44 @@ fun inbr (l, p, r) { } -- Primary expression -local primary = memo $ eta (decimal @ fun (x) {Const (stringInt (x))} | - lident @ fun (x) {Var (x)} | - inbr (s ("("), exp, s (")"))), - exp = memo $ eta (failure ("expression parsing not implemented\n")); +local primary = memo $ eta syntax (x=decimal {Const (stringInt (x))} | + x=lident {Var (x)} | + inbr[s("("), exp, s(")")]), + + + binOps = fun(l, op, r) {Binop(op, l, r)}, + ops = { + [Left, [s ("!!"), binOps] : {}], + [Left, [s ("&&"), binOps] : {}], + [Nona, [s ("<") | s (">") | s ("<=") | s (">=") | s ("==") | s ("!="), binOps] : {}], + [Left, [s ("+") | s ("-"), binOps] : {}], + [Left, [s ("*") | s ("/") | s ("%"), binOps] : {}] + }, + + exp = memo $ eta expr (ops, primary) ; -local stmt = memo $ eta (failure ("statement parsing not implemented\n")); - + +local stmt = memo $ eta syntax ( + kWrite x=inbr[s("("), exp, s(")")] {Write (x)} | + kRead x=inbr[s("("), lident, s(")")] {Read (x)} | + x=lident s[":="] e=exp {Assn (x, e)} | + stmt1=stmt s[";"] stmt2=stmt end {Seq (stmt1, stmt2)}| + + kSkip {Skip} | + kIf cond=exp kThen s1=seqq s2=ifTailParser {If (cond, s1, s2)} | + kWhile cond=exp kDo s=seqq kOd {While (cond, s)} | + kRepeat s=seqq kUntil cond=exp {Repeat (s, cond)} | + kFor e1=seqq s[","] e2=exp s[","] e3=seqq kDo s=seqq kOd {Seq (e1, While(e2, Seq(s, e3)))} + ), + ifTailParser = memo $ eta syntax( + -kFi {Skip} | + -kElse seqq -kFi | + -kElif cond=exp kThen s1=seqq s2=ifTailParser {If (cond, s1, s2)} + ), + + seqq = memo $ eta syntax (stmt | s1=stmt s[";"] s2=seqq {Seq(s1, s2)}); + + -- Public top-level parser -public parse = stmt; +public parse = stmt; \ No newline at end of file diff --git a/src/SM.lama b/src/SM.lama index 8aaa7943..8e887453 100644 --- a/src/SM.lama +++ b/src/SM.lama @@ -1,14 +1,17 @@ -- Stack machine. + import Array; import List; import Fun; import Collection; + import World; import State; import Expr; import Buffer; + -- Stack code printer. Takes a list of SM instructions, return its -- string representation. public fun showSMInsn (i) { @@ -29,6 +32,9 @@ public fun showSM (prg) { map (fun (i) {showSMInsn (i) ++ "\n"}, prg).stringcat } + +-- Stack machine interpreter. Takes an SM-configuration and a program, +-- returns a final configuration -- Evaluation environment: keeps a mapping between labels and (sub)programs fun initEvalEnv (insns) { local map = @@ -54,8 +60,30 @@ fun fromLabel (env, lab) { -- Stack machine interpreter. Takes an environment, an SM-configuration and a program, -- returns a final configuration -fun eval (env, c, insns) { - failure ("SM eval not implemented\n") +fun eval (env, c@[s, st, w@[i,o]], insns) { + case insns of + {} -> c | + READ: p -> case i of x : it -> eval(env, [x : s, st, [it, o]], p) esac | + WRITE: p -> case s of x : ss -> eval(env, [ss, st, [i, x : o]], p) esac | + BINOP (b): p -> case s of x : y: ss -> eval(env, [evalOp(b, y, x): ss, st, w], p) esac | + LD (x): p -> eval(env, [st(x) : s, st, w], p) | + ST (x): p -> case s of y : ss -> eval(env, [s, (st <- [x,y]), w], p) esac | + CONST (n): p -> eval(env, [n : s, st, w], p) | + + LABEL (s) : p -> eval(env, c, p) | + JMP (l) : p -> eval(env, c, fromLabel(env, l)) | + CJMP (cond, l) : p -> case s of cmp : ss -> case cond of + "z" -> case cmp of + 0 -> eval (env, c, fromLabel(env, l)) | + _ -> eval (env, c, p) + esac | + "nz" -> case cmp of + 0 -> eval (env, c, p) | + _ -> eval (env, c, fromLabel(env, l)) + esac + esac + esac + esac } -- Runs a stack machine for a given input and a given program, returns an output @@ -97,12 +125,46 @@ fun genLabels (env, n) { -- Takes an expression, returns a list (of, possibly, lists) -- of stack machine instructions fun compileExpr (expr) { - failure ("compileExpr not implemented\n") + case expr of + Var(x) -> singleton(LD(x)) | + Const(n) -> singleton(CONST(n)) | + Binop (op, expr1, expr2) -> compileExpr(expr1) +++ compileExpr(expr2) +++ singleton(BINOP(op)) + esac } -- Compiles a statement into a stack machine code. -- Takes a statement, returns a list of stack machine -- instructions. public fun compileSM (stmt) { - failure ("compileSM not implemented\n") -} + fun compileEnv (env, stmt) { + case stmt of + Assn(x, expr) -> [env, compileExpr(expr) +++ singleton(ST(x))] | + Read(x) -> [env, {READ, ST(x)}] | + Write(expr) -> [env, compileExpr(expr) +++ singleton(WRITE)] | + Skip -> [env, {}] | + Seq(s1,s2) -> case compileEnv(env, s1) of + [env1, stmt1] -> case compileEnv (env1, s2) of + [env2, stmt2] -> [env2, stmt1 +++ stmt2] + esac + esac | + While(expr, s) -> case genLabels(env,2) of + [l1, l2, env1] -> case compileEnv(env1, s) of + [env2, insns] -> [env2, {JMP (l2), LABEL (l1)} +++ insns +++ singleton (LABEL (l2)) +++ compileExpr(expr) +++ singleton(CJMP ("nz", l1))] + esac + esac | + Repeat(s, expr) -> case genLabels(env, 1) of + [l, env1] -> case compileEnv(env1, s) of + [env2, insns] -> [env2, singleton(LABEL (l)) +++ insns +++ compileExpr(expr) +++ singleton(CJMP("z", l))] + esac + esac | + If(expr, s1, s2) -> case genLabels(env, 2) of + [le, lf, env1] -> case compileEnv (env1, s1) of + [env2, stmt1] -> case compileEnv(env2, s2) of + [env3, stmt2] -> [env3, compileExpr(expr) +++ singleton(CJMP ("z", le)) +++ stmt1 +++ singleton (JMP(lf)) +++ singleton(LABEL (le)) +++ stmt2 +++ singleton(LABEL (lf))] + esac + esac + esac + esac + } + compileEnv(initCompEnv(), stmt)[1] +} \ No newline at end of file diff --git a/src/State.lama b/src/State.lama index 69cf6ed1..d4530b50 100644 --- a/src/State.lama +++ b/src/State.lama @@ -2,8 +2,7 @@ -- Empty state (undefined everywhere) public fun emptyState (x) { - 0 - --failure ("undefined variable ""%s""\n", x) + failure ("undefined variable ""%s""\n", x) } -- Putting a binding into a state diff --git a/src/Stmt.lama b/src/Stmt.lama index 90e59fe9..82e97d72 100644 --- a/src/Stmt.lama +++ b/src/Stmt.lama @@ -15,15 +15,30 @@ import World; -- Skip | -- Read (string) | -- Write (expr) | + +fun eval (c@[st, [i, o]], stmt) { + case stmt of + Skip -> c | + Assn(str, expr) -> [st <- [str, evalExpr(st, expr)], [i, o]] | + Read(x) -> + local tmp; + tmp := readWorld([i,o]); + [st <- [x, tmp[0]], tmp[1]] | + Write(expr) -> [st, writeWorld(evalExpr(st, expr), [i,o])] | + Seq(stmt1, stmt2) -> eval(eval(c, stmt1), stmt2) | + If(expr, s1, s2) -> if (evalExpr(c[0], expr)) then eval(c, s1) else eval(c, s2) fi | + While(expr, s) -> if (evalExpr(c[0], expr)) then eval(c, Seq(s, stmt)) else eval(c, Skip) fi | + Repeat(s, expr) -> + local c1 = eval(c, s); + if (evalExpr(c1[0], expr)) then eval(c1, Skip) else eval(c1, Repeat(s, expr)) fi + esac + -- if (expr, stmt, stmt) | -- While (expr, stmt) | -- Repeat (stmt, expr) -fun eval (c, stmt) { - failure ("Stmt eval not implemented\n") } - -- Evaluates a program with a given input and returns an output public fun evalStmt (input, stmt) { eval ([emptyState, createWorld (input)], stmt).snd.getOutput -} +} \ No newline at end of file diff --git a/src/X86.lama b/src/X86.lama index 0fdf1130..52b1ffeb 100644 --- a/src/X86.lama +++ b/src/X86.lama @@ -295,6 +295,40 @@ fun compile (env, code) { case env.pop of [s, env] -> [env, code <+ Push (s) <+ Call ("Lwrite") <+ Pop (eax)] esac + + | CONST(n) -> + case env.allocate of + [s, env] -> [env, code <+ Mov (L(n), s)] + esac + | LD(x) -> + case env.addGlobal(x).allocate of + [s, env] -> [env, code <+> move (env.loc(x), s)] + esac + | ST(x) -> + case env.addGlobal(x).pop of + [s, env] -> [env, code <+> move (s, env.loc(x))] + esac + | BINOP (op) -> + case env.pop2 of + [y, x, env] -> case env.allocate of + [s, env] -> case op of + + "/" -> [env, code <+ Mov(x, eax) <+ Cltd <+ IDiv(y) <+ Mov(eax, s)] + | "%" -> [env, code <+ Mov(x, eax) <+ Cltd <+ IDiv(y) <+ Mov(edx, s)] + + | "+" -> [env, code <+ Mov(x, eax) <+ Binop (op, y, eax) <+ Mov(eax, s)] + | "-" -> [env, code <+ Mov(x, eax) <+ Binop (op, y, eax) <+ Mov(eax, s)] + | "*" -> [env, code <+ Mov(x, eax) <+ Binop (op, y, eax) <+ Mov(eax, s)] + | "&&" -> [env, code <+ Mov (x, edx) <+ Binop ("*", y, edx) <+ Binop ("cmp", L (0), edx) <+ Mov (L (0), eax) <+ Set ("ne", "%al") <+ Mov (eax, s)] + + | "!!" -> [env, code <+ Mov (x, edx) <+ Binop ("!!", y, edx) <+ Binop ("cmp", L (0), edx) <+ Mov (L(0), eax) <+ Set ("ne", "%al") <+ Mov (eax, s)] + | _ -> [env, code <+ Mov(y, eax) <+ Binop ("cmp", eax, x) <+ Mov(L(0), eax) <+ Set(suffix(op), "%al") <+ Mov(eax, s)] + esac + esac + esac + | LABEL(l) -> [env, code <+ Label(l)] + | JMP(l) -> [env, code <+ Jmp (l)] + | CJMP(c, l) -> case env.pop of [x, env] -> [env, code <+ Mov (x, edx) <+ Binop ("cmp", L(0), edx) <+ CJmp (c, l)] esac | _ -> failure ("codegeneration for instruction %s is not yet implemented\n", i.string) esac }, [env, emptyBuffer ()], code) @@ -324,9 +358,10 @@ public fun compileX86 (args, code) { prologue (getStackSize (env)) <+> code <+> epilogue () - ) + ) + ).stringcat); system ({"gcc -g -m32 -o ", args.getBaseName, " ", runtime, " ", asmFile}.stringcat) esac -} +} \ No newline at end of file