Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
2d7a484
tests: test cases cleanup, fully migrated to lv_assert
Raumberg Feb 15, 2026
81cc1ab
std: added collections.lv, added new functions in hashset.h, vector.h
Raumberg Feb 15, 2026
ed51b3f
feat: 'extend' keyword, basic Lavina type methods extensions
Raumberg Feb 15, 2026
68636eb
docs: 'extend' keyword functionality in README and DOCUMENTATION
Raumberg Feb 15, 2026
eac5e0c
qol: added multilining support, referenced to PEP documentation (Pyth…
Raumberg Feb 15, 2026
99643c7
src: small refactoring using new multilining support
Raumberg Feb 15, 2026
8419622
examples: delete wrong class example
Raumberg Feb 15, 2026
b2386f2
std: adding std::net, udp/tcp
Raumberg Feb 17, 2026
27c0307
std: threading. omg. plus a huge test
Raumberg Feb 17, 2026
736e6cb
std, src: 'bytes' type, bytes operations, bytes extension
Raumberg Feb 17, 2026
8a601c8
docs: updated documentation with latest changes (std thread, net, bytes)
Raumberg Feb 17, 2026
675b6ea
examples: fully functional VLESS protocol
Raumberg Feb 17, 2026
82f5410
src: improved casting, improved memory management in vless.lv
Raumberg Feb 17, 2026
67532ac
src: added trailing comma support
Raumberg Feb 17, 2026
6b7e605
src: default arguments, method chaining
Raumberg Feb 17, 2026
fa7d5c6
examples: enchanced examples
Raumberg Feb 17, 2026
cdb1d2b
docs: updated documentation, referenced type casting, multiline expre…
Raumberg Feb 17, 2026
4ca3422
src: refactoring, new type_utils.lv file
Raumberg Feb 17, 2026
317971d
fix: windows linking with ws2_32 lib error fix
Raumberg Feb 17, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
683 changes: 646 additions & 37 deletions DOCUMENTATION.md

Large diffs are not rendered by default.

61 changes: 41 additions & 20 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
SHELL := /bin/bash
BOOTSTRAP_SRC = src/scanner.lv src/parser.lv src/checker.lv src/codegen.lv src/main.lv
BOOTSTRAP_SRC = src/scanner.lv src/ast.lv src/type_utils.lv src/parser.lv src/checker.lv src/codegen.lv src/main.lv
LATEST_STAGE = stages/stage-latest.cpp

# Windows (MSYS2) needs ws2_32 for Winsock
ifdef MSYSTEM
LDFLAGS += -lws2_32
endif

# ── Bootstrap from saved stage ───────────────────────────────

bootstrap: $(BOOTSTRAP_SRC)
@echo "Bootstrapping from $(LATEST_STAGE)"
cp runtime/lavina.h /tmp/lavina.h
rm -rf /tmp/liblavina && cp -r runtime/liblavina /tmp/liblavina
g++ -std=c++23 -I/tmp -o /tmp/lavina_prev $(LATEST_STAGE)
g++ -std=c++23 -I/tmp -o /tmp/lavina_prev $(LATEST_STAGE) $(LDFLAGS)
/tmp/lavina_prev --emit-cpp src/main.lv > /tmp/lavina_next.cpp
g++ -std=c++23 -I/tmp -o /tmp/lavina_next /tmp/lavina_next.cpp
g++ -std=c++23 -I/tmp -o /tmp/lavina_next /tmp/lavina_next.cpp $(LDFLAGS)
/tmp/lavina_next --emit-cpp src/main.lv > /tmp/lavina_verify.cpp
@diff -q /tmp/lavina_next.cpp /tmp/lavina_verify.cpp && echo "Fixed point OK" || (echo "MISMATCH" && exit 1)
@echo "Bootstrap successful."
Expand All @@ -31,15 +36,15 @@ evolve: $(BOOTSTRAP_SRC)
@echo "Bootstrapping from $(LATEST_STAGE)"
cp runtime/lavina.h /tmp/lavina.h
rm -rf /tmp/liblavina && cp -r runtime/liblavina /tmp/liblavina
g++ -std=c++23 -I/tmp -o /tmp/lavina_prev $(LATEST_STAGE)
g++ -std=c++23 -I/tmp -o /tmp/lavina_prev $(LATEST_STAGE) $(LDFLAGS)
/tmp/lavina_prev --emit-cpp src/main.lv > /tmp/lavina_next.cpp
g++ -std=c++23 -I/tmp -o /tmp/lavina_next /tmp/lavina_next.cpp
g++ -std=c++23 -I/tmp -o /tmp/lavina_next /tmp/lavina_next.cpp $(LDFLAGS)
/tmp/lavina_next --emit-cpp src/main.lv > /tmp/lavina_verify.cpp
@if diff -q /tmp/lavina_next.cpp /tmp/lavina_verify.cpp > /dev/null 2>&1; then \
echo "Fixed point OK — no evolution needed (use 'make bootstrap')."; \
else \
echo "Codegen changed — verifying new output is a fixed point..."; \
g++ -std=c++23 -I/tmp -o /tmp/lavina_verify /tmp/lavina_verify.cpp; \
g++ -std=c++23 -I/tmp -o /tmp/lavina_verify /tmp/lavina_verify.cpp $(LDFLAGS); \
/tmp/lavina_verify --emit-cpp src/main.lv > /tmp/lavina_verify2.cpp; \
if diff -q /tmp/lavina_verify.cpp /tmp/lavina_verify2.cpp > /dev/null 2>&1; then \
echo "New fixed point OK."; \
Expand All @@ -56,7 +61,7 @@ evolve: $(BOOTSTRAP_SRC)

# ── Run test suite ───────────────────────────────────────────

SKIP_WINDOWS_TESTS = test_std_fs test_std_os test_stdlib
SKIP_WINDOWS_TESTS = test_std_fs test_std_os test_std_net test_std_thread test_stdlib

test:
@if [ ! -f /tmp/lavina_next ]; then echo "Run 'make bootstrap' first"; exit 1; fi
Expand All @@ -77,17 +82,33 @@ test:
continue; \
fi; \
fi; \
/tmp/lavina_next compile $$f 2>/dev/null && \
$$dir/$$name 2>/dev/null; \
if [ $$? -eq 0 ]; then \
echo " PASS $$name"; \
passed=$$((passed + 1)); \
else \
echo " FAIL $$name"; \
failed=$$((failed + 1)); \
errors="$$errors $$name"; \
fi; \
rm -f $$dir/$$name $$dir/$$name.cpp; \
case "$$name" in \
test_fail_*) \
/tmp/lavina_next compile $$f 2>/dev/null; \
if [ $$? -ne 0 ]; then \
echo " PASS $$name (expected compile failure)"; \
passed=$$((passed + 1)); \
else \
echo " FAIL $$name (should not compile)"; \
failed=$$((failed + 1)); \
errors="$$errors $$name"; \
rm -f $$dir/$$name $$dir/$$name.cpp; \
fi \
;; \
*) \
/tmp/lavina_next compile $$f 2>/dev/null && \
$$dir/$$name 2>/dev/null; \
if [ $$? -eq 0 ]; then \
echo " PASS $$name"; \
passed=$$((passed + 1)); \
else \
echo " FAIL $$name"; \
failed=$$((failed + 1)); \
errors="$$errors $$name"; \
fi; \
rm -f $$dir/$$name $$dir/$$name.cpp; \
;; \
esac; \
done; \
echo ""; \
if [ $$skipped -gt 0 ]; then \
Expand All @@ -104,7 +125,7 @@ build:
@mkdir -p build
cp runtime/lavina.h /tmp/lavina.h
rm -rf /tmp/liblavina && cp -r runtime/liblavina /tmp/liblavina
g++ -std=c++23 -O2 -I/tmp -o build/lavina $(LATEST_STAGE)
g++ -std=c++23 -O2 -I/tmp -o build/lavina $(LATEST_STAGE) $(LDFLAGS)
@echo "Built build/lavina"

# ── Build lvpkg package manager ──────────────────────────────
Expand All @@ -115,7 +136,7 @@ lvpkg:
cp runtime/lavina.h /tmp/lavina.h
rm -rf /tmp/liblavina && cp -r runtime/liblavina /tmp/liblavina
/tmp/lavina_next --emit-cpp lvpkg/lvpkg.lv > /tmp/lvpkg.cpp
g++ -std=c++23 -O2 -I/tmp -o build/lvpkg /tmp/lvpkg.cpp
g++ -std=c++23 -O2 -I/tmp -o build/lvpkg /tmp/lvpkg.cpp $(LDFLAGS)
@echo "Built build/lvpkg"

# ── Install ──────────────────────────────────────────────────
Expand Down
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ sudo cp -r lib/* /usr/local/lib/
git clone https://github.com/Raumberg/lavina.git
cd lavina
make bootstrap # compile compiler from saved C++ snapshot
make test # run the test suite (27 tests)
make test # run the test suite (37 tests)
make build # optimized binary → build/
make install # install to /usr/local/ (or: make install PREFIX=~/.local)
```
Expand Down Expand Up @@ -114,6 +114,7 @@ Lavina ships with a standard library:
import std::fs
import std::os
import std::math
import std::collections

void fn main():
// File I/O
Expand All @@ -126,6 +127,11 @@ void fn main():
// Math
print("pi = ${math::PI}")
print("sqrt(2) = ${math::sqrt(2.0)}")

// Collections — dot-notation via extend
vector[int] nums = [1, 2, 3, 4, 5]
auto doubled = nums.map((int x) => x * 2)
auto evens = nums.filter((int x) => x % 2 == 0)
```

## Features
Expand All @@ -135,6 +141,7 @@ void fn main():
- **Generics** — `[T, U]` on functions, structs, and enums
- **Enums / sum types** with named fields and pattern matching
- **Enum methods** — methods defined inside enum bodies
- **Extension methods** — `extend vector:` adds dot-notation methods to built-in types
- **Operator overloading** — `Type operator + (params):`
- **References and ownership** — `ref`, `ref!`, `own`
- **Block lambdas** — `(params):` with indented body
Expand Down
8 changes: 5 additions & 3 deletions editors/nvim/syntax/lavina.vim
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ syn region lavinaInterp start='\${' end='}' contained contains=TOP

" Numbers
syn match lavinaFloat "\<\d\+\.\d\+\>"
syn match lavinaHex "\<0[xX][0-9a-fA-F]\+\>"
syn match lavinaInt "\<\d\+\>"

" Control flow
Expand All @@ -27,10 +28,10 @@ syn keyword lavinaLogical and or not
syn keyword lavinaKeyword fn constructor public private static inline const let comptime namespace ref own cpp extern link type operator

" Storage
syn keyword lavinaStorage class struct enum
syn keyword lavinaStorage class struct enum extend

" Types
syn keyword lavinaType int float string bool void auto dynamic vector hashmap hashset null int8 int16 int32 int64 float32 float64 usize cstring ptr
syn keyword lavinaType int float string bool void auto dynamic vector hashmap hashset bytes null int8 int16 int32 int64 float32 float64 usize cstring ptr

" Constants
syn keyword lavinaConstant true false null
Expand All @@ -39,7 +40,7 @@ syn keyword lavinaThis this
" Functions (name after fn)
syn match lavinaFuncDef "\<fn\s\+\zs\w\+"
" Class/struct/enum name in definitions
syn match lavinaTypeDef "\<\(class\|struct\|enum\)\s\+\zs\w\+"
syn match lavinaTypeDef "\<\(class\|struct\|enum\|extend\)\s\+\zs\w\+"
" User types: PascalCase identifiers (starts with uppercase)
syn match lavinaUserType "\<[A-Z][a-zA-Z0-9_]*\>"

Expand All @@ -55,6 +56,7 @@ hi def link lavinaString String
hi def link lavinaEscape SpecialChar
hi def link lavinaInterp Special
hi def link lavinaFloat Float
hi def link lavinaHex Number
hi def link lavinaInt Number
hi def link lavinaControl Conditional
hi def link lavinaImport Include
Expand Down
10 changes: 7 additions & 3 deletions editors/vscode/syntaxes/lavina.tmLanguage.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@
"name": "constant.numeric.float.lavina",
"match": "\\b\\d+\\.\\d+\\b"
},
{
"name": "constant.numeric.hex.lavina",
"match": "\\b0[xX][0-9a-fA-F]+\\b"
},
{
"name": "constant.numeric.integer.lavina",
"match": "\\b\\d+\\b"
Expand All @@ -74,11 +78,11 @@
]
},
"storage": {
"match": "\\b(class|struct|enum)\\b",
"match": "\\b(class|struct|enum|extend)\\b",
"name": "storage.type.lavina"
},
"types": {
"match": "\\b(int|float|string|bool|void|auto|dynamic|vector|hashmap|hashset|null|int8|int16|int32|int64|float32|float64|usize|cstring|ptr)\\b",
"match": "\\b(int|float|string|bool|void|auto|dynamic|vector|hashmap|hashset|bytes|null|int8|int16|int32|int64|float32|float64|usize|cstring|ptr)\\b",
"name": "support.type.lavina"
},
"constants": {
Expand Down Expand Up @@ -129,7 +133,7 @@
]
},
"classes": {
"match": "\\b(class|struct)\\s+([A-Z]\\w*)",
"match": "\\b(class|struct|extend)\\s+([A-Za-z]\\w*)",
"captures": {
"1": { "name": "storage.type.lavina" },
"2": { "name": "entity.name.type.class.lavina" }
Expand Down
16 changes: 0 additions & 16 deletions examples/class.lv

This file was deleted.

19 changes: 11 additions & 8 deletions examples/complex/lvg/lvg.lv
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
// lvg — lavina grep (colorized)
// Usage: lvg <pattern> [path] [--ext .lv] [-i] [-c]

import std::os
import std::fs

int match_count = 0
int file_count = 0

Expand Down Expand Up @@ -76,7 +79,7 @@ string fn highlight(string line, string pattern):
string last_file = ""

void fn search_file(string path, string pattern):
auto lines = __fs_read_lines(path)
auto lines = fs::read_lines(path)
int local_count = 0

int line_num = 1
Expand All @@ -100,24 +103,24 @@ void fn search_file(string path, string pattern):

string colored_line = highlight(line, pattern)
if flag_line_numbers:
print(" ${C_LINE}${__int_to_str(line_num)}${C_DIM}:${C_RESET} ${colored_line}")
print(" ${C_LINE}${to_string(line_num)}${C_DIM}:${C_RESET} ${colored_line}")
else:
print(" ${colored_line}")
line_num += 1

if local_count > 0:
file_count += 1
if flag_count_only:
print("${C_FILE}${path}${C_DIM}:${C_RESET} ${C_BOLD}${__int_to_str(local_count)}${C_RESET} matches")
print("${C_FILE}${path}${C_DIM}:${C_RESET} ${C_BOLD}${to_string(local_count)}${C_RESET} matches")

void fn search_dir(string path, string pattern):
auto entries = __fs_listdir(path)
auto entries = fs::list_dir(path)
for ref name in entries:
if name.substring(0, 1) == ".":
continue

string full = "${path}/${name}"
if __fs_is_dir(full):
if fs::is_dir(full):
search_dir(full, pattern)
else:
if is_binary_ext(name):
Expand All @@ -133,7 +136,7 @@ void fn search_dir(string path, string pattern):
void fn main():
init_colors()

auto args = __os_args()
auto args = os::args()
if args.len() < 2:
print("Usage: lvg <pattern> [path] [--ext .lv] [-i] [-c]")
exit(1)
Expand All @@ -156,10 +159,10 @@ void fn main():
path = args[i]
i += 1

if __fs_is_dir(path):
if fs::is_dir(path):
search_dir(path, pattern)
else:
search_file(path, pattern)

print("")
print("${C_SUMMARY}${__int_to_str(match_count)} matches${C_RESET} in ${C_SUMMARY}${__int_to_str(file_count)} files${C_RESET}")
print("${C_SUMMARY}${to_string(match_count)} matches${C_RESET} in ${C_SUMMARY}${to_string(file_count)} files${C_RESET}")
17 changes: 10 additions & 7 deletions examples/complex/tree/tree.lv
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
// tree — recursive directory listing utility
// Usage: tree [path]

import std::os
import std::fs

int dir_count = 0
int file_count = 0

void fn print_tree(string path, string prefix):
auto entries = __fs_listdir(path)
void fn print_tree(string path, string prefix = ""):
auto entries = fs::list_dir(path)

int i = 0
int total = entries.len()
Expand All @@ -23,10 +26,10 @@ void fn print_tree(string path, string prefix):
child_prefix = " "

string full_path = "${path}/${name}"
if __fs_is_dir(full_path):
if fs::is_dir(full_path):
print("${prefix}${connector}${name}/")
dir_count += 1
print_tree(full_path, "${prefix}${child_prefix}")
print_tree(full_path, prefix = "${prefix}${child_prefix}")
else:
print("${prefix}${connector}${name}")
file_count += 1
Expand All @@ -35,11 +38,11 @@ void fn print_tree(string path, string prefix):

void fn main():
string root = "."
auto args = __os_args()
auto args = os::args()
if args.len() > 1:
root = args[1]

print(root)
print_tree(root, "")
print_tree(root)
print("")
print("${__int_to_str(dir_count)} directories, ${__int_to_str(file_count)} files")
print("${dir_count as string} directories, ${file_count as string} files")
Loading