diff --git a/.gitignore b/.gitignore index fb21227..d01b649 100644 --- a/.gitignore +++ b/.gitignore @@ -33,5 +33,8 @@ _opam/ *.s *.run +# VSCode configs +.vscode/ + # Segmentation fault core \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index d4dcf49..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "ocaml.sandbox": { - "kind": "global" - } -} \ No newline at end of file diff --git a/LANGUAGE.md b/LANGUAGE.md new file mode 100644 index 0000000..4fd7b3e --- /dev/null +++ b/LANGUAGE.md @@ -0,0 +1,122 @@ +# RED language + +RED language is a programming language designed to write redcode programs. The language is designed to be easy to learn and use, and to be compiled to redcode programs. The following is a description of the language and its features. + +### Language features + +- Provide a simple syntax to write redcode programs with control flows +- Eliminate the need to write instruction modifiers and addressing modes + +### Language syntax + +The language syntax is based on s-expressions. The syntax is designed to be easy to read and write, and to be easy to parse and compile. The following is a description of the language components and their syntax. + +### Notices + +- The project is in development and RED language is not the final version. Important changes can be made until first release. + + +## Arguments (arg) + +Arguments are used in instructions and conditions to specify the operands. +If you want to store some value, you need to use a argument. + +An argument have a addressing mode and a value. + +- (none) replaced by immediate 0 +- (var) declare a argument using default mode +- (mode var) declare a argument using specified mode + +### Variables (var) + +Variables are numbers or strings used to store values. +By default, numbers use immediate mode and strings use direct mode. + +- (number) integer signed or unsigned number +- (string) string reference to let variable or label + +If the string corresponds to some variable in scope, the string will be replaced by a reference to this variable, otherwise the string will be kept referencing a label + +### Addresing modes (mode) + +Addresing modes are used to specify how the argument is used in the instruction. If the addressing mode is not specified, the default mode is used. + +- (Imm var) | (# var) immediate addresing to var +- (Dir var) | ($ var) direct addresing to var +- (Ind var) | (@ var) indirect addresing to var +- (Dec var) | (< var) decrement var and indirect addresing to var +- (Inc var) | (> var) indirect addresing to var and increment var +- (instr var) | (% var) points to the instruction instead of the value +- (store var) | (! var) store var value in this place (field is automatic) + + +## Conditions (cond) + +Conditions are used in a control flow intruction to specify when the control flow is executed. There are two types of conditions: unary and binary, each one with different extra instructions added to the code to work. + +### Unary conditions (cond1) + +Unary conditions are used to specify when the control flow is executed based on one argument. Generate an extra instruction in the code. + +- (JZ x) x is zero +- (JN x) x is not zero +- (DZ x) decrement x and x is zero +- (DN x) decrement x and x is not zero + +### Binary conditions (cond2) + +Binary conditions are used to specify when the control flow is executed based on two arguments. Generate two extra instructions in the code. + +- (EQ x y) x and y equals +- (NE x y) x and y not equals +- (GT x y) x is greater than y +- (LT x y) x is less than y + + +## Instructions + +Instructions are used to specify the operation to be performed. Instruction modifiers are automatically generated during compilation. + +### redcode instructions + +All redcode instructions are available for direct use. +Square brackets '[]' indicates that the argument is optional. + +- (NOP [arg1] [arg2]) no operation (arg1 & arg2 only store data) + +- (DAT arg1 arg2) data values +- (MOV arg1 arg2) move arg1 to arg2 + +- (ADD arg1 arg2) add arg1 to arg2 +- (SUB arg1 arg2) sub arg1 from arg2 +- (MUL arg1 arg2) mul arg1 to arg2 +- (DIV arg1 arg2) div arg1 from arg2 +- (MOD arg1 arg2) mod arg1 from arg2 + +- (JMP arg1 [arg2]) jump to arg1 (arg2 only store data) +- (SPL arg1 [arg2]) split to arg1 (arg2 only store data) + +- (JMZ arg1 arg2) jump to arg1 if arg2 is zero +- (JMN arg1 arg2) jump to arg1 if arg2 is not zero +- (DJN arg1 arg2) decrement arg2 and jump to arg1 if arg2 is not zero + +- (SEQ arg1 arg2) skip next instruction if arg1 equals arg2 +- (SNE arg1 arg2) skip next instruction if arg1 not equals arg2 +- (SLT arg1 arg2) skip next instruction if arg1 is less than arg2 + +### Control flows + +Control flows are used to specify the execution order of the instructions. Use control flows generates extra instructions in the code. + +- (repeat body) repeat body forever (one extra instruction) +- (while cond body) repeat body while cond is true (one extra instruction + cond) +- (if cond body) execute body if cond is true (no extra instruction + cond) +- (do-while cond body) repeat body while cond is true (no extra instruction + cond) + + +### Other instructions + +- (let (id arg) body) introduce a new variable in the scope (no extra instruction) + +- (seq instrs) execure a sequence of instructions (one extra instruction) +- (label text) create a label in the code (no extra instruction) diff --git a/Makefile b/Makefile index dfd7f6a..79dc635 100644 --- a/Makefile +++ b/Makefile @@ -17,7 +17,7 @@ src = # nothing by default init: dune build @check -test: +tests: dune exec execs/run_test.exe -- test '$(F)' ctest: @@ -26,18 +26,6 @@ ctest: compile: dune exec execs/run_compile.exe $(src) -compile-run: $(subst .src,.run,$(src)) - ./$< - -interp: - dune exec execs/run_interp.exe $(src) - -%.run: %.o rt/sys.c - clang -o $@ $(CFLAGS) rt/sys.c $< - -%.o: %.s - nasm -f $(BIN_FORMAT) -o $@ $< - %.s: %.src dune exec execs/run_compile.exe $< > $@ @@ -47,5 +35,5 @@ interp: clean: clean-test rm -Rf _build -clean-test: +clean-tests: find bbctests/ -type f -regex '.*\.\(o\|s\|run\|result\)' -delete diff --git a/README.md b/README.md index 87959e8..148f087 100644 --- a/README.md +++ b/README.md @@ -1,37 +1,61 @@ # corewars-compiler -This software aims to be an easier way to write and optimize code for corewars. corewars-compiler uses its own mini functional language which can then be compiled into optimized redcode. +#### By fabaindaiz -work in progress +This software aims to be an easier way to write and optimize code for corewars. corewars-compiler uses its own mini functional language called RED which can then be compiled into optimized redcode. -### Requirements -- [BBCStepTester](https://github.com/fabaindaiz/BBCStepTester) +work in progress. -### Intepreters + +### Instructions of use +- See REFERENCE.md for develop and run. +- See LANGUAGE.md for RED language reference. +- See TUTORIAL.md for a RED language tutorial. + +#### Intepreters + +To execute the resulting redcode you can use one of these redcode interpreters. + +- [pMARS](https://corewar.co.uk/pmars.htm) - [A.R.E.S.](https://corewar.co.uk/ares.htm) -- [PyMARS](https://github.com/rodrigosetti/corewar) +- [python MARS](https://github.com/rodrigosetti/corewar) + +#### TODO + +- Create a RED language tutorial +- Test all instructions modifiers and addressing modes +- Improve control flow instructions and conditions + +## Acknowledgements + +- Pleiad for [BBCTester](https://github.com/pleiad/BBCTester) ## References #### Getting started + - [basic manual (spanish)](https://fdist.ucm.es/corewar/CoreWar.pdf) - [the beginners' guide](https://vyznev.net/corewar/guide.html) - [my first corewars book](https://www.corewars.org/docs/book1.html) #### Redcode learning + - [corewars warrior hints](https://es.scribd.com/document/231018699/Core-War-Hints) - [corewars tips & tricks](https://www.corewars.org/docs/tips.html) #### Redcode wariors + - [redcode warriors](https://github.com/n1LS/redcode-warriors) - [warriors sorted by type](http://moscova.inria.fr/~doligez/corewar/by-types/idx.htm) - [warriors sorted by name](http://moscova.inria.fr/~doligez/corewar/by-name/complete.htm) #### Redcode reference + - [1994 Core War Standard](https://corewar.co.uk/standards/icws94.txt) - [REDCODE REFERENCE](https://corewa.rs/reference/pmars-redcode-94.txt) - [ICWS94 validate](http://www.koth.org/planar/post/Validate1.1R.txt) -#### Coreward koth +#### Corewars koth + - [KotH](http://www.koth.org/koth.html) - [hills](https://corewar.co.uk/hills.htm) diff --git a/REFERENCE.md b/REFERENCE.md index 71d43da..5bb1050 100644 --- a/REFERENCE.md +++ b/REFERENCE.md @@ -1,20 +1,60 @@ -# Reference Code for Deliverable 1 +# Requirements & Setup -Starter code for compilers homework, [deliverable 1](https://users.dcc.uchile.cl/~etanter/CC5116/hw_1_enunciado.html) +To develop and run the compiler, you will need to use the following: -## Requirements & Setup +- [OCaml](https://ocaml.org/), version 4.12 (or newer), a programming language well-suited for implementing compilers (see below for the specific installation instructions). +- [Opam](https://opam.ocaml.org/doc/Install.html), version 2.0 (or newer), a package manager for ocaml libraries and tools. -See the [detailed description](https://users.dcc.uchile.cl/~etanter/CC5116-2020/#(part._materials)) on the page of the course. +In order to setup your ocaml environment, you should first [install opam](https://opam.ocaml.org/doc/Install.html), following the instructions for your distribution. Then create a switch with the right ocaml version and install the tools and libraries used in the course with the following invocations from the command line. + +```bash +opam init +opam update +opam switch create compilation 5.0.0 + +# adapt according to your shell -- this is shown for bash +eval `opam env` +opam install dune utop merlin containers alcotest +``` + +A brief description of the installed tools and libraries: + +- [dune](https://dune.build/), version 2.9 (or newer), a build manager for ocaml. +- [utop](https://github.com/ocaml-community/utop), a rich REPL (Run-Eval-Print-Loop) for ocaml with autocompletion and syntax coloring. +- [merlin](https://github.com/ocaml/merlin), provides contextual information on ocaml code to various IDEs. +- [containers](http://c-cube.github.io/ocaml-containers/), an extension to the standard library. +- [alcotest](https://github.com/mirage/alcotest), a simple and colourful unit test framework. + +There is no specific IDE for OCaml. A time-tested solution is to use Emacs (with tuareg and merlin). I’m using the [OCaml Platform for VS Code](https://github.com/ocamllabs/vscode-ocaml-platform), which works pretty well and is under active development. There’s also some community-backed support for [IntelliJ](https://plugins.jetbrains.com/plugin/9440-reasonml), although I haven’t tried it. + +For VS Code, you first need to install [OCaml LSP](https://github.com/ocaml/ocaml-lsp): + +```bash +opam install ocaml-lsp-server +``` + +Then simply go to VS Code, lookup for the extension named VSCode OCaml Platform, and you should be good to go. + +Hint for VS Code: run this in the VS Code integrated terminal for automatic rebuild when a file changes: + +```bash +dune build --watch --terminal-persistence=clear-on-rebuild +``` + +We recommend using Linux or macOS, if possible. If you use Windows, then install the [Windows Subsystem for Linux](https://learn.microsoft.com/en-us/windows/wsl/install). Past experience from students with WSL indicates that: + +- When installing opam with add-apt-repository, it’s also necessary to apt install gcc, binutils-dev, make and pkg-config, and + +- Call opam init with the switch --disable-sandboxing, as [explained here](https://stackoverflow.com/questions/54987110/installing-ocaml-on-windows-10-using-wsl-ubuntu-problems-with-bwrap-bubblewr). ## Organization of the repository The organization of the repository is as follows: -- `dev/`: main OCaml files for the project submodules (ast, parser, interpreter, asm instructions, compiler) -- `execs/`: OCaml files for top-level executables (interpreter, compiler, tester) +- `src/`: main OCaml files for the project submodules (ast, parser, red instructions, compiler) +- `execs/`: OCaml files for top-level executables (compiler, tester) - `bbctests/`: folder for black-box compiler tests (uses the BBCTester library, see below) - `examples/`: folder for example source code files you may wish to interpret or compile directly -- `rt/sys.c`: the runtime system implemented in C Additionally, the root directory contains configuration files for the dune package manager (`dune-workspace`, `dune-project`), and each OCaml subdirectory also contains `dune` files in order to setup the project structure. @@ -30,7 +70,7 @@ The root directory contains a `Makefile` that provides shortcuts to build and te - `make clean-tests`: cleans the tests output in the `bbctests` directory -- `make test`: execute the tests for the compiler defined in `execs/test.ml` (see below). +- `make tests`: execute the tests for the compiler defined in `execs/test.ml` (see below). Variants include: * `make ctest` for compact representation of the tests execution * you can also add `F=` where `` is a pattern to filter which tests should be executed (eg. `make test F=arith` to run only test files whose name contains `arith`) @@ -39,18 +79,14 @@ The root directory contains a `Makefile` that provides shortcuts to build and te - you can build the executables manually with `make .exe`. For instance, `make run_compile.exe` builds the compiler executable. - you can run the executables manually as follows: - * `make interp src=examples/prog.src`: builds/runs the interpreter on the source file `examples/prog.src`, outputs the result - * `make compile src=examples/prog.src`: builds/runs the compiler on the source file `examples/prog.src`, outputs the generated assembly code - * `make compile-run src=examples/prog.src`: builds/runs the compiler on the source file `examples/prog.src`, generates the program binary (`examples/prog.run`), and runs it. + * `make compile src=examples/prog.src`: builds/runs the compiler on the source file `examples/prog.src`, outputs the generated redcode - you can also ask specific files to be built, eg.: - * `make examples/prog.s`: looks up `examples/prog.src`, compiles it, and generates the assembly file `examples/prog.s` - * `make examples/prog.o`: looks up `examples/prog.src`, compiles it, and generates the binary module `examples/prog.o` (unlinked) - * `make examples/prog.run`: looks up `examples/prog.src`, compiles it, assembles, links, and generates the binary executable `examples/prog.run` + * `make examples/prog.s`: looks up `examples/prog.src`, compiles it, and generates the redcode file `examples/prog.s` You can look at the makefile to see the underlying `dune` commands that are generated, and of course you can use `dune` directly if you find it more convenient. -## Writing tests +## Tests Tests are written using the [alcotest](https://github.com/mirage/alcotest) unit-testing framework. @@ -94,4 +130,8 @@ Remember that to execute your code interactively, use `dune utop` in a terminal, Documentation for ocaml libraries: - [containers](http://c-cube.github.io/ocaml-containers/last/) for extensions to the standard library - [alcotest](https://mirage.github.io/alcotest/alcotest/index.html) for unit-tests -- [BBCTester](https://github.com/pleiad/BBCTester) for blac-box compiler tests +- [BBCStepTester](https://github.com/fabaindaiz/BBCStepTester) for blac-box compiler tests + +## Acknowledgements + +- Document based on [CC5116](https://users.dcc.uchile.cl/~etanter/CC5116/) diff --git a/TUTORIAL.md b/TUTORIAL.md new file mode 100644 index 0000000..e69de29 diff --git a/bbctests/examples/prog0.bbc b/bbctests/examples/prog0.bbc index acc74fa..26f0c9d 100644 --- a/bbctests/examples/prog0.bbc +++ b/bbctests/examples/prog0.bbc @@ -1,9 +1,9 @@ NAME: test imp DESCRIPTION: evaluates a function declaration SRC: -(MOV 0 1) +(MOV (Dir 0) (Dir 1)) EXPECTED: ;redcode-94b - MOV.I #0 , #1 + MOV.I $0 , $1 DAT #0 , #0 \ No newline at end of file diff --git a/bbctests/examples/prog1.bbc b/bbctests/examples/prog1.bbc index 8504783..3dd931d 100644 --- a/bbctests/examples/prog1.bbc +++ b/bbctests/examples/prog1.bbc @@ -1,18 +1,18 @@ NAME: test dwarf DESCRIPTION: evaluates a function declaration SRC: -(let (x B dest) +(let (x dest) (seq - (MOV (Dir -1) (place x)) - (ADD 4 x) + (MOV (Ins -1) (store x)) + (ADD 4 x) (JMP x) (label dest) )) EXPECTED: ;redcode-94b -L1 +LET1 MOV.I $-1 , $dest - ADD.AB #4 , $L1 - JMP $L1 , #0 + ADD.AB #4 , $LET1 + JMP $LET1 , #0 dest DAT #0 , #0 \ No newline at end of file diff --git a/bbctests/examples/prog10.bbc b/bbctests/examples/prog10.bbc new file mode 100644 index 0000000..946890e --- /dev/null +++ b/bbctests/examples/prog10.bbc @@ -0,0 +1,18 @@ +NAME: long labels +DESCRIPTION: evaluates a function declaration +SRC: +(let (x dest_with_some_extra_characters) + (seq + (MOV (Ins -1) (store x)) + (ADD 4 x) + (JMP x) + (label dest_with_some_extra_characters) )) +EXPECTED: +;redcode-94b + +LET1 + MOV.I $-1 , $dest_with_some_extra_characters + ADD.AB #4 , $LET1 + JMP $LET1 , #0 +dest_with_some_extra_characters + DAT #0 , #0 \ No newline at end of file diff --git a/bbctests/examples/prog2.bbc b/bbctests/examples/prog2.bbc index 7d57235..29ac180 100644 --- a/bbctests/examples/prog2.bbc +++ b/bbctests/examples/prog2.bbc @@ -1,34 +1,34 @@ NAME: test modifiers DESCRIPTION: evaluates a function declaration SRC: -(let (x A 1) - (let (y B 2) - (let (a A 0) - (let (b B 0) +(let (x 1) + (let (y 2) + (let (a 0) + (let (b 0) (seq (JMP (Dir 2)) - (DAT (place x) (place y)) + (DAT (store x) (store y)) (ADD x y) (ADD y y) (ADD y x) (MOV x a) (MOV a b) - (DAT (place a) 0) - (DAT 0 (place b)) ))))) + (DAT (store a) 0) + (DAT 0 (store b)) ))))) EXPECTED: ;redcode-94b JMP $2 , #0 -L1 -L2 +LET1 +LET2 DAT #1 , #2 - ADD.AB $L1 , $L2 - ADD.B $L2 , $L2 - ADD.BA $L2 , $L1 - MOV.A $L1 , $L3 - MOV.AB $L3 , $L4 -L3 + ADD.AB $LET1 , $LET2 + ADD.B $LET2 , $LET2 + ADD.BA $LET2 , $LET1 + MOV.A $LET1 , $LET3 + MOV.AB $LET3 , $LET4 +LET3 DAT #0 , #0 -L4 +LET4 DAT #0 , #0 DAT #0 , #0 \ No newline at end of file diff --git a/bbctests/examples/prog3.bbc b/bbctests/examples/prog3.bbc index 908fbb1..f0e1e01 100644 --- a/bbctests/examples/prog3.bbc +++ b/bbctests/examples/prog3.bbc @@ -1,12 +1,12 @@ NAME: test indirection DESCRIPTION: evaluates a function declaration SRC: -(let (x A dest) - (let (y B (Ind x)) +(let (x dest) + (let (y (Ind x)) (seq (JMP (Dir 2)) - (DAT (place x) 0) - (MOV from (place y)) + (DAT (store x) 0) + (MOV from (store y)) (DAT 0 0) (label from) @@ -17,10 +17,10 @@ EXPECTED: ;redcode-94b JMP $2 , #0 -L1 +LET1 DAT $dest , #0 -L2 - MOV.I $from , *L1 +LET2 + MOV.I $from , *LET1 DAT #0 , #0 from ADD.I #1 , #1 diff --git a/bbctests/examples/prog4.bbc b/bbctests/examples/prog4.bbc index d4b44e8..1ecec30 100644 --- a/bbctests/examples/prog4.bbc +++ b/bbctests/examples/prog4.bbc @@ -1,36 +1,36 @@ NAME: test jmp indirection DESCRIPTION: evaluates a function declaration SRC: -(let (a A dest1) - (let (b B dest2) - (let (c B dest3) - (let (d B dest4) +(let (a dest1) + (let (b dest2) + (let (c dest3) + (let (d dest4) (seq (JMP (Ind d)) (label dest2) - (JMP (Ind a) (place c)) + (JMP (Ind a) (store c)) (label dest3) - (JMP (Ind b) (place d)) - (DAT (place a) (place b)) + (JMP (Ind b) (store d)) + (DAT (store a) (store b)) (label dest4) (JMP (Ind c)) (label dest1) - (MOV (Dir 0) (Dir 1)) ))))) + (MOV (Ins 0) (Dir 1)) ))))) EXPECTED: ;redcode-94b - JMP @L4 , #0 + JMP @LET4 , #0 dest2 -L3 - JMP *L1 , $dest3 +LET3 + JMP *LET1 , $dest3 dest3 -L4 - JMP @L2 , $dest4 -L1 -L2 +LET4 + JMP @LET2 , $dest4 +LET1 +LET2 DAT $dest1 , $dest2 dest4 - JMP @L3 , #0 + JMP @LET3 , #0 dest1 MOV.I $0 , $1 DAT #0 , #0 \ No newline at end of file diff --git a/bbctests/examples/prog5.bbc b/bbctests/examples/prog5.bbc index 2dc8518..d4023ee 100644 --- a/bbctests/examples/prog5.bbc +++ b/bbctests/examples/prog5.bbc @@ -1,15 +1,15 @@ NAME: test sum indirection DESCRIPTION: evaluates a function declaration SRC: -(let (x B 2) - (let (rx A x) - (let (y A 3) - (let (ry B y) +(let (x 2) + (let (rx x) + (let (y 3) + (let (ry y) (seq (JMP start) - (SUB (place y) 0) - (SUB 0 (place x)) - (SUB (place rx) (place ry)) + (SUB (store y) 0) + (SUB 0 (store x)) + (SUB (store rx) (store ry)) (label start) (ADD 4 (Ind rx)) (ADD 5 (Ind ry)) ))))) @@ -17,14 +17,14 @@ EXPECTED: ;redcode-94b JMP $start , #0 -L3 +LET3 SUB.I #3 , #0 -L1 +LET1 SUB.I #0 , #2 -L2 -L4 - SUB.BA $L1 , $L3 +LET2 +LET4 + SUB.BA $LET1 , $LET3 start - ADD.AB #4 , *L2 - ADD.A #5 , @L4 + ADD.AB #4 , *LET2 + ADD.A #5 , @LET4 DAT #0 , #0 \ No newline at end of file diff --git a/bbctests/examples/prog6.bbc b/bbctests/examples/prog6.bbc index 22ced94..a2b80bf 100644 --- a/bbctests/examples/prog6.bbc +++ b/bbctests/examples/prog6.bbc @@ -1,17 +1,17 @@ NAME: test repeat DESCRIPTION: evaluates a function declaration SRC: -(let (x B (Dir 3)) +(let (x (Dir 3)) (repeat (seq - (MOV (Dir -1) (place x)) + (MOV (Ins -1) (store x)) (ADD 1 x) ))) EXPECTED: ;redcode-94b -L2 -L1 +REP2 +LET1 MOV.I $-1 , $3 - ADD.AB #1 , $L1 - JMP $L2 , #0 + ADD.AB #1 , $LET1 + JMP $REP2 , #0 DAT #0 , #0 \ No newline at end of file diff --git a/bbctests/examples/prog7.bbc b/bbctests/examples/prog7.bbc index 3d9d1b1..d38be72 100644 --- a/bbctests/examples/prog7.bbc +++ b/bbctests/examples/prog7.bbc @@ -1,19 +1,19 @@ NAME: test do-while DESCRIPTION: evaluates a function declaration SRC: -(let (x B 10) +(let (x 10) (seq (JMP (Dir 2)) - (DAT 0 (place x)) + (DAT 0 (store x)) (do-while (DN 100) - (MOV (Dir -1) (Inc x)) ))) + (MOV (Ins -1) (Inc x)) ))) EXPECTED: ;redcode-94b JMP $2 , #0 -L1 +LET1 DAT #0 , #10 -L2 - MOV.I $-1 , >L1 - DJN.B $L2 , #100 +DWH8 + MOV.I $-1 , >LET1 + DJN.B $DWH8 , #100 DAT #0 , #0 \ No newline at end of file diff --git a/bbctests/examples/prog8.bbc b/bbctests/examples/prog8.bbc index 2548a68..313a1c2 100644 --- a/bbctests/examples/prog8.bbc +++ b/bbctests/examples/prog8.bbc @@ -1,25 +1,25 @@ NAME: test while comparation DESCRIPTION: evaluates a function declaration SRC: -(let (x A 100) - (let (y B 10) +(let (x 100) + (let (y 10) (seq (JMP (Dir 2)) - (DAT (place x) (place y)) + (DAT (store x) (store y)) (while (LT y x) (seq - (MOV x (Dec x)) ))))) + (MOV (Ins x) (Dec x)) ))))) EXPECTED: ;redcode-94b JMP $2 , #0 -L1 -L2 +LET1 +LET2 DAT #100 , #10 -L3 - SLT.BA $L2 , $L1 - JMP $L4 , #0 - MOV.A $L1 , {L1 - JMP $L3 , #0 -L4 +WHI9 + SLT.BA $LET2 , $LET1 + JMP $WHF9 , #0 + MOV.I $LET1 , {LET1 + JMP $WHI9 , #0 +WHF9 DAT #0 , #0 \ No newline at end of file diff --git a/commands.md b/commands.md index 04009f4..779d2ca 100644 --- a/commands.md +++ b/commands.md @@ -6,26 +6,21 @@ dune utop #### red.ml ```bash open Dev.Red; + ``` #### compile.ml ```bash open Dev.Ast; open Dev.Compile; + ``` -## Test all +## bbctests & examples ```bash -make test -make clean-test -``` +make tests +make clean-tests -## Compilation example -```bash make compile src=examples/prog0.src -make compile src=examples/prog1.src -make compile src=examples/prog2.src -make compile src=examples/prog3.src -make compile src=examples/prog4.src ``` diff --git a/dev/analyse.ml b/dev/analyse.ml new file mode 100644 index 0000000..05051c1 --- /dev/null +++ b/dev/analyse.ml @@ -0,0 +1,45 @@ +(** Analyser **) +open String +open Ast +open Lib + + +let analyse_store_arg (arg : arg) (id : string) (place : place) (penv : penv) : penv = + match arg with + | AStore (s) -> + (match (equal id s) with + | true -> (extend_penv s place penv) + | false -> penv) + | _ -> penv + +let analyse_store_cond (cond : cond) (id : string) (penv : penv) : penv = + match cond with + | Cond1 (_, a) -> (analyse_store_arg a id PB penv) + | Cond2 (_, a1, a2) -> + let penv' = (analyse_store_arg a1 id PA penv) in + (analyse_store_arg a2 id PB penv') + +let rec analyse_store_expr (e : tag eexpr) (id : string) (penv : penv) : penv = + match e with + | ELabel (_) -> penv + | EPrim2 (_, a1, a2, _) -> + let env' = (analyse_store_arg a1 id PA penv) in + (analyse_store_arg a2 id PB env') + | EFlow (_, exp, _) -> (analyse_store_expr exp id penv) + | EFlow1 (_, cond, exp, _) -> + let env' = (analyse_store_cond cond id penv) in + (analyse_store_expr exp id env') + | EFlow2 (_, cond, exp1, exp2, _) -> + let penv' = (analyse_store_cond cond id penv) in + let penv'' = (analyse_store_expr exp1 id penv') in + (analyse_store_expr exp2 id penv'') + | ELet (_, _, exp, _) -> (analyse_store_expr exp id penv) + | ESeq (exps, _) -> List.fold_left (fun penv' exp -> (analyse_store_expr exp id penv')) penv exps + + +let analyse_let (id : string) (arg : arg) (body : tag eexpr) (label : string) (env : env) : env = + let aenv, penv, lenv = env in + let aenv' = (extend_aenv id arg aenv) in + let penv' = (analyse_store_expr body id penv) in + let lenv' = (extend_lenv id label lenv) in + (aenv', penv', lenv') diff --git a/dev/ast.ml b/dev/ast.ml index 18733f4..37a9bcd 100644 --- a/dev/ast.ml +++ b/dev/ast.ml @@ -2,31 +2,46 @@ type place = -| A -| B +| PA +| PB + + +type imode = +| MINone +| MIInc +| MIDec type mode = -| ADir -| AInd -| ADec -| AInc +| MIns (* Instruction *) +| MImm (* Immediate *) +| MDir (* Direct *) +| MInd of imode (* Indirect *) type arg = | ANone -| Num of int -| Id of string -| Ref of mode * int -| Lab of mode * string -| Place of string +| AStore of string +| ANum of int +| AId of string +| ARef of mode * int +| ALab of mode * string + + +type cond1 = +| Cjz +| Cjn +| Cdz +| Cdn + +type cond2 = +| Ceq +| Cne +| Cgt +| Clt type cond = -| Cjz of arg -| Cjn of arg -| Cdn of arg -| Ceq of arg * arg -| Cne of arg * arg -| Cgt of arg * arg -| Clt of arg * arg +| Cond1 of cond1 * arg +| Cond2 of cond2 * arg * arg + type prim2 = | Dat @@ -38,14 +53,79 @@ type prim2 = | Mod | Jmp | Spl +| Nop + +| Jmz +| Jmn +| Djn +| Seq +| Sne +| Slt + +type flow = +| Repeat + +type flow1 = +| If +| While +| DoWhile + +type flow2 = +| IfElse + type expr = | Label of string | Prim2 of prim2 * arg * arg -| Nop -| Let of string * place *arg * expr +| Flow of flow * expr +| Flow1 of flow1 * cond * expr +| Flow2 of flow2 * cond * expr * expr +| Let of string * arg * expr | Seq of expr list -| Repeat of expr -| If of cond * expr -| While of cond * expr -| Dowhile of cond * expr + +type 'a eexpr = +| ELabel of string * 'a +| EPrim2 of prim2 * arg * arg * 'a +| EFlow of flow * 'a eexpr * 'a +| EFlow1 of flow1 * cond * 'a eexpr * 'a +| EFlow2 of flow2 * cond * 'a eexpr * 'a eexpr * 'a +| ELet of string * arg * 'a eexpr * 'a +| ESeq of 'a eexpr list * 'a + + +type tag = int + +let rec tag_expr_help (e : expr) (cur : tag) : (tag eexpr * tag) = + match e with + | Label (s) -> + let (next_tag) = (cur + 1) in + (ELabel (s, cur), next_tag) + | Prim2 (op, a1, a2) -> + let (next_tag) = (cur + 1) in + (EPrim2 (op, a1, a2, cur), next_tag) + | Flow (op, expr) -> + let (tag_expr, next_tag) = tag_expr_help expr (cur + 1) in + (EFlow (op, tag_expr, cur), next_tag) + | Flow1 (op, cond, expr) -> + let (tag_expr, next_tag) = tag_expr_help expr (cur + 1) in + (EFlow1 (op, cond, tag_expr, cur), next_tag) + | Flow2 (op, cond, expr1, expr2) -> + let (tag_expr1, next_tag1) = tag_expr_help expr1 (cur + 1) in + let (tag_expr2, next_tag2) = tag_expr_help expr2 next_tag1 in + (EFlow2 (op, cond, tag_expr1, tag_expr2, cur), next_tag2) + | Let (x, a, expr) -> + let (tag_expr, next_tag) = tag_expr_help expr (cur + 1) in + (ELet (x, a, tag_expr, cur), next_tag) + | Seq (exprs) -> + let rec tag_seq (exprs : expr list) (cur : tag) : tag eexpr list * tag = + (match exprs with + | head :: tail -> + let (tag_head, next_tag1) = tag_expr_help head (cur + 1) in + let (tag_tail, next_tag2) = tag_seq tail next_tag1 in + [tag_head] @ tag_tail, next_tag2 + | [] -> [], cur ) in + let (tag_e, next_tag) = tag_seq exprs (cur + 1) in + (ESeq (tag_e, cur), next_tag) + +let tag_expr (e : expr) : tag eexpr = + let (tagged, _) = tag_expr_help e 1 in tagged diff --git a/dev/compile.ml b/dev/compile.ml index 52d05c2..d2e0028 100644 --- a/dev/compile.ml +++ b/dev/compile.ml @@ -1,122 +1,136 @@ -(** Compile **) +(** Compiler **) open Printf open Red open Ast open Lib +open Util +open Analyse exception CTError of string -let rec compile_arg (arg : arg) (env : env) : rarg = - let aenv, penv, lenv = env in - match arg with - | ANone -> RNone - | Num (n) -> RNum (n) - | Id (s) -> - (match List.assoc_opt s lenv with - | Some l -> RLab ((compile_mode ADir B), l) - | None -> RLab ((compile_mode ADir B), s) ) - | Ref (m, n) -> RRef ((compile_mode m B), n) - | Lab (m, s) -> - (match List.assoc_opt s lenv with - | Some arg -> - let dest = (translate_penv s penv) in - RLab ((compile_mode m dest), arg) - | None -> RLab ((compile_mode m B), s) ) - | Place (s) -> - let arg = (translate_aenv s aenv) in - (match arg with - | Id (s) -> - (match List.assoc_opt s lenv with - | Some label -> RLab ((compile_mode ADir B), label) - | None -> (compile_arg arg env) ) - | Lab (m, s) -> - (match List.assoc_opt s lenv with - | Some label -> - let dest = (translate_penv s penv) in - RLab ((compile_mode m dest), label) - | None -> (compile_arg arg env) ) - | _ -> (compile_arg arg env) ) - - -let compile_label (args : arg list) (lenv : lenv) : instruction list = - let compile_label_aux (arg : arg) (lenv: lenv) : instruction list = +let compile_label (args : arg list) (env : env) : instruction list = + let compile_label_aux (arg : arg) (env : env) : instruction list = match arg with - | Place (s) -> + | AStore (s) -> + let _, _, lenv = env in (match List.assoc_opt s lenv with | Some l -> [ILabel (l)] - | None -> failwith (sprintf "unbound variable %s in lenv" s) ) + | None -> raise (CTError (sprintf "unbound variable %s in lenv" s)) ) | _ -> [] in - List.fold_left (fun res i -> res @ (compile_label_aux i lenv)) [] args - + List.fold_left (fun res i -> res @ (compile_label_aux i env)) [] args + let compile_precond (cond : cond) (label : string ) (env : env) : instruction list = match cond with - | Cjz (e) -> [IJMN (RB, RLab(RDir, label), (compile_arg e env))] - | Cjn (e) -> [IJMZ (RB, RLab(RDir, label), (compile_arg e env))] - | Cdn (_) -> raise (CTError (sprintf "DN cond is not available on precondition")) - | Ceq (e1, e2) -> [ISEQ ((compile_mod e1 e2 env), (compile_arg e1 env), (compile_arg e2 env)) ; IJMP (RLab(RDir, label), RNone)] - | Cne (e1, e2) -> [ISNE ((compile_mod e1 e2 env), (compile_arg e1 env), (compile_arg e2 env)) ; IJMP (RLab(RDir, label), RNone)] - | Cgt (e1, e2) -> [ISLT ((compile_mod e2 e1 env), (compile_arg e2 env), (compile_arg e1 env)) ; IJMP (RLab(RDir, label), RNone)] - | Clt (e1, e2) -> [ISLT ((compile_mod e1 e2 env), (compile_arg e1 env), (compile_arg e2 env)) ; IJMP (RLab(RDir, label), RNone)] + | Cond1 (op, a) -> + let _, rarg = (compile_arg a env) in + let rmod = RB in + (match op with + | Cjz -> [IJMN (rmod, RLab (RDir, label), rarg)] + | Cjn -> [IJMZ (rmod, RLab (RDir, label), rarg)] + | Cdz -> [IDJN (rmod, RLab (RDir, label), rarg)] + | Cdn -> raise (CTError (sprintf "DN cond is not available on precondition")) ) + | Cond2 (op, a1, a2) -> + let carg1, rarg1 = (compile_arg a1 env) in + let carg2, rarg2 = (compile_arg a2 env) in + let rmod = (compile_mod carg1 carg2 RI env) in + (match op with + | Ceq -> [ISEQ (rmod, rarg1, rarg2) ; IJMP (RLab (RDir, label), RNone)] + | Cne -> [ISNE (rmod, rarg1, rarg2) ; IJMP (RLab (RDir, label), RNone)] + | Cgt -> + let rmod' = (compile_mod carg2 carg1 RI env) in + [ISLT (rmod', rarg2, rarg1) ; IJMP (RLab (RDir, label), RNone)] + | Clt -> [ISLT (rmod, rarg1, rarg2) ; IJMP (RLab (RDir, label), RNone)] ) let compile_postcond (cond : cond) (label : string ) (env : env) : instruction list = match cond with - | Cjz (e) -> [IJMZ (RB, RLab(RDir, label), (compile_arg e env))] - | Cjn (e) -> [IJMN (RB, RLab(RDir, label), (compile_arg e env))] - | Cdn (e) -> [IDJN (RB, RLab(RDir, label), (compile_arg e env))] - | Ceq (e1, e2) -> [ISNE ((compile_mod e1 e2 env), (compile_arg e1 env), (compile_arg e2 env)) ; IJMP (RLab(RDir, label), RNone)] - | Cne (e1, e2) -> [ISEQ ((compile_mod e1 e2 env), (compile_arg e1 env), (compile_arg e2 env)) ; IJMP (RLab(RDir, label), RNone)] - | Cgt (e1, e2) -> [ISLT ((compile_mod e1 e2 env), (compile_arg e1 env), (compile_arg e2 env)) ; IJMP (RLab(RDir, label), RNone)] - | Clt (e1, e2) -> [ISLT ((compile_mod e2 e1 env), (compile_arg e2 env), (compile_arg e1 env)) ; IJMP (RLab(RDir, label), RNone)] + | Cond1 (op, a) -> + let _, rarg = (compile_arg a env) in + let rmod = RB in + (match op with + | Cjz -> [IJMZ (rmod, RLab (RDir, label), rarg)] + | Cjn -> [IJMN (rmod, RLab (RDir, label), rarg)] + | Cdz -> raise (CTError (sprintf "DZ cond is not available on postcondition")) + | Cdn -> [IDJN (rmod, RLab (RDir, label), rarg)] ) + | Cond2 (op, a1, a2) -> + let carg1, rarg1 = (compile_arg a1 env) in + let carg2, rarg2 = (compile_arg a2 env) in + let rmod = (compile_mod carg1 carg2 RI env) in + (match op with + | Ceq -> [ISNE (rmod, rarg1, rarg2) ; IJMP (RLab (RDir, label), RNone)] + | Cne -> [ISEQ (rmod, rarg1, rarg2) ; IJMP (RLab (RDir, label), RNone)] + | Cgt -> [ISLT (rmod, rarg1, rarg2) ; IJMP (RLab (RDir, label), RNone)] + | Clt -> + let rmod' = (compile_mod carg2 carg1 RI env) in + [ISLT (rmod', rarg2, rarg1) ; IJMP (RLab (RDir, label), RNone)] ) -let rec compile_expr (e : expr) (env : env) : instruction list = - let aenv, penv, lenv = env in +let rec compile_expr (e : tag eexpr) (env : env) : instruction list = match e with - | Nop -> [INOP] - | Label (l) -> [ILabel (l)] - | Prim2 (op, e1, e2) -> - (compile_label [e1; e2] lenv) @ + | ELabel (l, _) -> [ILabel (l)] + | EPrim2 (op, arg1, arg2, _) -> + let carg1, rarg1 = (compile_arg arg1 env) in + let carg2, rarg2 = (compile_arg arg2 env) in + let rmod = (compile_mod carg1 carg2 RI env) in + (compile_label [arg1; arg2] env) @ + (match op with + | Dat -> [IDAT (rarg1, rarg2)] + | Mov -> [IMOV (rmod, rarg1, rarg2)] + | Add -> [IADD (rmod, rarg1, rarg2)] + | Sub -> [ISUB (rmod, rarg1, rarg2)] + | Mul -> [IMUL (rmod, rarg1, rarg2)] + | Div -> [IDIV (rmod, rarg1, rarg2)] + | Mod -> [IMOD (rmod, rarg1, rarg2)] + | Spl -> [ISPL (rarg1, rarg2)] + | Jmp -> [IJMP (rarg1, rarg2)] + | Nop -> [INOP (rarg1, rarg2)] + | Jmz -> [IJMZ (rmod, rarg1, rarg2)] + | Jmn -> [IJMN (rmod, rarg1, rarg2)] + | Djn -> [IDJN (rmod, rarg1, rarg2)] + | Seq -> [ISEQ (rmod, rarg1, rarg2)] + | Sne -> [ISNE (rmod, rarg1, rarg2)] + | Slt -> [ISLT (rmod, rarg1, rarg2)]) + | EFlow (op, exp, tag) -> (match op with - | Dat -> [IDAT ((compile_arg e1 env), (compile_arg e2 env))] - | Mov -> [IMOV ((compile_mod e1 e2 env), (compile_arg e1 env), (compile_arg e2 env))] - | Add -> [IADD ((compile_mod e1 e2 env), (compile_arg e1 env), (compile_arg e2 env))] - | Sub -> [ISUB ((compile_mod e1 e2 env), (compile_arg e1 env), (compile_arg e2 env))] - | Mul -> [IMUL ((compile_mod e1 e2 env), (compile_arg e1 env), (compile_arg e2 env))] - | Div -> [IDIV ((compile_mod e1 e2 env), (compile_arg e1 env), (compile_arg e2 env))] - | Mod -> [IMOD ((compile_mod e1 e2 env), (compile_arg e1 env), (compile_arg e2 env))] - | Spl -> [ISPL ((compile_arg e1 env), (compile_arg e2 env))] - | Jmp -> [IJMP ((compile_arg e1 env), (compile_arg e2 env))] ) - | Let (id, place, arg, body) -> - let label = (gensym "L") in - let aenv' = (extend_aenv id arg aenv) in - let penv' = (extend_penv id place penv) in - let lenv' = (extend_lenv id label lenv) in - let env' = (aenv', penv', lenv') in + | Repeat -> + let ini = (sprintf "REP%d" tag) in + [ILabel (ini)] @ (compile_expr exp env) @ [IJMP (RLab (RDir, ini), RNone)] ) + | EFlow1 (op, cond, exp, tag) -> + (match op with + | If -> + let fin = (sprintf "IF%d" tag) in + (compile_precond cond fin env) @ (compile_expr exp env) @ [ILabel (fin)] + | While -> + let ini = (sprintf "WHI%d" tag) in + let fin = (sprintf "WHF%d" tag) in + [ILabel (ini)] @ (compile_precond cond fin env) @ (compile_expr exp env) @ + [IJMP (RLab(RDir, ini), RNone)] @ [ILabel (fin)] + | DoWhile -> + let ini = (sprintf "DWH%d" tag) in + [ILabel (ini)] @ (compile_expr exp env) @ (compile_postcond cond ini env) ) + | EFlow2 (op, cond, exp1, exp2, tag) -> + (match op with + | IfElse -> + let mid = (sprintf "IFM%d" tag) in + let fin = (sprintf "IFF%d" tag) in + (compile_precond cond mid env) @ (compile_expr exp1 env) @ [IJMP (RLab (RDir, fin), RNone)] @ + [ILabel (mid)] @ (compile_expr exp2 env) @ [ILabel (fin)] ) + | ELet (id, arg, body, tag) -> + let label = (sprintf "LET%d" tag) in + let env' = (analyse_let id arg body label env) in (compile_expr body env') - | Seq (exprs) -> - List.fold_left (fun res e -> res @ (compile_expr e env)) [] exprs - | Repeat (e) -> - let ini = (gensym "L") in - [ILabel (ini)] @ (compile_expr e env) @ [IJMP (RLab(RDir, ini), RNone)] - | If (c, e) -> - let fin = (gensym "L") in - (compile_precond c fin env) @ (compile_expr e env) @ [ILabel (fin)] - | While (c, e) -> - let ini = (gensym "L") in - let fin = (gensym "L") in - [ILabel (ini)] @ (compile_precond c fin env) @ (compile_expr e env) @ [IJMP (RLab(RDir, ini), RNone)] @ [ILabel (fin)] - | Dowhile (c, e) -> - let ini = (gensym "L") in - [ILabel (ini)] @ (compile_expr e env) @ (compile_postcond c ini env) + | ESeq (exps, _) -> + List.fold_left (fun res exp -> res @ (compile_expr exp env)) [] exps -let prelude = sprintf " +let prelude = " ;redcode-94b " +let epilogue = [IDAT (RRef (RImm, 0), RRef (RImm, 0))] + let compile_prog (e : expr) : string = - let _ = (gensym "") in - let instrs = (compile_expr e empty_env) @ [IDAT (RNum 0, RNum 0)] in - (prelude) ^ (pp_instrs instrs) + let tag_e = (tag_expr e) in + let instrs = (compile_expr tag_e empty_env) in + (prelude) ^ (pp_instrs instrs) ^ (pp_instrs epilogue) diff --git a/dev/dune b/dev/dune index 3d8d2c0..d48874c 100644 --- a/dev/dune +++ b/dev/dune @@ -1,4 +1,4 @@ (library (name dev) - (modules red ast compile lib parse) + (modules red ast analyse compile lib util parse) (libraries containers)) diff --git a/dev/lib.ml b/dev/lib.ml index cab56ec..6d0c67e 100644 --- a/dev/lib.ml +++ b/dev/lib.ml @@ -1,17 +1,8 @@ (* Lib *) open Printf -open Red open Ast - -let gensym = - let a_counter = ref 0 in - (fun basename -> - if (compare basename "") == 0 then - a_counter := 0 - else - incr a_counter; - (sprintf "%s%d" basename !a_counter) );; +exception CTError of string type aenv = (string * arg) list @@ -19,7 +10,6 @@ type penv = (string * place) list type lenv = (string * string) list type env = aenv * penv * lenv - let empty_env : env = ([], [], []) @@ -34,86 +24,16 @@ let extend_lenv (x : string) (label : string) (lenv : lenv) : lenv = let translate_aenv (x : string) (aenv : aenv) : arg = - (match List.assoc_opt x aenv with + match List.assoc_opt x aenv with | Some arg -> arg - | None -> failwith (sprintf "unbound variable %s in aenv" x) ) + | None -> raise (CTError (sprintf "unbound variable %s in aenv" x)) let translate_penv (x : string) (penv : penv) : place = - (match List.assoc_opt x penv with + match List.assoc_opt x penv with | Some place -> place - | None -> failwith (sprintf "unbound variable %s in penv" x) ) + | None -> raise (CTError (sprintf "unbound variable %s in penv" x)) let translate_lenv (x : string) (lenv : lenv) : string = - (match List.assoc_opt x lenv with + match List.assoc_opt x lenv with | Some label -> label - | None -> failwith (sprintf "unbound variable %s in lenv" x) ) - - -let compile_mode (mode : mode) (dest : place) : rmode = - match dest with - | A -> - (match mode with - | ADir -> RDir - | AInd -> RAInd - | ADec -> RAPre - | AInc -> RAPos ) - | B -> - (match mode with - | ADir -> RDir - | AInd -> RBInd - | ADec -> RBPre - | AInc -> RBPos ) - - -type opmod = -| TNum -| TRef -| TA -| TB - -let rec compile_opmod (arg : arg) (env : env) : opmod = - let aenv, penv, _ = env in - match arg with - | ANone -> TNum - | Num _ -> TNum - | Ref (_, _) -> TRef - | Id s | Lab (_, s) -> - (match List.assoc_opt s aenv with - | Some arg -> - (match arg with - | Id (x) | Lab (_, x) -> - (match List.assoc_opt x penv with - | Some place -> - (match place with - | A -> TA - | B -> TB ) - | None -> - let place = (translate_penv s penv) in - (match place with - | A -> TA - | B -> TB )) - | _ -> - let place = (translate_penv s penv) in - (match place with - | A -> TA - | B -> TB )) - | None -> TRef ) - | Place (s) -> - let arg = (translate_aenv s aenv) in - (compile_opmod arg env) - - -let compile_mod (arg1 : arg) (arg2 : arg) (env : env) : rmod = - let mod1 = (compile_opmod arg1 env) in - let mod2 = (compile_opmod arg2 env) in - match mod1, mod2 with - | TNum, TNum -> RI - | TNum, TA -> RA - | TNum, TB -> RAB - | TA, TNum -> RAB - | TA, TA -> RA - | TA, TB -> RAB - | TB, TNum -> RB - | TB, TA -> RBA - | TB, TB -> RB - | _, _ -> RI + | None -> raise (CTError (sprintf "unbound variable %s in lenv" x)) diff --git a/dev/parse.ml b/dev/parse.ml index c1f0e10..8aa2742 100644 --- a/dev/parse.ml +++ b/dev/parse.ml @@ -1,66 +1,66 @@ (** Parser **) -open Ast -open Printf open CCSexp +open Printf +open Ast exception CTError of string -let parse_place (sexp : sexp) : place = - match sexp with - | `Atom "A" -> A - | `Atom "B" -> B - | _ -> raise (CTError (sprintf "Not a valid mode: %s" (to_string sexp))) - let parse_mode (sexp : sexp) : mode = match sexp with - | `Atom "Dir" -> ADir - | `Atom "Ind" -> AInd - | `Atom "Dec" -> ADec - | `Atom "Inc" -> AInc + | `Atom "Ins" | `Atom "%" -> MIns + | `Atom "Imm" | `Atom "#" -> MImm + | `Atom "Dir" | `Atom "$" -> MDir + | `Atom "Ind" | `Atom "@" -> MInd (MINone) + | `Atom "Dec" | `Atom "<" -> MInd (MIDec) + | `Atom "Inc" | `Atom ">" -> MInd (MIInc) | _ -> raise (CTError (sprintf "Not a valid mode: %s" (to_string sexp))) let parse_arg (sexp : sexp) : arg = match sexp with - | `List [`Atom "place"; `Atom s] -> Place (s) + | `Atom "none" -> ANone + | `List [`Atom "store"; `Atom s] | `List [`Atom "!"; `Atom s] -> AStore (s) | `Atom s -> (match Int64.of_string_opt s with - | Some n -> Num (Int64.to_int n) - | None -> Id (s) ) + | Some n -> ANum (Int64.to_int n) + | None -> AId (s) ) | `List [m; `Atom s] -> (match Int64.of_string_opt s with - | Some n -> Ref ((parse_mode m), (Int64.to_int n)) - | None -> Lab ((parse_mode m), s) ) + | Some n -> ARef ((parse_mode m), (Int64.to_int n)) + | None -> ALab ((parse_mode m), s) ) | _ -> raise (CTError (sprintf "Not a valid arg: %s" (to_string sexp))) + let parse_cond (sexp : sexp) : cond = match sexp with - | `List [`Atom "JZ"; e] -> Cjz (parse_arg e) - | `List [`Atom "JN"; e] -> Cjn (parse_arg e) - | `List [`Atom "DN"; e] -> Cdn (parse_arg e) - | `List [`Atom "EQ"; e1 ; e2] -> Ceq (parse_arg e1, parse_arg e2) - | `List [`Atom "NE"; e1 ; e2] -> Cne (parse_arg e1, parse_arg e2) - | `List [`Atom "GT"; e1 ; e2] -> Cgt (parse_arg e1, parse_arg e2) - | `List [`Atom "LT"; e1 ; e2] -> Clt (parse_arg e1, parse_arg e2) + | `List [`Atom "JZ"; e] -> Cond1 (Cjz, parse_arg e) + | `List [`Atom "JN"; e] -> Cond1 (Cjn, parse_arg e) + | `List [`Atom "DZ"; e] -> Cond1 (Cdz, parse_arg e) + | `List [`Atom "DN"; e] -> Cond1 (Cdn, parse_arg e) + | `List [`Atom "EQ"; e1 ; e2] -> Cond2 (Ceq, parse_arg e1, parse_arg e2) + | `List [`Atom "NE"; e1 ; e2] -> Cond2 (Cne, parse_arg e1, parse_arg e2) + | `List [`Atom "GT"; e1 ; e2] -> Cond2 (Cgt, parse_arg e1, parse_arg e2) + | `List [`Atom "LT"; e1 ; e2] -> Cond2 (Clt, parse_arg e1, parse_arg e2) | _ -> raise (CTError (sprintf "Not a valid cond: %s" (to_string sexp))) + let rec parse_exp (sexp : sexp) : expr = match sexp with - | `List [`Atom "NOP"] -> Nop | `List [`Atom "label"; `Atom s] -> Label (s) | `List (`Atom "seq" :: exps) -> Seq (List.map parse_exp exps) + | `List [eop] -> + (match eop with + | `Atom "NOP" -> Prim2 (Nop, ANone, ANone) + | _ -> raise (CTError (sprintf "Not a valid expr: %s" (to_string sexp))) ) | `List [eop; e] -> (match eop with | `Atom "JMP" -> Prim2 (Jmp, parse_arg e, ANone) | `Atom "SPL" -> Prim2 (Spl, parse_arg e, ANone) - | `Atom "repeat" -> Repeat (parse_exp e) - | _ -> raise (CTError (sprintf "Not a valid expr: %s" (to_string sexp))) ) + | `Atom "NOP" -> Prim2 (Nop, parse_arg e, ANone) + | `Atom "repeat" -> Flow (Repeat, parse_exp e) + | _ -> raise (CTError (sprintf "Not a valid unary expr: %s" (to_string sexp))) ) | `List [eop; e1; e2] -> (match eop with - | `Atom "let" -> - (match e1 with - | `List [`Atom id; p; e] -> Let (id, parse_place p, parse_arg e, parse_exp e2) - | _ -> raise (CTError (sprintf "Not a valid let assignment: %s" (to_string e1))) ) | `Atom "DAT" -> Prim2 (Dat, parse_arg e1, parse_arg e2) | `Atom "MOV" -> Prim2 (Mov, parse_arg e1, parse_arg e2) | `Atom "ADD" -> Prim2 (Add, parse_arg e1, parse_arg e2) @@ -70,10 +70,19 @@ let rec parse_exp (sexp : sexp) : expr = | `Atom "MOD" -> Prim2 (Mod, parse_arg e1, parse_arg e2) | `Atom "JMP" -> Prim2 (Jmp, parse_arg e1, parse_arg e2) | `Atom "SPL" -> Prim2 (Spl, parse_arg e1, parse_arg e2) - | `Atom "if" -> If (parse_cond e1, parse_exp e2) - | `Atom "while" -> While (parse_cond e1, parse_exp e2) - | `Atom "do-while" -> Dowhile (parse_cond e1, parse_exp e2) - | _ -> raise (CTError (sprintf "Not a valid expr: %s" (to_string sexp))) ) + | `Atom "NOP" -> Prim2 (Nop, parse_arg e1, parse_arg e2) + | `Atom "if" -> Flow1 (If, parse_cond e1, parse_exp e2) + | `Atom "while" -> Flow1 (While, parse_cond e1, parse_exp e2) + | `Atom "do-while" -> Flow1 (DoWhile, parse_cond e1, parse_exp e2) + | `Atom "let" -> + (match e1 with + | `List [`Atom id; e] -> Let (id, parse_arg e, parse_exp e2) + | _ -> raise (CTError (sprintf "Not a valid let assignment: %s" (to_string e1))) ) + | _ -> raise (CTError (sprintf "Not a valid binary expr: %s" (to_string sexp))) ) + | `List [eop; e1; e2; e3] -> + (match eop with + | `Atom "if" -> Flow2 (IfElse, parse_cond e1, parse_exp e2, parse_exp e3) + | _ -> raise (CTError (sprintf "Not a valid ternary expr: %s" (to_string sexp))) ) | _ -> raise (CTError (sprintf "Not a valid expr: %s" (to_string sexp))) diff --git a/dev/red.ml b/dev/red.ml index 3e02a1b..cfd0fa2 100644 --- a/dev/red.ml +++ b/dev/red.ml @@ -4,32 +4,63 @@ open Printf (* addressing modes *) type rmode = -| RImm (* immediate *) -| RDir (* direct *) +| RImm (* immediate *) +| RDir (* direct *) | RAInd (* A-field indirect *) | RBInd (* B-field indirect *) -| RAPre (* A-field indirect with predecrement *) -| RBPre (* B-field indirect with predecrement *) -| RAPos (* A-field indirect with postincrement *) -| RBPos (* B-field indirect with postincrement *) +| RADec (* A-field indirect with predecrement *) +| RBDec (* B-field indirect with predecrement *) +| RAInc (* A-field indirect with postincrement *) +| RBInc (* B-field indirect with postincrement *) + +(* addressing modes to string *) +let pp_mode (rmode : rmode) : string = + match rmode with + | RImm -> "#" + | RDir -> "$" + | RAInd -> "*" + | RBInd -> "@" + | RADec -> "{" + | RBDec -> "<" + | RAInc -> "}" + | RBInc -> ">" + (* red arguments for opcodes *) type rarg = -| RNone -| RNum of int -| RId of string -| RRef of rmode * int -| RLab of rmode * string +| RNone (* none arg *) +| RRef of rmode * int (* number arg *) +| RLab of rmode * string (* string arg *) + +(* rarguments for instruction to string *) +let pp_rarg (rarg : rarg) : string = + match rarg with + | RNone -> sprintf "#%-6s" (Int.to_string 0) + | RRef (m, n) -> sprintf "%s%-6s" (pp_mode m) (Int.to_string n) + | RLab (m, l) -> sprintf "%s%-6s" (pp_mode m) (l) + (* instruction modifiers *) type rmod = -| RA -| RB -| RAB -| RBA -| RF -| RX -| RI +| RA (* A to A *) +| RB (* B to B *) +| RAB (* A to B *) +| RBA (* B to A *) +| RF (* AB to AB *) +| RX (* AB to BA *) +| RI (* instr to instr *) + +(* instruction modifiers to string *) +let pp_rmod (rmod : rmod) : string = + match rmod with + | RA -> ".A " + | RB -> ".B " + | RAB -> ".AB" + | RBA -> ".BA" + | RF -> ".F " + | RX -> ".X " + | RI -> ".I " + (* red opcode *) type instruction = @@ -52,40 +83,7 @@ type instruction = | ISLT of rmod * rarg * rarg (* skip if lower than *) | ILDP of rmod * rarg * rarg (* load from p-space *) | ISTP of rmod * rarg * rarg (* save to p-space *) -| INOP (* no operation *) - - -(* addressing modes to string *) -let pp_mode (mode : rmode) : string = - match mode with - | RImm -> "#" - | RDir -> "$" - | RAInd -> "*" - | RBInd -> "@" - | RAPre -> "{" - | RBPre -> "<" - | RAPos -> "}" - | RBPos -> ">" - -(* rarguments for instruction to string *) -let pp_rarg (rarg : rarg) : string = - match rarg with - | RNone -> sprintf "#%-6s" (Int.to_string 0) - | RNum (n) -> sprintf "#%-6s" (Int.to_string n) - | RId (l) -> sprintf "#%-6s" (l) - | RRef (m, n) -> sprintf "%s%-6s" (pp_mode m) (Int.to_string n) - | RLab (m, l) -> sprintf "%s%-6s" (pp_mode m) (l) - -(* instruction modifiers to string *) -let pp_rmod (rmod : rmod) : string = - match rmod with - | RA -> ".A " - | RB -> ".B " - | RAB -> ".AB" - | RBA -> ".BA" - | RF -> ".F " - | RX -> ".X " - | RI -> ".I " +| INOP of rarg * rarg (* no operation *) (* red opcode to string *) let pp_instr (opcode : instruction) : string = @@ -109,7 +107,8 @@ let pp_instr (opcode : instruction) : string = | ISLT (m, e1, e2) -> sprintf " SLT%s %s, %s" (pp_rmod m) (pp_rarg e1) (pp_rarg e2) | ILDP (m, e1, e2) -> sprintf " LDP%s %s, %s" (pp_rmod m) (pp_rarg e1) (pp_rarg e2) | ISTP (m, e1, e2) -> sprintf " STP%s %s, %s" (pp_rmod m) (pp_rarg e1) (pp_rarg e2) - | INOP -> sprintf " NOP" + | INOP (e1, e2) -> sprintf " NOP %s, %s" (pp_rarg e1) (pp_rarg e2) + (* red instruction list to string *) let pp_instrs (instrs : instruction list) : string = diff --git a/dev/util.ml b/dev/util.ml new file mode 100644 index 0000000..6b15c49 --- /dev/null +++ b/dev/util.ml @@ -0,0 +1,149 @@ +(** Util **) +open Printf +open Red +open Ast +open Lib + +exception CTError of string + + +let imode_to_rmode (imode : imode) (place : place) : rmode = + match imode, place with + | MINone, PA -> RAInd + | MINone, PB -> RBInd + | MIDec, PA -> RADec + | MIDec, PB -> RBDec + | MIInc, PA -> RAInc + | MIInc, PB -> RBInc + +let compile_mode (mode : mode) (dest : place) : rmode = + match dest with + | PA -> + (match mode with + | MIns -> RDir + | MImm -> RImm + | MDir -> RDir + | MInd (m) -> (imode_to_rmode m PA) ) + | PB -> + (match mode with + | MIns -> RDir + | MImm -> RImm + | MDir -> RDir + | MInd (m) -> (imode_to_rmode m PB) ) + + +type darg = +| ADRef of mode * int +| ADLab of mode * string + +let arg_to_darg (arg : arg) : darg = + match arg with + | ANone -> ADRef (MImm, 0) + | ANum (n) -> ADRef (MImm, n) + | AId (s) -> ADLab (MDir, s) + | ARef (m, n) -> ADRef (m, n) + | ALab (m, s) -> ADLab (m, s) + | AStore (s) -> raise (CTError (sprintf "Not a valid place to store: %s" s)) + + +type carg = +| ACRef of mode * int (* number variable *) +| ACLab of mode * string (* label variable *) +| ACVar of mode * string (* direct reference *) +| ACPnt of mode * string (* indirect reference *) + +let darg_to_carg (darg : darg) (env : env) : carg = + let _, _, lenv = env in + match darg with + | ADRef (m, n) -> ACRef (m, n) + | ADLab (m, s) -> + (match List.assoc_opt s lenv with + | Some _ -> + (match m with + | MIns | MImm | MDir -> ACVar (m, s) + | MInd (_) -> ACPnt (m, s) ) + | None -> ACLab (m, s) ) + +let carg_to_rarg (carg : carg) (env : env) : rarg = + let _, penv, lenv = env in + match carg with + | ACRef (m, n) -> RRef ((compile_mode m PB), n) + | ACLab (m, s) -> RLab ((compile_mode m PB), s) + | ACVar (m, s) -> + let l = (translate_lenv s lenv) in + RLab ((compile_mode m PB), l) + | ACPnt (m, s) -> + let p = (translate_penv s penv) in + let l = (translate_lenv s lenv) in + RLab ((compile_mode m p), l) + + +let replace_store (arg : arg) (env : env) : arg = + let aenv, _, _ = env in + match arg with + | AStore (s) -> (translate_aenv s aenv) + | _ -> arg + +let compile_arg (arg : arg) (env : env) : carg * rarg = + let arg' = (replace_store arg env) in + let darg = (arg_to_darg arg') in + let carg = (darg_to_carg darg env) in + let rarg = (carg_to_rarg carg env) in + (carg, rarg) + + +type opmod = +| TNum +| TRef +| TA +| TB + +let place_to_opmod (place : place) : opmod = + match place with + | PA -> TA + | PB -> TB + +let carg_to_opmod (carg : carg) (env : env) : opmod = + let aenv, penv, _ = env in + match carg with + | ACRef (m, _) | ACLab (m, _) -> + (match m with + | MImm -> TNum + | MIns | MDir | MInd (_) -> TRef ) + | ACVar (m, s) -> + (match m with + | MIns -> TRef + | MImm | MDir -> + let p = (translate_penv s penv) in + (place_to_opmod p) + | MInd (_) -> raise (CTError ("please report this bug, this error should not happen")) ) + | ACPnt (m, s) -> + (match m with + | MInd (_) -> + let arg = (translate_aenv s aenv) in + let darg = (arg_to_darg arg) in + (match darg with + | ADRef (_, _) -> + (match List.assoc_opt s penv with + | Some p -> (place_to_opmod p) + | None -> TB ) + | ADLab (_, s) -> + (match List.assoc_opt s penv with + | Some p -> (place_to_opmod p) + | None -> TB )) + | MIns | MImm | MDir -> raise (CTError ("please report this bug, this error should not happen")) ) + + +let compile_mod (carg1 : carg) (carg2 : carg) (def : rmod) (env : env) : rmod = + let mod1 = (carg_to_opmod carg1 env) in + let mod2 = (carg_to_opmod carg2 env) in + match mod1, mod2 with + | TNum, TA -> RA + | TNum, TB -> RAB + | TA, TNum -> RAB + | TB, TNum -> RB + | TA, TA -> RA + | TA, TB -> RAB + | TB, TA -> RBA + | TB, TB -> RB + | _, _ -> def diff --git a/examples/prog1.src b/examples/prog1.src index 8d15d48..f4b735f 100644 --- a/examples/prog1.src +++ b/examples/prog1.src @@ -1,6 +1,6 @@ -(let (x B hola) +(let (x dest) (seq - (MOV (Dir -1) (place x)) + (MOV (Dir -1) (store x)) (ADD 4 x) (JMP x) - (label hola) )) \ No newline at end of file + (label dest) )) \ No newline at end of file diff --git a/examples/prog2.src b/examples/prog2.src index 6f5bc06..29bfeba 100644 --- a/examples/prog2.src +++ b/examples/prog2.src @@ -1,14 +1,14 @@ -(let (x A 1) - (let (y B 2) - (let (a A 0) - (let (b B 0) +(let (x 1) + (let (y 2) + (let (a 0) + (let (b 0) (seq (JMP (Dir 2)) - (DAT (place x) (place y)) + (DAT (store x) (store y)) (ADD x y) (ADD y y) (ADD y x) (MOV x a) (MOV a b) - (DAT (place a) 0) - (DAT 0 (place b)) ))))) \ No newline at end of file + (DAT (store a) 0) + (DAT 0 (store b)) ))))) \ No newline at end of file diff --git a/examples/prog3.src b/examples/prog3.src index 010356e..78891fb 100644 --- a/examples/prog3.src +++ b/examples/prog3.src @@ -1,9 +1,9 @@ -(let (x A dest) - (let (y B (Ind x)) +(let (x dest) + (let (y (Ind x)) (seq (JMP (Dir 2)) - (DAT (place x) 0) - (MOV from (place y)) + (DAT (store x) 0) + (MOV from (store y)) (DAT 0 0) (label from) diff --git a/examples/prog4.src b/examples/prog4.src index 054d6cd..dc964b2 100644 --- a/examples/prog4.src +++ b/examples/prog4.src @@ -1,14 +1,14 @@ -(let (a A dest1) - (let (b B dest2) - (let (c B dest3) - (let (d B dest4) +(let (a dest1) + (let (b dest2) + (let (c dest3) + (let (d dest4) (seq (JMP (Ind d)) (label dest2) - (JMP (Ind a) (place c)) + (JMP (Ind a) (store c)) (label dest3) - (JMP (Ind b) (place d)) - (DAT (place a) (place b)) + (JMP (Ind b) (store d)) + (DAT (store a) (store b)) (label dest4) (JMP (Ind c)) (label dest1) diff --git a/examples/prog5.src b/examples/prog5.src index 779111f..57b66df 100644 --- a/examples/prog5.src +++ b/examples/prog5.src @@ -1,12 +1,12 @@ -(let (x B 2) - (let (rx A x) - (let (y A 3) - (let (ry B y) +(let (x 2) + (let (rx x) + (let (y 3) + (let (ry y) (seq (JMP start) - (SUB (place y) 0) - (SUB 0 (place x)) - (SUB (place rx) (place ry)) + (SUB (store y) 0) + (SUB 0 (store x)) + (SUB (store rx) (store ry)) (label start) (ADD 4 (Ind rx)) (ADD 5 (Ind ry)) ))))) \ No newline at end of file diff --git a/examples/prog6.src b/examples/prog6.src index 6e37f9a..a073bb7 100644 --- a/examples/prog6.src +++ b/examples/prog6.src @@ -1,5 +1,5 @@ -(let (x B (Dir 3)) +(let (x (Dir 3)) (repeat (seq - (MOV (Dir -1) (place x)) + (MOV (Dir -1) (store x)) (ADD 1 x) ))) \ No newline at end of file diff --git a/examples/prog7.src b/examples/prog7.src index 50b4d1d..388afb5 100644 --- a/examples/prog7.src +++ b/examples/prog7.src @@ -1,6 +1,6 @@ -(let (x B 10) +(let (x 10) (seq (JMP (Dir 2)) - (DAT 0 (place x)) + (DAT 0 (store x)) (do-while (DN 100) (MOV (Dir -1) (Inc x)) ))) \ No newline at end of file diff --git a/examples/prog8.src b/examples/prog8.src index abc3f04..7c6fcb8 100644 --- a/examples/prog8.src +++ b/examples/prog8.src @@ -1,8 +1,8 @@ -(let (x A 100) - (let (y B 10) +(let (x 100) + (let (y 10) (seq (JMP (Dir 2)) - (DAT (place x) (place y)) + (DAT (store x) (store y)) (while (LT y x) (seq (MOV x (Dec x)) ))))) \ No newline at end of file