2022-06-23 00:30:22 +02:00
|
|
|
In this tutorial I'll demonstrate C2V, a fully automatic C to V source code translator,
|
|
|
|
that has just been released.
|
2022-06-22 21:47:37 +02:00
|
|
|
|
2022-06-23 00:30:22 +02:00
|
|
|
We'll run some simple examples and then translate the entire original DOOM,
|
|
|
|
the legendary shooter, the first ever 3D game released in 1993 and open sourced in 1997.
|
2022-06-22 21:47:37 +02:00
|
|
|
|
|
|
|
A video version of this tutorial is available on YouTube:
|
|
|
|
|
|
|
|
https://www.youtube.com/watch?v=6oXrz3oRoEg
|
|
|
|
|
|
|
|
Let's start.
|
|
|
|
|
2022-06-23 01:45:14 +02:00
|
|
|
### A simple C program
|
|
|
|
|
2022-06-23 00:30:22 +02:00
|
|
|
First, We have a simple C program that prints prime numbers. It's a small program,
|
|
|
|
but it has some of the most widely used language features:
|
|
|
|
function definitions, control flow via for loops and if conditions, a libc function call,
|
|
|
|
and various expressions.
|
2022-06-22 21:47:37 +02:00
|
|
|
|
|
|
|
|
|
|
|
```c
|
|
|
|
#include <stdio.h>
|
2022-06-23 00:35:45 +02:00
|
|
|
#include <stdbool.h>
|
2022-06-22 21:47:37 +02:00
|
|
|
|
|
|
|
bool is_prime(int x) {
|
|
|
|
for (int i = 2; i <= x / 2; i++) {
|
|
|
|
if (x % i == 0) return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
int main() {
|
|
|
|
for (int i = 2; i < 100; i++) {
|
|
|
|
if (is_prime(i)) {
|
|
|
|
printf("%i\n", i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
We can translate the C file to V by simply running
|
|
|
|
|
|
|
|
```
|
|
|
|
v translate primes.c
|
|
|
|
```
|
|
|
|
|
|
|
|
It will create `primes.v` with the following contents:
|
|
|
|
|
|
|
|
|
|
|
|
```v
|
2022-06-23 00:39:39 +02:00
|
|
|
[translated]
|
|
|
|
module main
|
|
|
|
|
2022-06-22 21:47:37 +02:00
|
|
|
fn is_prime(x int) bool {
|
2022-06-23 00:30:22 +02:00
|
|
|
for i := 2; i <= x / 2; i++ {
|
|
|
|
if x % i == 0 {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true
|
2022-06-22 21:47:37 +02:00
|
|
|
}
|
|
|
|
|
2022-06-23 00:30:22 +02:00
|
|
|
fn main() {
|
|
|
|
for i := 2; i < 100; i++ {
|
|
|
|
if is_prime(i) {
|
|
|
|
C.printf(c'%i\n', i)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return
|
2022-06-22 21:47:37 +02:00
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
This is a valid V program, and running it will give us the same result.
|
|
|
|
|
2022-06-23 00:30:22 +02:00
|
|
|
This code is pretty similar to the original, you can notice one major difference.
|
|
|
|
C allows one statement code blocks without brackets, and V doesn't, so in the
|
|
|
|
translated code all blocks are explicitely marked with brackets.
|
2022-06-22 21:47:37 +02:00
|
|
|
|
|
|
|
C2V successfully converts C's bool type to V's bool.
|
|
|
|
|
2022-06-23 01:45:14 +02:00
|
|
|
### Wrapper generation
|
2022-06-22 21:47:37 +02:00
|
|
|
|
|
|
|
C2V also has a wrapper generation mode.
|
|
|
|
|
2022-06-23 00:30:22 +02:00
|
|
|
For many projects it would make more sense not to translate the entire C code base,
|
|
|
|
but just to generate V wrappers on top of C APIs.
|
2022-06-22 21:47:37 +02:00
|
|
|
|
2022-06-23 00:30:22 +02:00
|
|
|
For example, let's say you have some complicated crypto or networking library in C
|
|
|
|
with lots of functions and types, and you don't want to manually type their definitions in V.
|
2022-06-22 21:47:37 +02:00
|
|
|
|
|
|
|
For this C2V has a wrapper mode.
|
|
|
|
|
2022-06-23 00:30:22 +02:00
|
|
|
We have a simple file `usersapi.h`. By running `v translate -wrapper usersapi.c`
|
|
|
|
we get `usersapi.v` with just the function definitions. Function bodies are skipped.
|
2022-06-22 21:47:37 +02:00
|
|
|
|
|
|
|
```c
|
|
|
|
#include <stdio.h>
|
|
|
|
|
|
|
|
void usersapi_create_user(char* name, char* password, int age) {
|
|
|
|
// ...
|
|
|
|
puts("User created!");
|
|
|
|
}
|
|
|
|
|
|
|
|
int usersapi_get_number_of_users() {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
```
|
|
|
|
module usersapi
|
|
|
|
|
|
|
|
fn C.usersapi_create_user(name &i8, password &i8, age int)
|
|
|
|
|
|
|
|
pub fn f create_user(name &i8, password &i8, age int) {
|
|
|
|
C.usersapi_create_user(name, password, age)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn C.usersapi_get_number_of_users() int
|
|
|
|
|
|
|
|
pub fn f get_number_of_users() int {
|
|
|
|
return C.usersapi_get_number_of_users()
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
2022-06-23 00:30:22 +02:00
|
|
|
C doesn't have modules, so in most C libraries all functions start with the same prefix,
|
|
|
|
the name of the virtual module. C2V helpfully strips those prefixes and generates nice
|
|
|
|
clean wrapper functions that can be called with `usersapi.create_user()` instead of
|
|
|
|
`usersapi.usersapi_create_user()`
|
2022-06-22 21:47:37 +02:00
|
|
|
|
|
|
|
Perhaps in the future C2V will translate C strings (char pointers) to V strings as well.
|
|
|
|
|
2022-06-23 01:45:14 +02:00
|
|
|
### Translating DOOM
|
2022-06-22 21:47:37 +02:00
|
|
|
|
2022-06-23 00:30:22 +02:00
|
|
|
All right, this works, and it's nice, but these are very trivial examples.
|
|
|
|
Let's try something a lot more fun, like the DOOM game.
|
2022-06-22 21:47:37 +02:00
|
|
|
|
|
|
|
|
2022-06-23 00:30:22 +02:00
|
|
|
We think it's very important to have large functioning examples to demonstrate the power
|
|
|
|
of the tools but also to have something to test via CI to avoid regressions.
|
|
|
|
During V's release we had lots of various examples, now for C2V we have DOOM.
|
|
|
|
This is the primary test of C2V.
|
2022-06-22 21:47:37 +02:00
|
|
|
|
2022-06-23 00:30:22 +02:00
|
|
|
DOOM is a very complex project that uses some really tricky elements of the C language.
|
|
|
|
And making sure C2V can always handle DOOM not only serves as a good C2V test but also
|
|
|
|
as a test of the V compiler itself, since it can run entire DOOM in pure V.
|
2022-06-22 21:47:37 +02:00
|
|
|
|
|
|
|
First let's download the DOOM source and build it to make sure everything works.
|
2022-06-23 00:30:22 +02:00
|
|
|
The original 1997 open source release doesn't work nicely on modern OSs, so the community
|
|
|
|
has been working on different forks. I'll be using Chocolate Doom, it's one of the most
|
|
|
|
popular forks, and its goal is to be as close to the original as possible
|
|
|
|
(they even keep the original bugs).
|
2022-06-22 21:47:37 +02:00
|
|
|
|
2022-06-23 01:45:14 +02:00
|
|
|
|
|
|
|
```bash
|
|
|
|
git clone https://github.com/vlang/doom
|
|
|
|
```
|
|
|
|
|
2022-06-23 00:30:22 +02:00
|
|
|
The main difference in this fork is using SDL for cross platform rendering, so we'll need
|
|
|
|
to install SDL2 before we can build it.
|
2022-06-22 21:47:37 +02:00
|
|
|
|
|
|
|
A great thing about DOOM is that it has a very simple structure and is easy to build.
|
2022-06-23 00:30:22 +02:00
|
|
|
It's just a bunch of C files that are compiled into object files and are later linked
|
|
|
|
into a single executable.
|
2022-06-22 21:47:37 +02:00
|
|
|
|
|
|
|
To build the C version:
|
|
|
|
|
|
|
|
```bash
|
|
|
|
cd doom/chocolate-doom
|
|
|
|
cmake .
|
|
|
|
make chocolate-doom
|
|
|
|
```
|
|
|
|
|
2022-06-23 00:30:22 +02:00
|
|
|
We'll also need a freely distributed shareware doom1.wad with game resources and
|
|
|
|
the first free levels.
|
2022-06-22 21:47:37 +02:00
|
|
|
|
|
|
|
Now let's run the compiled game
|
|
|
|
|
|
|
|
```bash
|
|
|
|
./src/chocolate-doom -width 640
|
|
|
|
```
|
|
|
|
|
|
|
|
Great, as expected, it all works, now let's translate it to V and try to build that.
|
|
|
|
|
|
|
|
|
2022-06-23 00:30:22 +02:00
|
|
|
In the previous example we translated a single file, this time we'll translate
|
|
|
|
an entire directory.
|
2022-06-22 21:47:37 +02:00
|
|
|
|
|
|
|
```
|
|
|
|
v translate src/doom
|
|
|
|
```
|
|
|
|
|
|
|
|
This creates a new directory `src/doom/doom_v/`.
|
|
|
|
|
|
|
|
And we can use any name we want, there's a c2v configuration file.
|
|
|
|
|
2022-06-23 01:45:14 +02:00
|
|
|
`ll src/doom/doom_v`
|
2022-06-22 21:47:37 +02:00
|
|
|
|
|
|
|
It has a bunch of files translated from C to V.
|
|
|
|
|
2022-06-23 00:30:22 +02:00
|
|
|
We can open any file, for example g_game.c and verify that it is indeed DOOM code
|
|
|
|
translated from C to V. It's also been nicely formatted by vfmt.
|
2022-06-22 21:47:37 +02:00
|
|
|
|
|
|
|
Now let's build it.
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
cd src/doom
|
|
|
|
sh build_doom_v.sh
|
|
|
|
```
|
|
|
|
|
|
|
|
I'll explain what we have in the shell script a bit later.
|
|
|
|
|
|
|
|
But first let's run the translated code!
|
|
|
|
|
2022-06-23 00:30:22 +02:00
|
|
|
The script compiles into the `src/doom/doomv` binary. Everything works. We have the music,
|
|
|
|
we can watch the demo, go to the settings, start the game, walk around and shoot enemies.
|
2022-06-22 21:47:37 +02:00
|
|
|
|
2022-06-23 00:30:22 +02:00
|
|
|
There's a slight rendering bug at the bottom, but it can be cleaned up by pressing tab twice
|
|
|
|
and forcing a re-render.
|
2022-06-22 21:47:37 +02:00
|
|
|
|
|
|
|
There's a bug somewhere in C2V, it has just been released and needs some fixes.
|
|
|
|
|
|
|
|
|
|
|
|
Now let's look at those shell scripts and see how we built the V translation of DOOM.
|
|
|
|
|
|
|
|
C2V doesn't understand makefiles yet, so we have to do things manually.
|
|
|
|
|
|
|
|
In the future making such scripts won't be necessary.
|
|
|
|
|
|
|
|
|
2022-06-23 00:30:22 +02:00
|
|
|
Since doom uses C libraries like textscreen, pcsound, and opl, and we are only translating
|
|
|
|
doom itself, we need to build doom.o and then link it with the rest of object files.
|
2022-06-22 21:47:37 +02:00
|
|
|
|
|
|
|
`build_doom_v.sh`:
|
|
|
|
|
|
|
|
```bash
|
|
|
|
export DOOM=~/code/doom/chocolate-doom
|
|
|
|
|
|
|
|
v -o doom_v/doom.o -w -translated doom_v
|
|
|
|
|
|
|
|
|
|
|
|
cc -o doomv \
|
|
|
|
$DOOM/src/doom/doom_v/doom.o \
|
|
|
|
|
|
|
|
$DOOM/src/CMakeFiles/chocolate-doom.dir/*.o \
|
|
|
|
$DOOM/textscreen/CMakeFiles/textscreen.dir/*.o \
|
|
|
|
$DOOM/pcsound/CMakeFiles/pcsound.dir/*.o \
|
|
|
|
$DOOM/opl/CMakeFiles/opl.dir/*.o \
|
|
|
|
$(sdl2-config --libs) -lSDL2_mixer -lSDL2_net -lpng $LDFLAGS
|
|
|
|
```
|
|
|
|
|
|
|
|
The first line compiles all V files in `doom_v/` directory into a single doom.o object file.
|
2022-06-23 00:30:22 +02:00
|
|
|
`-translated` tells V that the code was translated from C, so the compiler is less strict.
|
|
|
|
For example it allows using `i++` as an expression (in V it always must be a statement).
|
|
|
|
In the future C2V will be able to translate such C expressions into valid V code,
|
|
|
|
and `-translated` will be removed entirely.
|
2022-06-22 21:47:37 +02:00
|
|
|
|
|
|
|
After generating doom.o, we can compile the entire project.
|
|
|
|
|
|
|
|
|
|
|
|
The second line links it with the rest of the object files.
|
|
|
|
|
2022-06-23 00:30:22 +02:00
|
|
|
Here we have the shared library used by other games in the chocolate doom project:
|
|
|
|
Hexen, Heretic, and Strife.
|
2022-06-22 21:47:37 +02:00
|
|
|
|
|
|
|
Then textscreen, pcsound, and opl.
|
|
|
|
|
|
|
|
|
2022-06-23 00:30:22 +02:00
|
|
|
In the near future we'll also translate all these C libraries DOOM depends on, and this
|
|
|
|
shell script and interacting with the C compiler won't be needed. The entire project will
|
|
|
|
be runnable with a simple `v translate src/doom && v .`
|
2022-06-22 21:47:37 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
One other thing I wanted to do is to show you that it actually is V code we are running.
|
|
|
|
|
2022-06-23 00:30:22 +02:00
|
|
|
Let's go to `p_enemy.v` and modify the `P_Move` function. We can add a simple println with
|
|
|
|
some V specific code, like a reversed V array.
|
2022-06-22 21:47:37 +02:00
|
|
|
|
|
|
|
|
|
|
|
`println([1, 2, 3, 4, 5].reverse())`
|
|
|
|
|
2022-06-23 00:30:22 +02:00
|
|
|
Let's rebuild it and run again. As expected, we're getting our array printed every time an
|
|
|
|
enemy moves.
|
2022-06-22 21:47:37 +02:00
|
|
|
|
2022-06-23 01:45:14 +02:00
|
|
|
### Replacing C with V file by file
|
2022-06-22 21:47:37 +02:00
|
|
|
|
2022-06-23 00:30:22 +02:00
|
|
|
We translated the entire project. But sometimes it may be better to do more gradual transition
|
|
|
|
from C to V. For example, if you want to move your project from C to V, you might prefer doing
|
|
|
|
it slowly and carefully, file by file. After each file migration you can run tests and see if
|
|
|
|
your application runs correctly.
|
2022-06-22 21:47:37 +02:00
|
|
|
|
2022-06-23 00:30:22 +02:00
|
|
|
You can do that with c2v. All you need to do is just translate a single file, generate an object
|
|
|
|
file, and replace your current C object file with the new V object file.
|
2022-06-22 21:47:37 +02:00
|
|
|
|
2022-06-23 00:30:22 +02:00
|
|
|
We can do this with DOOM too. In fact that's how we made the entire thing work. We were doing it
|
|
|
|
for every single file, one by one, fixing compilation errors, bugs in C2V, and runtime bugs.
|
2022-06-22 21:47:37 +02:00
|
|
|
|
|
|
|
```
|
|
|
|
c2v -o p_enemy.o p_enemy.v
|
|
|
|
cp p_enemy.o ...
|
|
|
|
make clean
|
|
|
|
make chocolate-doom
|
|
|
|
```
|
|
|
|
|
|
|
|
|
2022-06-23 00:30:22 +02:00
|
|
|
It is actually amazing how well it all works considering there have been two translation
|
|
|
|
processes. From C to V via C2V and then From V back to C again via the V compiler.
|
2022-06-22 21:47:37 +02:00
|
|
|
|
2022-06-23 00:30:22 +02:00
|
|
|
In fact we can actually look at the generated C code after all the translation and compare it
|
|
|
|
to the original. Very clean and barely changed:
|
2022-06-22 21:47:37 +02:00
|
|
|
|
2022-06-23 01:45:14 +02:00
|
|
|
![image](https://user-images.githubusercontent.com/687996/175178560-58e04248-bcb3-4e33-aebd-ad66bb94a887.png)
|
|
|
|
|
2022-06-22 21:47:37 +02:00
|
|
|
Both C2V and V generate clean human readable code.
|
|
|
|
|
|
|
|
|
2022-06-23 00:30:22 +02:00
|
|
|
The comments and C defines are missing right now, that's obviously something very nice to have,
|
|
|
|
so they will be translated as well.
|
2022-06-22 21:47:37 +02:00
|
|
|
|
|
|
|
|
2022-06-23 01:45:14 +02:00
|
|
|
### Conclusion
|
|
|
|
|
|
|
|
|
2022-06-23 00:30:22 +02:00
|
|
|
C2V has just been released on June 22th 2022, so it's going to mature with the help of
|
|
|
|
the community. With bug fixes and new C features supported, it should support the
|
|
|
|
entire C standard soon.
|
|
|
|
Luckily C is a relatively small language. Any kind of software written in C will be
|
|
|
|
translatable to V fully automatically.
|
2022-06-22 21:47:37 +02:00
|
|
|
|
|
|
|
I will also be publishing the same demo with Sqlite and Quake translation.
|
|
|
|
|
2022-06-23 00:30:22 +02:00
|
|
|
It's a huge milestone for V and gives V developers access to huge amounts of software
|
|
|
|
written in C.
|
|
|
|
|
2022-06-22 21:47:37 +02:00
|
|
|
We're very excited about this release.
|