HumC is a programming language that uses indentation to define blocks of code; code written in this language is translated into standard C. Its goal is to make C-style programming clearer and simpler through the use of indentation and concise constructs, such as let, fn, and print.
This project is a full-fledged compiler for the C11 standard, designed to be used via the command line. The compiler itself is written in C.
HumC translates .humc source files into .c files. The generated C code can then be compiled by GCC, Clang, or another compatible C compiler.
program.humc -> program.c -> cc/gcc/clang -> executable
For example, this HumC:
fn main
let a = 10
let b = 32
print a + b
return 0
is translated to C similar to:
#include <stdio.h>
int main(void) {
int a = 10;
int b = 32;
printf("%lld\n", (long long)(a + b));
return 0;
}The exact generated code can change as the compiler evolves. C preprocessor lines and many ordinary C statement expressions are currently passed through, but HumC does not yet parse or validate the entire C language.
make
make testmake installhumc examples/basic.humcBy default, the generated .c file is written alongside the source. Specify an output path with -o:
humc examples/basic.humc -o build/basic.chumc build examples/supported.humc
humc build examples -o build/generatedcheck runs the HumC translation/validation path without writing generated .c files. It is not a substitute for compiling the generated C with GCC or Clang.
humc check examples/basic.humc
humc check exampleshumc run examples/basic.humcTo choose a C compiler:
CC=gcc humc run examples/basic.humcProgram arguments can be placed after the source filename:
humc run program.humc arg1 arg2humc fmt examples/basic.humc
humc fmt exampleshumc watch examples/supported.humchumc init myapp
cd myappmake
./appprint "Hello from HumC!"
let name = "Bob"
let age = 25
let city = "Warsaw"
print name
print age
print cityBasic variable types are inferred from supported literal initializers. Explicit C type annotations can be used where inference is not sufficient:
let message: const char * = "hello"
let value: unsigned long = 42All let declarations in the supported safe subset must have an initializer.
let a = 10
let b = 5
let sum = a + b
let difference = a - b
let product = a * b
let division = a / b
print sum
print difference
print product
print divisionlet score = 75
if score >= 90
print "Excellent"
elif score >= 70
print "Good"
elif score >= 50
print "Pass"
else
print "Fail"let i = 0
while i < 5
print i
i++Numeric ranges use an exclusive upper bound:
for i in 0..3
print iArray iteration is supported for named numeric arrays initialized with literals:
let numbers = [1, 2, 3, 4, 5]
for number in numbers
if number == 3
continue
print numberfn greet(name)
print "Hello " + name
greet("Bob")
greet("Alex")A function can return a value:
fn add(int a, int b) -> int
return a + b
let result = add(10, 20)
print resultlet value = 2
switch value
case 1
print "one"
break
case 2
print "two"
break
default
print "other"Implemented for the currently supported subset:
- Accesses to known local fixed arrays are generated with runtime bounds checks for supported read/write forms.
- Negative and out-of-range indices in these supported forms produce a runtime diagnostic.
letvariables in the safe subset require initializers.- Duplicate declarations in one lexical scope are rejected.
- Variables in closed blocks are not intended to leak into sibling scopes.
- C keywords are rejected as HumC identifiers.
- Arena allocation checks size arithmetic before allocating chunks.
Temporary compiler data is allocated from an arena and released together when a compilation finishes. The generated C output is returned as a separate allocation. The arena simplifies cleanup for temporary data, but it does not automatically manage memory used by programs generated from HumC.