diff --git a/.gitignore b/.gitignore index 6b8dcf5..a638fb1 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,6 @@ *.c harpy *.txt +*.o +*.bin +*.img diff --git a/examples/dot_anon.cx b/examples/dot_anon.cx index ed88f3d..f922cb1 100644 --- a/examples/dot_anon.cx +++ b/examples/dot_anon.cx @@ -1,11 +1,11 @@ //# 67 struct Foo { int x; - static Foo new() => .{30} - static int get(Foo f) => f.x + static Foo new() => .{30}; + static int get(Foo f) => f.x; } -int get(Foo f) => f.x +int get(Foo f) => f.x; int main() { Foo f = .new(); diff --git a/examples/dot_anon4.cx b/examples/dot_anon4.cx new file mode 100644 index 0000000..1ae2185 --- /dev/null +++ b/examples/dot_anon4.cx @@ -0,0 +1,17 @@ +//# 67 +struct Bar { + union foo { + int i; + } +} + +struct Foo { + Bar bar; +} + +int main() { + Foo f = .{ + bar = .{ foo.i = 67 } + }; + return f.bar.foo.i; +} diff --git a/examples/enum2.cx b/examples/enum2.cx new file mode 100644 index 0000000..8cf1409 --- /dev/null +++ b/examples/enum2.cx @@ -0,0 +1,15 @@ +//! Bar\n +import std.io; + +enum Foo { + Baz, + Bar +} + +int main() { + Foo bar = .Bar; + if bar == .Baz + printf("Equals\n"); + printf("%s\n", bar.id); + return 0; +} diff --git a/examples/function_overload.cx b/examples/function_overload.cx index d12b083..960c96a 100644 --- a/examples/function_overload.cx +++ b/examples/function_overload.cx @@ -1,5 +1,5 @@ //# 60 -int sum(int x, int y) overload => x + y +int sum(int x, int y) overload => x + y; double sum(double x, double y) overload { return x + y; diff --git a/examples/kernel/Makefile b/examples/kernel/Makefile new file mode 100644 index 0000000..78aeef7 --- /dev/null +++ b/examples/kernel/Makefile @@ -0,0 +1,23 @@ +all: os.img + +boot.bin: boot.asm + nasm -f bin boot.asm -o boot.bin + +entry.o: entry.asm + nasm -f elf32 entry.asm -o entry.o + +kernel.o: + cx --cflags="-m32 -ffreestanding -fno-pie -fno-pic -fno-stack-protector -c" kernel.cx -o kernel.o --gcc --no-header + +kernel.bin: entry.o kernel.o + ld -m elf_i386 -T linker.ld entry.o kernel.o -o kernel.bin + +os.img: boot.bin kernel.bin + cat boot.bin kernel.bin > os.img + truncate -s 1440K os.img + +run: os.img + qemu-system-x86_64 -drive format=raw,file=os.img,index=0,media=disk + +clean: + rm -f *.bin *.o os.img diff --git a/examples/kernel/boot.asm b/examples/kernel/boot.asm new file mode 100644 index 0000000..07b53ed --- /dev/null +++ b/examples/kernel/boot.asm @@ -0,0 +1,72 @@ +bits 16 +org 0x7C00 + +start: + ; Configura segmentos em Real Mode + xor ax, ax + mov ds, ax + mov es, ax + mov ss, ax + mov sp, 0x7C00 + + ; Habilita a linha A20 (Fast A20 Gate) + in al, 0x92 + or al, 2 + out 0x92, al + + ; Carrega o kernel (setor 2 em diante) + mov ah, 0x02 ; Função de leitura + mov al, 10 ; Número de setores + mov ch, 0 ; Cilindro 0 + mov cl, 2 ; Setor 2 + mov dh, 0 ; Cabeça 0 + mov bx, 0x1000 ; Endereço de destino + int 0x13 ; Chamada BIOS + + ; Entra em Modo Protegido + cli + lgdt [gdt_descriptor] + + mov eax, cr0 + or eax, 1 + mov cr0, eax + + jmp 0x08:protected_mode + +bits 32 +protected_mode: + ; Atualiza os registradores de segmento para 32-bit (Data Segment selector 0x10) + mov ax, 0x10 + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + mov ss, ax + + ; --- CORREÇÃO CRUCIAL: Aponta a pilha para uma área segura --- + mov esp, 0x90000 + + ; Chama o ponto de entrada do kernel + call 0x1000 + + ; Loop infinito caso o kernel retorne + cli + hlt + jmp $ + +gdt_start: + dq 0x0 +gdt_code: + dw 0xFFFF, 0x0 + db 0x0, 0x9A, 0xCF, 0x0 +gdt_data: + dw 0xFFFF, 0x0 + db 0x0, 0x92, 0xCF, 0x0 +gdt_end: + +gdt_descriptor: + dw gdt_end - gdt_start - 1 + dd gdt_start + +times 510-($-$$) db 0 +dw 0xAA55 diff --git a/examples/kernel/entry.asm b/examples/kernel/entry.asm new file mode 100644 index 0000000..fcc5cf5 --- /dev/null +++ b/examples/kernel/entry.asm @@ -0,0 +1,10 @@ +bits 32 +section .text +global _start_trampoline +extern _start ; Referência para a função gerada pelo CX/C + +_start_trampoline: + call _start ; Pula com segurança para o kernel + cli ; Se o kernel retornar (não deveria), desabilita interrupções + hlt ; Trava a CPU + jmp $ diff --git a/examples/kernel/kernel.cx b/examples/kernel/kernel.cx new file mode 100644 index 0000000..cda7af6 --- /dev/null +++ b/examples/kernel/kernel.cx @@ -0,0 +1,105 @@ +const int VGA_WIDTH = 80; +const int VGA_HEIGHT = 25; +volatile char* VIDEO_MEM = (char*) 0xB8000; + +char make_color(char fg, char bg) => fg | (bg << 4); + +void clear_screen(char bg_color) { + char attr = make_color(0x0F, bg_color); + for (int i = 0; i < VGA_WIDTH * VGA_HEIGHT * 2; i += 2) { + VIDEO_MEM[i] = ' '; + VIDEO_MEM[i + 1] = attr; + } +} + +void put_char_at(char c, char color, int x, int y) { + int offset = (y * VGA_WIDTH + x) * 2; + VIDEO_MEM[offset] = c; + VIDEO_MEM[offset + 1] = color; +} + +void print_at(const char* msg, char color, int x, int y) { + int i = 0; + while msg[i] != '\0' { + put_char_at(msg[i], color, x + i, y); + i++; + } +} + +void draw_box(char color) { + // Linhas horizontais (topo e base) + for (int x = 0; x < VGA_WIDTH; x++) { + put_char_at('-', color, x, 0); + put_char_at('-', color, x, VGA_HEIGHT - 1); + } + // Linhas verticais (lados) + for (int y = 0; y < VGA_HEIGHT; y++) { + put_char_at('|', color, 0, y); + put_char_at('|', color, VGA_WIDTH - 1, y); + } + // Cantos + put_char_at('+', color, 0, 0); + put_char_at('+', color, VGA_WIDTH - 1, 0); + put_char_at('+', color, 0, VGA_HEIGHT - 1); + put_char_at('+', color, VGA_WIDTH - 1, VGA_HEIGHT - 1); +} + +void print_int_at(int num, char color, int x, int y) { + for (int c = 0; c < 8; c++) + put_char_at(' ', color, x + c, y); + + char[12] buf; + int idx = 0; + + if num == 0 { + put_char_at('0', color, x, y); + return; + } + + // Extrai os dígitos do fim para o começo + while num > 0 { + buf[idx++] = '0' + (num % 10); + num = num / 10; + } + + // Imprime invertido para sair na ordem certa + for (int i = 0; i < idx; i++) + put_char_at(buf[idx - 1 - i], color, x + i, y); +} + +int _start() { + // 0x00 = Preto, 0x01 = Azul, 0x02 = Verde, 0x04 = Vermelho, 0x0E = Amarelo, 0x0F = Branco + char theme_bg = 0x01; // Fundo Azul + clear_screen(theme_bg); + + // UI Básica + char border_color = make_color(0x0E, theme_bg); // Texto Amarelo no Fundo Azul + draw_box(border_color); + + char title_color = make_color(0x0F, theme_bg); // Texto Branco no Fundo Azul + print_at("=== CX OPERATING SYSTEM v0.1 ===", title_color, 24, 2); + + char text_color = make_color(0x0A, theme_bg); // Texto Verde Claro + print_at("[OK] Protected Mode active (32-bit)", text_color, 4, 5); + print_at("[OK] VGA Memory mapped to 0xB8000", text_color, 4, 6); + print_at("[OK] CX Driver initialized successfully!", text_color, 4, 7); + + // Status Bar / Ticks no Rodapé + char status_color = make_color(0x00, 0x07); // Texto Preto no Fundo Cinza + for (int x = 1; x < VGA_WIDTH - 1; x++) + put_char_at(' ', status_color, x, VGA_HEIGHT - 2); + + print_at("Kernel Status: RUNNING", status_color, 3, VGA_HEIGHT - 2); + + // Infinite Loop com Contador de Ciclos + int ticks = 0; + while true { + ticks++; + print_at("Cycles: ", status_color, 55, VGA_HEIGHT - 2); + print_int_at(ticks, status_color, 63, VGA_HEIGHT - 2); + // Pequeno atraso visual para não atualizar rápido demais + for (int delay = 0; delay < 1000000; delay++) __raw { asm("nop"); } + } + + return 0; +} diff --git a/examples/kernel/linker.ld b/examples/kernel/linker.ld new file mode 100644 index 0000000..55298a5 --- /dev/null +++ b/examples/kernel/linker.ld @@ -0,0 +1,29 @@ +ENTRY(_start_trampoline) +OUTPUT_FORMAT("binary") + +SECTIONS +{ + /* Diz ao linker que o bootloader carrega tudo em 0x1000 */ + . = 0x1000; + + .text : ALIGN(4) { + /* Garante que o trampolim seja literalmente o primeiro byte */ + entry.o(.text) + *(.text) + *(.text.*) + } + + .rodata : ALIGN(4) { + *(.rodata) + *(.rodata.*) + } + + .data : ALIGN(4) { + *(.data) + } + + .bss : ALIGN(4) { + *(COMMON) + *(.bss) + } +} diff --git a/examples/map.cx b/examples/map.cx index d48f578..fc35e63 100644 --- a/examples/map.cx +++ b/examples/map.cx @@ -10,7 +10,7 @@ struct Fun { } } -float toFloat(int x) => (float) x * 1.5F +float toFloat(int x) => (float) x * 1.5F; int main() { int[5] nums = [1, 2, 3, 4, 5]; diff --git a/examples/raw1.cx b/examples/raw1.cx index edeea5c..952b0af 100644 --- a/examples/raw1.cx +++ b/examples/raw1.cx @@ -13,7 +13,7 @@ u32 fast_add(u32 a, u32 b) { return result; } -u32 count_leading_zeros(u32 x) => __builtin_clz(x) +u32 count_leading_zeros(u32 x) => __builtin_clz(x); int main() { printf("fast_add: %u\n", fast_add(10, 20)); diff --git a/examples/vm/vm2.cx b/examples/vm/vm2.cx index 5c9d498..7468b8c 100644 --- a/examples/vm/vm2.cx +++ b/examples/vm/vm2.cx @@ -11,15 +11,6 @@ enum OpCode { Halt, } -char* opCodeToStr(OpCode op) -{ - if op == OpCode.Push return "Push"; - if op == OpCode.Add return "Add"; - if op == OpCode.Print return "Print"; - if op == OpCode.Halt return "Halt"; - return "Invalid OpCode"; -} - struct Instruction { OpCode op; VMValue value; @@ -36,19 +27,19 @@ struct VMValue { long i; float f; }; - static VMValue mkInt(long i) => (VMValue) {VMType.Int, value.i = i} - static VMValue mkFloat(float i) => (VMValue) {VMType.Float, value.f = i} + static VMValue mkInt(long i) => (VMValue) {VMType.Int, value.i = i}; + static VMValue mkFloat(float i) => (VMValue) {VMType.Float, value.f = i}; } alias SValue = VMValue!StackError -struct VM +struct VM { Array program; Stack stack; u32 offset; - static VM new(Array program) => (VM) { program, Stack.new(8), 0 } + static VM new(Array program) => (VM) { program, Stack.new(8), 0 }; void run() { while self.offset < self.program.size @@ -90,10 +81,10 @@ struct VM printf("%lld\n", v.value.i); else printf("%f\n", v.value.f); - return; + continue; default: - printf("Invalid opcode '%s'.\n", opCodeToStr(instr.op)); + printf("Invalid opcode '%s'.\n", instr.op.id); continue; } } diff --git a/examples/windows/langtoy.cx b/examples/windows/langtoy.cx new file mode 100644 index 0000000..a25d752 --- /dev/null +++ b/examples/windows/langtoy.cx @@ -0,0 +1,490 @@ +//# 0 +include +include +include + +enum LangError { + UnexpectedChar, + UnexpectedToken, + DivisionByZero, + UnclosedParen, + UndefinedVar, + ExpectedIdent, + ExpectedEquals, + ExpectedSemicolon, + TooManyVars +} + +enum TokKind { + Num, + Ident, + Let, + Print, + Plus, + Minus, + Star, + Slash, + Equals, + Semicolon, + LParen, + RParen, + Eof +} + +struct Tok { + TokKind kind; + double num; + char[16] name; +} + +struct Env { + char[16][32] names; + double[32] values; + u32 count; + + static Env new() { + Env e; + e.count = 0; + return e; + } + + void set(char* name, double val) { + for (u32 i = 0; i < self.count; i++) { + if self.names[i] === name { + self.values[i] = val; + return; + } + } + strcpy(self.names[self.count], name); + self.values[self.count] = val; + self.count = self.count + 1; + } + + double!LangError get(char* name) { + for (u32 i = 0; i < self.count; i++) { + if self.names[i] === name { + return self.values[i]; + } + } + return LangError.UndefinedVar; + } +} + +struct Lexer { + char* src; + u32 pos; + u32 len; + + static Lexer new(char* src) { + return (Lexer) {src, 0, src.length}; + } + + char current() { + if self.pos >= self.len { + return '\0'; + } + return self.src[self.pos]; + } + + void skipSpaces() { + while self.current() == ' ' || self.current() == '\n' || self.current() == '\t' { + self.pos = self.pos + 1; + } + } + + bool isDigit(char c) { + return c >= '0' && c <= '9'; + } + + bool isAlpha(char c) { + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'; + } + + double nextNumber() { + u32 start = self.pos; + while self.isDigit(self.current()) { + self.pos = self.pos + 1; + } + if self.current() == '.' { + self.pos = self.pos + 1; + while self.isDigit(self.current()) { + self.pos = self.pos + 1; + } + } + u32 len = self.pos - start; + char[32] buff; + for (u32 i = 0; i < len; i++) { + buff[i] = self.src[start + i]; + } + buff[len] = '\0'; + return atof(buff); + } + + Tok!LangError next() { + self.skipSpaces(); + char c = self.current(); + + if c == '\0' { + return (Tok) {TokKind.Eof, 0, ""}; + } + if self.isDigit(c) { + double n = self.nextNumber(); + return (Tok) {TokKind.Num, n, ""}; + } + if self.isAlpha(c) { + u32 start = self.pos; + while self.isAlpha(self.current()) || self.isDigit(self.current()) { + self.pos = self.pos + 1; + } + u32 len = self.pos - start; + char[16] name; + for (u32 i = 0; i < len; i++) { + name[i] = self.src[start + i]; + } + name[len] = '\0'; + + if name === "let" { + return (Tok) {TokKind.Let, 0, ""}; + } + if name === "print" { + return (Tok) {TokKind.Print, 0, ""}; + } + return (Tok) {TokKind.Ident, 0, name}; + } + if c == '+' { + self.pos = self.pos + 1; + return (Tok) {TokKind.Plus, 0, ""}; + } + if c == '-' { + self.pos = self.pos + 1; + return (Tok) {TokKind.Minus, 0, ""}; + } + if c == '*' { + self.pos = self.pos + 1; + return (Tok) {TokKind.Star, 0, ""}; + } + if c == '/' { + self.pos = self.pos + 1; + return (Tok) {TokKind.Slash, 0, ""}; + } + if c == '=' { + self.pos = self.pos + 1; + return (Tok) {TokKind.Equals, 0, ""}; + } + if c == ';' { + self.pos = self.pos + 1; + return (Tok) {TokKind.Semicolon, 0, ""}; + } + if c == '(' { + self.pos = self.pos + 1; + return (Tok) {TokKind.LParen, 0, ""}; + } + if c == ')' { + self.pos = self.pos + 1; + return (Tok) {TokKind.RParen, 0, ""}; + } + + return LangError.UnexpectedChar; + } +} + +// ---- Interpretador: parser + execucao, tudo direto (tree-walking) ---- +struct Interp { + Lexer lex; + Tok tok; + Env env; + + static Interp!LangError new(char* src) { + Lexer l = Lexer.new(src); + Tok!LangError first = l.next(); + if !first.valid { + return first.error; + } + return (Interp) {l, first.ok, Env.new()}; + } + + double!LangError advance() { + Tok!LangError t = self.lex.next(); + if !t.valid { + return t.error; + } + self.tok = t.ok; + return 0; + } + + double!LangError factor() { + if self.tok.kind == TokKind.Num { + double val = self.tok.num; + double!LangError adv = self.advance(); + if !adv.valid { + return adv.error; + } + return val; + } + if self.tok.kind == TokKind.Ident { + char[16] name; + strcpy(name, self.tok.name); + double!LangError adv = self.advance(); + if !adv.valid { + return adv.error; + } + return self.env.get(name); + } + if self.tok.kind == TokKind.Minus { + double!LangError adv = self.advance(); + if !adv.valid { + return adv.error; + } + double!LangError inner = self.factor(); + if !inner.valid { + return inner.error; + } + return -inner.ok; + } + if self.tok.kind == TokKind.LParen { + double!LangError adv = self.advance(); + if !adv.valid { + return adv.error; + } + double!LangError inner = self.expr(); + if !inner.valid { + return inner.error; + } + if self.tok.kind != TokKind.RParen { + return LangError.UnclosedParen; + } + double!LangError adv2 = self.advance(); + if !adv2.valid { + return adv2.error; + } + return inner.ok; + } + return LangError.UnexpectedToken; + } + + double!LangError term() { + double!LangError left = self.factor(); + if !left.valid { + return left.error; + } + double acc = left.ok; + while self.tok.kind == TokKind.Star || self.tok.kind == TokKind.Slash { + TokKind op = self.tok.kind; + double!LangError adv = self.advance(); + if !adv.valid { + return adv.error; + } + double!LangError right = self.factor(); + if !right.valid { + return right.error; + } + if op == TokKind.Star { + acc = acc * right.ok; + } else { + if right.ok == 0.0 { + return LangError.DivisionByZero; + } + acc = acc / right.ok; + } + } + return acc; + } + + double!LangError expr() { + double!LangError left = self.term(); + if !left.valid { + return left.error; + } + double acc = left.ok; + while self.tok.kind == TokKind.Plus || self.tok.kind == TokKind.Minus { + TokKind op = self.tok.kind; + double!LangError adv = self.advance(); + if !adv.valid { + return adv.error; + } + double!LangError right = self.term(); + if !right.valid { + return right.error; + } + if op == TokKind.Plus { + acc = acc + right.ok; + } else { + acc = acc - right.ok; + } + } + return acc; + } + + // let IDENT = expr ; + double!LangError letStmt() { + double!LangError adv = self.advance(); // consome 'let' + if !adv.valid { + return adv.error; + } + if self.tok.kind != TokKind.Ident { + return LangError.ExpectedIdent; + } + char[16] name; + strcpy(name, self.tok.name); + + double!LangError adv2 = self.advance(); // consome IDENT + if !adv2.valid { + return adv2.error; + } + if self.tok.kind != TokKind.Equals { + return LangError.ExpectedEquals; + } + double!LangError adv3 = self.advance(); // consome '=' + if !adv3.valid { + return adv3.error; + } + double!LangError val = self.expr(); + if !val.valid { + return val.error; + } + if self.tok.kind != TokKind.Semicolon { + return LangError.ExpectedSemicolon; + } + double!LangError adv4 = self.advance(); // consome ';' + if !adv4.valid { + return adv4.error; + } + self.env.set(name, val.ok); + return 0; + } + + // IDENT = expr ; + double!LangError assignStmt() { + char[16] name; + strcpy(name, self.tok.name); + + double!LangError adv = self.advance(); // consome IDENT + if !adv.valid { + return adv.error; + } + if self.tok.kind != TokKind.Equals { + return LangError.ExpectedEquals; + } + double!LangError adv2 = self.advance(); // consome '=' + if !adv2.valid { + return adv2.error; + } + double!LangError val = self.expr(); + if !val.valid { + return val.error; + } + if self.tok.kind != TokKind.Semicolon { + return LangError.ExpectedSemicolon; + } + double!LangError adv3 = self.advance(); // consome ';' + if !adv3.valid { + return adv3.error; + } + self.env.set(name, val.ok); + return 0; + } + + // print expr ; + double!LangError printStmt() { + double!LangError adv = self.advance(); // consome 'print' + if !adv.valid { + return adv.error; + } + double!LangError val = self.expr(); + if !val.valid { + return val.error; + } + if self.tok.kind != TokKind.Semicolon { + return LangError.ExpectedSemicolon; + } + double!LangError adv2 = self.advance(); // consome ';' + if !adv2.valid { + return adv2.error; + } + printf("%g\n", val.ok); + return 0; + } + + double!LangError statement() { + if self.tok.kind == TokKind.Let { + return self.letStmt(); + } + if self.tok.kind == TokKind.Print { + return self.printStmt(); + } + if self.tok.kind == TokKind.Ident { + return self.assignStmt(); + } + return LangError.UnexpectedToken; + } + + double!LangError run() { + while self.tok.kind != TokKind.Eof { + double!LangError r = self.statement(); + if !r.valid { + return r.error; + } + } + return 0; + } +} + +char* errorToStr(LangError e) { + if e == LangError.UnexpectedChar return "unexpected character"; + if e == LangError.UnexpectedToken return "unexpected token"; + if e == LangError.DivisionByZero return "division by zero"; + if e == LangError.UnclosedParen return "unclosed parenthesis"; + if e == LangError.UndefinedVar return "undefined variable"; + if e == LangError.ExpectedIdent return "expected identifier"; + if e == LangError.ExpectedEquals return "expected '='"; + if e == LangError.ExpectedSemicolon return "expected ';'"; + if e == LangError.TooManyVars return "too many variables"; + return "unknown error"; +} + +void execute(char* src) { + Interp!LangError i = Interp.new(src); + if !i.valid { + printf("Error: %s\n", errorToStr(i.error)); + return; + } + Interp interp = i.ok; + double!LangError r = interp.run(); + if !r.valid { + printf("Error: %s\n", errorToStr(r.error)); + } +} + +int main() { + printf("--- program 1 ---\n"); + execute( + "let x = 10;" + "let y = 20;" + "print x + y;" + "x = x * 2;" + "print x;" + "print x - y;" + ); + + printf("--- program 2 (fibonacci manual) ---\n"); + execute( + "let a = 0;" + "let b = 1;" + "print a;" + "print b;" + "let c = a + b;" + "print c;" + "a = b;" + "b = c;" + "c = a + b;" + "print c;" + ); + + printf("--- program 3 (erro: variavel indefinida) ---\n"); + execute("print z;"); + + printf("--- program 4 (erro: divisao por zero) ---\n"); + execute("let x = 10; print x / 0;"); + + return 0; +} diff --git a/examples/windows/main.cx b/examples/windows/main.cx new file mode 100644 index 0000000..53589d2 --- /dev/null +++ b/examples/windows/main.cx @@ -0,0 +1,18 @@ +//# 0 +include + +int sum(int x, int y) { + return x + y; +} + +int main() { + char* name = "Fernando"; + int val = 18; + + int x = sum(val * 18, 1); + int* y = &x; + *y = 67; + + printf("%s %d %d\n", name, val, x); + return 0; +} diff --git a/src/backend/codegen.d b/src/backend/codegen.d index 376f2da..c09a76e 100644 --- a/src/backend/codegen.d +++ b/src/backend/codegen.d @@ -182,10 +182,22 @@ private: string name = node.name; typedefs ~= format("typedef enum %s %s;", name, name); string _data = format("enum %s\n{\n", name); + /* + const char* ArrayError_ids[] = { + [ArrayError_NotFound] = "NotFound" + }; + */ + string ids = format("const char* %s_ids[] = {\n", name); foreach (string field; node.fields) - _data ~= indent(format("%s_%s,\n", name, field), ind + 4); + { + string namem = format("%s_%s", name, field); + _data ~= indent(namem ~ ",\n", ind + 4); + ids ~= indent(format("[%s] = \"%s\",\n", namem, field), 4); + } + ids ~= "};\n"; _data ~= "};\n"; data ~= _data; + data ~= ids; } string compileStmt(Node node, uint ind) @@ -309,7 +321,7 @@ private: emit(format("for (;%s.offset < %s.length; %s.offset++)", temp, temp, temp), ind); emit(format("{"), ind); if (fe.k !is null) - emit(format("size_t %s = %s.offset;", compileExpr(fe.k), temp), ind); + emit(format("size_t %s = %s.offset;", compileExpr(fe.k), temp), ind+4); emit(format("__typeof__(%s(%s.ptr)) %s = %s(%s.ptr[%s.offset]);", isRef ? "" : "*", temp, val, isRef ? "&" : "", temp, temp), ind+4); foreach (Node n; fe.body) @@ -678,10 +690,17 @@ private: return format("%s.val.%s", expr, val); } - if (isString(type) && node.right.kind == NodeKind.IdentExpr) - if ((cast(IdentExpr) node.right).val == "length") + IdentExpr idcast = cast(IdentExpr) node.right; + string rval = idcast ? idcast.val : ""; + + if (isString(type) && idcast) + if (rval == "length") return format("strlen(%s)", compileExpr(node.left)); + if (isEnum(type) && idcast) + if (rval == "id") + return format("%s_ids[%s]", type.toStr(), compileExpr(node.left)); + if (type is null) isArrow = true; else if (type.kind == TypeExprKind.Pointer) @@ -955,6 +974,13 @@ private: return type.kind == TypeExprKind.Result; } + bool isEnum(TypeExpr type) + { + if (TypeExprUser p = cast(TypeExprUser) type) + return p.kind == TypeExprKind.Enum; + return false; + } + public: this(Program program, TypeRegistry types, bool[string] staticFunctions, bool noHeader, bool genHeaderFile, string headerFile, ImportResolverContext* context, bool isCpp, TypeResolver resolver) diff --git a/src/builder.d b/src/builder.d new file mode 100644 index 0000000..d9beb3b --- /dev/null +++ b/src/builder.d @@ -0,0 +1,114 @@ +module builder; + +import main : CXArgs, compile; +import utils; + + +import std.stdio : writeln, writefln, stdout, readln, dwrite = write; +import std.process; +import std.format; +import std.string; +import std.json; +import std.file; +import std.path; +import std.file; + +enum ERROR_MESSAGE = "The 'compile' command must be used within cx projects."; +const string maincx = `import std.io; + +int main() { + printf("Hello World!\n"); + return 0; +} +`; + +const string cxjson = `{ + "name": "%s", + "output": "%s" +} +`; + +string getInput(string message, string fallback) +{ + dwrite(format("%s (default: %s): ", message, fallback)); + stdout.flush(); + string input = readln().strip(); + return input == "" ? fallback : input; +} + +int runBuild() +{ + /* + src/main.cx + cx.json + */ + string name = getInput("Project name", "cx-project"); + string binary = getInput("Output file", "main"); + + if (!exists("src")) + { + mkdir("src"); + write("./src/main.cx", maincx); + } + + if (!exists("cx.json")) + write("./cx.json", format(cxjson, name, binary)); + + writeln("Done!"); + + return 0; +} + +bool keyExists(string key, JSONValue json) +{ + try + auto _ = json[key]; + catch(Exception e) + return false; + return true; +} + +T getValue(T)(string key, JSONValue json, T fallback) +{ + if (!keyExists(key, json)) + return fallback; + + T val; + try + { + static if (is(T == string)) + val = json[key].str; + else static if (is(T == bool)) + val = json[key].boolean; + else + static assert(0, "Error: " ~ T.stringof); + } + catch (Exception e) + return fallback; + + return val; +} + +int runCompile(bool isRun, ref CXArgs args) +{ + string main = "./src/main.cx"; + string fileJson = "./cx.json"; + + cx_enforce(exists(main) && exists(fileJson), ERROR_MESSAGE); + cx_enforce(isFile(main) && isFile(fileJson), ERROR_MESSAGE); // 2Auth + + string jsonContent = readText(fileJson); + JSONValue json = parseJSON(jsonContent); + + string binary = getValue!string("output", json, "main"); + args.output = binary; + + int _ = compile(main, args); + if (!isRun) return _; + + cx_enforce(exists(binary), "An error occurred while compiling the project."); + auto exec = executeShell("./" ~ binary); + dwrite(exec.output); + + return exec.status; +} diff --git a/src/env.d b/src/env.d index 8b72460..b237c58 100644 --- a/src/env.d +++ b/src/env.d @@ -1,4 +1,4 @@ module env; -const string COMPILER_VERSION = "0.2.1"; +const string COMPILER_VERSION = "0.2.2"; const string GITHUB_REPO = "https://github.com/FernandoTheDev/cx.git"; diff --git a/src/frontend/lexer/lexer.d b/src/frontend/lexer/lexer.d index 76438ea..8647acc 100644 --- a/src/frontend/lexer/lexer.d +++ b/src/frontend/lexer/lexer.d @@ -25,102 +25,7 @@ private: uint offset, loffset; uint line = 1; - TokenKind[string] keywords = [ - "__is": TokenKind.Is, - "__type": TokenKind.Type, - "__typename": TokenKind.TypeName, - - "foreach": TokenKind.ForEach, - "default": TokenKind.Default, - "switch": TokenKind.Switch, - "case": TokenKind.Case, - "register": TokenKind.Register, - "_Atomic": TokenKind.Atomic, - "restrict": TokenKind.Restrict, - "volatile": TokenKind.Volatile, - "const": TokenKind.Const, - "return": TokenKind.Return, - "static": TokenKind.Static, - "inline": TokenKind.Inline, - "overload": TokenKind.Overload, - "struct": TokenKind.Struct, - "alias": TokenKind.Alias, - "enum": TokenKind.Enum, - "union": TokenKind.Union, - "defer": TokenKind.Defer, - "if": TokenKind.If, - "else": TokenKind.Else, - "for": TokenKind.For, - "while": TokenKind.While, - "goto": TokenKind.Goto, - "import": TokenKind.Import, - "continue": TokenKind.Continue, - "break": TokenKind.Break, - "sizeof": TokenKind.SizeOf, - "true": TokenKind.True, - "false": TokenKind.False, - "null": TokenKind.Null, - "NULL": TokenKind.Null, - ]; - - TokenKind[string] symbols = [ - "(": TokenKind.LParen, - ")": TokenKind.RParen, - "{": TokenKind.LBrace, - "}": TokenKind.RBrace, - "[": TokenKind.LBracket, - "]": TokenKind.RBracket, - - ".": TokenKind.Dot, - "..": TokenKind.Range, - "...": TokenKind.Ellipsis, - - ",": TokenKind.Comma, - ":": TokenKind.Colon, - ";": TokenKind.SemiColon, - "@": TokenKind.At, - - "+": TokenKind.Plus, - "++": TokenKind.PPlus, - "-": TokenKind.Minus, - "--": TokenKind.MMinus, - "*": TokenKind.Star, - "/": TokenKind.Slash, - "%": TokenKind.Modulo, - - "=>": TokenKind.Arrow, - "==": TokenKind.EEquals, - "===": TokenKind.EEEquals, - "<": TokenKind.LThan, - ">": TokenKind.GThan, - "<=": TokenKind.LEquals, - ">=": TokenKind.GEquals, - "!": TokenKind.Bang, - "!=": TokenKind.NEquals, - "&&": TokenKind.And, - "||": TokenKind.Or, - "?": TokenKind.Question, - "??": TokenKind.QQuestion, - "?.": TokenKind.QDot, - - "+=": TokenKind.PLUSEquals, - "-=": TokenKind.MINUSEquals, - "/=": TokenKind.DIVEquals, - "*=": TokenKind.STAREquals, - "%=": TokenKind.MODEquals, - "|=": TokenKind.OBWEquals, - "&=": TokenKind.EBWEquals, - "<<=": TokenKind.SHLEquals, - ">>=": TokenKind.SHREquals, - "=": TokenKind.Equals, - - "<<": TokenKind.BITLeft, - ">>": TokenKind.BITRight, - "&": TokenKind.BITAnd, - "|": TokenKind.BITOr, - "~": TokenKind.BITNot, - "^": TokenKind.BITXor, - ]; + TokenKind[string] keywords, symbols; pragma(inline, true) bool isAtEnd(uint i = 0) @@ -177,29 +82,27 @@ private: pragma(inline, true) bool checkNewLine(char ch) { - version (Windows) + if (ch == '\r' && !isAtEnd()) { - if (ch == '\r' && !isAtEnd()) - { - if (check('\n')) - { - // o \r ja chegou com um advance() - advance(); // pula \n - loffset = 0; - line++; - return true; - } - } - return false; - } else { - if (ch == '\n') + if (check('\n')) { + advance(); // pula \n + // o \r ja chegou com um advance() loffset = 0; line++; - return true; + return true; } return false; } + + if (ch == '\n') + { + loffset = 0; + line++; + return true; + } + + return false; } pragma(inline, true) @@ -372,6 +275,103 @@ public: this.source = source; this.err = err; this.type = t; + + this.keywords = [ + "__is": TokenKind.Is, + "__type": TokenKind.Type, + "__typename": TokenKind.TypeName, + + "foreach": TokenKind.ForEach, + "default": TokenKind.Default, + "switch": TokenKind.Switch, + "case": TokenKind.Case, + "register": TokenKind.Register, + "_Atomic": TokenKind.Atomic, + "restrict": TokenKind.Restrict, + "volatile": TokenKind.Volatile, + "const": TokenKind.Const, + "return": TokenKind.Return, + "static": TokenKind.Static, + "inline": TokenKind.Inline, + "overload": TokenKind.Overload, + "struct": TokenKind.Struct, + "alias": TokenKind.Alias, + "enum": TokenKind.Enum, + "union": TokenKind.Union, + "defer": TokenKind.Defer, + "if": TokenKind.If, + "else": TokenKind.Else, + "for": TokenKind.For, + "while": TokenKind.While, + "goto": TokenKind.Goto, + "import": TokenKind.Import, + "continue": TokenKind.Continue, + "break": TokenKind.Break, + "sizeof": TokenKind.SizeOf, + "true": TokenKind.True, + "false": TokenKind.False, + "null": TokenKind.Null, + "NULL": TokenKind.Null, + ]; + + this.symbols = [ + "(": TokenKind.LParen, + ")": TokenKind.RParen, + "{": TokenKind.LBrace, + "}": TokenKind.RBrace, + "[": TokenKind.LBracket, + "]": TokenKind.RBracket, + + ".": TokenKind.Dot, + "..": TokenKind.Range, + "...": TokenKind.Ellipsis, + + ",": TokenKind.Comma, + ":": TokenKind.Colon, + ";": TokenKind.SemiColon, + "@": TokenKind.At, + + "+": TokenKind.Plus, + "++": TokenKind.PPlus, + "-": TokenKind.Minus, + "--": TokenKind.MMinus, + "*": TokenKind.Star, + "/": TokenKind.Slash, + "%": TokenKind.Modulo, + + "=>": TokenKind.Arrow, + "==": TokenKind.EEquals, + "===": TokenKind.EEEquals, + "<": TokenKind.LThan, + ">": TokenKind.GThan, + "<=": TokenKind.LEquals, + ">=": TokenKind.GEquals, + "!": TokenKind.Bang, + "!=": TokenKind.NEquals, + "&&": TokenKind.And, + "||": TokenKind.Or, + "?": TokenKind.Question, + "??": TokenKind.QQuestion, + "?.": TokenKind.QDot, + + "+=": TokenKind.PLUSEquals, + "-=": TokenKind.MINUSEquals, + "/=": TokenKind.DIVEquals, + "*=": TokenKind.STAREquals, + "%=": TokenKind.MODEquals, + "|=": TokenKind.OBWEquals, + "&=": TokenKind.EBWEquals, + "<<=": TokenKind.SHLEquals, + ">>=": TokenKind.SHREquals, + "=": TokenKind.Equals, + + "<<": TokenKind.BITLeft, + ">>": TokenKind.BITRight, + "&": TokenKind.BITAnd, + "|": TokenKind.BITOr, + "~": TokenKind.BITNot, + "^": TokenKind.BITXor, + ]; } Token[] tokenizer() diff --git a/src/frontend/parser/parse_decl.d b/src/frontend/parser/parse_decl.d index d8c66f8..79447dc 100644 --- a/src/frontend/parser/parse_decl.d +++ b/src/frontend/parser/parse_decl.d @@ -115,6 +115,7 @@ public: { Node val = p.parseExpr.parse(); body ~= new ReturnStmt(val, val.pos); + p.consume(TokenKind.SemiColon, "Expected ';' after arrow function."); } else { diff --git a/src/frontend/type_resolve.d b/src/frontend/type_resolve.d index 490d35c..2fdccdd 100644 --- a/src/frontend/type_resolve.d +++ b/src/frontend/type_resolve.d @@ -139,6 +139,11 @@ private: case NodeKind.BinaryExpr: BinaryExpr b = cast(BinaryExpr) n; TypeExpr lt = resolveExprType(b.left, scp); + + TypeExpr re = reference; + reference = lt; + scope (exit) reference = re; + resolveExprType(b.right, scp); b.type_expr = lt; // aproximação: tipo do lado esquerdo domina return lt; diff --git a/src/main.d b/src/main.d index c220ac9..45b017d 100644 --- a/src/main.d +++ b/src/main.d @@ -2,12 +2,14 @@ module main; import backend.codegen; import frontend; +import builder; +import updater; import errors; import utils; import env; import std.path : dirName, baseName, extension; -import std.stdio : writeln, writefln; +import std.stdio : writeln, writefln, dwrite = write; import core.stdc.stdlib : exit; import std.algorithm; import std.exception; @@ -19,6 +21,13 @@ import std.file; __gshared Generic generic; __gshared bool noHeader; +__gshared string stdDir, OS; + +pragma(inline, true) +bool isCommand(string[] argv, string command) +{ + return argv.length > 1 ? argv[1] == command : false; +} pragma(inline, true) void check_diagnostic(Diagnostics d) @@ -27,10 +36,25 @@ void check_diagnostic(Diagnostics d) exit(1); } +struct CXArgs +{ + bool emitc, opt, dbg, verMessage, helpMessage, genHeader, cpp, gcc, noHeader; + string[] link, cflags; + string output, target; +} + pragma(inline, true) void showHelp() { - writeln("Usage: cx [options] "); + writeln("Usage:"); + writeln(" cx "); + writeln(" cx [options]"); + writeln(); + writeln("Commands:"); + writeln(" update Update your compiler to the latest version."); + writeln(" compile Compile your Cx project."); + writeln(" build Create your Cx project."); + writeln(" run Compile and execute your Cx project."); writeln(); writeln("Options:"); writeln( @@ -45,9 +69,10 @@ void showHelp() writeln(" --gen-header It will generate a .h file and a .c file without compiling at the end."); writeln(" --cflags Pass compilation flags to the C compiler."); writeln(" --cpp Compiles with a C++ compiler."); + writeln(" --gcc Set GCC as the default compiler."); writeln(); writeln("Environment:"); - writeln(" CC C compiler used to build the output (default: cc)"); + writeln(" CC C compiler used to build the output (default: tcc -> gcc -> cc)"); writeln(); writeln("Examples:"); writeln(" cx update"); @@ -70,116 +95,17 @@ void showVersion() bool which(string c) { return executeShell(format("which %s", c)).status == 0; -} +} -int main(string[] argv) +int compile(string filename, ref CXArgs args) { - string stdDir; - string OS; - - version (Windows) - { - writeln( - "The compiler does not yet support Windows, even though there is a build script and you managed to compile it."); - return 1; - } - - version (OSX) - OS = "macos"; - else version (linux) - OS = "linux"; - else version (Posix) - OS = "unix"; - else - OS = "unknown"; - - if (OS == "unknown") - { - writefln("Unable to detect your operating system; please create an issue in the GitHub repository: '%s'", - GITHUB_REPO); - return 0; - } - - if (OS != "windows") - { - string home = environment.get("HOME", ""); - stdDir = home ~ "/" ~ ".cx/"; - if (!home || !exists(stdDir)) - { - writefln("An error occurred while validating the compiler installation."); - writefln("Some folders may be missing; check if this path is valid: '%s'.", stdDir); - writefln( - "If it does not exist, then an error occurred while installing the compiler on your system."); - return 0; - } - } - - if (argv.length > 1 && argv[1] == "update") - { - import updater; - return runUpdate(); - } - - bool emitc, opt, dbg, verMessage, helpMessage, genHeader, cpp; - string[] link, cflags; - string output, target; - - try - getopt(argv, - "opt", &opt, - "version|v", &verMessage, - "help|h", &helpMessage, - "debug|d", &dbg, - "emit-c", &emitc, - "link|L", &link, - "output|o", &output, - "target", &target, - "no-header", &noHeader, - "gen-header", &genHeader, - "cflags", &cflags, - "cpp", &cpp, - ); - catch (GetOptException e) - { - writefln("Invalid flag '%s'.", e.message[20 .. $]); - return 1; - } - - if (verMessage) - { - showVersion(); - return 0; - } - - if (helpMessage) - { - showHelp(); - return 0; - } - - if (target == "") - { - version (linux) - target = "linux"; - else version (Windows) - target = "windows"; - else version (OSX) - target = "macos"; - else version (Unix) - target = "unix"; - else - target = "unknown"; - } - - cx_enforce(argv.length == 2, "The compiler expects at least one file, see 'cx -h'."); - string filename = argv[1]; cx_enforce(extension(filename) == ".cx", "The file is not a valid .cx file."); cx_enforce(exists(filename), format("The file '%s' does not exist.", filename)); string dir = dirName(filename) ~ "/"; string content = readText(filename); string file = baseName(filename); - output = output == "" ? file[0 .. $ - 3] : output; + args.output = args.output == "" ? file[0 .. $ - 3] : args.output; Diagnostics err = new Diagnostics; TypeRegistry registry = new TypeRegistry; @@ -196,8 +122,9 @@ int main(string[] argv) program = p.parse(); catch (Exception e) { + check_diagnostic(err); writefln("An internal error occurred in the parser: %s", e.message); - if (dbg) writeln(e); + if (args.dbg) writeln(e); return 1; } @@ -217,27 +144,28 @@ int main(string[] argv) new StructOrder(err).resolve(program); check_diagnostic(err); - string fileh = output ~ (cpp ? ".hpp" : ".h"); - string filec = output ~ (cpp ? ".cpp" : ".c"); - string[2] src = new CodeGen(program, registry, ctx.statics, noHeader, genHeader, fileh, ctx, cpp, resolver).compile(); + string fileh = args.output ~ (args.cpp ? ".hpp" : ".h"); + string filec = args.output ~ (args.cpp ? ".cpp" : ".c"); + string[2] src = new CodeGen(program, registry, ctx.statics, noHeader, args.genHeader, fileh, ctx, args.cpp, resolver) + .compile(); check_diagnostic(err); write(filec, src[0]); - if (genHeader) + if (args.genHeader) { write(fileh, src[1]); writefln("Success: two individual files, '%s' and '%s', were generated.", filec, fileh); return 0; } - if (emitc) + if (args.emitc) { writefln("File '%s' generated.", filec); return 0; } - string comp = "cc"; - if (cpp) + string comp = args.gcc ? "gcc" : (which("tcc") ? "tcc" : (which("gcc") ? "gcc" : "cc")); + if (args.cpp) { // decide o compilador a ser usado comp = which("g++") ? "g++" : (which("clang++") ? "clang++" : ""); @@ -249,14 +177,16 @@ int main(string[] argv) } string c_compiler = environment.get("CC", comp); - string command = format("%s %s %s -o %s %s %s", c_compiler, filec, (opt ? "-O2" : ""), output, - link.length > 0 ? (link.map!(l => format("-l%s", l).array).join(" ")) : "", cflags.join(" ")); - if (dbg) + string command = format("%s %s %s -o %s %s %s", c_compiler, filec, (args.opt ? "-O2" : ""), args.output, + args.link.length > 0 ? (args.link.map!(l => format("-l%s", l).array).join(" ")) : "", args.cflags.join(" ")); + + if (args.dbg) writeln("C Compiler: ", c_compiler); auto exec = executeShell(command); - if (dbg) + if (args.dbg) writeln("Command: ", command); + if (exec.status != 0) { writeln("An error occurred while compiling the program."); @@ -267,3 +197,92 @@ int main(string[] argv) executeShell(format("rm -f %s", filec)); return 0; } + +int main(string[] argv) +{ + version (Windows) + { + writeln( + "The compiler does not yet support Windows, even though there is a build script and you managed to compile it."); + return 1; + } + + CXArgs args; + + version (OSX) + OS = "macos"; + else version (linux) + OS = "linux"; + else version (Posix) + OS = "unix"; + else + OS = "unknown"; + + if (OS == "unknown") + { + writefln("Unable to detect your operating system, please create an issue in the GitHub repository: '%s'", + GITHUB_REPO); + return 0; + } + + if (OS != "windows") + { + string home = environment.get("HOME", ""); + stdDir = home ~ "/" ~ ".cx/"; + if (!home || !exists(stdDir)) + { + writefln("An error occurred while validating the compiler installation."); + writefln("Some folders may be missing; check if this path is valid: '%s'.", stdDir); + writefln( + "If it does not exist, then an error occurred while installing the compiler on your system."); + return 0; + } + } + + if (isCommand(argv, "update")) + return runUpdate(); + + if (isCommand(argv, "build")) + return runBuild(); + + bool isRun = isCommand(argv, "run"); + if (isCommand(argv, "compile") || isRun) + return runCompile(isRun, args); + + try + getopt(argv, + "opt", &args.opt, + "version|v", &args.verMessage, + "help|h", &args.helpMessage, + "debug|d", &args.dbg, + "emit-c", &args.emitc, + "link|L", &args.link, + "output|o", &args.output, + "target", &args.target, + "no-header", &args.noHeader, + "gen-header", &args.genHeader, + "cflags", &args.cflags, + "cpp", &args.cpp, + "gcc", &args.gcc, + ); + catch (GetOptException e) + { + writefln("Invalid flag '%s'.", e.message[20 .. $]); + return 1; + } + + if (args.verMessage) + { + showVersion(); + return 0; + } + + if (args.helpMessage) + { + showHelp(); + return 0; + } + + cx_enforce(argv.length == 2, "The compiler expects at least one file, see 'cx -h'."); + return compile(argv[1], args); +} diff --git a/std/array.cx b/std/array.cx index 653d115..0414c9c 100644 --- a/std/array.cx +++ b/std/array.cx @@ -33,7 +33,7 @@ struct Array { return self.data[offset]; } - Iterator iter() => .{self.data, 0, self.size} + Iterator iter() => .{self.data, 0, self.size}; void free() { diff --git a/std/box.cx b/std/box.cx index 9582a49..d470ad0 100644 --- a/std/box.cx +++ b/std/box.cx @@ -2,8 +2,11 @@ struct Box { V value; bool hasValue; - static Box empty() => (Box) {hasValue = false} - static Box of(V val) => (Box) {val, true} - V unwrap() => self.value - bool isEmpty() => !self.hasValue + static Box empty() => .{hasValue = false}; + + static Box of(V val) => .{val, true}; + + V unwrap() => self.value; + + bool isEmpty() => !self.hasValue; } diff --git a/std/hashmap.cx b/std/hashmap.cx index 82ace7a..7e087e1 100644 --- a/std/hashmap.cx +++ b/std/hashmap.cx @@ -53,8 +53,6 @@ u32 murmur3(char* key, u32 len, u32 seed) { return h1; } -// Fernando -// 91839181 u32 hashKey(char* key) { return murmur3(key, key.length, 0x9747b28c); } @@ -82,7 +80,7 @@ struct HashMap { return .{calloc(cap, sizeof(Entry*)), cap, 0}; } - u32 bucketIndex(char* key) => hashKey(key) % self.cap + u32 bucketIndex(char* key) => hashKey(key) % self.cap; void set(char* key, V value) { u32 idx = self.bucketIndex(key); @@ -115,7 +113,7 @@ struct HashMap { return .empty(); } - bool has(char* key) => !self.get(key).isEmpty() + bool has(char* key) => !self.get(key).isEmpty(); void free() { u32 i = 0; diff --git a/std/slice.cx b/std/slice.cx index 48a4358..02e00b1 100644 --- a/std/slice.cx +++ b/std/slice.cx @@ -6,7 +6,7 @@ struct Slice { u32 length; bool owned; - static Slice of(T* data, u32 start, u32 end) => .{data + start, end - start, false} + static Slice of(T* data, u32 start, u32 end) => .{data + start, end - start, false}; static Slice copyOf(T* data, u32 start, u32 end) { u32 len = end - start; @@ -15,8 +15,8 @@ struct Slice { return .{newData, len, true}; } - bool cmp(T* other) => memcmp(self.ptr, other, self.length) == 0 - T get(u32 idx) => self.ptr[idx] + bool cmp(T* other) => memcmp(self.ptr, other, self.length) == 0; + T get(u32 idx) => self.ptr[idx]; void free() { if self.owned diff --git a/std/stack.cx b/std/stack.cx index 3e56900..e44af25 100644 --- a/std/stack.cx +++ b/std/stack.cx @@ -18,8 +18,8 @@ struct Stack return (Stack){data, 0, cap}; } - bool isEmpty() => self.size == 0 - bool isFull() => self.size >= self.cap + bool isEmpty() => self.size == 0; + bool isFull() => self.size >= self.cap; int!StackError push(T val) { diff --git a/std/string.cx b/std/string.cx index fb95a73..d350cd1 100644 --- a/std/string.cx +++ b/std/string.cx @@ -7,9 +7,9 @@ struct String size_t length; bool owner; - static String from(char* str) => .{str, str.length, false} - static String new(size_t cap) => .{malloc(cap), cap, true} - char* data() => self.ptr + static String from(char* str) => .{str, str.length, false}; + static String new(size_t cap) => .{malloc(cap), cap, true}; + char* data() => self.ptr; char* concat(char* other) overload { diff --git a/tests/unit.d b/tests/unit.d index 7d0ddd0..82d2656 100644 --- a/tests/unit.d +++ b/tests/unit.d @@ -120,7 +120,11 @@ TestResult runTest(string filename, string llvmLinkFlag) if (fromCode) { - int expected = to!int(firstLine); + int expected; + try + expected = to!int(firstLine); + catch (Exception e) + expected = 0; if (expected != code) { res.ok = false;