Building Johann Programs
First, you'll need an arm64-apple-darwin machine (aka Apple Silicon, with macOS) to run on, with the command-line developer/Xcode tools installed.
If your source is in program.jn, first compile it with ./bin/jnc < program.jn > program.s. Next, assemble and link with gcc program.s ./lib/jstdlib.o -o program. Now you can execute it: ./program. If you have multiple source files, compile and assemble them separately (with -c), and then link the object files into the final binary. The standard library (./lib/jstdlib.o) is pre-assembled and only needs to be linked.
Warning
Compiled Johann programs are version specific. When changing versions of the compiler, you need to recompile all your sources. The standard library is always compiled with the compiler version it is bundled with. If you have third-party dependencies, their sources will need to be recompiled as well.
Implicit is a command shell that understands redirection. Compilation is always "read from STDIN and write to STDOUT" - Johann doesn't know about files!
The various Makefile may provide additional inspiration. It's worth mentioning that my make skills are commensurate with my skill coding assembly.
"Header" Files
If you compile with jnc --header, the compiler will emit a "header" file with the input's pub declarations instead of assembly codes. On modern platforms, this linkage info is implicit and used automatically. In C/C++, you (meaning your IDE) write the header file manually and explicitly use it. With Johann, the info is implicit, but you have to explicitly materialize and use it.
Why is it useful? jnc can give you better errors with header info from libraries you intend to link against. Consider a simple add library and a (buggy) program that uses it:
% cat add.jn
# I add two integers. An extremely useful library routine.
pub fn add(int a, int b) {
return a + b;
}
% cat main.jn
pub fn main() {
return add(2); # BUG! add takes two params
}
Compiling main.jn by itself succeeds:
% ./bin/jnc < main.jn > main.s
If you assemble and link, the program will even run! Which is bad. If you create and use a header file (the extra < add.jnh in the second command below), compilation will fail and tell you why:
% ./bin/jnc --header < add.jn > add.jnh
% ./bin/jnc < add.jnh < main.jn > main.s
ERROR(101): Function call expects 2 params at line 2, col 12
Compilation error
The double redirection of STDIN is a zsh feature; if you switched your shell, you may need to tweak the command.
Strict Mode
If you pass --strict to jnc, it will require complete header info. It doesn't have to be a header file, you can manually declare everything in your source, but everything needs a declaration. Recompiling the above program with strict mode (and no header):
% ./bin/jnc --strict < main.jn > main.s
ERROR(100): Unknown function 'add' at line 2, col 12
Compilation error
Debugging Johann Programs
Tip
It's generally better to simply not write bugs to begin with.
Johann provides no debugging support. Start adding printf everywhere to narrow it down. Don't discount defects in jnc and/or jstdlib themselves!
You might be able to use various third-party tools (e.g., GDB) to help, though jnc doesn't emit debug symbols...
Compiler Errors
A few errors are explicitly caught by the compiler, with the exit status they yield:
14- Unassignable lvalue17- Unrecognized character20- Missing token22- Duplicate declaration23- Unknown symbol26- Bad token/value27- Bad statement28- Bad expression29- Bad operator35- Empty struct36- Duplicate struct member37- Certain types of invalid block nesting47- Unrecognized format conversion spec forprintf75- Unknown struct member76- Member or index off non-ID-pointer77- Multibyte character98- Certain alloc/free errors99- Failed to get memory from the OS (a panic, unlike C)100- Unknown function101- Incorrect number of function params
Most errors are not caught and result in compiler crashes, invalid assembly code, or code which will crash when executed.