စံကား

02The compiler

Commands

Each zakac command, what it reads, what it writes, and each of its options.

zakac has one command for each stage of the compiler, and one to run a program. These share one shape:

$ zakac <command> [options] [INPUT]

INPUT is a source file, or - for standard input (the default when it is left out). A stage command reads the source, runs the stages up to its own, and writes what that stage made, the artifact. The run command goes to the end and runs the program. Stages tells what each artifact holds.

CommandRuns up toWritesRaw / text file
segmentthe word segmenterthe pieces of the text.zkseg / .zksegt
lexthe lexerthe tokens.zktok / .zktokt
parsethe parserthe tree.zkast / .zkastt
semantic-checknames, kinds and natural wordingthe tree and the kind of each expression.zksem / .zksemt
irgenthe IR builder and constant foldingthe IR.zkir / .zkirt
borrow-checkthe ownership checkthe IR and a digest of each function.zkbck / .zkbckt
codegenthe code generatorthe ops for the VM.zkbc / .zkbct
runthe VMnothing: the program runs

Each stage also builds as a program of its own (zakac_lexer, zakac_parser, zakac_semcheck, zakac_ir_builder, zakac_borrow_checker, zakac_codegen, zakac_burmese_segmenter, zaka_vm) with the same options as its command.

A small session

Here is a program that adds up two numbers and prints the sum:

စံကား

Expected output

၃

Saved as sum.zaka, it runs with one command. The second command stops after the code generator and prints a listing of the ops, not an artifact:

$ zakac run sum.zaka
၃
$ zakac codegen --asm sum.zaka
; zakac asm listing v1, platform 64 bits

fn f0 main (params 0)
     0  push 3:isize
     1  print
     2  ret

The sum was worked out at compile time, so the VM just pushes ၃. Stages follows this sentence through each stage.

Input and output

All the stage commands take these three:

OptionMeaning
[INPUT]the source file, or - for standard input
-o, --output <FILE>where to write the artifact: a file, a folder, or - for standard output (the default)
--output-format <FORMAT>text or raw; when left out, the format follows the output

The format is picked in this order:

  1. --output-format, when it is given.
  2. Standard output gets text, a readable YAML file.
  3. A file name that ends in the text extension of the stage (.zkirt) gets text; one that ends in the raw extension (.zkir) gets raw.
  4. A folder (an existing one, or a path ending in /) gets a file named after the input, with the raw extension, unless --output-format text is given.
  5. Each other file name gets raw.

The raw form is compact and quick to load; the text form is for reading and for diffs. Both hold the same data.

$ zakac codegen age.zaka -o out/age.zkbc
$ zakac codegen age.zaka -o out/age.zkbct
$ zakac codegen age.zaka -o out/
$ ls out
age.zkbc  age.zkbct

A stage writes its artifact even when it finds errors; the errors are in the artifact too, and the exit code is 1. See Diagnostics.

What run reads

run takes a source file, or a codegen artifact in either form. It tells them apart from the first octets of the input, not from the file name:

$ zakac codegen age.zaka -o age.zkbc
$ zakac run age.zkbc
၂၀
$ zakac run age.zaka
၂၀

The other artifacts are not programs: run reads one of them as source and reports errors at once. Running an artifact skips the compile, and the checks that were made when it was written are the checks it carries.

Options of the compile stages

These options belong to semantic-check and the stages after it, and to run.

OptionDefaultMeaning
--platform-bits <BITS>64the bit count of the platform-sized integer kinds, ကိန်းပြည့် and လက္ခဏာမဲ့ ကိန်းပြည့်: 8, 16, 32, 64 or 128
--strict-particlesoffreport the natural wording warnings (ZK05) as errors

These belong to irgen and the stages after it, and to run:

OptionDefaultMeaning
--fold <LIST>allthe foldings to run: all, none, or a comma list of exprs, consts and branches
--no-rawoffrefuse each raw address kind (လိပ်စာအကြမ်း) with ZK0717

--fold changes the IR, not what the program does. exprs replaces an operation on constants with its value, consts replaces a read of a ကိန်းသေ that was set to a constant, and branches keeps the branch of an if that is taken and drops a loop whose condition is false. Errors in constant operations, such as a division with the constant zero as divisor (ZK0602), are reported with --fold none too.

$ zakac irgen --fold none sum.zaka

With --fold none the IR of sum.zaka keeps add with 1 and 2 as its two sides; without it, the IR holds the constant 3.

These belong to borrow-check and the stages after it, and to run. These are explained in Pointer checks.

OptionDefaultMeaning
--mem-check <MODE>errorhow the ownership check reports: error, warn or off
--mem-strictoffalso report the accesses that the check cannot prove safe

segment

$ zakac segment [--coalesce-non-burmese] [INPUT]

Cuts the text into the pieces the lexer starts from: Burmese words, digits, and the other characters. --coalesce-non-burmese joins a run of characters that are not Burmese into one piece.

$ echo 'abc ၁' | zakac segment --coalesce-non-burmese
- kind: non_burmese_char
  lexeme: "abc "
- kind: burmese_digit
  lexeme: ၁
- kind: non_burmese_char
  lexeme: "\n"

(The span of each piece is left out here.) See Finding words in Burmese.

lex, parse, semantic-check, irgen, borrow-check

These five take the input and output options and the compile options listed above for their stage (lex and parse have none), and nothing else. lex segments the text first, parse lexes first, and so on, so a command never needs the artifact of the stage before it: it starts from the source.

codegen

$ zakac codegen [--asm] [options] [INPUT]

Besides all the compile options, codegen takes --asm, which writes a listing of the ops in place of the artifact. The listing is plain text, and it goes to the file in -o or to standard output. Here is the listing of a program with a variable (age.zaka), for a 32-bit platform:

စံကား

Expected output

၂၀
$ zakac codegen --platform-bits 32 --asm age.zaka
; zakac asm listing v1, platform 32 bits
globals
  g0 အသက် isize

fn f0 main (params 0)
     0  global_addr g0       ; အသက်
     1  push 20:isize
     2  store isize
     3  global_addr g0       ; အသက်
     4  load isize
     5  print
     6  ret

The ops are listed in Instructions.

run

$ zakac run [options] [INPUT]

run takes the compile options and the two checks above. It adds limits on the VM, which stop a program that goes too far, and two options to look at the use of the heap and the stack:

OptionDefaultMeaning
--max-depth <CALLS>4096the most nested calls before the program stops
--max-steps <OPS>no limitthe most ops to run before the program stops
--max-heap <OCTETS>268435456the most live heap octets before an allocation fails
--max-stack <OCTETS>1048576the most octets of the stack region, where the locals of each call live
--frames <MODE>checkedchecked gives each variable a block of its own; packed puts the locals of a call next to each other, as C does
--leaksofflist the heap blocks left unfreed when the program ends

A program that passes a limit stops with a message from the VM, listed in When a program stops. --frames is explained in Checked and packed blocks.

--leaks writes one line to standard error for each block that was never given back:

$ zakac run --mem-check warn --leaks leak.zaka
၇
leak: block at 0x101020, 16 octets

Exit codes

CodeMeaning
0the stage finished, with warnings at most; for run, the program ended
1an error in the program, or the VM stopped the program

Messages go to standard error and the artifact or the program output to standard output, so zakac codegen age.zaka -o - can be piped while the messages still show.