a language :3
  • C 98.5%
  • JavaScript 1.3%
  • Lua 0.1%
  • Scheme 0.1%
Find a file
2025-10-29 22:16:47 +01:00
src function pointers 2025-10-29 22:16:47 +01:00
std fix generics once again 2025-10-16 22:12:04 +02:00
tests make a better cstd 2025-10-12 17:40:53 +02:00
tree-sitter-paw implement include 2025-07-17 21:39:17 +02:00
.gitignore thingies 2025-10-15 00:18:29 +02:00
cmd.txt allow including .so files 2025-08-24 22:22:28 +02:00
COPYING add license 2025-08-24 22:31:16 +02:00
highlights.scm implement include 2025-07-17 21:39:17 +02:00
main.c stuff 2025-10-27 20:25:50 +01:00
main.h work on llvm ir 2025-10-12 12:36:05 +02:00
main.paw function pointers 2025-10-29 22:16:47 +01:00
nob.c fix varargs and properly scope for loops 2025-10-16 00:05:37 +02:00
nob.h :3 2025-05-25 22:04:51 +02:00
nvim.lua update treesitter 2025-07-13 22:23:46 +02:00
README.md update readme 2025-09-03 18:25:40 +02:00
test.c function pointers 2025-10-29 22:16:47 +01:00
test.h function pointers 2025-10-29 22:16:47 +01:00
test.so function pointers 2025-10-29 22:16:47 +01:00

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*);