- C 98.5%
- JavaScript 1.3%
- Lua 0.1%
- Scheme 0.1%
| src | ||
| std | ||
| tests | ||
| tree-sitter-paw | ||
| .gitignore | ||
| cmd.txt | ||
| COPYING | ||
| highlights.scm | ||
| main.c | ||
| main.h | ||
| main.paw | ||
| nob.c | ||
| nob.h | ||
| nvim.lua | ||
| README.md | ||
| test.c | ||
| test.h | ||
| test.so | ||
a simple programming language
(Note: this file is out of date, proper documentation will be (probably) provided once the syntax is a little more stable)
parsed into an ir and then run in a very simple vm (without registers).
take a look at main.paw :3
compiling & running
git clone https://kitsunes.dev/lunacat/pawlang.git
cd pawlang
cc -o nob nob.c
./nob
./main main.paw
design
Programs are first compiled into an intermediate representation and then executed on a simple stack based virtual machine.
types
Each type consists of three parts: A base type, which can either be 'int', 'char', 'bool', 'float', 'long', 'void', a compound type, a named alias or a single uppercase letter generic placeholder. Compound types are either structs, enums or slices. Structs behave like in c: struct { type ident; } Enums behave like in rust: enum { ident, ident (type), } Slices are an aliases for structs: [int] is equivalent to struct { elm: int*, len: int }
The second part are generic definitions. These are placed inside '<' '>' and follow the pattern of ident ':' type, for example: vec<T:int>
The third part is the number of indirections, indicated by repeated use '*', like in c.
Named type aliases can be defined using the pattern type ident anywhere in the code.
Types can be incomplete: They can contain generics but not the definition for them. For example, a dynamic array type might look like this:
struct { T* items; int count; int capacity; } Array
Incomplete types are severly limited: As their size isn't known they can't be allocated on the stack and only passed as function parameters through indirection. An incomplete type may later be completed, eg Array<T:int>
function definitions
type ident '(' (type ident)* (seperated by ',') ')' type ident '<' generic-ident* (seperated by ',') '>' '(' (type ident)* (seperated by ',') ')'
expressions
An expression is a sequence of sub-expressions seperated by ';', where the result of the expression is the result of the last subexpression. Each expression also creates a new scope in which variables can be declared, which will not exist outside of that scope.
A sub-expression can be one of the following:
nesting
'{' expression '}'
if
'if' '(' condition-expression ')' '{' truthy-expression '}' An if-expression. truthy-expression only gets executed when condition-expression evaluates to true. If-expressions always return void. Note that because sub-expressions must be seperated by ';', an if-expression also has to be followed by ';'. 'if' '(' condition-expression ')' '{' truthy-expression '}' 'else' '{' falsy-expression '}' Contrary to a single if-expression, an if-else expression can return a value, although the type must be the same in the truthy as in the falsy expression.
while
'while' '(' condition-expression ')' '{' body-expression '}' Always returns void.
references
'&' body-subexpression The subexpression will evaluate to a pointer to the body-subexpression. If body-subexpression is an operation or a literal, it is guaranted to exist until the end of the current scope. '*' body-subexpression The subexpression will evaluate to whatever is located at body-subexpression. The type of body-subexpression must have at least one level of indirection and be complete.
asign
lhs-subexpression '=' rhs-subexpression Lhs-subexpression must be either a dereference of a pointer or a lvalue.
declare
type ident '=' subexpression
lvalue
ident lvalue '.' lvalue lvalue '->' lvalue
literals
Structs: type '{' (ident '=' subexpression)* (seperated by ',') '}'
Enums: type:ident type:ident(subexpression)
Slices: type '{' items-subexpression ',' count-subexpression '}'
'true' 'false' 'null' '"' any c string '"' any number
calls
ident '(' subexpression* (seperated by ',') ')' ident '<' generic ':' type '>' '(' subexpression* (seperated by ',') ')' The sizes of generics are passed as hidden arguments to the function.
the following std functions are currently implemented:
char* read_file(char*);
void* malloc(long);
void* realloc(void*, long);
void memcpy(void*, void*, long);
int memcmp(void*, void*, long);
void free(void*);
void printf(char*, ...);
void printfn(char*, ...); // printf but adds a newline
char* fgets(char*, int);
long strtol(char*);