diff --git a/.vscode/launch.json b/.vscode/launch.json index 8c076a4d..2b8dd521 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -7,10 +7,9 @@ "name": "Launch", "target": "helix", "args": [ - "/Volumes/Development/Projects/Helix/helix-lang/tests/main.hlx", - "--emit-ast" + "tests/test.hlx" ], - "cwd": "${cwd}/helix-lang/", + "cwd": "${cwd}", "stopAtEntry": true } ] diff --git a/assets/code/arc/async-example.hlx b/assets/code/arc/async-example.hlx index 1bea7e9b..e3eb9854 100644 --- a/assets/code/arc/async-example.hlx +++ b/assets/code/arc/async-example.hlx @@ -6,8 +6,8 @@ async fn fetch_data() -> string { } fn main() { - let future: Future = spawn fetch_data(); // start a os-manged child process - let data = await future; + var future: Future = spawn fetch_data(); // start a os-manged child process + var data = await future; for i in data: print(f"{i=}", end=", "); diff --git a/assets/code/arc/control-flow-example.hlx b/assets/code/arc/control-flow-example.hlx index 0ddb0bef..d0df394d 100644 --- a/assets/code/arc/control-flow-example.hlx +++ b/assets/code/arc/control-flow-example.hlx @@ -1,4 +1,4 @@ -let x: int = 10; +var x: int = 10; if x > 5 { print("x is greater than 5"); @@ -12,6 +12,6 @@ unless x < 5 { // unless is the same as if not print("x is 5 or less"); } -for let i: int = 0; i < 10; i += 1{ +for var i: int = 0; i < 10; i += 1{ print(i); } \ No newline at end of file diff --git a/assets/code/arc/ffi-example.hlx b/assets/code/arc/ffi-example.hlx index 5dac20dc..f673093a 100644 --- a/assets/code/arc/ffi-example.hlx +++ b/assets/code/arc/ffi-example.hlx @@ -2,13 +2,13 @@ ffi "python" import sympy as sp; ffi "c++" import "circle.hh"; fn main() { - let eq1: sp::Eq = sp::Eq(x + y, 10) - let eq2: sp::Eq = sp::Eq(x - y, 4) - let solutions = sp.solve((eq1, eq2), (x, y)); // type inferd by the compiler + var eq1: sp::Eq = sp::Eq(x + y, 10) + var eq2: sp::Eq = sp::Eq(x - y, 4) + var solutions = sp.solve((eq1, eq2), (x, y)); // type inferd by the compiler print(solutions); - let circle = circle::Circle{10, 20}; + var circle = circle::Circle{10, 20}; print(circle->to_string()); } \ No newline at end of file diff --git a/assets/code/arc/showcase.hlx b/assets/code/arc/showcase.hlx index 4488e982..0c7a0c78 100644 --- a/assets/code/arc/showcase.hlx +++ b/assets/code/arc/showcase.hlx @@ -1,6 +1,6 @@ class Point { - let x: int; - let y: int; + var x: int; + var y: int; eval fn Point(self, x: int, y: int) { self.x = x; @@ -18,14 +18,14 @@ class Point { } fn main() { - let p = Point(0, 0); + var p = Point(0, 0); for i in 1..4 { p.move(i, i); p.display(); } - let value: u32 = p.x as u32; + var value: u32 = p.x as u32; match value { 0 -> print("The point is at the origin"), diff --git a/assets/code/arc/type-bounds-example.hlx b/assets/code/arc/type-bounds-example.hlx index 35fc14ac..e5d4fd0f 100644 --- a/assets/code/arc/type-bounds-example.hlx +++ b/assets/code/arc/type-bounds-example.hlx @@ -4,7 +4,7 @@ interface Drawable { } class Circle derives Drawable { - let radius: float; + var radius: float; fn Circle(self, radius: float) { self.radius = radius; @@ -20,7 +20,7 @@ class Circle derives Drawable { } class Square derives Drawable { - let side: float; + var side: float; fn Square(self, side: float) { self.side = side; @@ -35,15 +35,15 @@ class Square derives Drawable { } } -fn draw_all(...objects: T) - requires if T is Drawable { +fn draw_all(...objects: T) + requires T impl Drawable { for obj in objects { obj.draw(); } } fn main() { - let shapes = (Circle(1.0), Square(1.0), Circle(2.0)); // type of tuple + var shapes = (Circle(1.0), Square(1.0), Circle(2.0)); // type of tuple draw_all(shapes...); } diff --git a/assets/code/language/types.hlx b/assets/code/language/types.hlx index 5d34b4bb..afa7122d 100644 --- a/assets/code/language/types.hlx +++ b/assets/code/language/types.hlx @@ -14,7 +14,7 @@ fn main() -> i32 { delete int_holder } - // Helix has type inference, so theres no need to specify the type of value. But any modifiers + // Helix impl type inference, so theres no need to specify the type of value. But any modifiers // like pointers, questionable, etc. must be specified. (enforced for readability and safety) var *value? = int_holder.get(1) @@ -22,7 +22,7 @@ fn main() -> i32 { value = Heap::construct(21) // Equivalent to new int(21) in C++ } - // Helix has support for named parameters/default values + // Helix impl support for named parameters/default values var *invalid_value? = int_holder.get(index=100) // Attempt to fetch an out-of-bounds value if invalid_value?: @@ -40,14 +40,14 @@ fn main() -> i32 { // Structs in Helix are data containers with no methods. // They can include: operator overloads, nested structs, destructors, enums, and fields. // All fields are public by default. -struct Data { +struct Data { // Regular pointers: non-nullable, no arithmetic, and auto-dereferenced (like references in other languages). var value: *T? } // Classes in Helix support methods, fields, and constructors. // Fields are private by default, while methods and constructors are public by default. -class Holder requires { +class Holder { // Unsafe pointers: nullable, allow pointer arithmetic, and require manual dereferencing. var num_values: usize var many_values: unsafe *Data @@ -67,7 +67,7 @@ class Holder requires { } } - op delete fn (self) { + fn op delete (self) { for i in 0..self.num_values { if self.many_values[i].value? { Heap::drop(self.many_values[i].value) @@ -77,8 +77,7 @@ class Holder requires { Heap::drop(self.many_values) } - fn get(self, index: usize) -> *T? - requires { + fn get(self, index: usize) -> *T? { if index < self.num_values { return self.many_values[index].value } else { diff --git a/build.hlx b/build.hlx new file mode 100755 index 00000000..ebe9a301 --- /dev/null +++ b/build.hlx @@ -0,0 +1,47 @@ +/* +Everything in the api: +fn set_target(name: string) +fn end_target(name: string? = null) +fn set_builddir(path: string) +fn set_project(name: string) +fn set_version(version: string) +fn set_description(description: string) +fn add_links(links: vec::) +fn add_link(link: string) +fn add_include(include: string) +fn add_includes(includes: vec::) +fn add_source(source: string) +fn set_linkage(linkage: Linkage) +fn after_build(callback: fn (*Target) -> void) +fn before_build(callback: fn (*Target) -> void) +fn set_cxxflags(flags: vec::) +fn add_define(name: string, value: string) + +*/ +import vial::rt::api::*; + +fn build() -> i32 { + + set_target("vial_runtime"); + add_source("vial/rt/api.hlx"); + add_source("vial/rt/logger.hlx"); + add_source("vial/rt/fs.hlx"); + + add_include("vial/"); + set_cxxflags(["-std=c++20", "-Wall", "-Wextra", "-Werror"]); + add_define("USING_MSVC", "true"); + add_define("USING_CLANG", "true"); + end_target(); + + set_target("vial_build"); + add_source("vial/vial.hlx"); + add_include("vial/"); + + set_cxxflags(["-std=c++20", "-Wall", "-Wextra", "-Werror"]); + + add_define("USING_MSVC", "true" ); + add_define("USING_CLANG", "true" ); + end_target(); + + return 0; +} \ No newline at end of file diff --git a/lang/arc/.hlx b/lang/arc/.hlx index 6b9d907b..0a8aad8a 100644 --- a/lang/arc/.hlx +++ b/lang/arc/.hlx @@ -4,8 +4,8 @@ class Point { - pub let x: f32 - pub let y: f32 + pub var x: f32 + pub var y: f32 } @@ -15,7 +15,7 @@ class Shape { class Circle derives Shape { pub fn area() -> f32 { return 3.14 * radius * radius } - pub let radius: f32 + pub var radius: f32 } class Base { @@ -53,12 +53,12 @@ fn make_noise(a: Box) { print(a.speak()) // Uses v-table lookup } -fn make_noise(a: T) requires if T has Animal { +fn make_noise(a: T) requires T impl Animal { print(a.speak()) // No v-table lookup, method is inlined } class SecureData { - priv let key: str + priv var key: str pub fn get_key() -> str { return key } } \ No newline at end of file diff --git a/lang/arc/all_keyword_usage.hlx b/lang/arc/all_keyword_usage.hlx index da45e5bf..63cb93c8 100644 --- a/lang/arc/all_keyword_usage.hlx +++ b/lang/arc/all_keyword_usage.hlx @@ -6,8 +6,8 @@ macro _string! { class Foo { - let a: u8; - let b: u8; + var a: u8; + var b: u8; op as fn (self) -> *string { return "Foo"; @@ -19,8 +19,8 @@ class Foo { } class Foo { - let a: u8; - let b: u8; + var a: u8; + var b: u8; fn to_string_some_special_name() -> string { @@ -35,14 +35,14 @@ class Foo { } fn main() { - let foo = Foo(); + var foo = Foo(); unsafe foo[1]; const foo[1]; } class CustomType { - let ptr: *char; - let len: usize; + var ptr: *char; + var len: usize; op as fn () -> PyObject* { /// ... @@ -53,9 +53,9 @@ class CustomType { } } -let a = CustomType("hello world"); +var a = CustomType("hello world"); -let b = a as PyObject*; +var b = a as PyObject*; @@ -106,7 +106,7 @@ const for i in 0:10 { class Point { - let static n_points = 0; + var static n_points = 0; static const fn get_n_points() -> int { return Point::n_points; @@ -140,26 +140,26 @@ fn div_by_0_soft(a: int, b: int = 0) -> Result { fn main() { // SOFT ERROR - let const a = div_by_0_soft(10, 0); + var const a = div_by_0_soft(10, 0); if a.is_err() { print("DivByZeroError"); } else { - let a = a.unwrap(); + var a = a.unwrap(); } // HARD ERROR try { - let b = div_by_0_hard_a(10, 0); + var b = div_by_0_hard_a(10, 0); } catch (err: DivByZeroError) { print("DivByZeroError"); } // or - let b = try div_by_0_hard_a(10, 0) catch (err: DivByZeroError) 10 catch (err: BaseError) 9; + var b = try div_by_0_hard_a(10, 0) catch (err: DivByZeroError) 10 catch (err: BaseError) 9; - let c = div_by_0_hard_b(10, 0); // not forced to handle but you can if you want + var c = div_by_0_hard_b(10, 0); // not forced to handle but you can if you want } @@ -215,22 +215,22 @@ return ( )(a, b); -let foo: tuple = (1, 2, 3, 4, 4, 5, 6) -let foo: (u8) = (6) // says error that not -let foo: u8 = 6 +var foo: tuple = (1, 2, 3, 4, 4, 5, 6) +var foo: (u8) = (6) // says error that not +var foo: u8 = 6 // optimize using avx -let mat: matrix { +var mat: matrix { 1, 2, 3, 2, 5, 6, 3, 4, 7, } // dangling dildo pointer -let a: int* = &10; // invalid since you cant refrence a temp lvalue +var a: int* = &10; // invalid since you cant refrence a temp lvalue // dyn and final cant be used together -let unsafe a = dyn!(10); +var unsafe a = dyn!(10); a = "string"; @@ -253,7 +253,7 @@ f64 < 64 bytes f64 > 64 bytes -let a:u32 = 10; +var a:u32 = 10; a.add(1) in cpp @@ -262,16 +262,16 @@ std::uint_32 add (std::uint_32 a, std::uint_32 b) { return a + b; } -fn Map(iter: T, fn (T) -> T) -requires if T has Iterable { +fn Map(iter: T, fn (T) -> T) + requires T impl Iterable { } module extend_syntax { class Foo { - let a: u8; - let b: u8; + var a: u8; + var b: u8; } } @@ -282,7 +282,7 @@ module idk_something_else { } } - let inst = Foo(); + var inst = Foo(); inst.add(); @@ -291,22 +291,22 @@ module idk_something_else { } // ffi "py" import os; ffi "py" import time; ffi "py" import -// math; fn main() { let A: float = 0.0; let B: -// float = 0.0; while true { let z: list = [0.0]*1760; let -// b: list = [" "]*1760; for (let j in range(0, 628, 7)){for(let i in -// range(0, 628, 2)) { let c: float = math:: sin(i); let d:float=math::cos(j);let -// e: float = math::sin(A); let f: float =math::sin(j);let g:float=math::cos(A); -// let h: float = d + 2; let D: float=1/( c * h * e+ f * g + 5); -// let l: float = math:: cos ( i ) ; let m : float -// = math::cos(B); let n : float = math :: sin(B); -// let t: float = c * h * g-f*e;let x: int=int(40 + 30 * D * (l * h * m - t * n)); -// let y: int = int(12 + 15 * D * ( l * h * n + t * m)); -// let o: int = int(x + 80 * y); let N:int=int(8* ((f * e - c * +// math; fn main() { var A: float = 0.0; var B: +// float = 0.0; while true { var z: list = [0.0]*1760; let +// b: list = [" "]*1760; for (var j in range(0, 628, 7)){for(var i in +// range(0, 628, 2)) { var c: float = math:: sin(i); var d:float=math::cos(j);let +// e: float = math::sin(A); var f: float =math::sin(j);var g:float=math::cos(A); +// var h: float = d + 2; var D: float=1/( c * h * e+ f * g + 5); +// var l: float = math:: cos ( i ) ; var m : float +// = math::cos(B); var n : float = math :: sin(B); +// var t: float = c * h * g-f*e;var x: int=int(40 + 30 * D * (l * h * m - t * n)); +// var y: int = int(12 + 15 * D * ( l * h * n + t * m)); +// var o: int = int(x + 80 * y); var N:int=int(8* ((f * e - c * // d * g) * m - c * d * e - f * g - l * d * n)); if 0 <= y < 22 // && 0 <= x < 80 && D > z[o] { z[o] = D; if N > 0 { let -// charIndex: int = N; } else { let charIndex: int = 0; } +// charIndex: int = N; } else { var charIndex: int = 0; } // b[o] = ".,-~:;=!*#$@"[charIndex]; } } } os::system("cls"); -// for (let k: int = 0; k < 1760; k++) { if k % +// for (var k: int = 0; k < 1760; k++) { if k % // 80 == 0 { print('\n', end=''); } else // { print(b[k], end=''); }} A // += 0.04; B += 0.02;time diff --git a/lang/arc/examples/test.hlx b/lang/arc/examples/test.hlx index 06bfa099..0bfc980d 100644 --- a/lang/arc/examples/test.hlx +++ b/lang/arc/examples/test.hlx @@ -17,26 +17,26 @@ ffi "c++" { import cxx::primitives; // we define helix bindings for the c++ primitives - inline let globalVar: cxx::primitives::_int; + inline var globalVar: cxx::primitives::_int; // C-style struct struct CStyleStruct { - let a: cxx::primitives::_int; - let b: cxx::primitives::_double; + var a: cxx::primitives::_int; + var b: cxx::primitives::_double; } struct CPPStyleStruct { - let a: cxx::primitives::_int; - let b: cxx::primitives::_double; + var a: cxx::primitives::_int; + var b: cxx::primitives::_double; const fn toString() -> cxx_file_test_includes::std::string; } - fn add(a: T, b: T) -> T requires ; + fn add(a: T, b: T) -> T; - interface Arithmetic requires if T is cxx::std::integral | cxx::std::floating_point; + interface Arithmetic requires T is cxx::std::integral | cxx::std::floating_point; - class Calculator requires if T is Arithmetic { + class Calculator requires T is Arithmetic { pub fn add(a: T, b: T) -> T; pub fn subtract(a: T, b: T) -> T; } @@ -47,7 +47,7 @@ ffi "c++" { pub fn setValue(self, val: cxx::primitives::_int); pub const fn getValue(self) -> cxx::primitives::_int; - priv let value: cxx::primitives::_int; + priv var value: cxx::primitives::_int; } #[DEFINE_NEW_SYMBOL(fn createClosure(), prms=helix::SYMBOL_TYPE::UKNOWN, ret=helix::SYMBOL_TYPE::UKNOWN)] @@ -55,7 +55,7 @@ ffi "c++" { struct Generator { struct promise_type { - let current_value: cxx::primitives::_int; + var current_value: cxx::primitives::_int; fn yield_value(cxx::primitives::_int value) -> cxx_file_test_includes::std::suspend_always; fn initial_suspend() -> cxx_file_test_includes::std::suspend_always; @@ -68,7 +68,7 @@ ffi "c++" { } struct iterator { - let coro: cxx_file_test_includes::std::coroutine_handle; + var coro: cxx_file_test_includes::std::coroutine_handle; const op != fn ne(const other: &iterator); op ++ fn inc() -> &iterator; const op * fn mul() -> cxx::primitives::_int; @@ -77,7 +77,7 @@ ffi "c++" { fn begin() -> iterator; fn end() -> iterator; - let handle: cxx_file_test_includes::std::coroutine_handle; + var handle: cxx_file_test_includes::std::coroutine_handle; } inline fn sequence(start: cxx::primitives::_int, step: cxx::primitives::_int) -> cxx_file_test_includes::Generator; diff --git a/lang/docs/rewrite_roadmap.md b/lang/docs/rewrite_roadmap.md index 84e4939c..cb6583c7 100644 --- a/lang/docs/rewrite_roadmap.md +++ b/lang/docs/rewrite_roadmap.md @@ -153,12 +153,12 @@ class Location { fn column(self) -> usize; fn serialize(self) -> map::; - op == fn (self, other: Location) -> bool; - op != fn (self, other: Location) -> bool; - op < fn (self, other: Location) -> bool; - op > fn (self, other: Location) -> bool; - op <= fn (self, other: Location) -> bool; - op >= fn (self, other: Location) -> bool; + fn op == (self, other: Location) -> bool; + fn op != (self, other: Location) -> bool; + fn op < (self, other: Location) -> bool; + fn op > (self, other: Location) -> bool; + fn op <= (self, other: Location) -> bool; + fn op >= (self, other: Location) -> bool; } ``` @@ -186,12 +186,12 @@ class Token { fn value(self) -> string?; fn serialize(self) -> map::; - op == fn (self, other: Token) -> bool; - op != fn (self, other: Token) -> bool; - op == fn (self, other: TokenKind) -> bool; - op != fn (self, other: TokenKind) -> bool; - op == fn (self, other: string) -> bool; - op != fn (self, other: string) -> bool; + fn op == (self, other: Token) -> bool; + fn op != (self, other: Token) -> bool; + fn op == (self, other: TokenKind) -> bool; + fn op != (self, other: TokenKind) -> bool; + fn op == (self, other: string) -> bool; + fn op != (self, other: string) -> bool; } ``` @@ -211,15 +211,15 @@ class TokenKind { fn to_kind(self, value: string?) -> Kind?; fn serialize(self) -> map::; - op == fn (self, other: TokenKind) -> bool; - op != fn (self, other: TokenKind) -> bool; - op == fn (self, other: string) -> bool; - op != fn (self, other: string) -> bool; - op == fn (self, other: Kind) -> bool; - op != fn (self, other: Kind) -> bool; + fn op == (self, other: TokenKind) -> bool; + fn op != (self, other: TokenKind) -> bool; + fn op == (self, other: string) -> bool; + fn op != (self, other: string) -> bool; + fn op == (self, other: Kind) -> bool; + fn op != (self, other: Kind) -> bool; - op as fn (self) -> string; - op as fn (self) -> Kind; + fn op as (self) -> string; + fn op as (self) -> Kind; } ``` @@ -235,10 +235,10 @@ class File { fn File(self, abs_path: string, content: string); fn serialize(self) -> map::; fn read(self) -> string; - op [] fn (self, index: Range::) -> string?; + fn op [] (self, index: Range::) -> string?; - op == fn (self, other: File) -> bool; - op != fn (self, other: File) -> bool; + fn op == (self, other: File) -> bool; + fn op != (self, other: File) -> bool; } ``` diff --git a/lang/syntax/.ebnf b/lang/syntax/.ebnf index b6fade5e..21a6be35 100644 --- a/lang/syntax/.ebnf +++ b/lang/syntax/.ebnf @@ -10,12 +10,12 @@ hex_digit ::= [0-9a-fA-F]; // generic only allows for const or eval'd expressions to be used, they can be variables but must // be const and known at compile time self ::= '&'? "self"; // self == *this | &self == this -generic ::= '<' (type | expr) (',' (type | expr))* '>'; // | requires +generic ::= '<' (type | expr) (',' (type | expr))* '>'; // | fn_return ::= "->" type; // -> T // change this to a binary operator operator_path ::= "op" operator; // op+ | op- -scoped_path_elm ::= "*"* (ident | operator_path) '?'? "requires"? generic?; // x | op+ | self | x | *x | Foo requires +scoped_path_elm ::= "*"* (ident | operator_path) '?'? generic?; // x | op+ | self | x | *x | Foo member_path ::= expr '.' scoped_path_elm; // x.y | x.y | x::V.y | x::V[T].*y.z scoped_path ::= ("::")? scoped_path_elm ("::" scoped_path_elm)*; // ::V | V | V[T] | V[T]::R @@ -194,43 +194,43 @@ structured_binding ::= '(' (untyped_param | param) (',' (untyped_param | param)) var_param ::= (untyped_param ('=' expr) | param ('=' expr)?); var_decl ::= visibility? (specifiers* | "var") ((var_param (',' var_param)*) | (structured_binding '=' expr)); -requires_bound ::= "where" expr; // where x impl T -requires_param ::= "const"? type_hint? (param | ident) ('=' expr | type)?; -requires_decl ::= "requires" '<' (requires_param (',' (requires_param))*)? '>'; // requires | requires +generic_bound ::= "requires" expr; // requires x impl T +generic_param ::= "const"? type_hint? (param | ident) ('=' expr | type)?; +generic_decl ::= '<' (generic_param (',' (generic_param))*)? '>'; // | -requires ::= requires_decl requires_bound?; // requires | requires implements ::= "impl" (type (',' type)*); // impl Foo, Bar | impl Foo derives ::= "derives" (visibility? type (',' visibility? type)*); // derives Foo, Bar | derives Foo // add union support to this +// // union Some { // x: i32, // y: (f32 | i32, i32), // } +// // var val: Some = Some::x(1); // var val2 Some::y(1, 2); -struct_decl ::= visibility? "struct" scoped_path implements? requires? block?; // struct Foo { ... } -class_decl ::= visibility? "class" scoped_path implements? derives? requires? block?; // class Foo { ... } -interface_decl ::= visibility? "interface" scoped_path implements? requires? block?; // interface Foo { ... } -extend_decl ::= "extend" scoped_path implements? derives? requires? block; // extend Foo { ... } +struct_decl ::= visibility? "struct" generic_decl? scoped_path implements? generic_bound? block?; // struct Foo { ... } +class_decl ::= visibility? "class" generic_decl? scoped_path implements? derives? generic_bound? block?; // class Foo { ... } +interface_decl ::= visibility? "interface" generic_decl? scoped_path implements? generic_bound? block?; // interface Foo { ... } +extend_decl ::= "extend" generic_decl? scoped_path implements? derives? generic_bound? block; // extend Foo { ... } enum_values ::= ident ('=' expr)? (',' ident ('=' expr)?)*; // A = 1, B = 2 -enum_decl ::= visibility? "enum" scoped_path derives? requires? '{' enum_values? '}'; // enum Foo { A = 1, B = 2 } +enum_decl ::= visibility? "enum" generic_decl? scoped_path derives? generic_bound? '{' enum_values? '}'; // enum Foo { A = 1, B = 2 } -type_alias ::= visibility? "type" scoped_path requires? '=' type; // type Foo requires = i32 +type_alias ::= visibility? "type" generic_decl? scoped_path generic_bound? '=' type; // type Foo = i32 module_decl ::= visibility? "inline"? "module" scoped_path block; // module Foo { ... } // both override and visibility cant be used together func_decl ::= visibility? ("override" | "final")? "fn" scoped_path '(' parameters? ')' qualifier* fn_return? block?; // fn foo(x: i32) -> i32 { ... } - -// operator_decl.scoped_path == (("::"? ident "requires"? generic?)+ "::")? "op" operator -operator_decl ::= visibility? ("override" | "final")? scoped_path '(' parameters? ')' ('[' ident ']')? qualifier* fn_return? block?; // op+ (x: i32) -> i32 { ... } +// |-------------------------< ^^^^^^^^^^^ +// |-> also covers operators with op+, ... being a part of scoped_path test_decl ::= "test" string_literal block; // test "foo" { ... } ffi ::= "ffi" string_literal; // ffi "c++" import "include/lang/panic.h"; -primary_declaration ::= var_decl | struct_decl | class_decl | interface_decl | extend_decl | enum_decl | type_alias | func_decl | operator_decl +primary_declaration ::= var_decl | struct_decl | class_decl | interface_decl | extend_decl | enum_decl | type_alias | func_decl declaration ::= (proc_macro_invoke* ffi? primary_declaration) | test_decl | module_decl; /* Macros */ @@ -267,4 +267,4 @@ built_in_directives ::= '@' "volatile" '(' ')' // makes the compiler not opt | '@' "alignas" '(' Number ')'; // sets the alignment of the variable/declaration built_in_macros ::= "__file__" '!' | "__line__" '!' | "__column__" '!' | "__function__" '!' | "__module__" '!'; -program ::= (declaration | (import | multi_import) | macro | proc_macro | statement)*; \ No newline at end of file +program ::= (declaration | (import | multi_import) | macro | proc_macro | statement | expr)*; \ No newline at end of file diff --git a/lang/syntax/new_helix.ebnf b/lang/syntax/new_helix.ebnf index 9ca1ceaa..a8f7a4e8 100644 --- a/lang/syntax/new_helix.ebnf +++ b/lang/syntax/new_helix.ebnf @@ -28,8 +28,8 @@ NamedArgument ::= Identifier ':' Expression; // x: 1 ArgumentSeq ::= (NamedArgument | Expression) (',' (NamedArgument | Expression))*; // (x: 1, y: 2) | (1, 2) | () ExplicitArgumentSeq ::= ('.'? NamedArgument) (',' ('.'? NamedArgument))*; // if a dot is found all the following named args must have a . -AssertExpression ::= "assert" Expression "," StringLiteral; -TryCatchExpression ::= "try" ':' Expression ("catch" CatchArguments? ':' Expression)+; // let x: i32 = try: foo() catch e: 1 catch: 2 +AssertExpression ::= "assert" Expression "else" StringLiteral; +TryCatchExpression ::= "try" ':' Expression ("catch" CatchArguments? ':' Expression)+; // var x: i32 = try: foo() catch e: 1 catch: 2 ObjectInitializer ::= Type '{' ExplicitArgumentSeq? '}'; // T { x: 1, y: 2 } ImplicitObjectInitializer ::= '{' ExplicitArgumentSeq? '}'; // { .x: 1, .y: 2 } TernaryExpression ::= "if" Expression ':' Expression "else" ':' Expression; // if z: x else: y @@ -153,7 +153,7 @@ StructuredVar ::= (ExplicitParam | ImplicitParam) ('=' Expression)?; // ffi variables allow for ffi mangling as defined by the abi rule se | "eval"; VariableDeclaration ::= VisibilityMod? - (VariableSpec | "let" | "const" | "static") + (VariableSpec | "var" | "const" | "static") StorageQual? // const | static | inline | const eval | unsafe | async ((DestructuredVar | StructuredVar) (',' (DestructuredVar | StructuredVar))*); @@ -162,32 +162,32 @@ FFIBase ::= "ffi" StringLiteral; FFIScope ::= FFIBase ScopeBlock; /* Generics/With */ -RequiresDeclaration ::= "requires" '<' RequiresParam* '>' RequiresConstraint?; -RequiresParam ::= ("const" Identifier (':' Type)?) | Identifier; -RequiresConstraint ::= ( "if" | "unless") Expression +GenericDeclaration ::= '<' GenericParam* '>' GenericConstraint?; +GenericParam ::= ("const" Identifier (':' Type)?) | (Identifier (("impl" | "derives") Type)?); +GenericConstraint ::= ( "if" | "unless") Expression ("else" ("if" | "unless") Expression ScopeBlock)* ( "else" ScopeBlock)?; -HasDeclaration ::= "has" Type (',' Type)*; -DerivesDeclaration ::= "derives" (UnsafeModifier? VisibilityMod?) Type (',' (UnsafeModifier? VisibilityMod?) Type)*; +ImplDeclaration ::= "impl" Type (',' Type)*; +DerivesDeclaration ::= "derives" (UnsafeModifier? VisibilityMod?) Type (',' (UnsafeModifier? VisibilityMod?) Type)*; /* Function Declarations */ FunctionBase ::= FFIBase? VisibilityMod?; // NO-ORDER // impl of ops are fn ...::op...() -> ...; - this implicitly implements the alias as well (or vice versa) -FunctionDeclaration ::= FunctionBase "fn" ScopedPath '(' ExplicitDefaultParamPack? ')' StorageQual? ArrowReturn? RequiresDeclaration? (ScopeBlock | ';'); -OperatorDeclaration ::= FunctionBase "fn" ("op" Operator) '(' ExplicitDefaultParamPack? ')' ('[' ScopedPath ']')? StorageQual? ArrowReturn? RequiresDeclaration? (ScopeBlock | ';'); -// ^^^^^^^^^^^^^^^ this is also a scoped path so there may be ambiguity in parsing +FunctionDeclaration ::= FunctionBase "fn" GenericDeclaration? ScopedPath '(' ExplicitDefaultParamPack? ')' StorageQual? ArrowReturn? (ScopeBlock | ';'); +OperatorDeclaration ::= FunctionBase "fn" GenericDeclaration? ("op" Operator) '(' ExplicitDefaultParamPack? ')' ('[' ScopedPath ']')? StorageQual? ArrowReturn? (ScopeBlock | ';'); +// ^^^^^^^^^^^^^^^ this is also a scoped path so there may be ambiguity in parsing || ALSO maybe swap op to operator /* Class Declarations */ UDTBase ::= FFIBase? VisibilityMod? "const"?; // NO-ORDER (const is only for class structs and interfaces) -ClassDeclaration ::= UDTBase "class" Identifier (DerivesDeclaration? HasDeclaration? RequiresDeclaration?) ScopeBlock; +ClassDeclaration ::= UDTBase "class" GenericDeclaration? Identifier (DerivesDeclaration? ImplDeclaration?) ScopeBlock; EnumDeclaration ::= UDTBase "enum" Identifier ("derives" Type)? (('{' (',' Identifier ('=' Expression)?)* '}') | ';'); -TypeDeclaration ::= VisibilityMod? "type" Identifier RequiresDeclaration? '=' Type; +TypeDeclaration ::= VisibilityMod? "type" GenericDeclaration? Identifier '=' Type; ModuleDeclaration ::= VisibilityMod? "inline"? "module" ScopedPath? ScopeBlock; -StructDeclaration ::= UDTBase "struct" Identifier (HasDeclaration? RequiresDeclaration?) ScopeBlock; -InterfaceDeclaration ::= UDTBase "interface" Identifier (HasDeclaration? RequiresDeclaration?) ScopeBlock; -ExtendsDeclaration ::= "extend" ScopedPath (HasDeclaration? RequiresDeclaration?) ScopeBlock; +StructDeclaration ::= UDTBase "struct" GenericDeclaration? Identifier (ImplDeclaration?) ScopeBlock; +InterfaceDeclaration ::= UDTBase "interface" GenericDeclaration? Identifier (ImplDeclaration?) ScopeBlock; +ExtendsDeclaration ::= "extend" GenericDeclaration? ScopedPath (ImplDeclaration?) ScopeBlock; /* Test Declarations */ // tests run when the following is passed to the helix compiler: --test @@ -299,7 +299,7 @@ HexDigit ::= [0-9a-fA-F]; /* Literals */ // blank expressions only need to be used if the type is questionable otherwise for something like -// `let x: [i32: 5];` is default initialized +// `var x: [i32: 5];` is default initialized NumericSuffix ::= PrimitiveTypes | Identifier; // i32 | f32 | u8 | ... ExponentPart ::= ('e' | 'E' | 'p' | 'P') ('+' | '-')? Digit+; // e-3 | p+3 diff --git a/lib-helix b/lib-helix index 7978c11f..b98af68e 160000 --- a/lib-helix +++ b/lib-helix @@ -1 +1 @@ -Subproject commit 7978c11ffeaf96b176d4d4380d56ea219e3ccec0 +Subproject commit b98af68eb4e957649a90496c3068526c4f7baee9 diff --git a/libs/neo-panic/enums/error_codes.def b/libs/neo-panic/enums/error_codes.def index 7f29bb5d..d5c51e59 100644 --- a/libs/neo-panic/enums/error_codes.def +++ b/libs/neo-panic/enums/error_codes.def @@ -71,7 +71,7 @@ const helix::CompTimeMap ERROR_MAP({ MP(0.3005, Errors{"cant use static with self", "remove static from the function specifier.", error::ERR}), MP(0.3006, Errors{"self cant contain a type or a value", "remove the type or value from self.", error::ERR}), MP(0.3007, Errors{"class constructor must have a self parameter, and cannot have a return type or be static", "", error::ERR}), - MP(0.3008, Errors{"This function can panic but is not marked as questionable. Functions that panic must be explicitly marked.", "Add a '?' to the function's return type. If the function is void, change the return type to 'void?'. Note: constructors should not panic.", error::ERR}), + MP(0.3008, Errors{"This function can panic but is not marked as questionable. Doing so will result in an exception being thrown.", "Add a '?' to the function's return type. If the function is void, change the return type to 'void?'. Note: constructors should not panic.", error::ERR}), MP(0.3018, Errors{"Panic found here", "", error::NOTE}), MP(0.3009, Errors{"This function yields values but is not marked as a generator. Functions that yield must be explicitly marked.", "Add 'yield' to the return type of the function. For example: 'yield i32'.", error::ERR}), MP(0.3019, Errors{"Yield found here", "", error::NOTE}), diff --git a/libs/neo-process/include/process.hlx b/libs/neo-process/include/process.hlx index b29c64ff..685d5c3e 100644 --- a/libs/neo-process/include/process.hlx +++ b/libs/neo-process/include/process.hlx @@ -25,33 +25,33 @@ module neo { inline module detail { struct process_handle { eval if defined(__NEO_WINDOWS__) { - let hProcess: HANDLE; - let hThread: HANDLE; + var hProcess: HANDLE; + var hThread: HANDLE; } else { - let pid: pid_t; + var pid: pid_t; } } struct pipe_handle { eval if defined(__NEO_WINDOWS__) { - let hRead: HANDLE; - let hWrite: HANDLE; + var hRead: HANDLE; + var hWrite: HANDLE; } else { - let fd: *int = salloc(sizeof(int) * 2); + var fd: *int = salloc(sizeof(int) * 2); } } class process_info { - let handle: process_handle?; - let stdin: pipe_handle?; - let stdout: pipe_handle?; - let stderr: pipe_handle?; + var handle: process_handle?; + var stdin: pipe_handle?; + var stdout: pipe_handle?; + var stderr: pipe_handle?; inline fn create_pipe() -> pipe_handle { - let pipe: pipe_handle; + var pipe: pipe_handle; eval if defined(__NEO_WINDOWS__) { - let saAttr: SECURITY_ATTRIBUTES; + var saAttr: SECURITY_ATTRIBUTES; saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); saAttr.bInheritHandle = TRUE; saAttr.lpSecurityDescriptor = &null; @@ -81,11 +81,11 @@ module neo { } inline fn create_process(const command: &std::string) -> process_handle { - let handle: process_handle; + var handle: process_handle; eval if defined(__NEO_WINDOWS__) { - let si: STARTUPINFO; - let pi: PROCESS_INFORMATION; + var si: STARTUPINFO; + var pi: PROCESS_INFORMATION; ZeroMemory(&si, sizeof(si)); si.cb = sizeof(si); @@ -100,7 +100,7 @@ module neo { handle.hThread = pi.hThread; } else { - let pid = fork(); + var pid = fork(); if pid == -1 { panic std::runtime_error("failed to create process"); @@ -128,7 +128,7 @@ module neo { inline fn write_to_pipe(pipe: &pipe_handle, const data: &std::string) -> void { eval if defined(__NEO_WINDOWS__) { - let bytes_written: DWORD; + var bytes_written: DWORD; WriteFile(pipe.hWrite, data.c_str(), data.size(), &bytes_written, &null); } else { write(pipe.fd[1], data.c_str(), data.size()); @@ -136,11 +136,11 @@ module neo { } inline fn read_from_pipe(pipe: &pipe_handle) -> std::string { - let data: std::string; + var data: std::string; eval if defined(__NEO_WINDOWS__) { - let bytes_read: DWORD; - let buffer[4096]: CHAR; + var bytes_read: DWORD; + var buffer[4096]: CHAR; while (true) { if (!ReadFile(pipe.hRead, buffer, sizeof(buffer), &bytes_read, &null) || @@ -152,8 +152,8 @@ module neo { } } else { - let buffer[4096]: char; - let bytes_read: size_t; + var buffer[4096]: char; + var bytes_read: size_t; while (true) { bytes_read = read(pipe.fd[0], buffer, sizeof(buffer)); diff --git a/source/controller/include/config/cxx_flags.hh b/source/controller/include/config/cxx_flags.hh index 71d04c33..d195007d 100644 --- a/source/controller/include/config/cxx_flags.hh +++ b/source/controller/include/config/cxx_flags.hh @@ -12,7 +12,7 @@ class CF { std::string_view mingw; }; -constexpr CF debugModeFlag{.gcc="-g -g3", .clang="-g -g3", .msvc="/Zi", .mingw="-g -g3"}; +constexpr CF debugModeFlag{.gcc="-g -g3 -DDEBUG", .clang="-g -g3 -DDEBUG", .msvc="/Zi /DDEBUG", .mingw="-g -g3 -DDEBUG"}; constexpr CF warnAllFlag{.gcc="-Wall", .clang="-Wall", .msvc="/W4", .mingw="-Wall"}; constexpr CF noWarningsFlag{.gcc="-w", .clang="-w", .msvc="/w", .mingw="-w"}; constexpr CF stdC23Flag{.gcc="-std=c23", .clang="-std=c23", .msvc="/std:c23", .mingw="-std:c23"}; diff --git a/source/controller/include/tooling/__new_tooling.hlx b/source/controller/include/tooling/__new_tooling.hlx index 6294e881..5f4df91e 100644 --- a/source/controller/include/tooling/__new_tooling.hlx +++ b/source/controller/include/tooling/__new_tooling.hlx @@ -75,12 +75,12 @@ class FileMemoryBuffer { /// \param rel_to the file path relative to which this buffer's path should be resolved. /// defaults to the current working directory. /// \param file_type the type of file this buffer represents, defaults to FileLevel::User. - fn FileMemoryBuffer(self, + fn FileMemoryBuffer(self, src: T, rel_to: Path = __CONTROLLER_FS_N!::get_cwd(), name: string = "__/helix$$internal/__", file_level: FileLevel = FileLevel::User, - file_type: FileType = FileType::Source) requires { + file_type: FileType = FileType::Source) { self.source = src; self.name = name; self.rel_to = rel_to; @@ -105,7 +105,7 @@ class FileMemoryBuffer { /// replaced after initialization. /// /// \param other the buffer to copy from. - op = fn assign_op(self, const _: &FileMemoryBuffer) -> &FileMemoryBuffer = delete; + fn op = (self, const _: &FileMemoryBuffer)[assign_op] -> &FileMemoryBuffer = delete; /// move constructor to transfer ownership of a file memory buffer. this /// constructor is used to move the contents of a buffer to another buffer, @@ -121,7 +121,7 @@ class FileMemoryBuffer { /// functions. /// /// \param other the buffer to move from. - op = fn assign_op(self, _: FileMemoryBuffer &&) -> &FileMemoryBuffer = default; + fn op = (self, _: FileMemoryBuffer &&)[assign_op] -> &FileMemoryBuffer = default; /// returns a reference to the source string. this provides direct access to /// modify the source string held within the buffer, typically used when the @@ -188,13 +188,13 @@ class FileMemoryBuffer { fn set_source(self, const src: &string); priv { // TODO: AST - add priv blocks - let source: string; - let name: string; - let rel_to: Path = controller::file_system::get_cwd(); - let file_level: FileLevel = FileLevel::User; - let file_type: FileType = FileType::Source; - let is_valid: bool = false; - let locked: bool = false; + var source: string; + var name: string; + var rel_to: Path = controller::file_system::get_cwd(); + var file_level: FileLevel = FileLevel::User; + var file_type: FileType = FileType::Source; + var is_valid: bool = false; + var locked: bool = false; } } @@ -227,8 +227,8 @@ class LexicalProcessor derives pub FrontendProcessor { fn LexicalProcessor(self, const _: &LexicalProcessor) = default; fn LexicalProcessor(self, _: &&LexicalProcessor) = default; - op = fn assign_op(self, const _: &LexicalProcessor) -> &LexicalProcessor = default; - op = fn move_op(self, _: &&LexicalProcessor) -> &LexicalProcessor = default; + fn op = (self, const _: &LexicalProcessor)[assign_op] -> &LexicalProcessor = default; + fn op = (self, _: &&LexicalProcessor)[move_op] -> &LexicalProcessor = default; #[override] fn delete(self) = default; @@ -241,8 +241,8 @@ class PreProcessor derives pub FrontendProcessor { fn PreProcessor(self, const _: &PreProcessor) = default; fn PreProcessor(self, _: &&PreProcessor) = default; - op = fn assign_op(self, const _: &PreProcessor) -> &PreProcessor = default; - op = fn move_op(self, _: &&PreProcessor) -> &PreProcessor = default; + fn op = (self, const _: &PreProcessor)[assign_op] -> &PreProcessor = default; + fn op = (self, _: &&PreProcessor)[move_op] -> &PreProcessor = default; #[override] fn delete(self) = default; @@ -255,8 +255,8 @@ class ASTProcessor derives pub FrontendProcessor { fn ASTProcessor(self, const _: &ASTProcessor) = default; fn ASTProcessor(self, _: &&ASTProcessor) = default; - op = fn assign_op(self, const _: &ASTProcessor) -> &ASTProcessor = default; - op = fn move_op(self, _: &&ASTProcessor) -> &ASTProcessor = default; + fn op = (self, const _: &ASTProcessor)[assign_op] -> &ASTProcessor = default; + fn op = (self, _: &&ASTProcessor)[move_op] -> &ASTProcessor = default; #[override] fn delete(self) = default; @@ -280,8 +280,8 @@ class ExecutableGenerator derives pub BackendProcessor { fn ExecutableGenerator(self, const _: &ExecutableGenerator) = default; fn ExecutableGenerator(self, _: &&ExecutableGenerator) = default; - op = fn assign_op(self, const _: &ExecutableGenerator) -> &ExecutableGenerator = default; - op = fn move_op(self, _: &&ExecutableGenerator) -> &ExecutableGenerator = default; + fn op = (self, const _: &ExecutableGenerator)[assign_op] -> &ExecutableGenerator = default; + fn op = (self, _: &&ExecutableGenerator)[move_op] -> &ExecutableGenerator = default; #[override] fn delete(self) = default; @@ -351,11 +351,11 @@ class CompilationUnit { fn release_resources(); - let invocation: *InvocationManager; - let diagnostic_handler: *DiagnosticHandler; - let file_manager: *FileManager; - let frontend_processor: *FrontendProcessor; - let backend_processor: *BackendProcessor; + var invocation: *InvocationManager; + var diagnostic_handler: *DiagnosticHandler; + var file_manager: *FileManager; + var frontend_processor: *FrontendProcessor; + var backend_processor: *BackendProcessor; - let hasErrors: bool; + var hasErrors: bool; }; diff --git a/source/controller/include/tooling/tooling.hh b/source/controller/include/tooling/tooling.hh index fd21a3e7..8d7e4a84 100644 --- a/source/controller/include/tooling/tooling.hh +++ b/source/controller/include/tooling/tooling.hh @@ -38,6 +38,7 @@ namespace types { None = 0, Debug = 1 << 0, Verbose = 1 << 1, + Library = 1 << 2, }; enum class Compiler : u8 { diff --git a/source/controller/source/core/compilation_unit.cc b/source/controller/source/core/compilation_unit.cc index fd560c30..01369967 100644 --- a/source/controller/source/core/compilation_unit.cc +++ b/source/controller/source/core/compilation_unit.cc @@ -277,6 +277,10 @@ std::pair CompilationUnit::build_unit( action_flags |= flag::CompileFlags(flag::types::CompileFlags::Verbose); } + if (parsed_args.build_lib == __CONTROLLER_CLI_N::CLIArgs::ABI::HELIX) { + action_flags |= flag::CompileFlags(flag::types::CompileFlags::Library); + } + return {CXXCompileAction::init(emitter, out_file, action_flags, parsed_args.cxx_args), 0}; } @@ -298,7 +302,7 @@ int CompilationUnit::compile(__CONTROLLER_CLI_N::CLIArgs &parsed_args) { std::chrono::time_point start = std::chrono::high_resolution_clock::now(); auto [action, result] = build_unit(parsed_args); - CXXCompileAction _; + switch (result) { case 0: break; @@ -308,6 +312,10 @@ int CompilationUnit::compile(__CONTROLLER_CLI_N::CLIArgs &parsed_args) { case 2: return 0; + + default: + helix::log("unknown result code: ", result); + return 1; } helix::log_opt(action.flags.contains(flag::types::CompileFlags::Verbose), "compiling"); diff --git a/source/controller/source/core/compile_cxir.cc b/source/controller/source/core/compile_cxir.cc index 021bc91e..e8c614cc 100644 --- a/source/controller/source/core/compile_cxir.cc +++ b/source/controller/source/core/compile_cxir.cc @@ -120,6 +120,10 @@ CXIRCompiler::CompileResult CXIRCompiler::CXIR_CXX(const CXXCompileAction &actio ? cxx::flags::debugModeFlag : cxx::flags::optimizationLevel3), + ((action.flags.contains(flag::types::CompileFlags::Library)) + ? cxx::flags::compileOnlyFlag + : cxx::flags::None), + "-rdynamic", cxx::flags::cxxStandardFlag, @@ -140,11 +144,6 @@ CXIRCompiler::CompileResult CXIRCompiler::CXIR_CXX(const CXXCompileAction &actio ? cxx::flags::SanitizeFlag : cxx::flags::None), -// #if defined(__unix__) || defined(__APPLE__) || defined(__linux__) || defined(__FreeBSD__) || \ -// defined(__NetBSD__) || defined(__OpenBSD__) || defined(__bsdi__) || defined(__DragonFly__) || \ -// defined(__MACH__) -// "-Wl,-w,-rpath,/usr/local/lib", -// #endif cxx::flags::warnAllFlag, cxx::flags::outputFlag, "\"" + action.cc_output.generic_string() + "\"" // output diff --git a/source/controller/source/core/compile_cxir_msvc.cc b/source/controller/source/core/compile_cxir_msvc.cc index 8c2e8743..acb0e53d 100644 --- a/source/controller/source/core/compile_cxir_msvc.cc +++ b/source/controller/source/core/compile_cxir_msvc.cc @@ -150,6 +150,10 @@ CXIRCompiler::CompileResult CXIRCompiler::CXIR_MSVC(const CXXCompileAction &acti ? cxx::flags::SanitizeFlag : cxx::flags::None), + ((action.flags.contains(flag::types::CompileFlags::Library)) + ? cxx::flags::compileOnlyFlag + : cxx::flags::None), + // cxx::flags::noDefaultLibrariesFlag, // cxx::flags::noCXXSTDLibrariesFlag, // cxx::flags::noCXXSTDIncludesFlag, diff --git a/source/draft.hlx b/source/draft.hlx index a0ced04c..5de6606f 100644 --- a/source/draft.hlx +++ b/source/draft.hlx @@ -28,7 +28,7 @@ union ReturnStatus { DiagnosticPass: (diag::Error, i32) } -let foo = ReturnStatus(.Success = 12) +var foo = ReturnStatus(.Success = 12) match foo { case Success(var x): print("passed"); @@ -62,7 +62,7 @@ module LexicalProcessor { class Lexer; enum TokenKind; - static let TokenKindRepresentation: map; + static var TokenKindRepresentation: map; } module PreProcessor { // may not have this in the future @@ -77,13 +77,13 @@ module PreProcessor { // may not have this in the future /// everything the DiagnosticsEngine needs to cover: /// concatenation of diagnostics: -/// lets say we have 2 reports for the same location, like `let u8 = 5;` 2 errors for `u8` - excepted an identifier but got a type, primitive type's can not be used in this context. +/// lets say we have 2 reports for the same location, like `var u8 = 5;` 2 errors for `u8` - excepted an identifier but got a type, primitive type's can not be used in this context. /// this should concat into 1 error: /// error: [S...E] excepted an identifier but got 'u8' (type) /// related error: [S...E] primitive type's can not be used in this context. /// --> some_file.hlx:10:5 /// 9 | ... -/// 10 | let _: u8 = "5"; +/// 10 | var _: u8 = "5"; /// | ++ ^^ /// 11 | ... /// fix: Replace 'u8' with a valid identifier or alias. @@ -98,7 +98,7 @@ module PreProcessor { // may not have this in the future /// replated diagnostics /// fixes /// -/// in this case the following has to be reported +/// in this case the following impl to be reported /// base_uuid = handler.report(Report{ /// level: Level::Error, /// code: Errors::Syntax::UnexpectedToken(current_token, Foundation::TokenKind::Identifier), @@ -108,8 +108,8 @@ module PreProcessor { // may not have this in the future module DiagnosticsEngine { /// used like: - /// let diag_handler = DiagnosticsEngine::Handlers::Pretty(DiagnosticsEngine::Options::ShowColors, DiagnosticsEngine::Options::ShowLocation, DiagnosticsEngine::Options::ShowFixes) - /// let ci = LexicalProcessor::Lexer + /// var diag_handler = DiagnosticsEngine::Handlers::Pretty(DiagnosticsEngine::Options::ShowColors, DiagnosticsEngine::Options::ShowLocation, DiagnosticsEngine::Options::ShowFixes) + /// var ci = LexicalProcessor::Lexer interface Handler { fn Handler(self, ...options: DiagnosticsEngine::Options); fn report(self, report: DiagnosticsEngine::Report) -> DiagnosticsEngine::UUID; @@ -117,22 +117,22 @@ module DiagnosticsEngine { fn finalize(); - op ? fn (self) -> bool; // allows to question the handler and see if an error is preset - op [] fn (self, uuid: DiagnosticsEngine::UUID) -> *DiagnosticsEngine::Report?; + fn op ? (self) -> bool; // allows to question the handler and see if an error is preset + fn op [] (self, uuid: DiagnosticsEngine::UUID) -> *DiagnosticsEngine::Report?; } enum Options { ShowColors, ShowLocation, ShowFixes, - StaticTests // this checks for things likes 'let x: int = "y"; // test { }' + StaticTests // this checks for things likes 'var x: int = "y"; // test { }' } class UUID; struct Context { - let uuid: DiagnosticsEngine::UUID; + var uuid: DiagnosticsEngine::UUID; } enum Level { Ignored, Note, Remark, Warning, Error, Fatal } @@ -140,12 +140,12 @@ module DiagnosticsEngine { module Errors { interface Error { fn Error(self); // all errors should have a default constructor - op as fn (self) -> string; // conversion to string + fn op as (self) -> string; // conversion to string } module Syntax { struct UnexpectedToken with Error { - priv let data: string; + priv var data: string; fn UnexpectedToken(self) default; // default construct fn UnexpectedToken(self, token: Foundation::Token, except: Foundation::TokenKind) { @@ -156,10 +156,10 @@ module DiagnosticsEngine { } struct Report { - let level: DiagnosticsEngine::Level; - let context: - let location: Diagnostics::LocationRange; - let fixes: + var level: DiagnosticsEngine::Level; + var context: + var location: Diagnostics::LocationRange; + var fixes: } } @@ -186,9 +186,9 @@ module DiagnosticsEngine { template int helix_compiler(std::array args) { - let invocation = InvocationManager(args, true); - let compile = CompilationUnit(invocation); - let diag_handle = PrettyDiagnosticHandler(); + var invocation = InvocationManager(args, true); + var compile = CompilationUnit(invocation); + var diag_handle = PrettyDiagnosticHandler(); // possible options: PrettyDiagnosticHandler(), JsonDiagnosticHandler(), YmlDiagnosticHandler(), // base: DiagnosticHandler diff --git a/source/generator/include/CX-IR/CXIR.hh b/source/generator/include/CX-IR/CXIR.hh index 5711b9d8..70037a7f 100644 --- a/source/generator/include/CX-IR/CXIR.hh +++ b/source/generator/include/CX-IR/CXIR.hh @@ -443,99 +443,99 @@ __CXIR_CODEGEN_BEGIN { return cxir; } - void visit(const parser::ast::node::LiteralExpr &node) override; - void visit(const parser::ast::node::BinaryExpr &node) override; - void visit(const parser::ast::node::UnaryExpr &node) override; - void visit(const parser::ast::node::IdentExpr &node) override; - void visit(const parser::ast::node::NamedArgumentExpr &node) override; - void visit(const parser::ast::node::ArgumentExpr &node) override; - void visit(const parser::ast::node::ArgumentListExpr &node) override; - void visit(const parser::ast::node::GenericInvokeExpr &node) override; - void visit(const parser::ast::node::ScopePathExpr &node) override { visit(node, true); } - void visit(const parser::ast::node::ScopePathExpr &node, bool access); - void visit(const parser::ast::node::DotPathExpr &node) override; - void visit(const parser::ast::node::ArrayAccessExpr &node) override; - void visit(const parser::ast::node::PathExpr &node) override; - void visit(const parser::ast::node::FunctionCallExpr &node) override; - void visit(const parser::ast::node::ArrayLiteralExpr &node) override; - void visit(const parser::ast::node::TupleLiteralExpr &node) override; - void visit(const parser::ast::node::SetLiteralExpr &node) override; - void visit(const parser::ast::node::MapPairExpr &node) override; - void visit(const parser::ast::node::MapLiteralExpr &node) override; - void visit(const parser::ast::node::ObjInitExpr &node) override; - void visit(const parser::ast::node::LambdaExpr &node) override; - void visit(const parser::ast::node::TernaryExpr &node) override; - void visit(const parser::ast::node::ParenthesizedExpr &node) override; - void visit(const parser::ast::node::CastExpr &node) override; - void visit(const parser::ast::node::InstOfExpr &node) override; - void visit(const parser::ast::node::AsyncThreading &node) override; - void visit(const parser::ast::node::Type &node) override; - void visit(const parser::ast::node::NamedVarSpecifier &node, bool omit_t); - void visit(const parser::ast::node::NamedVarSpecifier &node) override { + void visit(parser::ast::node::LiteralExpr &node) override; + void visit(parser::ast::node::BinaryExpr &node) override; + void visit(parser::ast::node::UnaryExpr &node) override; + void visit(parser::ast::node::IdentExpr &node) override; + void visit(parser::ast::node::NamedArgumentExpr &node) override; + void visit(parser::ast::node::ArgumentExpr &node) override; + void visit(parser::ast::node::ArgumentListExpr &node) override; + void visit(parser::ast::node::GenericInvokeExpr &node) override; + void visit(parser::ast::node::ScopePathExpr &node) override { visit(node, true); } + void visit(parser::ast::node::ScopePathExpr &node, bool access); + void visit(parser::ast::node::DotPathExpr &node) override; + void visit(parser::ast::node::ArrayAccessExpr &node) override; + void visit(parser::ast::node::PathExpr &node) override; + void visit(parser::ast::node::FunctionCallExpr &node) override; + void visit(parser::ast::node::ArrayLiteralExpr &node) override; + void visit(parser::ast::node::TupleLiteralExpr &node) override; + void visit(parser::ast::node::SetLiteralExpr &node) override; + void visit(parser::ast::node::MapPairExpr &node) override; + void visit(parser::ast::node::MapLiteralExpr &node) override; + void visit(parser::ast::node::ObjInitExpr &node) override; + void visit(parser::ast::node::LambdaExpr &node) override; + void visit(parser::ast::node::TernaryExpr &node) override; + void visit(parser::ast::node::ParenthesizedExpr &node) override; + void visit(parser::ast::node::CastExpr &node) override; + void visit(parser::ast::node::InstOfExpr &node) override; + void visit(parser::ast::node::AsyncThreading &node) override; + void visit(parser::ast::node::Type &node) override; + void visit(parser::ast::node::NamedVarSpecifier &node, bool omit_t); + void visit(parser::ast::node::NamedVarSpecifier &node) override { visit(node, false); } - void visit(const parser::ast::node::NamedVarSpecifierList &node, bool omit_t); - void visit(const parser::ast::node::NamedVarSpecifierList &node) override { + void visit(parser::ast::node::NamedVarSpecifierList &node, bool omit_t); + void visit(parser::ast::node::NamedVarSpecifierList &node) override { visit(node, false); } - void visit(const parser::ast::node::ForPyStatementCore &node) override; - void visit(const parser::ast::node::ForCStatementCore &node) override; - void visit(const parser::ast::node::ForState &node) override; - void visit(const parser::ast::node::WhileState &node) override; - void visit(const parser::ast::node::ElseState &node) override; - void visit(const parser::ast::node::IfState &node) override; - void visit(const parser::ast::node::SwitchCaseState &node) override; - void visit(const parser::ast::node::SwitchState &node) override; - void visit(const parser::ast::node::YieldState &node) override; - void visit(const parser::ast::node::DeleteState &node) override; - - void visit(const parser::ast::node::ImportState &node) override; - void visit(const parser::ast::node::ImportItems &node) override; - void visit(const parser::ast::node::SingleImport &node) override; - void visit(const parser::ast::node::SpecImport &node) override; - void visit(const parser::ast::node::MultiImportState &node) override; - - void visit(const parser::ast::node::ReturnState &node) override; - void visit(const parser::ast::node::BreakState &node) override; - void visit(const parser::ast::node::BlockState &node) override; - void visit(const parser::ast::node::SuiteState &node) override; - void visit(const parser::ast::node::ContinueState &node) override; - void visit(const parser::ast::node::CatchState &node) override; - void visit(const parser::ast::node::FinallyState &node) override; - void visit(const parser::ast::node::TryState &node) override; - void visit(const parser::ast::node::PanicState &node) override; - void visit(const parser::ast::node::ExprState &node) override; - void visit(const parser::ast::node::RequiresParamDecl &node) override; - void visit(const parser::ast::node::RequiresParamList &node) override; - void visit(const parser::ast::node::EnumMemberDecl &node) override; - void visit(const parser::ast::node::UDTDeriveDecl &node) override; - void visit(const parser::ast::node::TypeBoundList &node) override; - void visit(const parser::ast::node::TypeBoundDecl &node) override; - void visit(const parser::ast::node::RequiresDecl &node) override; - void visit(const parser::ast::node::ModuleDecl &node) override; - void visit(const parser::ast::node::StructDecl &node) override; - void visit(const parser::ast::node::ConstDecl &node) override; - void visit(const parser::ast::node::ClassDecl &node) override; - void visit(const parser::ast::node::ExtendDecl &node) override; - void visit(const parser::ast::node::InterDecl &node) override; - void visit(const parser::ast::node::EnumDecl &node) override; - void visit(const parser::ast::node::TypeDecl &node) override; - void visit(const parser::ast::node::FuncDecl &node, bool no_return_t); - void visit(const parser::ast::node::VarDecl &node) override; - void visit(const parser::ast::node::FFIDecl &node) override; - - void visit(const parser::ast::node::LetDecl &node) override { visit(node, false); }; - void visit(const parser::ast::node::LetDecl &node, bool is_in_statement); - - void visit(const parser::ast::node::OpDecl &node) override { visit(node, false); }; - void visit(const parser::ast::node::OpDecl &node, bool in_udt); - - /// REMOVED: void visit(const parser::ast::node::OpDecl &node, bool remove_self) {}; - void visit(const parser::ast::node::Program &node) override { - visit(const_cast(node)); - } - void visit(parser::ast::node::Program &node); - void visit(const parser::ast::node::FuncDecl &node) override { visit(node, false); }; + void visit(parser::ast::node::ForPyStatementCore &node) override; + void visit(parser::ast::node::ForCStatementCore &node) override; + void visit(parser::ast::node::ForState &node) override; + void visit(parser::ast::node::WhileState &node) override; + void visit(parser::ast::node::ElseState &node) override; + void visit(parser::ast::node::IfState &node) override; + void visit(parser::ast::node::SwitchCaseState &node) override; + void visit(parser::ast::node::SwitchState &node) override; + void visit(parser::ast::node::YieldState &node) override; + void visit(parser::ast::node::DeleteState &node) override; + + void visit(parser::ast::node::ImportState &node) override; + void visit(parser::ast::node::ImportItems &node) override; + void visit(parser::ast::node::SingleImport &node) override; + void visit(parser::ast::node::SpecImport &node) override; + void visit(parser::ast::node::MultiImportState &node) override; + + void visit(parser::ast::node::ReturnState &node) override; + void visit(parser::ast::node::BreakState &node) override; + void visit(parser::ast::node::BlockState &node) override; + void visit(parser::ast::node::SuiteState &node) override; + void visit(parser::ast::node::ContinueState &node) override; + void visit(parser::ast::node::CatchState &node) override; + void visit(parser::ast::node::FinallyState &node) override; + void visit(parser::ast::node::TryState &node) override; + void visit(parser::ast::node::PanicState &node) override; + void visit(parser::ast::node::ExprState &node) override; + void visit(parser::ast::node::RequiresParamDecl &node) override; + void visit(parser::ast::node::RequiresParamList &node) override; + void visit(parser::ast::node::EnumMemberDecl &node) override; + void visit(parser::ast::node::UDTDeriveDecl &node) override; + void visit(parser::ast::node::TypeBoundList &node) override; + void visit(parser::ast::node::TypeBoundDecl &node) override; + void visit(parser::ast::node::RequiresDecl &node) override; + void visit(parser::ast::node::ModuleDecl &node) override; + void visit(parser::ast::node::StructDecl &node) override; + void visit(parser::ast::node::ConstDecl &node) override; + void visit(parser::ast::node::ClassDecl &node) override; + void visit(parser::ast::node::ExtendDecl &node) override; + void visit(parser::ast::node::InterDecl &node) override; + void visit(parser::ast::node::EnumDecl &node) override; + void visit(parser::ast::node::TypeDecl &node) override; + void visit(parser::ast::node::FuncDecl &node, bool no_return_t); + void visit(parser::ast::node::FuncDecl &node, bool in_udt, bool is_op); + void visit(parser::ast::node::VarDecl &node) override; + void visit(parser::ast::node::FFIDecl &node) override; + + void visit(parser::ast::node::LetDecl &node) override { visit(node, false); }; + void visit(parser::ast::node::LetDecl &node, bool is_in_statement); + + void visit(parser::ast::node::Program &node) override; + void visit(parser::ast::node::FuncDecl &node) override { + if (node.is_op) { + visit(node, false, true); + } else { + visit(node, false); + } + }; }; // inline CXIR get_node_json(const __AST_VISITOR::NodeT<> &node) { diff --git a/source/generator/include/config/Gen_config.def b/source/generator/include/config/Gen_config.def index 4f7b8407..b985064b 100644 --- a/source/generator/include/config/Gen_config.def +++ b/source/generator/include/config/Gen_config.def @@ -21,9 +21,9 @@ #define __CXIR_CODEGEN_N generator::CXIR #define CX_VISIT_IMPL(type) \ - void __CXIR_CODEGEN_N::CXIR::visit([[maybe_unused]] const __AST_NODE::type &node) + void __CXIR_CODEGEN_N::CXIR::visit([[maybe_unused]] __AST_NODE::type &node) #define CX_VISIT_IMPL_VA(type, ...) \ - void __CXIR_CODEGEN_N::CXIR::visit(const __AST_NODE::type &node /* NOLINT */, __VA_ARGS__) + void __CXIR_CODEGEN_N::CXIR::visit(__AST_NODE::type &node /* NOLINT */, __VA_ARGS__) #define MAKE_CXIR_TOKEN(name, string) name, #define MAKE_CXIR_TOKEN_PAIR(name, string) std::pair{name, string}, diff --git a/source/generator/source/CX-IR/CX_Class.cc b/source/generator/source/CX-IR/CX_Class.cc index 3b2fa230..6692585a 100644 --- a/source/generator/source/CX-IR/CX_Class.cc +++ b/source/generator/source/CX-IR/CX_Class.cc @@ -25,7 +25,7 @@ CX_VISIT_IMPL(ClassDecl) { bool has_constructor = false; for (auto &child : body->body->body) { - if (child->getNodeType() == __AST_NODE::nodes::FuncDecl) { + if (child->getNodeType() == __AST_NODE::nodes::FuncDecl && !__AST_N::as<__AST_NODE::FuncDecl>(child)->is_op) { auto func_decl = __AST_N::as<__AST_NODE::FuncDecl>(child); token::Token func_name = func_decl->name->get_back_name(); @@ -51,7 +51,7 @@ CX_VISIT_IMPL(ClassDecl) { self->visit(*func_decl); } - } else if (child->getNodeType() == __AST_NODE::nodes::OpDecl) { + } else if (child->getNodeType() == __AST_NODE::nodes::FuncDecl && __AST_N::as<__AST_NODE::FuncDecl>(child)->is_op) { // we need to handle the `in` operator since its codegen also has to check for // the presence of the begin and end functions 2 variations of the in operator // are possible @@ -59,11 +59,11 @@ CX_VISIT_IMPL(ClassDecl) { // 2. `in` operator that takes 1 arg and returns a bool (used in expressions) // we need to handle both of these cases token::Token op_name; - auto op_decl = __AST_N::as<__AST_NODE::OpDecl>(child); + auto op_decl = __AST_N::as<__AST_NODE::FuncDecl>(child); auto op_t = OpType(*op_decl, true); - if (op_decl->func->name != nullptr) { - op_name = op_decl->func->name->get_back_name(); + if (op_decl->name != nullptr) { + op_name = op_decl->name->get_back_name(); } else { op_name = op_decl->op.back(); } @@ -80,27 +80,30 @@ CX_VISIT_IMPL(ClassDecl) { for (auto &child : body->body->body) { if (child->getNodeType() == __AST_NODE::nodes::FuncDecl) { auto func_decl = __AST_N::as<__AST_NODE::FuncDecl>(child); - token::Token func_name = func_decl->name->get_back_name(); - - if (func_name.value() == "begin") { - if (func_decl->params.size() == 0) { - error::Panic(error::CodeError{ - .pof = &func_name, - .err_code = 0.3002, - .err_fmt_args = { - "can not define both begin/end fuctions and " - "overload the `in` genrator operator"}}); + + if (func_decl->name != nullptr) { + token::Token func_name = func_decl->name->get_back_name(); + + if (func_name.value() == "begin") { + if (func_decl->params.size() == 0) { + error::Panic(error::CodeError{ + .pof = &func_name, + .err_code = 0.3002, + .err_fmt_args = { + "can not define both begin/end fuctions and " + "overload the `in` genrator operator"}}); + } } - } - if (func_name.value() == "end") { - if (func_decl->params.size() == 0) { - error::Panic(error::CodeError{ - .pof = &func_name, - .err_code = 0.3002, - .err_fmt_args = { - "can not define both begin/end fuctions and " - "overload the `in` genrator operator"}}); + if (func_name.value() == "end") { + if (func_decl->params.size() == 0) { + error::Panic(error::CodeError{ + .pof = &func_name, + .err_code = 0.3002, + .err_fmt_args = { + "can not define both begin/end fuctions and " + "overload the `in` genrator operator"}}); + } } } } @@ -153,7 +156,7 @@ CX_VISIT_IMPL(ClassDecl) { op_decl->modifiers.get(__TOKEN_N::KEYWORD_DEFAULT))); } - if (op_decl->func->body == nullptr) { + if (op_decl->body == nullptr) { self->append(cxir_tokens::CXX_SEMICOLON); } else { if (op_decl->modifiers.contains(__TOKEN_N::KEYWORD_DELETE) || @@ -165,14 +168,14 @@ CX_VISIT_IMPL(ClassDecl) { "defaulted function"}}); } - self->visit(*op_decl->func->body); + self->visit(*op_decl->body); } continue; } - add_visibility(self, op_decl->func); - self->visit(*op_decl, true); + add_visibility(self, op_decl); + self->visit(*op_decl, true, true); } else if (child->getNodeType() == __AST_NODE::nodes::LetDecl) { auto let_decl = __AST_N::as<__AST_NODE::LetDecl>(child); diff --git a/source/generator/source/CX-IR/CX_Exprs.cc b/source/generator/source/CX-IR/CX_Exprs.cc index 8eb0c9de..c4c5a8cc 100644 --- a/source/generator/source/CX-IR/CX_Exprs.cc +++ b/source/generator/source/CX-IR/CX_Exprs.cc @@ -381,7 +381,7 @@ CX_VISIT_IMPL(FunctionCallExpr) { } CX_VISIT_IMPL(ArrayLiteralExpr) { - ADD_TOKEN_AS_VALUE(CXX_CORE_IDENTIFIER, "list"); + ADD_TOKEN_AS_VALUE(CXX_CORE_IDENTIFIER, "vec"); BRACE_DELIMIT(COMMA_SEP(values);); } @@ -583,7 +583,7 @@ CX_VISIT_IMPL(InstOfExpr) { ADD_TOKEN_AS_VALUE(CXX_CORE_IDENTIFIER, "value"); break; - case __AST_NODE::InstOfExpr::InstanceType::Has: + case __AST_NODE::InstOfExpr::InstanceType::Implement: // since type is just an expr we would do // lhs.operator$contains(type); diff --git a/source/generator/source/CX-IR/CX_Extend.cc b/source/generator/source/CX-IR/CX_Extend.cc index df3669f9..dbe39eaf 100644 --- a/source/generator/source/CX-IR/CX_Extend.cc +++ b/source/generator/source/CX-IR/CX_Extend.cc @@ -26,7 +26,7 @@ CX_VISIT_IMPL(ExtendDecl) { bool has_constructor = false; for (auto &child : body->body->body) { - if (child->getNodeType() == __AST_NODE::nodes::FuncDecl) { + if (child->getNodeType() == __AST_NODE::nodes::FuncDecl && !__AST_N::as<__AST_NODE::FuncDecl>(child)->is_op) { auto func_decl = __AST_N::as<__AST_NODE::FuncDecl>(child); token::Token func_name = func_decl->name->get_back_name(); @@ -52,7 +52,7 @@ CX_VISIT_IMPL(ExtendDecl) { self->visit(*func_decl); } - } else if (child->getNodeType() == __AST_NODE::nodes::OpDecl) { + } else if (child->getNodeType() == __AST_NODE::nodes::FuncDecl && __AST_N::as<__AST_NODE::FuncDecl>(child)->is_op) { // we need to handle the `in` operator since its codegen also has to check for // the presence of the begin and end functions 2 variations of the in operator // are possible @@ -60,11 +60,11 @@ CX_VISIT_IMPL(ExtendDecl) { // 2. `in` operator that takes 1 arg and returns a bool (used in expressions) // we need to handle both of these cases token::Token op_name; - auto op_decl = __AST_N::as<__AST_NODE::OpDecl>(child); + auto op_decl = __AST_N::as<__AST_NODE::FuncDecl>(child); auto op_t = OpType(*op_decl, true); - if (op_decl->func->name != nullptr) { - op_name = op_decl->func->name->get_back_name(); + if (op_decl->name != nullptr) { + op_name = op_decl->name->get_back_name(); } else { op_name = op_decl->op.back(); } @@ -127,13 +127,13 @@ CX_VISIT_IMPL(ExtendDecl) { self->append(std::make_unique(cxir_tokens::CXX_LPAREN)); self->append(std::make_unique(cxir_tokens::CXX_RPAREN)); - self->visit(*op_decl->func->body); + self->visit(*op_decl->body); continue; } - add_visibility(self, op_decl->func); - self->visit(*op_decl, true); + add_visibility(self, op_decl); + self->visit(*op_decl, true, true); } else if (child->getNodeType() == __AST_NODE::nodes::LetDecl) { auto let_decl = __AST_N::as<__AST_NODE::LetDecl>(child); diff --git a/source/generator/source/CX-IR/CX_Function.cc b/source/generator/source/CX-IR/CX_Function.cc index e2cbc358..04721edb 100644 --- a/source/generator/source/CX-IR/CX_Function.cc +++ b/source/generator/source/CX-IR/CX_Function.cc @@ -23,6 +23,13 @@ #include "utils.hh" CX_VISIT_IMPL_VA(FuncDecl, bool no_return_t) { + if (node.is_op) { + throw std::runtime_error( + std::string(colors::fg16::green) + std::string(__FILE__) + ":" + + std::to_string(__LINE__) + colors::reset + std::string(" - ") + + "Function Declaration is missing the name param (ub), open an issue on github."); + } + ADD_NODE_PARAM(generics); // add the modifiers diff --git a/source/generator/source/CX-IR/CX_Interface.cc b/source/generator/source/CX-IR/CX_Interface.cc index 11e1ee4f..ad14fb91 100644 --- a/source/generator/source/CX-IR/CX_Interface.cc +++ b/source/generator/source/CX-IR/CX_Interface.cc @@ -23,7 +23,7 @@ CX_VISIT_IMPL(InterDecl) { // S.Suite // ADD_NODE_PARAM(generics); // WE need a custom generics impl here as Self is the first generic - /// op + fn add(self, other: self) -> self; + /// fn op + (self, other: self)[add] -> self; /// { $self + b } -> std::same_as; /// { $self.add(b) } -> std::same_as; @@ -45,7 +45,6 @@ CX_VISIT_IMPL(InterDecl) { /// the only things allwoed in a n itnerface delc: /// - LetDecl /// - FuncDecl - /// - OpDecl /// - TypeDecl /// - ConstDecl @@ -96,83 +95,87 @@ CX_VISIT_IMPL(InterDecl) { for (auto &decl : node.body->body->body) { switch (decl->getNodeType()) { case __AST_NODE::nodes::FuncDecl: { - __AST_N::NodeT<__AST_NODE::FuncDecl> func_decl = + { + __AST_N::NodeT<__AST_NODE::FuncDecl> func_decl = __AST_N::as<__AST_NODE::FuncDecl>(decl); - auto [$self, $static] = contains_self_static(func_decl); - if ($self && $static) { - CODEGEN_ERROR(self, - "function is marked static but also takes a self parameter"); - break; - } - - if (func_decl->body != nullptr) { - CODEGEN_ERROR(func_decl->get_name_t().back(), - "functions have to be forward declarations in an interface.") - break; - } - - if (func_decl->generics != nullptr) { - CODEGEN_ERROR(func_decl->get_name_t().back(), - "functions can not have `requires` in an interface, apply them " - "to the interface itself instead."); - break; - } + if (func_decl->is_op) { + goto __AST_NODE_Func_OpDecl; + } - if (!func_decl->params.empty()) { - bool first = $self; + auto [$self, $static] = contains_self_static(func_decl); + if ($self && $static) { + CODEGEN_ERROR(self, + "function is marked static but also takes a self parameter"); + break; + } - for (auto ¶m : func_decl->params) { - if (first) { - first = !first; - continue; - } + if (func_decl->body != nullptr) { + CODEGEN_ERROR(func_decl->get_name_t().back(), + "functions have to be forward declarations in an interface.") + break; + } - type_map[¶m] = generate_unique_name(); + if (func_decl->generics != nullptr) { + CODEGEN_ERROR(func_decl->get_name_t().back(), + "functions can not have `requires` in an interface, apply them " + "to the interface itself instead."); + break; } - } - break; - } + if (!func_decl->params.empty()) { + bool first = $self; - case __AST_NODE::nodes::OpDecl: { // WARN: this does not remove the self param from the - // function decl - __AST_N::NodeT<__AST_NODE::OpDecl> op_decl = __AST_N::as<__AST_NODE::OpDecl>(decl); + for (auto ¶m : func_decl->params) { + if (first) { + first = !first; + continue; + } - auto [$self, $static] = contains_self_static(op_decl); - if ($self && $static) { - CODEGEN_ERROR(self, - "function is marked static but also takes a self parameter"); - break; - } + type_map[¶m] = generate_unique_name(); + } + } - if (op_decl->func->body != nullptr) { - CODEGEN_ERROR(op_decl->func->get_name_t().back(), - "functions have to be forward declarations in an interface.") break; } - if (op_decl->func->generics != nullptr) { - CODEGEN_ERROR(op_decl->func->get_name_t().back(), - "functions can not have `requires` in an interface, apply them " - "to the interface itself instead."); - break; - } + __AST_NODE_Func_OpDecl: { + __AST_N::NodeT<__AST_NODE::FuncDecl> func_decl = __AST_N::as<__AST_NODE::FuncDecl>(decl); + auto [$self, $static] = contains_self_static(func_decl); + if ($self && $static) { + CODEGEN_ERROR(self, + "function is marked static but also takes a self parameter"); + break; + } - if (!op_decl->func->params.empty()) { - bool first = $self; + if (func_decl->body != nullptr) { + CODEGEN_ERROR(func_decl->get_name_t().back(), + "functions have to be forward declarations in an interface.") + break; + } - for (auto ¶m : op_decl->func->params) { - if (first) { - first = !first; - continue; - } + if (func_decl->generics != nullptr) { + CODEGEN_ERROR(func_decl->get_name_t().back(), + "functions can not have `requires` in an interface, apply them " + "to the interface itself instead."); + break; + } + + if (!func_decl->params.empty()) { + bool first = $self; + + for (auto ¶m : func_decl->params) { + if (first) { + first = !first; + continue; + } - type_map[¶m] = generate_unique_name(); + type_map[¶m] = generate_unique_name(); + } } - } - break; + break; + } } /// ------------------------- only validation at this stage ------------------------ /// @@ -340,12 +343,16 @@ CX_VISIT_IMPL(InterDecl) { /// at this stage validation is done, now we need to only focus on the codegen for (auto &decl : node.body->body->body) { switch (decl->getNodeType()) { - case __AST_NODE::nodes::FuncDecl: { // fn foo(self, a: U) -> i32; + case __AST_NODE::nodes::FuncDecl: {{ // fn foo(self, a: U) -> i32; /// if static codegen: { self::foo(a) } -> std::same_as; /// if instance codegen: { self_parm_name.foo(a) } -> std::same_as; __AST_N::NodeT<__AST_NODE::FuncDecl> func_decl = __AST_N::as<__AST_NODE::FuncDecl>(decl); + if (func_decl->is_op) { + goto LINE417_Func_OpDecl; + } + auto [$self, $static] = contains_self_static(func_decl); ADD_TOKEN(CXX_LBRACE); // { @@ -411,8 +418,8 @@ CX_VISIT_IMPL(InterDecl) { break; } - case __AST_NODE::nodes::OpDecl: { - /// op + fn add(self, other: self) -> self; + LINE417_Func_OpDecl: { + /// fn op + (self, other: self)[add] -> self; /// if static codegen: /// { self::add(a, b) } -> std::same_as; /// { a + b } -> std::same_as; @@ -421,16 +428,16 @@ CX_VISIT_IMPL(InterDecl) { /// { self_parm_name + b } -> std::same_as; /// heres is all the decls: - /// unary (prefix): op + fn add(self) -> self; - /// unary (postfix): op r+ fn add(self) -> self; - /// binary: op + fn add(self, other: self) -> self; - /// array: op [] fn add(self, other: self) -> self; + /// unary (prefix): fn op + (self)[add] -> self; + /// unary (postfix): fn op r+ (self)[add] -> self; + /// binary: fn op + (self, other: self)[add] -> self; + /// array: fn op [] (self, other: self)[add] -> self; - __AST_N::NodeT<__AST_NODE::OpDecl> op_decl = __AST_N::as<__AST_NODE::OpDecl>(decl); + __AST_N::NodeT<__AST_NODE::FuncDecl> op_decl = __AST_N::as<__AST_NODE::FuncDecl>(decl); auto [$self, $static] = contains_self_static(op_decl); - if (op_decl->func->name != nullptr) { // first declaration + if (op_decl->name != nullptr) { // first declaration ADD_TOKEN(CXX_LBRACE); if ($self) { @@ -441,13 +448,13 @@ CX_VISIT_IMPL(InterDecl) { ADD_TOKEN(CXX_SCOPE_RESOLUTION); } - ADD_PARAM(op_decl->func->name); + ADD_PARAM(op_decl->name); ADD_TOKEN(CXX_LPAREN); - if (!op_decl->func->params.empty()) { + if (!op_decl->params.empty()) { bool first = $self; - for (auto ¶m : op_decl->func->params) { + for (auto ¶m : op_decl->params) { if (first) { first = !first; continue; @@ -458,7 +465,7 @@ CX_VISIT_IMPL(InterDecl) { ADD_TOKEN(CXX_COMMA); } - if (!$self || op_decl->func->params.size() != 1) { + if (!$self || op_decl->params.size() != 1) { tokens.pop_back(); } } @@ -468,18 +475,18 @@ CX_VISIT_IMPL(InterDecl) { ADD_TOKEN(CXX_RBRACE); ADD_TOKEN(CXX_PTR_ACC); ADD_TOKEN_AS_VALUE_AT_LOC( - CXX_CORE_IDENTIFIER, "std", op_decl->func->get_name_t().back()); + CXX_CORE_IDENTIFIER, "std", op_decl->get_name_t().back()); ADD_TOKEN(CXX_SCOPE_RESOLUTION); ADD_TOKEN_AS_VALUE_AT_LOC( - CXX_CORE_IDENTIFIER, "Meta", op_decl->func->get_name_t().back()); + CXX_CORE_IDENTIFIER, "Meta", op_decl->get_name_t().back()); ADD_TOKEN(CXX_SCOPE_RESOLUTION); ADD_TOKEN_AS_VALUE_AT_LOC( - CXX_CORE_IDENTIFIER, "is_convertible_to", op_decl->func->get_name_t().back()); + CXX_CORE_IDENTIFIER, "is_convertible_to", op_decl->get_name_t().back()); ADD_TOKEN(CXX_LESS_THAN); - if (op_decl->func->returns) { - if (!is_self_t(op_decl->func->returns)) { - ADD_PARAM(op_decl->func->returns); + if (op_decl->returns) { + if (!is_self_t(op_decl->returns)) { + ADD_PARAM(op_decl->returns); } } else { ADD_TOKEN(CXX_VOID); @@ -502,7 +509,7 @@ CX_VISIT_IMPL(InterDecl) { CODEGEN_ERROR(op_decl->op.front(), "Invalid operator/parameters for operator overload"); } else { - CODEGEN_ERROR(op_decl->func->get_name_t().back(), + CODEGEN_ERROR(op_decl->get_name_t().back(), "Invalid operator/parameters for operator overload"); } @@ -519,7 +526,7 @@ CX_VISIT_IMPL(InterDecl) { ADD_TOKEN_AS_VALUE(CXX_CORE_IDENTIFIER, self_parm_name); } else { ADD_TOKEN_AS_VALUE(CXX_CORE_IDENTIFIER, - type_map[&op_decl->func->params.front()]); + type_map[&op_decl->params.front()]); } break; @@ -529,7 +536,7 @@ CX_VISIT_IMPL(InterDecl) { ADD_TOKEN_AS_VALUE(CXX_CORE_IDENTIFIER, self_parm_name); } else { ADD_TOKEN_AS_VALUE(CXX_CORE_IDENTIFIER, - type_map[&op_decl->func->params.front()]); + type_map[&op_decl->params.front()]); } for (auto &tok : op_decl->op) { @@ -551,7 +558,7 @@ CX_VISIT_IMPL(InterDecl) { ADD_TOKEN_AS_VALUE(CXX_CORE_IDENTIFIER, self_parm_name); } else { ADD_TOKEN_AS_VALUE(CXX_CORE_IDENTIFIER, - type_map[&op_decl->func->params.front()]); + type_map[&op_decl->params.front()]); } for (auto &tok : op_decl->op) { @@ -559,7 +566,7 @@ CX_VISIT_IMPL(InterDecl) { } ADD_TOKEN_AS_VALUE(CXX_CORE_IDENTIFIER, - type_map[&op_decl->func->params.back()]); + type_map[&op_decl->params.back()]); break; } @@ -568,12 +575,12 @@ CX_VISIT_IMPL(InterDecl) { ADD_TOKEN_AS_VALUE(CXX_CORE_IDENTIFIER, self_parm_name); } else { ADD_TOKEN_AS_VALUE(CXX_CORE_IDENTIFIER, - type_map[&op_decl->func->params.front()]); + type_map[&op_decl->params.front()]); } ADD_TOKEN(CXX_LBRACKET); ADD_TOKEN_AS_VALUE(CXX_CORE_IDENTIFIER, - type_map[&op_decl->func->params.back()]); + type_map[&op_decl->params.back()]); ADD_TOKEN(CXX_RBRACKET); break; @@ -582,15 +589,15 @@ CX_VISIT_IMPL(InterDecl) { ADD_TOKEN_AS_VALUE(CXX_CORE_IDENTIFIER, self_parm_name); } else { ADD_TOKEN_AS_VALUE(CXX_CORE_IDENTIFIER, - type_map[&op_decl->func->params.front()]); + type_map[&op_decl->params.front()]); } ADD_TOKEN(CXX_LPAREN); bool first = $self; - if (!op_decl->func->params.empty()) { - for (auto ¶m : op_decl->func->params) { + if (!op_decl->params.empty()) { + for (auto ¶m : op_decl->params) { if (first) { first = !first; continue; @@ -602,7 +609,7 @@ CX_VISIT_IMPL(InterDecl) { ADD_TOKEN(CXX_COMMA); } - if (!$self || op_decl->func->params.size() != 1) { + if (!$self || op_decl->params.size() != 1) { tokens.pop_back(); } } @@ -619,18 +626,18 @@ CX_VISIT_IMPL(InterDecl) { ADD_TOKEN(CXX_RBRACE); ADD_TOKEN(CXX_PTR_ACC); ADD_TOKEN_AS_VALUE_AT_LOC( - CXX_CORE_IDENTIFIER, "std", op_decl->func->get_name_t().back()); + CXX_CORE_IDENTIFIER, "std", op_decl->get_name_t().back()); ADD_TOKEN(CXX_SCOPE_RESOLUTION); ADD_TOKEN_AS_VALUE_AT_LOC( - CXX_CORE_IDENTIFIER, "Meta", op_decl->func->get_name_t().back()); + CXX_CORE_IDENTIFIER, "Meta", op_decl->get_name_t().back()); ADD_TOKEN(CXX_SCOPE_RESOLUTION); ADD_TOKEN_AS_VALUE_AT_LOC( - CXX_CORE_IDENTIFIER, "is_convertible_to", op_decl->func->get_name_t().back()); + CXX_CORE_IDENTIFIER, "is_convertible_to", op_decl->get_name_t().back()); ADD_TOKEN(CXX_LESS_THAN); - if (op_decl->func->returns) { - if (!is_self_t(op_decl->func->returns)) { - ADD_PARAM(op_decl->func->returns); + if (op_decl->returns) { + if (!is_self_t(op_decl->returns)) { + ADD_PARAM(op_decl->returns); } } else { ADD_TOKEN(CXX_VOID); @@ -641,7 +648,7 @@ CX_VISIT_IMPL(InterDecl) { } break; - } + }} case __AST_NODE::nodes::LetDecl: { // let foo: i32; /// if static codegen: { self::foo } -> std::same_as; diff --git a/source/generator/source/CX-IR/CX_Operator.cc b/source/generator/source/CX-IR/CX_Operator.cc index 437cf120..555ffc20 100644 --- a/source/generator/source/CX-IR/CX_Operator.cc +++ b/source/generator/source/CX-IR/CX_Operator.cc @@ -16,9 +16,8 @@ #include "generator/include/config/Gen_config.def" #include "utils.hh" -CX_VISIT_IMPL_VA(OpDecl, bool in_udt) { +CX_VISIT_IMPL_VA(FuncDecl, bool in_udt, bool is_op) { OpType op_t = OpType(node, in_udt); - auto _node = std::make_shared<__AST_NODE::OpDecl>(node); token::Token tok; /// FIXME: really have to add markers to the rewrite of the compiler @@ -32,17 +31,17 @@ CX_VISIT_IMPL_VA(OpDecl, bool in_udt) { if (!node.op.empty()) { tok = node.op.front(); } else { - auto name = node.func->get_name_t(); + auto name = node.get_name_t(); if (!name.empty()) { tok = name.back(); } else { - tok = node.func->marker; + tok = node.marker; } } } - handle_static_self_fn_decl(_node, tok, in_udt); - check_for_yield_and_panic(node.func->body, node.func->returns); + handle_static_self_fn_decl(node, tok, in_udt); + check_for_yield_and_panic(node.body, node.returns); // ---------------------------- add generator state ---------------------------- // if (in_udt && op_t.type == OpType::GeneratorOp) { @@ -51,7 +50,7 @@ CX_VISIT_IMPL_VA(OpDecl, bool in_udt) { ADD_TOKEN(CXX_COLON); ADD_TOKEN(CXX_MUTABLE); - ADD_NODE_PARAM(func->returns); + ADD_NODE_PARAM(returns); ADD_TOKEN_AS_VALUE_AT_LOC(CXX_CORE_IDENTIFIER, "$gen_state", tok); ADD_TOKEN(CXX_ASSIGN); @@ -66,8 +65,8 @@ CX_VISIT_IMPL_VA(OpDecl, bool in_udt) { // ---------------------------- operator declaration ---------------------------- // - if (node.func->generics) { // - ADD_NODE_PARAM(func->generics); + if (node.generics) { // + ADD_NODE_PARAM(generics); }; if (!node.modifiers.contains(__TOKEN_N::KEYWORD_INLINE)) { @@ -107,7 +106,7 @@ CX_VISIT_IMPL_VA(OpDecl, bool in_udt) { auto type = __AST_N::make_node<__AST_NODE::Type>(__AST_N::make_node<__AST_NODE::UnaryExpr>( - node.func->returns, + node.returns, __TOKEN_N::Token(__TOKEN_N::OPERATOR_MUL, "*", *op_t.tok), __AST_NODE::UnaryExpr::PosType::PreFix, true)); @@ -119,7 +118,7 @@ CX_VISIT_IMPL_VA(OpDecl, bool in_udt) { ), type)); - node.func->params.emplace_back(param); + node.params.emplace_back(param); } // if its a panic op @@ -146,8 +145,8 @@ CX_VISIT_IMPL_VA(OpDecl, bool in_udt) { } ADD_TOKEN(CXX_LPAREN); - if (!node.func->params.empty()) { - for (auto ¶m : node.func->params) { + if (!node.params.empty()) { + for (auto ¶m : node.params) { ADD_PARAM(param); ADD_TOKEN(CXX_COMMA); } @@ -160,10 +159,10 @@ CX_VISIT_IMPL_VA(OpDecl, bool in_udt) { ADD_TOKEN(CXX_PTR_ACC); - if (node.func->returns) { // - ADD_NODE_PARAM(func->returns); + if (node.returns) { // + ADD_NODE_PARAM(returns); } else { - ADD_TOKEN_AT_LOC(CXX_VOID, node.func->marker); + ADD_TOKEN_AT_LOC(CXX_VOID, node.marker); } if (node.modifiers.contains(__TOKEN_N::KEYWORD_OVERRIDE)) { @@ -180,8 +179,8 @@ CX_VISIT_IMPL_VA(OpDecl, bool in_udt) { __CXIR_CODEGEN_N::cxir_tokens::CXX_DELETE, node.modifiers.get(__TOKEN_N::KEYWORD_DELETE))); - if (node.func->name != nullptr) { - auto fail = node.func->name->get_back_name(); + if (node.name != nullptr) { + auto fail = node.name->get_back_name(); error::Panic( error::CodeError{.pof = &fail, .err_code = 0.3002, @@ -195,8 +194,8 @@ CX_VISIT_IMPL_VA(OpDecl, bool in_udt) { __CXIR_CODEGEN_N::cxir_tokens::CXX_DEFAULT, node.modifiers.get(__TOKEN_N::KEYWORD_DEFAULT))); - if (node.func->name != nullptr) { - auto fail = node.func->name->get_back_name(); + if (node.name != nullptr) { + auto fail = node.name->get_back_name(); error::Panic( error::CodeError{.pof = &fail, .err_code = 0.3002, @@ -204,7 +203,7 @@ CX_VISIT_IMPL_VA(OpDecl, bool in_udt) { } } - if (node.func->body && node.func->body->body) { + if (node.body && node.body->body) { if (node.modifiers.contains(__TOKEN_N::KEYWORD_DELETE) || node.modifiers.contains(__TOKEN_N::KEYWORD_DEFAULT)) { auto fail = node.op.back(); @@ -215,7 +214,7 @@ CX_VISIT_IMPL_VA(OpDecl, bool in_udt) { } // adds and removes any nested functions BRACE_DELIMIT( // - std::erase_if(node.func->body->body->body, ModifyNestedFunctions(this));); // + std::erase_if(node.body->body->body, ModifyNestedFunctions(this));); // } else { ADD_TOKEN(CXX_SEMICOLON); } @@ -223,28 +222,28 @@ CX_VISIT_IMPL_VA(OpDecl, bool in_udt) { // ---------------------------- function declaration ---------------------------- // // add the alias function first if it has a name - if (node.func->name != nullptr) { - if (node.func->generics) { // - ADD_NODE_PARAM(func->generics); + if (node.name != nullptr) { + if (node.generics) { // + ADD_NODE_PARAM(generics); }; add_func_modifiers(this, node.modifiers); ADD_TOKEN(CXX_AUTO); - ADD_NODE_PARAM(func->name); + ADD_NODE_PARAM(name); PAREN_DELIMIT( // - COMMA_SEP(func->params); // + COMMA_SEP(params); // ); add_func_specifiers(this, node.modifiers); ADD_TOKEN(CXX_PTR_ACC); - if (node.func->returns) { // - ADD_NODE_PARAM(func->returns); + if (node.returns) { // + ADD_NODE_PARAM(returns); } else { - ADD_TOKEN_AT_LOC(CXX_VOID, node.func->marker); + ADD_TOKEN_AT_LOC(CXX_VOID, node.marker); } if (node.modifiers.contains(__TOKEN_N::KEYWORD_OVERRIDE)) { @@ -281,8 +280,8 @@ CX_VISIT_IMPL_VA(OpDecl, bool in_udt) { } ADD_TOKEN(CXX_LPAREN); - if (!node.func->params.empty()) { - for (auto ¶m : node.func->params) { + if (!node.params.empty()) { + for (auto ¶m : node.params) { ADD_PARAM(param->var->path); ADD_TOKEN(CXX_COMMA); } @@ -338,7 +337,7 @@ CX_VISIT_IMPL_VA(OpDecl, bool in_udt) { ADD_TOKEN(CXX_EXPLICIT); add_func_modifiers(this, node.modifiers); ADD_TOKEN(CXX_OPERATOR); - ADD_PARAM(node.func->returns); + ADD_PARAM(node.returns); ADD_TOKEN(CXX_LPAREN); ADD_TOKEN(CXX_RPAREN); add_func_specifiers(this, node.modifiers); @@ -349,12 +348,12 @@ CX_VISIT_IMPL_VA(OpDecl, bool in_udt) { ADD_TOKEN_AT_LOC(CXX_PTR_ACC, *op_t.tok); ADD_TOKEN_AS_VALUE_AT_LOC(CXX_CORE_IDENTIFIER, "operator$cast", *op_t.tok); // ANGLE_DELIMIT( // - // ADD_PARAM(node.func->returns); // + // ADD_PARAM(node.returns); // // ); // PAREN_DELIMIT( // ADD_TOKEN(CXX_STATIC_CAST); // ANGLE_DELIMIT( // - ADD_PARAM(node.func->returns); // + ADD_PARAM(node.returns); // ADD_TOKEN_AS_VALUE(CXX_CORE_OPERATOR, "*"); // ); // PAREN_DELIMIT( // diff --git a/source/generator/source/CX-IR/CX_Struct.cc b/source/generator/source/CX-IR/CX_Struct.cc index 1c3ba200..56784010 100644 --- a/source/generator/source/CX-IR/CX_Struct.cc +++ b/source/generator/source/CX-IR/CX_Struct.cc @@ -29,10 +29,9 @@ CX_VISIT_IMPL(StructDecl) { case __AST_NODE::nodes::EnumDecl: case __AST_NODE::nodes::StructDecl: case __AST_NODE::nodes::TypeDecl: - case __AST_NODE::nodes::OpDecl: case __AST_NODE::nodes::LetDecl: case __AST_NODE::nodes::ConstDecl: - break; + break; case __AST_NODE::nodes::IfState: { auto if_node = __AST_N::as<__AST_NODE::IfState>(child); @@ -40,7 +39,13 @@ CX_VISIT_IMPL(StructDecl) { break; } } - default: + case __AST_NODE::nodes::FuncDecl: { + auto func_decl = __AST_N::as<__AST_NODE::FuncDecl>(child); + + if (func_decl->is_op && (func_decl->name == nullptr)) { + break; + } + } default: CODEGEN_ERROR( node.name->name, "struct declaration cannot have a node of type: '" + @@ -50,10 +55,10 @@ CX_VISIT_IMPL(StructDecl) { continue; } - if (child->getNodeType() == __AST_NODE::nodes::OpDecl) { - auto op_decl = __AST_N::as<__AST_NODE::OpDecl>(child); - if (op_decl->func->name) { - auto marker = op_decl->func->name->get_back_name(); + if (child->getNodeType() == __AST_NODE::nodes::FuncDecl && __AST_N::as<__AST_NODE::FuncDecl>(child)->is_op) { + auto op_decl = __AST_N::as<__AST_NODE::FuncDecl>(child); + if (op_decl->name) { + auto marker = op_decl->name->get_back_name(); CODEGEN_ERROR( marker, "struct declaration cannot have named operators; remove the named alias."); @@ -111,13 +116,13 @@ CX_VISIT_IMPL(StructDecl) { self->append(std::make_unique(cxir_tokens::CXX_LPAREN)); self->append(std::make_unique(cxir_tokens::CXX_RPAREN)); - self->visit(*op_decl->func->body); + self->visit(*op_decl->body); continue; } - add_visibility(self, op_decl->func); - self->visit(*op_decl, true); + add_visibility(self, op_decl); + self->visit(*op_decl, true, true); } else if (child->getNodeType() == __AST_NODE::nodes::LetDecl) { auto let_decl = __AST_N::as<__AST_NODE::LetDecl>(child); diff --git a/source/generator/source/CX-IR/utils.hh b/source/generator/source/CX-IR/utils.hh index d1e4e3e1..abab3cf9 100644 --- a/source/generator/source/CX-IR/utils.hh +++ b/source/generator/source/CX-IR/utils.hh @@ -95,18 +95,20 @@ contains_self_static(const __AST_N::NodeT<__AST_NODE::FuncDecl> &func_decl) { return found_self_static; } -inline std::pair -contains_self_static(const __AST_N::NodeT<__AST_NODE::OpDecl> &op_decl) { - auto func_decl = op_decl->func; - func_decl->modifiers = op_decl->modifiers; - - return contains_self_static(func_decl); -} - inline void handle_static_self_fn_decl(__AST_N::NodeT<__AST_NODE::FuncDecl> &func_decl, - token::Token &pof) { + token::Token &pof, + bool in_udt = true) { auto [has_self, has_static] = contains_self_static(func_decl); + if (!in_udt) { // free fucntion, cannot have self + if (has_self) { + CODEGEN_ERROR(pof, "cannot have 'self' in a free function"); + return; + } + + return; // free function, no need to do any processing or checks + } + if (!has_static) { if (!has_self) { error::Panic(error::CodeError{.pof = &pof, .err_code = 0.3004}); @@ -123,15 +125,12 @@ inline void handle_static_self_fn_decl(__AST_N::NodeT<__AST_NODE::FuncDecl> &fun } } -inline void handle_static_self_fn_decl(__AST_N::NodeT<__AST_NODE::OpDecl> &op_decl, - token::Token &pof, - bool in_udt = true) { - auto &func_decl = op_decl->func; - func_decl->modifiers = op_decl->modifiers; +inline void handle_static_self_fn_decl(__AST_NODE::FuncDecl&func_decl, + token::Token &pof, + bool in_udt = true) { + auto [has_self, has_static] = contains_self_static(std::make_shared<__AST_NODE::FuncDecl>(func_decl)); if (!in_udt) { // free fucntion, cannot have self - auto [has_self, has_static] = contains_self_static(func_decl); - if (has_self) { CODEGEN_ERROR(pof, "cannot have 'self' in a free function"); return; @@ -140,7 +139,20 @@ inline void handle_static_self_fn_decl(__AST_N::NodeT<__AST_NODE::OpDecl> &op_de return; // free function, no need to do any processing or checks } - handle_static_self_fn_decl(func_decl, pof); + if (!has_static) { + if (!has_self) { + error::Panic(error::CodeError{.pof = &pof, .err_code = 0.3004}); + + func_decl.modifiers.add(__AST_N::FunctionSpecifier( + token::Token(__TOKEN_N::KEYWORD_STATIC, "helix_internal.file"))); + } + } else if (has_self) { + error::Panic(error::CodeError{.pof = &pof, .err_code = 0.3005}); + } + + if (has_self) { + func_decl.params.erase(func_decl.params.begin()); + } } template @@ -174,17 +186,6 @@ inline void add_visibility(generator::CXIR::CXIR *self, } } -inline void add_visibility(generator::CXIR::CXIR *self, - const __AST_N::NodeT<__AST_NODE::OpDecl> &op_decl) { - if (op_decl->modifiers.contains(__TOKEN_N::KEYWORD_PROTECTED)) { - add_protected(self); - } else if (op_decl->modifiers.contains(__TOKEN_N::KEYWORD_PRIVATE)) { - add_private(self); - } else { - add_public(self); - } -} - template inline void add_visibility(generator::CXIR::CXIR *self, const T &decl) { if constexpr (HasVisiblityModifier) { @@ -198,7 +199,7 @@ inline void add_visibility(generator::CXIR::CXIR *self, const T &decl) { } } -inline OperatorType determine_operator_type(const __AST_N::NodeT<__AST_NODE::OpDecl> &op_decl) { +inline OperatorType determine_operator_type(const __AST_N::NodeT<__AST_NODE::FuncDecl> &op_decl) { // check for self and static context auto [is_self, is_static] = contains_self_static(op_decl); @@ -216,7 +217,7 @@ inline OperatorType determine_operator_type(const __AST_N::NodeT<__AST_NODE::OpD // distinguish unary prefix vs unary postfix auto is_unary_postfix = [&]() -> bool { - return op_decl->func->params.size() == 1 && + return op_decl->params.size() == 1 && (op_decl->op[0] == __TOKEN_N::tokens::OPERATOR_R_INC || op_decl->op[0] == __TOKEN_N::tokens::OPERATOR_R_DEC); }; @@ -227,7 +228,7 @@ inline OperatorType determine_operator_type(const __AST_N::NodeT<__AST_NODE::OpD return OperatorType::Call; // call operator `()` } - if (op_decl->func->params.size() == 2) { // (self, ...) + if (op_decl->params.size() == 2) { // (self, ...) if (is_array_operator()) { return OperatorType::Array; // array operator `[]` } @@ -235,7 +236,7 @@ inline OperatorType determine_operator_type(const __AST_N::NodeT<__AST_NODE::OpD return OperatorType::Binary; // binary operator } - if (op_decl->func->params.size() == 1) { // (self) + if (op_decl->params.size() == 1) { // (self) if (is_unary_postfix()) { return OperatorType::UnaryPostfix; // unary postfix operator `x++` or `x--` } @@ -243,7 +244,7 @@ inline OperatorType determine_operator_type(const __AST_N::NodeT<__AST_NODE::OpD return OperatorType::UnaryPrefix; // unary prefix operator } - if (op_decl->func->params.size() > 2) { // (self, ..., ...) + if (op_decl->params.size() > 2) { // (self, ..., ...) if (is_call_operator()) { return OperatorType::Array; // array operator `[]` } @@ -266,7 +267,7 @@ inline OperatorType determine_operator_type(const __AST_N::NodeT<__AST_NODE::OpD return OperatorType::None; } - if (op_decl->func->params.empty() || op_decl->func->params.size() == 0) { // (...) + if (op_decl->params.empty() || op_decl->params.size() == 0) { // (...) if (is_call_operator()) { return OperatorType::Call; // call operator `()` } @@ -274,7 +275,7 @@ inline OperatorType determine_operator_type(const __AST_N::NodeT<__AST_NODE::OpD return OperatorType::None; } - if (op_decl->func->params.size() == 1) { // (...) + if (op_decl->params.size() == 1) { // (...) if (is_unary_postfix()) { return OperatorType::UnaryPostfix; // unary postfix operator `x++` or `x--` } @@ -282,11 +283,11 @@ inline OperatorType determine_operator_type(const __AST_N::NodeT<__AST_NODE::OpD return OperatorType::UnaryPrefix; // unary prefix operator } - if (op_decl->func->params.size() == 2) { // (..., ...) + if (op_decl->params.size() == 2) { // (..., ...) return OperatorType::Binary; // binary operator } - if (op_decl->func->params.size() > 2) { // (..., ..., ...) + if (op_decl->params.size() > 2) { // (..., ..., ...) return OperatorType::Call; } } @@ -512,20 +513,20 @@ struct OpType { Type type = None; __TOKEN_N::Token *tok = nullptr; - OpType(const __AST_NODE::OpDecl &op, bool in_udt) { + OpType(const __AST_NODE::FuncDecl &op, bool in_udt) { if (in_udt) { type = det_t(op); } } - Type det_t(const __AST_NODE::OpDecl &op) { + Type det_t(const __AST_NODE::FuncDecl &op) { /// see the token and signature if (op.op.size() < 1) { return None; } tok = const_cast<__TOKEN_N::Token *>(&op.op.front()); - auto [$self, $static] = contains_self_static(std::make_shared<__AST_NODE::OpDecl>(op)); + auto [$self, $static] = contains_self_static(std::make_shared<__AST_NODE::FuncDecl>(op)); if (op.op.size() == 1 && op.op.back() == __TOKEN_N::KEYWORD_IN) { if ($static) { @@ -548,16 +549,16 @@ struct OpType { } // check the signature we don't give a shit about the func name rn - if ($self && op.func->params.size() == 1) { // most likely GeneratorOp - if (op.func->returns && - op.func->returns->specifiers.contains(__TOKEN_N::KEYWORD_YIELD)) { + if ($self && op.params.size() == 1) { // most likely GeneratorOp + if (op.returns && + op.returns->specifiers.contains(__TOKEN_N::KEYWORD_YIELD)) { return GeneratorOp; } - } else if ($self && op.func->params.size() == 2) { // most likely ContainsOp - if (op.func->returns && - op.func->returns->value->getNodeType() == __AST_NODE::nodes::IdentExpr) { + } else if ($self && op.params.size() == 2) { // most likely ContainsOp + if (op.returns && + op.returns->value->getNodeType() == __AST_NODE::nodes::IdentExpr) { // make sure the ret is a bool - auto node = __AST_N::as<__AST_NODE::IdentExpr>(op.func->returns->value); + auto node = __AST_N::as<__AST_NODE::IdentExpr>(op.returns->value); if (node->is_reserved_primitive && node->name.value() == "bool") { return ContainsOp; @@ -575,19 +576,19 @@ struct OpType { } if (op.op.size() == 1 && op.op.back() == __TOKEN_N::KEYWORD_AS) { - // op as fn (self) -> string {} + // fn op as (self) -> string {} // the as op must take self as the only arg and return any type if ($static) { error::Panic(error::CodeError{ .pof = tok, .err_code = 0.3002, .err_fmt_args = {"can not mark 'as' operator with static, signature must be " - "'op as fn (self) -> '"}}); + "'fn op as (self) -> '"}}); return Error; } - if (!op.func->returns) { + if (!op.returns) { error::Panic(error::CodeError{ .pof = tok, .err_code = 0.3002, @@ -596,8 +597,8 @@ struct OpType { return Error; } - if ($self && op.func->params.size() == 1) { - if (op.func->returns) { + if ($self && op.params.size() == 1) { + if (op.returns) { return CastOp; } } @@ -605,7 +606,7 @@ struct OpType { error::Panic(error::CodeError{ .pof = tok, .err_code = 0.3002, - .err_fmt_args = {"invalid 'as' operator, must be 'op as fn (self) -> '"}}); + .err_fmt_args = {"invalid 'as' fn op erator, must be 'op as (self) -> '"}}); return Error; } @@ -620,14 +621,14 @@ struct OpType { .err_code = 0.3002, .err_fmt_args = { "can not mark 'delete' operator with static, signature must be " - "'op delete fn (self)' delete should not return anything even if its " + "'fn op delete (self)' delete should not return anything even if its " "'void'."}}); return Error; } - if ($self && op.func->params.size() == 1) { - if (!op.func->returns) { + if ($self && op.params.size() == 1) { + if (!op.returns) { return DeleteOp; } } @@ -635,7 +636,7 @@ struct OpType { error::Panic(error::CodeError{ .pof = tok, .err_code = 0.3002, - .err_fmt_args = {"invalid 'delete' operator, must be 'op delete fn (self)' delete " + .err_fmt_args = {"invalid 'delete' fn op erator, must be 'op delete (self)' delete " "should not return anything even if its 'void'."}}); return Error; @@ -644,7 +645,7 @@ struct OpType { if (op.op.size() == 1 && op.op.back() == __TOKEN_N::OPERATOR_ASSIGN) { // ClassName& operator=(ClassName&&) // ClassName& operator=(const ClassName&) - // signature for move assign is `op = fn (self, _: const ClassName&) -> ... {}` + // signature for move assign is `fn op = (self, _: const ClassName&) -> ... {}` // for copy we need to check: // takes 2 params incl. self @@ -661,13 +662,13 @@ struct OpType { } } - // panic operator signature is `op panic fn (self) -> string {}` or `static op panic fn () + // panic fn op erator signature is `op panic fn (self) -> string {}` or `static op panic () // -> string {}` if (op.op.size() == 1 && op.op.back() == __TOKEN_N::KEYWORD_PANIC) { - if ((($self && op.func->params.size() == 1) || ($static && op.func->params.empty())) && - op.func->returns && - op.func->returns->value->getNodeType() == __AST_NODE::nodes::IdentExpr && - __AST_N::as<__AST_NODE::IdentExpr>(op.func->returns->value)->name.value() == + if ((($self && op.params.size() == 1) || ($static && op.params.empty())) && + op.returns && + op.returns->value->getNodeType() == __AST_NODE::nodes::IdentExpr && + __AST_N::as<__AST_NODE::IdentExpr>(op.returns->value)->name.value() == "string") { return PanicOp; } @@ -676,15 +677,15 @@ struct OpType { .pof = tok, .err_code = 0.3002, .err_fmt_args = { - "invalid 'panic' operator, must be 'op panic fn (self) -> string' or 'static " - "op panic fn () -> string'"}}); + "invalid 'panic' fn op erator, must be 'op panic (self) -> string' or 'static " + "fn op panic () -> string'"}}); } - // question operator signature is `op ? fn (self) -> bool {}` + // question fn op erator signature is `op ? (self) -> bool {}` if (op.op.size() == 1 && op.op.back() == __TOKEN_N::PUNCTUATION_QUESTION_MARK) { - if ($self && op.func->params.size() == 1 && op.func->returns && - op.func->returns->value->getNodeType() == __AST_NODE::nodes::IdentExpr && - __AST_N::as<__AST_NODE::IdentExpr>(op.func->returns->value)->name.value() == + if ($self && op.params.size() == 1 && op.returns && + op.returns->value->getNodeType() == __AST_NODE::nodes::IdentExpr && + __AST_N::as<__AST_NODE::IdentExpr>(op.returns->value)->name.value() == "bool") { return QuestionOp; } @@ -692,7 +693,7 @@ struct OpType { error::Panic(error::CodeError{ .pof = tok, .err_code = 0.3002, - .err_fmt_args = {"invalid '?' operator, must be 'op ? fn (self) -> bool'"}}); + .err_fmt_args = {"invalid '?' fn op erator, must be 'op ? (self) -> bool'"}}); return Error; } @@ -779,7 +780,7 @@ class ModifyNestedFunctions { bool operator()(const __AST_N::NodeT<> &elm) const { emitter->append(std::make_unique(CXX_SEMICOLON)); - if (elm->getNodeType() == __AST_NODE::nodes::FuncDecl) { + if (elm->getNodeType() == __AST_NODE::nodes::FuncDecl && !(__AST_N::as<__AST_NODE::FuncDecl>(elm)->is_op)) { __AST_N::NodeT<__AST_NODE::FuncDecl> func_decl = __AST_N::as<__AST_NODE::FuncDecl>(elm); if (func_decl->name != nullptr) { @@ -834,11 +835,11 @@ class ModifyNestedFunctions { return true; } - if (elm->getNodeType() == __AST_NODE::nodes::OpDecl) { - __AST_N::NodeT<__AST_NODE::OpDecl> func_decl = __AST_N::as<__AST_NODE::OpDecl>(elm); + if (elm->getNodeType() == __AST_NODE::nodes::FuncDecl && __AST_N::as<__AST_NODE::FuncDecl>(elm)->is_op) { + __AST_N::NodeT<__AST_NODE::FuncDecl> func_decl = __AST_N::as<__AST_NODE::FuncDecl>(elm); error::Panic _(error::CodeError{ - .pof = &func_decl->func->marker, + .pof = &func_decl->marker, .err_code = 0.0001, .err_fmt_args = {"operator declarenanos are not allowed in functions, remove it or " "modify the function to be a functor."}}); diff --git a/source/helix.hlx b/source/helix.hlx index c4751365..9241ac53 100644 --- a/source/helix.hlx +++ b/source/helix.hlx @@ -37,7 +37,7 @@ fn abc() -> i32 { // // couple of notes about helix: // - the language is still in development and is not stable yet. - // - the language right now has a c++ backend, and the self-hosted compiler would initially also + // - the language right now impl a c++ backend, and the self-hosted compiler would initially also // have a c++ backend. but that would be migrated to a llvm backend later on. (the c++ backend // is just a temporary solution, since llvm is really hard to work with and the c++ backend is // much easier to work with - for now) @@ -52,8 +52,7 @@ fn abc() -> i32 { // most part, the self-hosted compiler will be able to do much more and be much more flexible. } -fn helix_compiler(std::array:: args) -> i32 - requires { +fn helix_compiler(std::array:: args) -> i32 { var invoke = InvocationManager(args, true); var compile = CompilationUnit(invoke); var diag_handle = PrettyDiagnosticHandler(); diff --git a/source/parser/ast/include/config/AST_Declarations.def b/source/parser/ast/include/config/AST_Declarations.def index fdb3fe08..383a0798 100644 --- a/source/parser/ast/include/config/AST_Declarations.def +++ b/source/parser/ast/include/config/AST_Declarations.def @@ -35,7 +35,6 @@ MACRO(FuncDecl) \ MACRO(VarDecl) \ MACRO(FFIDecl) \ - MACRO(LetDecl) \ - MACRO(OpDecl) + MACRO(LetDecl) #endif // __AST_DECLARATIONS_DEF__ \ No newline at end of file diff --git a/source/parser/ast/include/config/AST_config.def b/source/parser/ast/include/config/AST_config.def index 0e3962f0..717ce08d 100644 --- a/source/parser/ast/include/config/AST_config.def +++ b/source/parser/ast/include/config/AST_config.def @@ -33,7 +33,7 @@ #define AST_BASE_IMPL(name, fn) __AST_N::ParseResult<> __AST_NODE::name::fn() #define AST_NODE_IMPL_VISITOR(visitor, name) \ - void __AST_VISITOR::visitor::visit([[maybe_unused]] const __AST_NODE::name &node) + void __AST_VISITOR::visitor::visit([[maybe_unused]] __AST_NODE::name &node) #define BASE_CORE_METHODS(name) \ public: \ @@ -43,7 +43,7 @@ name &operator=(const name &) = default; \ name(name &&) = default; \ name &operator=(name &&) = default; \ - void accept(__AST_VISITOR::Visitor &visitor) const override { visitor.visit(*this); } \ + void accept(__AST_VISITOR::Visitor &visitor) override { visitor.visit(*this); } \ [[nodiscard]] nodes getNodeType() const override { return nodes::name; } \ [[nodiscard]] std::string getNodeName() const override { return #name; } \ [[nodiscard]] bool is(nodes node) const override { return getNodeType() == node; } @@ -165,8 +165,8 @@ #define FORWARD_DECL(name) class name; #define DECL_PARSE_FUNC(name) ParseResult parse_##name(); #define NODE_ENUM(name) name, -#define VISIT_FUNC(name) virtual void visit(const __AST_NODE::name &) = 0; -#define VISIT_EXTEND(name) void visit(const __AST_NODE::name &node) override; +#define VISIT_FUNC(name) virtual void visit(__AST_NODE::name &) = 0; +#define VISIT_EXTEND(name) void visit(__AST_NODE::name &node) override; #define GENERATE_MACRO_HELPER(MACRO) EXPRS(MACRO) STATES(MACRO) DECLS(MACRO) @@ -174,9 +174,9 @@ #define GENERATE_NODES_FORWARD_DECLS GENERATE_MACRO_HELPER(FORWARD_DECL) #define GENERATE_PARSE_FUNCS #define GENERATE_VISIT_FUNCS \ - GENERATE_MACRO_HELPER(VISIT_FUNC) virtual void visit(const __AST_NODE::Program &) = 0; + GENERATE_MACRO_HELPER(VISIT_FUNC) virtual void visit(__AST_NODE::Program &) = 0; #define GENERATE_VISIT_EXTENDS \ - GENERATE_MACRO_HELPER(VISIT_EXTEND) void visit(const __AST_NODE::Program &) override; + GENERATE_MACRO_HELPER(VISIT_EXTEND) void visit(__AST_NODE::Program &) override; namespace parser::preprocessor { class ImportProcessor; diff --git a/source/parser/ast/include/nodes/AST_declarations.hh b/source/parser/ast/include/nodes/AST_declarations.hh index 9535cae0..dbf1181e 100644 --- a/source/parser/ast/include/nodes/AST_declarations.hh +++ b/source/parser/ast/include/nodes/AST_declarations.hh @@ -37,6 +37,8 @@ __AST_NODE_BEGIN { NodeT var; NodeT<> value; bool is_const = false; + NodeT bound; + }; class RequiresParamList final : public Node { @@ -80,6 +82,8 @@ __AST_NODE_BEGIN { (this->bounds).emplace_back(std::move(bound)); } + explicit TypeBoundList(bool /* unused */) {} + NodeV bounds; }; @@ -94,7 +98,7 @@ __AST_NODE_BEGIN { class RequiresDecl final : public Node { BASE_CORE_METHODS(RequiresDecl); - // RequiresDecl := 'requires' '<' RequiresParamList '>' ('if' TypeBoundList)? + // RequiresDecl := 'requires' '<' RequiresParamList '>' ('where' TypeBoundList)? explicit RequiresDecl(NodeT params) : params(std::move(params)) {} @@ -244,18 +248,21 @@ __AST_NODE_BEGIN { return result; } - // op + fn (); + // fn op + (); Modifiers modifiers = Modifiers(Modifiers::ExpectedModifier::FuncSpec, Modifiers::ExpectedModifier::AccessSpec); Modifiers qualifiers = Modifiers(Modifiers::ExpectedModifier::FuncQual); token::Token marker; - NodeT name; + NodeT name; // if an op then this would be alias NodeV params; NodeT generics; NodeT returns; NodeT body; + + bool is_op = false; // if this is an operator function + std::vector<__TOKEN_N::Token> op; }; class VarDecl final : public Node { @@ -292,18 +299,6 @@ __AST_NODE_BEGIN { NodeV vars; }; - class OpDecl final : public Node { - BASE_CORE_METHODS(OpDecl); - - // OpDecl := SharedModifiers? 'op' T FuncDecl[no_SharedModifiers=true] - explicit OpDecl(bool /* unused */) {} - - Modifiers modifiers = Modifiers(Modifiers::ExpectedModifier::FuncSpec, - Modifiers::ExpectedModifier::AccessSpec); - std::vector<__TOKEN_N::Token> op; - NodeT func; - }; - class ModuleDecl final : public Node { BASE_CORE_METHODS(ModuleDecl); diff --git a/source/parser/ast/include/nodes/AST_expressions.hh b/source/parser/ast/include/nodes/AST_expressions.hh index 7168320c..2fe4f524 100644 --- a/source/parser/ast/include/nodes/AST_expressions.hh +++ b/source/parser/ast/include/nodes/AST_expressions.hh @@ -133,6 +133,9 @@ __AST_NODE_BEGIN { } } + explicit ArgumentListExpr(NodeV<> args) + : args(std::move(args)) {} + explicit ArgumentListExpr(bool /* unused */) {} NodeV<> args; @@ -157,8 +160,12 @@ __AST_NODE_BEGIN { /// TODO: add support for generics BASE_CORE_METHODS(ScopePathExpr); - explicit ScopePathExpr(NodeT first) { path.emplace_back(std::move(first)); } + explicit ScopePathExpr(NodeT full) { path.emplace_back(std::move(full)); } explicit ScopePathExpr(bool /* unused */) {} + explicit ScopePathExpr(NodeV path, NodeT<> access, bool global_scope) + : path(std::move(path)) + , access(std::move(access)) + , global_scope(global_scope) {} // -- Helper Functions -- // @@ -171,6 +178,8 @@ __AST_NODE_BEGIN { return path.back()->name; } + + NodeV path; NodeT<> access; bool global_scope = false; @@ -211,16 +220,20 @@ __AST_NODE_BEGIN { class PathExpr final : public Node { // := ScopePathExpr | DotPathExpr BASE_CORE_METHODS(PathExpr); - explicit PathExpr(NodeT<> path) - : path(std::move(path)) - , type(PathType::Identifier) {} - enum class PathType : char { Scope, Dot, Identifier, }; + explicit PathExpr(NodeT<> path) + : path(std::move(path)) + , type(PathType::Identifier) {} + + explicit PathExpr(NodeT<> path, PathType type) + : path(std::move(path)) + , type(type) {} + NodeT<> path; PathType type = PathType::Identifier; @@ -426,7 +439,7 @@ __AST_NODE_BEGIN { BASE_CORE_METHODS(InstOfExpr); enum class InstanceType { - Has, + Implement, Derives, }; diff --git a/source/parser/ast/include/private/base/AST_base.hh b/source/parser/ast/include/private/base/AST_base.hh index a90a372e..b111f799 100644 --- a/source/parser/ast/include/private/base/AST_base.hh +++ b/source/parser/ast/include/private/base/AST_base.hh @@ -38,7 +38,7 @@ __AST_NODE_BEGIN { public: Node() = default; virtual ~Node() = default; - virtual void accept(__AST_VISITOR::Visitor &visitor) const = 0; + virtual void accept(__AST_VISITOR::Visitor &visitor) = 0; [[nodiscard]] virtual nodes getNodeType() const = 0; [[nodiscard]] virtual std::string getNodeName() const = 0; [[nodiscard]] virtual bool is(nodes node) const = 0; @@ -67,7 +67,7 @@ __AST_NODE_BEGIN { : filename(std::move(file_name)) , source_tokens(source_tokens) {} - void accept(parser ::ast ::visitor ::Visitor &visitor) const override { + void accept(parser ::ast ::visitor ::Visitor &visitor) override { visitor.visit(*this); } diff --git a/source/parser/ast/include/private/base/AST_base_declaration.hh b/source/parser/ast/include/private/base/AST_base_declaration.hh index 159b755f..1c764a2d 100644 --- a/source/parser/ast/include/private/base/AST_base_declaration.hh +++ b/source/parser/ast/include/private/base/AST_base_declaration.hh @@ -98,8 +98,6 @@ __AST_NODE_BEGIN { return parse_FFIDecl(std::forward(args)...); } else if constexpr (std::same_as) { return parse_LetDecl(std::forward(args)...); - } else if constexpr (std::same_as) { - return parse_OpDecl(std::forward(args)...); } else if constexpr (std::same_as) { return parse_ModuleDecl(std::forward(args)...); } @@ -140,8 +138,6 @@ __AST_NODE_BEGIN { parse_FFIDecl(const std::shared_ptr<__TOKEN_N::TokenList> &modifiers = nullptr); ParseResult parse_LetDecl(const std::shared_ptr<__TOKEN_N::TokenList> &modifiers = nullptr); - ParseResult - parse_OpDecl(const std::shared_ptr<__TOKEN_N::TokenList> &modifiers = nullptr); }; } // namespace __AST_NODE_BEGIN diff --git a/source/parser/ast/source/nodes/Decls.cc b/source/parser/ast/source/nodes/Decls.cc index eabed90f..9c8c0ed8 100644 --- a/source/parser/ast/source/nodes/Decls.cc +++ b/source/parser/ast/source/nodes/Decls.cc @@ -75,7 +75,6 @@ /// [x] * ConstDecl * VisDecl? 'const' SharedModifiers VarDecl* ';' /// /// [x] * TypeDecl * VisDecl? 'type' E.IdentExpr RequiresDecl? '=' E ';' /// /// [x] * EnumDecl * VisDecl? 'enum' ('derives' E.Type)? E.ObjInitExpr /// -/// [x] * OpDecl * SharedModifiers? 'op' T FuncDecl[no_SharedModifiers=true] /// /// [x] * FuncDecl * SharedModifiers? 'fn' E.PathExpr '(' VarDecl[true]* ')' RequiresDecl? S.Suite /// [x] * StructDecl* 'const'? VisDecl? 'struct' E.IdentExpr UDTDeriveDecl? RequiresDecl? S.Suite /// [x] * ClassDecl * 'const'? VisDecl? 'class' E.IdentExpr UDTDeriveDecl? ExtendsDecl? @@ -110,6 +109,8 @@ #include "parser/ast/include/types/AST_modifiers.hh" #include "parser/ast/include/types/AST_types.hh" #include "token/include/Token.hh" +#include "token/include/config/Token_config.def" +#include "token/include/private/Token_base.hh" #include "token/include/private/Token_generate.hh" AST_NODE_IMPL(Declaration, RequiresParamDecl) { @@ -129,12 +130,32 @@ AST_NODE_IMPL(Declaration, RequiresParamDecl) { node->var = var.value(); + __TOKEN_N::Token op; + + node->bound = nullptr; + + if (CURRENT_TOKEN_IS(__TOKEN_N::KEYWORD_IMPL) || CURRENT_TOKEN_IS(__TOKEN_N::KEYWORD_DERIVES)) { + op = CURRENT_TOK; + iter.advance(); // skip 'impl' or 'derives' + + ParseResult type = expr_parser.parse(); + RETURN_IF_ERROR(type); + + NodeT bound = make_node(node->var->path, type.value(), + op == __TOKEN_N::KEYWORD_IMPL + ? InstOfExpr::InstanceType::Implement + : InstOfExpr::InstanceType::Derives, + op); + bound->in_requires = true; + node->bound = bound; + } + if CURRENT_TOKEN_IS (__TOKEN_N::OPERATOR_ASSIGN) { iter.advance(); // skip '=' ParseResult<> value = expr_parser.parse(); RETURN_IF_ERROR(value); - + node->value = value.value(); } @@ -280,7 +301,8 @@ AST_NODE_IMPL(Declaration, TypeBoundList) { while (CURRENT_TOKEN_IS(__TOKEN_N::tokens::OPERATOR_LOGICAL_AND)) { iter.advance(); // skip '&&' - ParseResult next = expr_parser.parse(expr_parser.parse(), true); + ParseResult next = + expr_parser.parse(expr_parser.parse(), true); RETURN_IF_ERROR(next); node->bounds.emplace_back(next.value()); @@ -311,13 +333,21 @@ AST_NODE_IMPL_VISITOR(Jsonify, TypeBoundDecl) { json.section("TypeBoundDecl"); } // ---------------------------------------------------------------------------------------------- // +#define PARSE_REQUIRES_BOUNDS(req_node) \ + if (CURRENT_TOKEN_IS(__TOKEN_N::KEYWORD_REQUIRES)) { \ + iter.advance(); \ + \ + ParseResult bounds = parse(); \ + RETURN_IF_ERROR(bounds); \ + \ + (req_node)->bounds = bounds.value(); \ + } + AST_NODE_IMPL(Declaration, RequiresDecl) { IS_NOT_EMPTY; - // RequiresDecl := requires' '<' RequiresParamList '>' ('if' TypeBoundList)? - - IS_EXCEPTED_TOKEN(__TOKEN_N::KEYWORD_REQUIRES); - iter.advance(); // skip 'requires + // RequiresDecl := '<' RequiresParamList '>' ... ('requires' TypeBoundList)? + /// --------------------- params --------------------- /// IS_EXCEPTED_TOKEN(__TOKEN_N::PUNCTUATION_OPEN_ANGLE); iter.advance(); // skip '<' @@ -329,13 +359,18 @@ AST_NODE_IMPL(Declaration, RequiresDecl) { IS_EXCEPTED_TOKEN(__TOKEN_N::PUNCTUATION_CLOSE_ANGLE); iter.advance(); // skip '>' - if (CURRENT_TOKEN_IS(__TOKEN_N::KEYWORD_IF)) { - iter.advance(); // skip 'if' - - ParseResult bounds = parse(); - RETURN_IF_ERROR(bounds); + /// --------------------- bounds --------------------- /// + NodeV bounds; + for (const auto ¶m : node->params->params) { + if (param->bound != nullptr) { + bounds.push_back(param->bound); + } + } - node->bounds = bounds.value(); + if (!bounds.empty()) { + NodeT bound_list = make_node(true); + bound_list->bounds = bounds; + node->bounds = bound_list; } return node; @@ -371,6 +406,15 @@ AST_NODE_IMPL(Declaration, StructDecl, const std::shared_ptr<__TOKEN_N::TokenLis IS_EXCEPTED_TOKEN(__TOKEN_N::KEYWORD_STRUCT); iter.advance(); // skip 'struct' + /// ----- generic ----- /// + if CURRENT_TOKEN_IS (__TOKEN_N::PUNCTUATION_OPEN_ANGLE) { + ParseResult generics = parse(); + RETURN_IF_ERROR(generics); + + node->generics = generics.value(); + } + /// ----- generic ----- /// + ParseResult name = expr_parser.parse(); RETURN_IF_ERROR(name); @@ -384,10 +428,12 @@ AST_NODE_IMPL(Declaration, StructDecl, const std::shared_ptr<__TOKEN_N::TokenLis } if (CURRENT_TOKEN_IS(__TOKEN_N::KEYWORD_REQUIRES)) { - ParseResult generics = parse(); - RETURN_IF_ERROR(generics); + if (node->generics == nullptr) { + return std::unexpected(PARSE_ERROR( + CURRENT_TOK, "requires declaration must be preceded by a generic declaration")); + } - node->generics = generics.value(); + PARSE_REQUIRES_BOUNDS(node->generics); } if (CURRENT_TOKEN_IS(__TOKEN_N::PUNCTUATION_SEMICOLON)) { // forward declaration @@ -545,6 +591,15 @@ AST_NODE_IMPL(Declaration, ClassDecl, const std::shared_ptr<__TOKEN_N::TokenList IS_EXCEPTED_TOKEN(__TOKEN_N::KEYWORD_CLASS); iter.advance(); // skip 'class' + /// ----- generic ----- /// + if CURRENT_TOKEN_IS (__TOKEN_N::PUNCTUATION_OPEN_ANGLE) { + ParseResult generics = parse(); + RETURN_IF_ERROR(generics); + + node->generics = generics.value(); + } + /// ----- generic ----- /// + ParseResult name = expr_parser.parse(); RETURN_IF_ERROR(name); @@ -562,10 +617,12 @@ AST_NODE_IMPL(Declaration, ClassDecl, const std::shared_ptr<__TOKEN_N::TokenList } if (CURRENT_TOKEN_IS(__TOKEN_N::KEYWORD_REQUIRES)) { - ParseResult generics = parse(); - RETURN_IF_ERROR(generics); + if (node->generics == nullptr) { + return std::unexpected(PARSE_ERROR( + CURRENT_TOK, "requires declaration must be preceded by a generic declaration")); + } - node->generics = generics.value(); + PARSE_REQUIRES_BOUNDS(node->generics); } if (CURRENT_TOKEN_IS(__TOKEN_N::PUNCTUATION_SEMICOLON)) { // forward declaration @@ -614,6 +671,15 @@ AST_NODE_IMPL(Declaration, InterDecl, const std::shared_ptr<__TOKEN_N::TokenList IS_EXCEPTED_TOKEN(__TOKEN_N::KEYWORD_INTERFACE); iter.advance(); // skip 'interface' + /// ----- generic ----- /// + if CURRENT_TOKEN_IS (__TOKEN_N::PUNCTUATION_OPEN_ANGLE) { + ParseResult generics = parse(); + RETURN_IF_ERROR(generics); + + node->generics = generics.value(); + } + /// ----- generic ----- /// + ParseResult name = expr_parser.parse(); RETURN_IF_ERROR(name); @@ -627,10 +693,12 @@ AST_NODE_IMPL(Declaration, InterDecl, const std::shared_ptr<__TOKEN_N::TokenList } if (CURRENT_TOKEN_IS(__TOKEN_N::KEYWORD_REQUIRES)) { - ParseResult generics = parse(); - RETURN_IF_ERROR(generics); + if (node->generics == nullptr) { + return std::unexpected(PARSE_ERROR( + CURRENT_TOK, "requires declaration must be preceded by a generic declaration")); + } - node->generics = generics.value(); + PARSE_REQUIRES_BOUNDS(node->generics); } if (CURRENT_TOKEN_IS(__TOKEN_N::PUNCTUATION_SEMICOLON)) { // forward declaration @@ -659,7 +727,8 @@ AST_NODE_IMPL_VISITOR(Jsonify, InterDecl) { AST_NODE_IMPL(Declaration, EnumDecl, const std::shared_ptr<__TOKEN_N::TokenList> &modifiers) { IS_NOT_EMPTY; - // EnumDecl := Modifiers 'enum' (('derives' E.Type)? Ident)? (('{' (EnumMemberDecl (',' EnumMemberDecl)*)? '}') | (':' (EnumMemberDecl) ';')) + // EnumDecl := Modifiers 'enum' (('derives' E.Type)? Ident)? (('{' (EnumMemberDecl (',' + // EnumMemberDecl)*)? '}') | (':' (EnumMemberDecl) ';')) NodeT node = make_node(true); @@ -693,10 +762,11 @@ AST_NODE_IMPL(Declaration, EnumDecl, const std::shared_ptr<__TOKEN_N::TokenList> node->name = name.value(); } else { if (node->derives) { - return std::unexpected(PARSE_ERROR(CURRENT_TOK, "anonymous enum cannot have specified type")); + return std::unexpected( + PARSE_ERROR(CURRENT_TOK, "anonymous enum cannot have specified type")); } } - + if (CURRENT_TOKEN_IS(__TOKEN_N::PUNCTUATION_OPEN_BRACE)) { iter.advance(); // skip '{' @@ -709,7 +779,7 @@ AST_NODE_IMPL(Declaration, EnumDecl, const std::shared_ptr<__TOKEN_N::TokenList> if (!CURRENT_TOKEN_IS(__TOKEN_N::PUNCTUATION_COMMA)) { break; } - + iter.advance(); // skip ',' } @@ -772,16 +842,27 @@ AST_NODE_IMPL(Declaration, TypeDecl, const std::shared_ptr<__TOKEN_N::TokenList> IS_EXCEPTED_TOKEN(__TOKEN_N::KEYWORD_TYPE); iter.advance(); // skip 'type' + /// ----- generic ----- /// + if CURRENT_TOKEN_IS (__TOKEN_N::PUNCTUATION_OPEN_ANGLE) { + ParseResult generics = parse(); + RETURN_IF_ERROR(generics); + + node->generics = generics.value(); + } + /// ----- generic ----- /// + ParseResult name = expr_parser.parse(); RETURN_IF_ERROR(name); node->name = name.value(); if (CURRENT_TOKEN_IS(__TOKEN_N::KEYWORD_REQUIRES)) { - ParseResult generics = parse(); - RETURN_IF_ERROR(generics); + if (node->generics == nullptr) { + return std::unexpected(PARSE_ERROR( + CURRENT_TOK, "requires declaration must be preceded by a generic declaration")); + } - node->generics = generics.value(); + PARSE_REQUIRES_BOUNDS(node->generics); } IS_EXCEPTED_TOKEN(__TOKEN_N::OPERATOR_ASSIGN); @@ -811,7 +892,7 @@ AST_NODE_IMPL_VISITOR(Jsonify, TypeDecl) { AST_NODE_IMPL(Declaration, FuncDecl, const std::shared_ptr<__TOKEN_N::TokenList> &modifiers, - bool force_name) { + bool /*deprecated*/ force_name) { IS_NOT_EMPTY; // FuncDecl := Modifiers 'fn' E.PathExpr '(' VarDecl[true]* ')' RequiresDecl? ('->' // E.TypeExpr)? (S.Suite | ';' | '=' ('default' | 'delete')) @@ -836,33 +917,68 @@ AST_NODE_IMPL(Declaration, } IS_EXCEPTED_TOKEN(__TOKEN_N::KEYWORD_FUNCTION); - node->marker = CURRENT_TOK; // save 'fn' token - iter.advance(); // skip 'fn' + node->marker = CURRENT_TOK; // save 'fn' token + iter.advance(); // skip 'fn' - bool has_name = true; + /// ----- generic ----- /// only if not in an op situation + if (force_name && CURRENT_TOKEN_IS(__TOKEN_N::PUNCTUATION_OPEN_ANGLE)) { + ParseResult generics = parse(); + RETURN_IF_ERROR(generics); - if (!force_name && !(CURRENT_TOKEN_IS(__TOKEN_N::IDENTIFIER) || CURRENT_TOKEN_IS(__TOKEN_N::OPERATOR_SCOPE))) { - has_name = false; + node->generics = generics.value(); } + /// ----- generic ----- /// - if (has_name) { - ParseResult name = expr_parser.parse(); - RETURN_IF_ERROR(name); + bool has_name = true; - if (name.value()->type == PathExpr::PathType::Dot) { - return std::unexpected(PARSE_ERROR(CURRENT_TOK, "invalid function name")); - } + if CURRENT_TOKEN_IS (__TOKEN_N::KEYWORD_OPERATOR) { + /// this is an operator decl that goes like fn on ... ()[alias] -> T {} + node->is_op = true; - node->name = name.value(); + IS_EXCEPTED_TOKEN(__TOKEN_N::KEYWORD_OPERATOR); + auto starting_tok = CURRENT_TOK; + iter.advance(); // skip 'op' + + for (int op_token_count = 0; op_token_count < 4; ++op_token_count) { + if CURRENT_TOKEN_IS (token::PUNCTUATION_OPEN_PAREN) { + break; + } + + node->op.push_back(CURRENT_TOK); + + if (op_token_count == 3) { + return std::unexpected(PARSE_ERROR( + starting_tok, + "operator declaration incomplete, exceeded maximum allowed tokens before '('")); + } + + iter.advance(); // skip operator token + } } else { - node->name = nullptr; + if (!force_name && !(CURRENT_TOKEN_IS(__TOKEN_N::IDENTIFIER) || + CURRENT_TOKEN_IS(__TOKEN_N::OPERATOR_SCOPE))) { + has_name = false; + } + + if (has_name) { + ParseResult name = expr_parser.parse(); + RETURN_IF_ERROR(name); + + if (name.value()->type == PathExpr::PathType::Dot) { + return std::unexpected(PARSE_ERROR(CURRENT_TOK, "invalid function name")); + } + + node->name = name.value(); + } else { + node->name = nullptr; + } } IS_EXCEPTED_TOKEN(__TOKEN_N::PUNCTUATION_OPEN_PAREN); iter.advance(); // skip '(' if (!CURRENT_TOKEN_IS(__TOKEN_N::PUNCTUATION_CLOSE_PAREN)) { - __TOKEN_N::Token starting = CURRENT_TOK; + __TOKEN_N::Token starting = CURRENT_TOK; ParseResult first_param = parse(true); RETURN_IF_ERROR(first_param); @@ -876,7 +992,7 @@ AST_NODE_IMPL(Declaration, if (param.value()->value != nullptr) { has_keyword = true; - } else { // if theres no value but theres a keyword arg + } else { // if theres no value but theres a keyword arg if (has_keyword) { return std::unexpected( PARSE_ERROR(starting, "positional argument after default argument")); @@ -886,15 +1002,44 @@ AST_NODE_IMPL(Declaration, node->params.emplace_back(param.value()); } } - + IS_EXCEPTED_TOKEN(__TOKEN_N::PUNCTUATION_CLOSE_PAREN); iter.advance(); // skip ')' + if (node->is_op) { + // we can have an alias name + if CURRENT_TOKEN_IS (__TOKEN_N::PUNCTUATION_OPEN_BRACKET) { + iter.advance(); // skip '[' + + if (CURRENT_TOKEN_IS(__TOKEN_N::IDENTIFIER) || + CURRENT_TOKEN_IS(__TOKEN_N::OPERATOR_SCOPE)) { + ParseResult alias = expr_parser.parse(); + RETURN_IF_ERROR(alias); + + if (alias.value()->type == PathExpr::PathType::Dot) { + return std::unexpected(PARSE_ERROR(CURRENT_TOK, "invalid operator alias")); + } + + node->name = alias.value(); + } else { + return std::unexpected( + PARSE_ERROR(CURRENT_TOK, "expected identifier for operator alias")); + } + + IS_EXCEPTED_TOKEN(__TOKEN_N::PUNCTUATION_CLOSE_BRACKET); + iter.advance(); // skip ']' + } else { + node->name = nullptr; + } + } + if (CURRENT_TOKEN_IS(__TOKEN_N::KEYWORD_REQUIRES)) { - ParseResult generics = parse(); - RETURN_IF_ERROR(generics); + if (node->generics == nullptr) { + return std::unexpected(PARSE_ERROR( + CURRENT_TOK, "requires declaration must be preceded by a generic declaration")); + } - node->generics = generics.value(); + PARSE_REQUIRES_BOUNDS(node->generics); found_requires = true; } @@ -911,10 +1056,12 @@ AST_NODE_IMPL(Declaration, return std::unexpected(PARSE_ERROR(CURRENT_TOK, "duplicate requires clause")); } - ParseResult generics = parse(); - RETURN_IF_ERROR(generics); + if (node->generics == nullptr) { + return std::unexpected(PARSE_ERROR( + CURRENT_TOK, "requires declaration must be preceded by a generic declaration")); + } - node->generics = generics.value(); + PARSE_REQUIRES_BOUNDS(node->generics); } } @@ -938,6 +1085,133 @@ AST_NODE_IMPL(Declaration, return std::unexpected(PARSE_ERROR(CURRENT_TOK, "expected function body")); } + /// now we append the following to the params: + /// const loc: libcxx::source_location = libcxx::source_location::current() + /// along with the following to elm 0 of the body: + /// __REGISTER_HELIX_TRACE_BLOCK__(loc.file_name(), loc.line(), __HELIX_FUNCNAME__); + + auto function_name = node->name; + // if (!(node->is_op) && (function_name != nullptr && !((function_name->get_back_name().value() == "main" || + // function_name->get_back_name().value() == "_main" || + // function_name->get_back_name().value() == "wmain" || + // function_name->get_back_name().value() == "WinMain" || + // function_name->get_back_name().value() == "wWinMain" || + // function_name->get_back_name().value() == "_tmain" || + // function_name->get_back_name().value() == "_tWinMain")))) { + // node->params.emplace_back(make_node( + // make_node( + // make_node( + // token::Token(__TOKEN_N::IDENTIFIER, "$source$loc", node->marker)), + + // make_node(make_node( + // NodeV{ + // make_node( + // token::Token(__TOKEN_N::IDENTIFIER, "libcxx", node->marker)), + // }, + + // make_node( + // token::Token(__TOKEN_N::IDENTIFIER, "source_location", node->marker)), + + // true))), + + // make_node( + // make_node( + // make_node( + // NodeV{ + // make_node( + // token::Token(__TOKEN_N::IDENTIFIER, "libcxx", node->marker)), + + // make_node(token::Token( + // __TOKEN_N::IDENTIFIER, "source_location", node->marker)), + // }, + + // make_node( + // token::Token(__TOKEN_N::IDENTIFIER, "current", node->marker)), + + // true), + + // PathExpr::PathType::Scope), + + // make_node(nullptr), + // nullptr))); + // } + + NodeT register_trace; + + // if (!(node->is_op) && (function_name != nullptr && !((function_name->get_back_name().value() == "main" || + // function_name->get_back_name().value() == "_main" || + // function_name->get_back_name().value() == "wmain" || + // function_name->get_back_name().value() == "WinMain" || + // function_name->get_back_name().value() == "wWinMain" || + // function_name->get_back_name().value() == "_tmain" || + // function_name->get_back_name().value() == "_tWinMain")))) { + // register_trace = make_node(NodeV<>{ + // make_node(make_node( + // make_node(make_node(token::Token( + // __TOKEN_N::IDENTIFIER, "$source$loc", node->marker)), + + // make_node( + // make_node(make_node(token::Token( + // __TOKEN_N::IDENTIFIER, "file_name", node->marker))), + + // make_node(nullptr), + // nullptr)), + + // PathExpr::PathType::Dot)), + + // make_node(make_node( + // make_node(make_node(token::Token( + // __TOKEN_N::IDENTIFIER, "$source$loc", node->marker)), + + // make_node( + // make_node(make_node(token::Token( + // __TOKEN_N::IDENTIFIER, "line", node->marker))), + + // make_node(nullptr), + // nullptr)), + + // PathExpr::PathType::Dot)), + + // make_node(make_node( + // token::Token(__TOKEN_N::IDENTIFIER, "__HELIX_FUNCNAME__", node->marker))), + // }); + // } else { + // register_trace = make_node(NodeV<>{ + // make_node(make_node( + // token::Token(__TOKEN_N::IDENTIFIER, "__FILE__", node->marker))), + + // make_node(make_node( + // token::Token(__TOKEN_N::IDENTIFIER, "__LINE__", node->marker))), + + // make_node(make_node( + // token::Token(__TOKEN_N::IDENTIFIER, "__HELIX_FUNCNAME__", node->marker))), + // }); + // } + + register_trace = make_node(NodeV<>{ + make_node(make_node( + token::Token(__TOKEN_N::IDENTIFIER, "__FILE__", node->marker))), + + make_node(make_node( + token::Token(__TOKEN_N::IDENTIFIER, "__LINE__", node->marker))), + + make_node(make_node( + token::Token(__TOKEN_N::IDENTIFIER, "__HELIX_FUNCNAME__", node->marker))), + }); + + if (node->body != nullptr && node->body->body != nullptr) { + node->body->body->body.emplace( + node->body->body->body.begin(), + + make_node( + make_node(make_node(token::Token( + __TOKEN_N::IDENTIFIER, "__REGISTER_HELIX_TRACE_BLOCK__", node->marker))), + + register_trace, + + nullptr)); + } + return node; } @@ -955,7 +1229,9 @@ AST_NODE_IMPL_VISITOR(Jsonify, FuncDecl) { .add("returns", get_node_json(node.returns)) .add("body", get_node_json(node.body)) .add("modifiers", node.modifiers.to_json()) - .add("qualifiers", node.qualifiers.to_json()); + .add("qualifiers", node.qualifiers.to_json()) + .add("is_op", node.is_op) + .add("op", node.op); } // ---------------------------------------------------------------------------------------------- // @@ -1127,53 +1403,6 @@ AST_NODE_IMPL_VISITOR(Jsonify, LetDecl) { // ---------------------------------------------------------------------------------------------- // -/* TODO: MERGE WITH FUNCTION DECL */ -AST_NODE_IMPL(Declaration, OpDecl, const std::shared_ptr<__TOKEN_N::TokenList> &modifiers) { - IS_NOT_EMPTY; - // OpDecl := Modifiers 'op' T FuncDecl[no_SharedModifiers=true] - - NodeT node = make_node(true); - - if (modifiers != nullptr) { - for (auto &tok : *modifiers) { - if (!node->modifiers.find_add(tok.current().get())) { - return std::unexpected( - PARSE_ERROR(tok.current().get(), "invalid modifier for an operator")); - } - } - } else { - while (node->modifiers.find_add(CURRENT_TOK)) { - iter.advance(); // skip modifier - } - } - - IS_EXCEPTED_TOKEN(__TOKEN_N::KEYWORD_OPERATOR); - - // TODO: Find a better way to do this... - while (iter.advance().get().token_kind() != token::KEYWORD_FUNCTION) { - node->op.push_back(CURRENT_TOK); // skip token and push it - } - - /// 'fn' here, so we now need to parse the function in name optional mode - ParseResult fn = parse(nullptr, false); - RETURN_IF_ERROR(fn); - - // NodeT fn_node = make_node(); - - node->func.swap(fn.value()); - - return node; -} - -AST_NODE_IMPL_VISITOR(Jsonify, OpDecl) { - json.section("OpDecl") - .add("op", node.op) - .add("func", get_node_json(node.func)) - .add("modifiers", node.modifiers.to_json()); -} - -// ---------------------------------------------------------------------------------------------- // - AST_NODE_IMPL(Declaration, ModuleDecl, const std::shared_ptr<__TOKEN_N::TokenList> &modifiers) { IS_NOT_EMPTY; // ModuleDecl := 'inline'? 'module' E.PathExpr[scopeOnly=true] S.Suite @@ -1218,7 +1447,8 @@ AST_BASE_IMPL(Declaration, parse) { IS_NOT_EMPTY; __TOKEN_N::Token tok = CURRENT_TOK; /// get the current token from the iterator - std::shared_ptr<__TOKEN_N::TokenList> modifiers = nullptr; /// create a pointer to the modifiers + std::shared_ptr<__TOKEN_N::TokenList> modifiers = + nullptr; /// create a pointer to the modifiers /* TODO: make this not happen if bool is passed */ while (Modifiers::is_modifier(tok)) { @@ -1270,7 +1500,8 @@ AST_BASE_IMPL(Declaration, parse) { case __TOKEN_N::KEYWORD_FUNCTION: return parse(modifiers); case __TOKEN_N::KEYWORD_OPERATOR: - return parse(modifiers); + return std::unexpected( + PARSE_ERROR(tok, "operator declaration is in the wrong place/format")); case __TOKEN_N::KEYWORD_TYPE: return parse(modifiers); // case __TOKEN_N::KEYWORD_UNION: diff --git a/source/parser/ast/source/nodes/Exprs.cc b/source/parser/ast/source/nodes/Exprs.cc index 817643e7..82c3bb23 100644 --- a/source/parser/ast/source/nodes/Exprs.cc +++ b/source/parser/ast/source/nodes/Exprs.cc @@ -347,7 +347,7 @@ parser ::ast ::ParseResult<> parser::ast::node::Expression::parse( break; - case __TOKEN_N::KEYWORD_HAS: + case __TOKEN_N::KEYWORD_IMPL: case __TOKEN_N::KEYWORD_DERIVES: expr = parse(expr, in_requires); RETURN_IF_ERROR(expr); @@ -1645,12 +1645,12 @@ AST_NODE_IMPL(Expression, InstOfExpr, ParseResult<> lhs, bool in_requires) { RETURN_IF_ERROR(lhs); } -#define INST_OF_OPS {__TOKEN_N::KEYWORD_HAS, __TOKEN_N::KEYWORD_DERIVES} +#define INST_OF_OPS {__TOKEN_N::KEYWORD_IMPL, __TOKEN_N::KEYWORD_DERIVES} IS_IN_EXCEPTED_TOKENS(INST_OF_OPS); #undef INST_OF_OPS - if CURRENT_TOKEN_IS (__TOKEN_N::KEYWORD_HAS) { - op = InstOfExpr::InstanceType::Has; + if CURRENT_TOKEN_IS (__TOKEN_N::KEYWORD_IMPL) { + op = InstOfExpr::InstanceType::Implement; } tok = iter.current(); @@ -1659,7 +1659,7 @@ AST_NODE_IMPL(Expression, InstOfExpr, ParseResult<> lhs, bool in_requires) { ParseResult<> rhs; - if (op == InstOfExpr::InstanceType::Has) { + if (op == InstOfExpr::InstanceType::Implement) { if (in_requires) { rhs = parse(); } else { diff --git a/source/parser/ast/source/visitors/JsonifyVisitor.cc b/source/parser/ast/source/visitors/JsonifyVisitor.cc index 4a5071c5..633fd754 100644 --- a/source/parser/ast/source/visitors/JsonifyVisitor.cc +++ b/source/parser/ast/source/visitors/JsonifyVisitor.cc @@ -19,7 +19,7 @@ #include "parser/ast/include/types/AST_jsonify_visitor.hh" __AST_VISITOR_BEGIN { - void Jsonify::visit(const parser ::ast ::node ::Program & node) { + void Jsonify::visit(parser ::ast ::node ::Program & node) { neo::json children("children"); neo::json annotations("annotations"); diff --git a/source/parser/preprocessor/include/private/utils.hh b/source/parser/preprocessor/include/private/utils.hh index 24365f26..bcedfeff 100644 --- a/source/parser/preprocessor/include/private/utils.hh +++ b/source/parser/preprocessor/include/private/utils.hh @@ -21,11 +21,14 @@ // helix_mod -> 3 // helix_mod_lib -> 4 // invalid -> 0 + +#include +#include +#include +#include #include #include -#include "neo-types/include/hxint.hh" - inline int find_import_priority(bool is_module, // NOLINT bool found_helix_mod, bool found_helix_hdr, @@ -123,12 +126,21 @@ inline std::string sanitize_string(const std::string &input) { return output + "_N"; } -enum class ObjectType : u8 { Module, Class, Struct, Function, Operator, Reserved, Internal, None }; - +enum class ObjectType : unsigned char { + Module, + Class, + Struct, + Function, + Operator, + Reserved, + Internal, + None +}; + +// --- Mangler: encodes non-alnum/_ as $XXXX, appends $L_E$ inline std::string mangle(const std::string &input, ObjectType type) { - if (input.empty()) { + if (input.empty()) throw std::invalid_argument("Input string cannot be empty"); - } std::string prefix; switch (type) { @@ -153,27 +165,71 @@ inline std::string mangle(const std::string &input, ObjectType type) { case ObjectType::Internal: prefix = "_$I_"; break; + default: + break; } std::string output = prefix; for (char ch : input) { - if (std::isalnum(ch) || ch == '_') { + if (std::isalnum(static_cast(ch)) || ch == '_') { output += ch; } else { - output += '$'; - char hex[3]; - snprintf(hex, sizeof(hex), "%02X", static_cast(ch)); - output += hex; + std::ostringstream oss; + oss << '$' << std::uppercase << std::hex << std::setw(4) << std::setfill('0') + << (static_cast(static_cast(ch))); + output += oss.str(); } } - output += "$L"; - output += std::to_string(input.length()) + "_E$"; - + output += "$L" + std::to_string(input.length()) + "_E$"; return output; } +// --- Detect mangled type by prefix +inline ObjectType is_mangled(const std::string &input) { + if (input.size() < 4 || input[0] != '_' || input[1] != '$') + return ObjectType::None; + + std::string prefix = input.substr(0, 3); + if (prefix == "_$M_") + return ObjectType::Module; + if (prefix == "_$C_") + return ObjectType::Class; + if (prefix == "_$S_") + return ObjectType::Struct; + if (prefix == "_$F_") + return ObjectType::Function; + if (prefix == "_$O_") + return ObjectType::Operator; + if (prefix == "_$R_") + return ObjectType::Reserved; + if (prefix == "_$I_") + return ObjectType::Internal; + + return ObjectType::None; +} + +// --- Check hex digit +inline bool is_hex_digit(char c) { + return std::isdigit(static_cast(c)) || (c >= 'a' && c <= 'f') || + (c >= 'A' && c <= 'F'); +} + +// --- Extract basename without extension +inline std::string basename_no_ext(const std::string &path) { + size_t slash = path.rfind('/'); + size_t bslash = path.rfind('\\'); + size_t sep = (slash == std::string::npos) + ? bslash + : ((bslash != std::string::npos && bslash > slash) ? bslash : slash); + size_t start = (sep == std::string::npos) ? 0 : sep + 1; + size_t dot = path.rfind('.'); + size_t end = (dot != std::string::npos && dot > start) ? dot : path.length(); + return path.substr(start, end - start); +} + +// --- Demangler: supports $XX and $XXXX, validates length inline std::string demangle(const std::string &input, ObjectType type) { std::string expected_prefix; switch (type) { @@ -198,128 +254,114 @@ inline std::string demangle(const std::string &input, ObjectType type) { case ObjectType::Internal: expected_prefix = "_$I_"; break; + default: + break; } - if (!input.starts_with(expected_prefix)) { + if (!input.starts_with(expected_prefix)) throw std::invalid_argument("Invalid mangled name or type mismatch"); - } size_t len_pos = input.rfind("$L"); - if (len_pos == std::string::npos) { - throw std::invalid_argument("Invalid mangled name: missing length"); - } - size_t end_pos = input.rfind("_E$"); - if (end_pos == std::string::npos || end_pos <= len_pos) { - throw std::invalid_argument("Invalid mangled name: missing end marker"); - } + if (len_pos == std::string::npos || end_pos == std::string::npos || end_pos <= len_pos) + throw std::invalid_argument("Invalid mangled name: missing length markers"); - std::string len_str = input.substr(len_pos + 2 /* part after: "$L" */, - end_pos - len_pos - 2 /* part before: "_E$" */); - - if (len_str.empty()) { + std::string len_str = input.substr(len_pos + 2, end_pos - len_pos - 2); + if (len_str.empty()) throw std::invalid_argument("Invalid length in mangled name"); - } - size_t expected_len; - try { - expected_len = std::stoul(len_str); - } catch (...) { throw std::invalid_argument("Invalid length in mangled name"); } + size_t expected_len = std::stoul(len_str); std::string encoded = input.substr(expected_prefix.length(), len_pos - expected_prefix.length()); std::string output; - for (size_t i = 0; i < encoded.length(); ++i) { - if (encoded[i] == '$' && i + 2 < encoded.length()) { - std::string hex = encoded.substr(i + 1, 2); - try { - unsigned char ch = static_cast(std::stoul(hex, nullptr, 16)); - output += ch; - i += 2; - } catch (...) { throw std::invalid_argument("Invalid hex encoding in mangled name"); } - } else { - output += encoded[i]; + for (size_t i = 0; i < encoded.size();) { + if (encoded[i] == '$') { + size_t j = i + 1; + size_t count = 0; + while (j < encoded.size() && count < 4 && is_hex_digit(encoded[j])) { + ++j; + ++count; + } + if (count == 4 || count == 2) { + unsigned long code = std::stoul(encoded.substr(i + 1, count), nullptr, 16); + output += static_cast(code); + i = j; + continue; + } } + output += encoded[i++]; } - if (output.length() != expected_len) { + if (output.size() != expected_len) throw std::invalid_argument("Demangled length mismatch"); - } return output; } -inline ObjectType is_mangled(const std::string &input) { - if (input.length() < 4 || input[0] != '_' || input[1] != '$') { - return ObjectType::None; // Not a recognized mangled name - } - - std::string prefix = input.substr(0, 3); - if (prefix == "_$M_") - return ObjectType::Module; - if (prefix == "_$C_") - return ObjectType::Class; - if (prefix == "_$S_") - return ObjectType::Struct; - if (prefix == "_$F_") - return ObjectType::Function; - if (prefix == "_$O_") - return ObjectType::Operator; - if (prefix == "_$R_") - return ObjectType::Reserved; - if (prefix == "_$I_") - return ObjectType::Internal; - - return ObjectType::None; // Not a recognized mangled name -} - -// function to demangle a part of a name such as in helix::_$M_foo$L3::bar -// to helix::foo::bar -inline std::string demangle_parttial(const std::string &input) { - // read from each char in the string untill we find a _$ - // then read ill the chars until we find a _E$ and then keep checking the rest of the string - +// --- Demangle partial segments, strip directories for Modules +inline std::string demangle_partial(const std::string &input) { std::string output; - - for (size_t i = 0; i < input.length(); ++i) { - if (i + 3 < input.length() && input[i] == '_' && input[i + 1] == '$') { + for (size_t i = 0; i < input.size(); ++i) { + if (i + 3 < input.size() && input[i] == '_' && input[i + 1] == '$') { + ObjectType ty = ObjectType::None; switch (input[i + 2]) { case 'M': + ty = ObjectType::Module; + break; case 'C': + ty = ObjectType::Class; + break; case 'S': + ty = ObjectType::Struct; + break; case 'F': + ty = ObjectType::Function; + break; case 'O': + ty = ObjectType::Operator; + break; case 'R': + ty = ObjectType::Reserved; + break; case 'I': + ty = ObjectType::Internal; break; default: output += input[i]; continue; } - // found a prefix, now read until we find a _E$ size_t end_pos = input.find("_E$", i); - - if (end_pos != std::string::npos) { - std::string mangled = input.substr(i, end_pos - i + 3); - i = end_pos + 2; // move past _E$ - - auto type = is_mangled(mangled); - if (type == ObjectType::None) { - output += input.substr(i, end_pos - i + 3); // keep the mangled name as is - continue; - } - - // demangle the name - std::string demangled = demangle(mangled, type); - output += demangled; + if (end_pos == std::string::npos) { + output += input[i]; continue; } + + std::string mangled = input.substr(i, end_pos - i + 3); + std::string dem = demangle(mangled, ty); + if (ty == ObjectType::Module) + dem = basename_no_ext(dem); + output += dem; + i = end_pos + 2; // loop will ++ + continue; } output += input[i]; } + return output; +} + +// --- Strip leading helix:: prefix +inline std::string strip_helix_prefix(const std::string &input) { + const std::string p1 = "helix::"; + const std::string p2 = "::helix::"; + if (input.starts_with(p1)) + return input.substr(p1.length()); + if (input.starts_with(p2)) + return input.substr(p2.length()); + return input; } } // namespace helix::abi diff --git a/source/token/include/enums/Token_keywords.def b/source/token/include/enums/Token_keywords.def index 52ca7137..9406696a 100644 --- a/source/token/include/enums/Token_keywords.def +++ b/source/token/include/enums/Token_keywords.def @@ -55,8 +55,8 @@ /* D */ GENERATE(KEYWORD_CLASS, "class") \ /* D */ GENERATE(KEYWORD_UNION, "union") \ /* D */ GENERATE(KEYWORD_STRUCT, "struct") \ + /* D */ GENERATE(KEYWORD_WHERE, "where") \ /* D */ GENERATE(KEYWORD_INTERFACE, "interface") \ - /* H */ GENERATE(KEYWORD_HAS, "has") \ /* S */ GENERATE(KEYWORD_TRY, "try") \ /* S */ GENERATE(KEYWORD_PANIC, "panic") \ /* S */ GENERATE(KEYWORD_CATCH, "catch") \ diff --git a/test.hlx b/test.hlx new file mode 100644 index 00000000..80eca5a9 --- /dev/null +++ b/test.hlx @@ -0,0 +1,56 @@ +import std::Error::ValueError as ValueError; + +ffi "c++" import "source_location"; + +interface BasicI { + fn op==(self, other: i32) -> bool; +} + +/* we need to add the following to every function def const loc: libcxx::source_location = libcxx::source_location::current() along with */ +fn other_foo() -> i32 { + panic std::Error::NullValueError("This is a value error"); + return 42; +} + +struct Basic { + var count: i32 = 109; + + fn op == (self, other: i32) -> bool { + + var val = other_foo(); + std::print(f"Value: {val}"); + + return self.count == other; + } +} + + +fn new_function() -> T? { + + panic ValueError::ValueError("This is a value error"); + + return T(); +} + + +class Foo { + fn something() -> i32 { + return 42; + } + +} + + +fn main() -> i32 { + var call = new_function::(); + + std::print(f"Count: {(*call).count}"); + + var obj = Foo(); + obj.something(); + + var eq = (*call) == 109; + std::print(f"Equal: {eq}"); + + return 0; +} \ No newline at end of file diff --git a/tests/language/interfaces_advanced.hlx b/tests/language/interfaces_advanced.hlx index 9bee6296..b116a36d 100644 --- a/tests/language/interfaces_advanced.hlx +++ b/tests/language/interfaces_advanced.hlx @@ -1,8 +1,8 @@ -interface Foo requires { +interface Foo requires { fn flip(self) -> U; } -interface Advanced requires if U has Foo:: { +interface Advanced requires U impl Foo:: { fn advanced_method(self) -> U; static fn advanced_static_method(value: U) -> U; var advanced_field: U; @@ -22,8 +22,8 @@ class Achoo { } // A class implementing Advanced -class AdvancedTavern impl Advanced:: - requires if U has Foo:: { +class AdvancedTavern impl Advanced:: + requires U impl Foo:: { pub var advanced_field: U; fn advanced_method(self) -> U { @@ -57,7 +57,7 @@ fn main() -> i32 { var advanced_instance = AdvancedTavern::(Tavern{}); print(advanced_instance.advanced_method().flip()); // Should print 10 - // print("true" if (InvalidTavern has Advanced::) else "false"); // Should print false + // print("true" if (InvalidTavern impl Advanced::) else "false"); // Should print false print("false"); // TODO: Fix scope path for generics before enabling this test diff --git a/tests/language/interfaces_basic.hlx b/tests/language/interfaces_basic.hlx index 9b5ca25f..3d01b326 100644 --- a/tests/language/interfaces_basic.hlx +++ b/tests/language/interfaces_basic.hlx @@ -1,5 +1,5 @@ // Basic interface with a generic -interface Foo requires { +interface Foo { fn poo(self) -> U; } diff --git a/tests/language/interfaces_constraints.hlx b/tests/language/interfaces_constraints.hlx index 59f011e0..18ec0fca 100644 --- a/tests/language/interfaces_constraints.hlx +++ b/tests/language/interfaces_constraints.hlx @@ -4,7 +4,7 @@ interface SimpleI { fn innit(self) -> i32; } -interface ConstrainedI requires if U has SimpleI { +interface ConstrainedI requires U impl SimpleI { fn mandem(self, other: U) -> bool; } @@ -27,7 +27,7 @@ class ConstrainedPub impl ConstrainedI:: { } // Function that uses constrained interfaces -fn use_constrained() requires if T has ConstrainedI:: && U has SimpleI { +fn use_constrained() requires T impl ConstrainedI:: && U impl SimpleI { var instance: T; print(instance.mandem(U())); } diff --git a/tests/language/invalid_impls.hlx b/tests/language/invalid_impls.hlx index 2bcf4e82..8f6f9a99 100644 --- a/tests/language/invalid_impls.hlx +++ b/tests/language/invalid_impls.hlx @@ -1,5 +1,5 @@ // Basic interface with a generic -interface Haggler requires { +interface Haggle { fn tia(self) -> U; } @@ -15,7 +15,7 @@ class MissingMethodC impl Haggler:: { // Missing `fn tia(self) -> i32` } -fn tia() requires if T has Haggler:: { // function must be defined so the check is done +fn tia() requires T impl Haggler:: { // function must be defined so the check is done var instance: T; print(instance.tia()); } diff --git a/tests/language/keywords_conditional.hlx b/tests/language/keywords_conditional.hlx index 016f5fed..565829b6 100644 --- a/tests/language/keywords_conditional.hlx +++ b/tests/language/keywords_conditional.hlx @@ -47,7 +47,7 @@ fn main() -> i32 { // }; // if stdout.len() < 3: -// return false; // indicates the test has failed +// return false; // indicates the test impl failed // var excepted: list = [ // "x is greater than 5", @@ -65,9 +65,8 @@ fn main() -> i32 { // } // } -// fn zip(args: ...T) -> (T::underlying_t, ...) -// requires <...T> -// if (T... has std::ranges::Iterable) -> { +// fn <...T> zip(args: ...T) -> (T::underlying_t, ...) +// requires (T... impl std::ranges::Iterable) -> { // var zipped_result: std::Vec<(T::underlying_t?...)>; // [(string, int, ...)] // bool has_next = true; diff --git a/tests/language/requires_explicit.hlx b/tests/language/requires_explicit.hlx index 955ed7e4..c0e7f783 100644 --- a/tests/language/requires_explicit.hlx +++ b/tests/language/requires_explicit.hlx @@ -1,4 +1,4 @@ -fn add(a: T, b: T) -> T requires { +fn add(a: T, b: T) -> T { return a + b; } diff --git a/tests/language/requires_inference.hlx b/tests/language/requires_inference.hlx index 3e4314b7..7743ad50 100644 --- a/tests/language/requires_inference.hlx +++ b/tests/language/requires_inference.hlx @@ -1,4 +1,4 @@ -fn add(a: T, b: T) -> T requires { +fn add(a: T, b: T) -> T { return a + b; } diff --git a/tests/language/strcuts_erroring.hlx b/tests/language/strcuts_erroring.hlx index f4f8a9c9..44e24269 100644 --- a/tests/language/strcuts_erroring.hlx +++ b/tests/language/strcuts_erroring.hlx @@ -9,7 +9,7 @@ struct LAO { struct LOB { var x: int; - op + fn bar(self) -> int { // error: structs cant have named operators + fn op + (self)[bar] -> int { // error: structs cant have named operators return self.x; } } diff --git a/tests/language/strcuts_passing.hlx b/tests/language/strcuts_passing.hlx index 852e0e4f..3f4daf4d 100644 --- a/tests/language/strcuts_passing.hlx +++ b/tests/language/strcuts_passing.hlx @@ -2,7 +2,7 @@ struct Point { var x: i32; var y: i32; - op + fn (self, other: &Point) -> Point { + fn op + (self, other: &Point) -> Point { return Point { x = self.x + other.x, y = self.y + other.y @@ -13,7 +13,7 @@ struct Point { struct Foam { var x: i32; - op + fn (self) -> i32 { // works + fn op + (self) -> i32 { // works return self.x; } } diff --git a/tests/language/type_bounds_explicit.hlx b/tests/language/type_bounds_explicit.hlx index 467d09b4..0c4efdfc 100644 --- a/tests/language/type_bounds_explicit.hlx +++ b/tests/language/type_bounds_explicit.hlx @@ -1,8 +1,8 @@ interface Addable { - op + fn (self, other: self) -> self; + fn op + (self, other: self) -> self; } -fn add(a: T, b: T) -> T requires if T has Addable { +fn add(a: T, b: T) -> T requires T impl Addable { return a + b; } diff --git a/tests/language/type_bounds_inferred.hlx b/tests/language/type_bounds_inferred.hlx index 5b8c5458..cbe95fb5 100644 --- a/tests/language/type_bounds_inferred.hlx +++ b/tests/language/type_bounds_inferred.hlx @@ -1,8 +1,8 @@ interface Addable { - op + fn (self, other: self) -> self; + fn op + (self, other: self) -> self; } -fn add(a: T, b: T) -> T requires if T has Addable { +fn add(a: T, b: T) -> T requires T impl Addable { return a + b; } diff --git a/tests/language/type_casting.hlx b/tests/language/type_casting.hlx index 8bc47ea2..7d255f99 100644 --- a/tests/language/type_casting.hlx +++ b/tests/language/type_casting.hlx @@ -14,7 +14,7 @@ class Derived derives Base { return self.val; } - op as fn (self) -> string { + fn op as (self) -> string { return "class: Derived"; } } @@ -30,13 +30,13 @@ class ClassCasting { self.val = val; } - op as fn (self) -> TempContainer { + fn op as (self) -> TempContainer { return TempContainer { vb = self.val }; } - op delete fn (self) { + fn op delete (self) { print("destroying"); } } @@ -154,7 +154,7 @@ class Book { self.val = val; } - op as fn (self) -> string { + fn op as (self) -> string { return "class: Book"; } } diff --git a/tests/main.hlx b/tests/main.hlx index 84492990..a047aa69 100644 --- a/tests/main.hlx +++ b/tests/main.hlx @@ -29,26 +29,26 @@ fn add(a: i32, b: i32) -> i32 { } // Overload the addition operator for Fraction -op + fn add(a: Fraction, b: Fraction) -> Fraction { +fn op + (a: Fraction, b: Fraction)[add] -> Fraction { var new_n = add(a.n + b.d, b.n + a.d); var new_d = a.d * b.d; return { .n = new_n, .d = new_d }; } -op - fn subtract(a: Fraction, b: Fraction) -> Fraction { +fn op - (a: Fraction, b: Fraction)[subtract] -> Fraction { var new_n = add(a.n - b.d, b.n - a.d); var new_d = a.d * b.d; return Fraction { n= new_n, d= new_d }; } -op * fn multiply(a: Fraction, b: Fraction) -> Fraction { +fn op * (a: Fraction, b: Fraction)[multiply] -> Fraction { var new_n = a.n * b.n; var new_d = a.d * b.d; return Fraction { n= new_n, d= new_d }; } -op / fn divide(a: Fraction, b: Fraction) -> Fraction { +fn op / (a: Fraction, b: Fraction)[divide] -> Fraction { var new_n = a.n * b.d; var new_d = a.d * b.n; return Fraction { n= new_n, d= new_d }; @@ -59,143 +59,143 @@ op / fn divide(a: Fraction, b: Fraction) -> Fraction { // + - * / % ^ & | ~ ! = < > += -= *= /= %= ^= &= |= << >> >>= <<= == != <= >= <=>(since C++20) && || ++ -- , ->* -> ( ) [ ] // Overload the unary minus operator for Fraction -op - fn unary_minus(a: Fraction) -> Fraction { +fn op - (a: Fraction)[unary_minus] -> Fraction { return Fraction { n= -a.n, d= a.d }; } // Overload the unary plus operator for Fraction -op + fn unary_plus(a: Fraction) -> Fraction { +fn op + (a: Fraction)[unary_plus] -> Fraction { return Fraction { n= a.n, d= a.d }; } // Overload the increment operator for Fraction -op ++ fn increment(a: Fraction) -> Fraction { +fn op ++ (a: Fraction)[increment] -> Fraction { return Fraction { n= a.n + a.d, d= a.d }; } // Overload the decrement operator for Fraction -op -- fn decrement(a: Fraction) -> Fraction { +fn op -- (a: Fraction)[decrement] -> Fraction { return Fraction { n= a.n - a.d, d= a.d }; } // Overload the addition assignment operator for Fraction -op += fn add_assign(a: Fraction, b: Fraction) -> Fraction { +fn op += (a: Fraction, b: Fraction)[add_assign] -> Fraction { return a + b; } // Overload the subtraction assignment operator for Fraction -op -= fn subtract_assign(a: Fraction, b: Fraction) -> Fraction { +fn op -= (a: Fraction, b: Fraction)[subtract_assign] -> Fraction { return a - b; } // Overload the multiplication assignment operator for Fraction -op *= fn multiply_assign(a: Fraction, b: Fraction) -> Fraction { +fn op *= (a: Fraction, b: Fraction)[multiply_assign] -> Fraction { return a * b; } // Overload the division assignment operator for Fraction -op /= fn divide_assign(a: Fraction, b: Fraction) -> Fraction { +fn op /= (a: Fraction, b: Fraction)[divide_assign] -> Fraction { return a / b; } // Overload the modulus assignment operator for Fraction -op %= fn modulus_assign(a: Fraction, b: Fraction) -> Fraction { +fn op %= (a: Fraction, b: Fraction)[modulus_assign] -> Fraction { return Fraction { n= a.n % b.n, d= a.d % b.d }; } // Overload the bitwise AND assignment operator for Fraction -op &= fn bitwise_and_assign(a: Fraction, b: Fraction) -> Fraction { +fn op &= (a: Fraction, b: Fraction)[bitwise_and_assign] -> Fraction { return Fraction { n= a.n & b.n, d= a.d & b.d }; } // Overload the bitwise OR assignment operator for Fraction -op |= fn bitwise_or_assign(a: Fraction, b: Fraction) -> Fraction { +fn op |= (a: Fraction, b: Fraction)[bitwise_or_assign] -> Fraction { return Fraction { n= a.n | b.n, d= a.d | b.d }; } // Overload the bitwise XOR assignment operator for Fraction -op ^= fn bitwise_xor_assign(a: Fraction, b: Fraction) -> Fraction { +fn op ^= (a: Fraction, b: Fraction)[bitwise_xor_assign] -> Fraction { return Fraction { n= a.n ^ b.n, d= a.d ^ b.d }; } // Overload the left shift assignment operator for Fraction -op <<= fn left_shift_assign(a: Fraction, b: Fraction) -> Fraction { +fn op <<= (a: Fraction, b: Fraction)[left_shift_assign] -> Fraction { return Fraction { n= a.n << b.n, d= a.d << b.d }; } // Overload the right shift assignment operator for Fraction -op >>= fn right_shift_assign(a: Fraction, b: Fraction) -> Fraction { +fn op >>= (a: Fraction, b: Fraction)[right_shift_assign] -> Fraction { return Fraction { n= a.n >> b.n, d= a.d >> b.d }; } // Overload the modulus operator for Fraction -op % fn modulus(a: Fraction, b: Fraction) -> Fraction { +fn op % (a: Fraction, b: Fraction)[modulus] -> Fraction { return Fraction { n= a.n % b.n, d= a.d % b.d }; } // Overload the bitwise AND operator for Fraction -op & fn bitwise_and(a: Fraction, b: Fraction) -> Fraction { +fn op & (a: Fraction, b: Fraction)[bitwise_and] -> Fraction { return Fraction { n= a.n & b.n, d= a.d & b.d }; } // Overload the bitwise OR operator for Fraction -op | fn bitwise_or(a: Fraction, b: Fraction) -> Fraction { +fn op | (a: Fraction, b: Fraction)[bitwise_or] -> Fraction { return Fraction { n= a.n | b.n, d= a.d | b.d }; } // Overload the bitwise XOR operator for Fraction -op ^ fn bitwise_xor(a: Fraction, b: Fraction) -> Fraction { +fn op ^ (a: Fraction, b: Fraction)[bitwise_xor] -> Fraction { return Fraction { n= a.n ^ b.n, d= a.d ^ b.d }; } // Overload the left shift operator for Fraction -op << fn left_shift(a: Fraction, b: Fraction) -> Fraction { +fn op << (a: Fraction, b: Fraction)[left_shift] -> Fraction { return Fraction { n= a.n << b.n, d= a.d << b.d }; } // Overload the right shift operator for Fraction -op >> fn right_shift(a: Fraction, b: Fraction) -> Fraction { +fn op >> (a: Fraction, b: Fraction)[right_shift] -> Fraction { return Fraction { n= a.n >> b.n, d= a.d >> b.d }; } // Overload the comparison operators for Fraction -op < fn less_than(a: Fraction, b: Fraction) -> bool { +fn op < (a: Fraction, b: Fraction)[less_than] -> bool { return a.n * b.d < b.n * a.d; } -op > fn greater_than(a: Fraction, b: Fraction) -> bool { +fn op > (a: Fraction, b: Fraction)[greater_than] -> bool { return a.n * b.d > b.n * a.d; } -op <= fn less_than_or_equal(a: Fraction, b: Fraction) -> bool { +fn op <= (a: Fraction, b: Fraction)[less_than_or_equal] -> bool { return a.n * b.d <= b.n * a.d; } -op >= fn greater_than_or_equal(a: Fraction, b: Fraction) -> bool { +fn op >= (a: Fraction, b: Fraction)[greater_than_or_equal] -> bool { return a.n * b.d >= b.n * a.d; } -// op <=> fn three_way_comparison(a: Fraction, b: Fraction) -> i32 { +// fn op <=> (a: Fraction, b: Fraction)[three_way_comparison] -> i32 { // if a.n * b.d < b.n * a.d { // return -1; // } else if a.n * b.d > b.n * a.d { @@ -205,25 +205,25 @@ op >= fn greater_than_or_equal(a: Fraction, b: Fraction) -> bool { // } // } -op && fn logical_and(a: Fraction, b: Fraction) -> bool { +fn op && (a: Fraction, b: Fraction)[logical_and] -> bool { return a.n && b.n && a.d && b.d; } -op || fn logical_or(a: Fraction, b: Fraction) -> bool { +fn op || (a: Fraction, b: Fraction)[logical_or] -> bool { return a.n || b.n || a.d || b.d; } -op ! fn logical_not(a: Fraction) -> bool { +fn op ! (a: Fraction)[logical_not] -> bool { return !a.n && !a.d; } // Overload the equality operator for Fraction -op == fn equals(a: Fraction, b: Fraction) -> bool { +fn op == (a: Fraction, b: Fraction)[equals] -> bool { return a.n * b.d == b.n * a.d; } // // Overload the subscript operator for Fraction (returning numerator or denominator) -// op [] fn access_element(a: Fraction, index: i32) -> i32 { +// fn op [] (a: Fraction, index: i32)[access_element] -> i32 { // if index == 0 { // return a.n; // } else if index == 1 { @@ -234,12 +234,12 @@ op == fn equals(a: Fraction, b: Fraction) -> bool { // } // // Function call operator, which returns the fraction's value as a floating-point number -// op () fn fraction_to_float(a: Fraction) -> float { +// fn op () (a: Fraction)[fraction_to_float] -> float { // return a.n as float / a.d as float; // } // TODO: references do not work - THEY DO NOW!!!! // Overload the stream insertion operator (for printing) -// op << fn stream_insert(out: libcxx::ostream , a: Fraction) -> libcxx::ostream { +// fn op << (out: libcxx::ostream , a: Fraction)[stream_insert] -> libcxx::ostream { // return out << a.n << "/" << a.d; // } @@ -280,14 +280,14 @@ class Fuzz { -class Fish requires if T has Bar, T derives Fuzz { +class Fish requires T impl Bar, T derives Fuzz { var fish: T; } -// fn fishy(f: Fish) -> Fish requires { +// fn fishy(f: Fish) -> Fish { // return f; // } @@ -330,7 +330,7 @@ class ABC { // free(name) // } -class array requires { +class array { var data: *T = alloca(N * sizeof(T)) as *T; var size: u32 = N; } diff --git a/tests/mat_test.hlx b/tests/mat_test.hlx index de3e603b..78697675 100644 --- a/tests/mat_test.hlx +++ b/tests/mat_test.hlx @@ -5,13 +5,13 @@ ffi "c++" import "memory"; /* ------------------------------------------- helper ------------------------------------------- */ /// define a basic range method since c++ does not provide one and helix does not have a std (yet) interface Incrementable { - op ++ fn inc(self) -> Self; - op < fn lt(self, other: Self) -> bool; - op == fn eq(self, other: Self) -> bool; + fn op ++ (self)[inc] -> Self; + fn op < (self, other: Self)[lt] -> bool; + fn op == (self, other: Self)[eq] -> bool; } -fn range(start: T, end: T, step: T = 1) -> yield T - requires if Incrementable in T { +fn range(start: T, end: T, step: T = 1) -> yield T + requires Incrementable in T { while start < end { yield start; @@ -19,8 +19,8 @@ fn range(start: T, end: T, step: T = 1) -> yield T } } -fn range(end: T) -> yield T - requires if Incrementable in T { +fn range(end: T) -> yield T + requires Incrementable in T { var start = T(); @@ -37,8 +37,7 @@ struct Point { }; #[assignable] // this denotes if a class can be reassigned (like primitive arrays in c/c++) -class Matrix - requires /* if Arithmetic in T || T derives Point */ { +class Matrix /* requires Arithmetic in T || T derives Point */ { priv var rows: i32, cols: i32, data: libc::array::; pub var count = 9; @@ -59,7 +58,7 @@ class Matrix /// requires to be moved, since the object would /// be destroyed after creation /// example: - /// fn foo(std::Temporary obj) requires {} + /// fn foo(std::Temporary obj) {} /// foo(T()); // creates a temporary object /// // and passes it to foo self.rows = other.rows; @@ -82,16 +81,16 @@ class Matrix } /// mat access operator - op () fn call_op(self, r: i32, c: i32) -> &T { + fn op () (self, r: i32, c: i32)[call_op] -> &T { return self.data[r * self.cols + c]; } - // op () fn const_call_op(self, r: i32, c: i32) -> T { /// this isnt going to compile 99% sure + // fn op () (self, r: i32, c: i32)[const_call_op] -> T { /// this isnt going to compile 99% sure // return self.data[r * self.cols + c]; // } /// mat addition - op + fn add(self, other: &Matrix::) -> Matrix:: { + fn op + (self, other: &Matrix::)[add] -> Matrix:: { var result = Matrix::(self.rows, self.cols); for (var i: i32 = 0; i < self.rows * self.cols; ++i) { @@ -102,7 +101,7 @@ class Matrix } /// mat scalar multiplication - op * fn mul(self, scalar: T) -> Matrix:: { + fn op * (self, scalar: T)[mul] -> Matrix:: { var result = Matrix::(self.rows, self.cols); for i in range(rows * cols) { @@ -124,7 +123,7 @@ class Matrix extend Matrix for Point { /// mat addition - op + fn add(const other: &Matrix::) -> Matrix:: { + fn op + (const other: &Matrix::)[add] -> Matrix:: { var result = Matrix::(rows, cols); for i in range(rows * cols) { diff --git a/tests/range.hlx b/tests/range.hlx index f36fb3da..78e1e666 100644 --- a/tests/range.hlx +++ b/tests/range.hlx @@ -30,13 +30,13 @@ /// op's in class defs need to ignore self parm and work without self while calling as well. // interface Incrementable { -// op ++ fn inc(self) -> Self; -// op < fn lt(self, other: Self) -> bool; -// op == fn eq(self, other: Self) -> bool; +// fn op ++ (self)[inc] -> Self; +// fn op < (self, other: Self)[lt] -> bool; +// fn op == (self, other: Self)[eq] -> bool; // } module some_namespace { - class Foo requires { + class Foo { fn Foo(self) { print("Foo"); } @@ -45,7 +45,7 @@ module some_namespace { type Bar = i32; } -fn range(start: T, end: T, step: T = 1) -> yield T requires { +fn range(start: T, end: T, step: T = 1) -> yield T { while start < end { yield start; start += step; @@ -70,7 +70,7 @@ class SomeBad { } } -fn range(end: T) -> yield T requires { +fn range(end: T) -> yield T { var start = T(); while start < end { diff --git a/vial/README.md b/vial/README.md new file mode 100644 index 00000000..926241c9 --- /dev/null +++ b/vial/README.md @@ -0,0 +1,5 @@ +## This is a really simple build tool for Helix written in Helix. +## It is designed to be used with the Helix language and provides a simple way to compile +## and run Helix programs. +## It is not intended to be a full-featured build tool, but rather a simple way +## to compile and run Helix programs without having to write a lot of boilerplate code. \ No newline at end of file diff --git a/vial/rt/api.hlx b/vial/rt/api.hlx new file mode 100644 index 00000000..f491347f --- /dev/null +++ b/vial/rt/api.hlx @@ -0,0 +1,480 @@ +/* +# Synopsis + +make a tool similar to xmake in terms of functionality but have it be extendable with helix code +and support of the c++ std to allow things like cmd exec and file sys acc. + +# API +```helix +//// build.hlx + +import vial_build::*; + + +fn build(args: vec) -> i32 { + set_builddir(); + + set_project(); + set_version(); + set_description(); + + set_target("helix"); + add_links([...]); + add_link(...); + + add_include(...); + add_includes([...]); + + add_source(...); + + set_linkage(Object|Binary); + + after_build(fn () {}); + before_build(fn () {}); + + set_cxxflags(...); + add_define(..., ...); + end_target(); +} + +``` + +```bash +helix-lang on  feature/vial [$!?] via 🌙 +$ vial +``` + +1. Look for a build.hlx in pwd +2. compile that into an object after verifying there is a build function in here +3. link against the vial runtime + +vial runtime: +```hlx +import vial_build; + +inline_cpp("extern i32 build(vec args)"); + +fn main(argc argv) -> i32 { + /// sanatize args first + build(args); + return vial_build::compile(); +} +``` -> vial.so/.dll + + +vial.so/.dll + build.o -> build +*/ + +/// THIS FILE: is not the vial runtime nor is it the build.hlx it is for VIAL_BUILD. + +/* +Tests: + +first: +try the toolchain without a build.hlx file in the current directory +should fail +second: +try the toolchain with a build.hlx file in the current directory +should succeed and print the path to the build.hlx file + + +struct Target { + var name: string; + var linkage: string; // Object or Binary + var sources: vec::; + var includes: vec::; + var links: vec::; + var builddir: types::Path = types::Path(fs::get_cwd() / "build".raw_string()); + var cxxflags: vec::; + var defines: vec::; + var before_build: fn (*Target) -> void; + var after_build: fn (*Target) -> void; +} + +struct ExecResult { + std::string output; + int return_code{}; + }; + +*/ + +ffi "c++" import "exec.hh"; +ffi "c++" import "exe.hh"; +ffi "c++" import "thread.hh"; + +import linkage::*; +import target::*; +import fs::*; +import types::*; +import logger::*; +import triple::*; + +static var log = Logger(); + +class BUILD_FILE { + __inline_cpp("inline static Path instance{}"); + + static fn value() -> ref!(Path) { + return instance; + } + + static fn set(value: Path) { + instance = value; + } +} + +class BUILD_DIR { + __inline_cpp("inline static Path instance{}"); + + static fn value() -> ref!(Path) { + return instance; + } + + static fn set(value: Path) { + instance = value; + } +} + +class CXX_COMPILER { + __inline_cpp("inline static string instance{}"); + + static fn value() -> ref!(string) { + return instance; + } + + static fn set(value: string) { + instance = value; + } +} + +class HELIX_COMPILER { + __inline_cpp("inline static string instance{}"); + + static fn value() -> ref!(string) { + return instance; + } + + static fn set(value: string) { + instance = value; + } +} + +class PROJECT { + __inline_cpp("inline static string instance{}"); + + static fn value() -> ref!(string) { + return instance; + } + + static fn set(value: string) { + instance = value; + } +} + +class DESCRIPTION { + __inline_cpp("inline static string instance{}"); + + static fn value() -> ref!(string) { + return instance; + } + + static fn set(value: string) { + instance = value; + } +} + +class VERSION { + __inline_cpp("inline static string instance{}"); + + static fn value() -> ref!(string) { + return instance; + } + + static fn set(value: string) { + instance = value; + } +} + +class TARGETS { + __inline_cpp("inline static vec instance{}"); + + static fn value() -> ref!(vec::<*Target>) { + return instance; + } + + static fn set(value: vec::<*Target>) { + instance = value; + } +} + +class IN_TARGET { + __inline_cpp("inline static bool instance{}"); + + static fn value() -> ref!(bool) { + return instance; + } + + static fn set(value: bool) { + instance = value; + } +} + +class TARGET_TRIPLE { + __inline_cpp("inline static string instance{}"); + + static fn value() -> ref!(string) { + return instance; + } + + static fn set(value: string) { + instance = value; + } +} + +/// Build dir is both relative to a target and the project, +/// defaulting to the project builddir if a target does not specify one. + +// PROJECT = ""; +// DESCRIPTION = ""; +// VERSION = "0.1.0"; +// IN_TARGET = false; +// TARGET_TRIPLE = f"{get_arch()}-{get_vendor()}-{get_system()}"; +// BUILD_DIR = Path(get_cwd() / "build".raw_string() / TARGET_TRIPLE::value().raw_string()); + +inline fn set_helix_compiler(path: Path) { + if !libcxx::filesystem::exists(path) { + log.error(f"helix compiler path does not exist: {path}"); + } + + CXX_COMPILER::set(path.generic_wstring()); +} + +inline fn set_cpp_compiler(path: Path) { + if !libcxx::filesystem::exists(path) { + log.error(f"c++ compiler path does not exist: {path}"); + } + + HELIX_COMPILER::set(path.generic_wstring()); +} + +inline fn set_target(name: string) { + if IN_TARGET::value() { + log.error("cannot set target inside another target."); + return; + } + + IN_TARGET::set(true); + + var target = std::create::(); + + target->name = name; + + target->linkage = Linkage::Binary; + target->sources = vec::(); + target->includes = vec::(); + target->links = vec::(); + target->cxxflags = vec::(); + target->defines = vec::(); + + target->before_build = fn (_ctx: *Target) -> void { return; }; + target->after_build = fn (_ctx: *Target) -> void { return; }; + + TARGETS::value().push_back(target); +} + +inline fn end_target(name: string? = null) { + if !IN_TARGET::value() { + log.error("not in a target, cannot end target."); + return; + } + + if name != null { + var target = TARGETS::value().back(); + + if target->name != (*name) { + log.error(f"target name mismatch, expected {target->name} but got {name}"); + return; + } + } + + IN_TARGET::set(false); +} + +inline fn set_builddir(path: string) { + if !libcxx::filesystem::exists(path.raw()) { + libcxx::filesystem::create_directories(path.raw()); + } + + if IN_TARGET::value() { + var target = TARGETS::value().back(); + + if target != &null { + try { + target->builddir = normalize_path(Path(path.raw())); + } catch { + log.error(f"path not found {path}"); + } + } + } else { + try { + BUILD_DIR::set(normalize_path(Path(path.raw()))); + } catch { + log.error(f"path not found {path}"); + } + } +} + +inline fn set_project(name: string) { + if IN_TARGET::value() { + log.error("cannot set project inside a target."); + return; + } + + PROJECT::set(name); +} + +inline fn set_version(version: string) { + if IN_TARGET::value() { + log.error("cannot set version inside a target."); + return; + } + + VERSION::set(version); +} + +inline fn set_description(description: string) { + if IN_TARGET::value() { + log.error("cannot set description inside a target."); + return; + } + + DESCRIPTION::set(description); +} + +inline fn add_links(links: vec::) { + if !IN_TARGET::value() { + log.error("not in a target, cannot add links."); + return; + } + + var target = TARGETS::value().back(); + + for link in links { + target->links.push_back(link); + } +} + +inline fn add_link(link: string) { + if !IN_TARGET::value() { + log.error("not in a target, cannot add link."); + return; + } + + var target = TARGETS::value().back(); + + target->links.push_back(link); +} + +inline fn add_include(include: string) { + if !IN_TARGET::value() { + log.error("not in a target, cannot add include."); + return; + } + + var target = TARGETS::value().back(); + + try { + target->includes.push_back(normalize_path(Path(include.raw()))); + } catch { + log.error(f"path not found {include}"); + } +} + +inline fn add_includes(includes: vec::) { + if !IN_TARGET::value() { + log.error("not in a target, cannot add includes."); + return; + } + + var target = TARGETS::value().back(); + + for include in includes { + try { + target->includes.push_back(normalize_path(Path(include.raw()))); + } catch { + log.error(f"path not found {include}"); + } + } +} + +inline fn add_source(source: string) { + if !IN_TARGET::value() { + log.error("not in a target, cannot add source."); + return; + } + + var target = TARGETS::value().back(); + + try { + target->sources.push_back(normalize_path(Path(source.raw()))); + } catch { + log.error(f"path not found {source}"); + } +} + +inline fn set_linkage(linkage: Linkage) { + if !IN_TARGET::value() { + log.error("not in a target, cannot set linkage."); + return; + } + + var target = TARGETS::value().back(); + + target->linkage = linkage; +} + +inline fn after_build(callback: fn (*Target) -> void) { + if !IN_TARGET::value() { + log.error("not in a target, cannot set after build callback."); + return; + } + + var target = TARGETS::value().back(); + + target->after_build = callback; +} + +inline fn before_build(callback: fn (*Target) -> void) { + if !IN_TARGET::value() { + log.error("not in a target, cannot set before build callback."); + return; + } + + var target = TARGETS::value().back(); + + target->before_build = callback; +} + +inline fn set_cxxflags(flags: vec::) { + if !IN_TARGET::value() { + log.error("not in a target, cannot set C++ flags."); + return; + } + + var target = TARGETS::value().back(); + + for flag in flags { + target->cxxflags.push_back(flag); + } +} + +inline fn add_define(name: string, value: string) { + if !IN_TARGET::value() { + log.error("not in a target, cannot add define."); + return; + } + + var target = TARGETS::value().back(); + + target->defines.push_back(f"{name}={value}"); +} \ No newline at end of file diff --git a/vial/rt/exe.hh b/vial/rt/exe.hh new file mode 100644 index 00000000..40e27968 --- /dev/null +++ b/vial/rt/exe.hh @@ -0,0 +1,87 @@ +///--- The Helix Project ------------------------------------------------------------------------/// +/// /// +/// Part of the Helix Project, under the Attribution 4.0 International license (CC BY 4.0). /// +/// You are allowed to use, modify, redistribute, and create derivative works, even for /// +/// commercial purposes, provided that you give appropriate credit, and indicate if changes /// +/// were made. /// +/// /// +/// For more information on the license terms and requirements, please visit: /// +/// https://creativecommons.org/licenses/by/4.0/ /// +/// /// +/// SPDX-License-Identifier: CC-BY-4.0 /// +/// Copyright (c) 2024 The Helix Project (CC BY 4.0) /// +/// /// +///-------------------------------------------------------------------------------------- C++ ---/// + +#ifndef __EXE_H__ +#define __EXE_H__ + +/// uncomment only for lsp support otherwise there will be build errors. +// #include +// "/Volumes/Development/Projects/Helix/helix-lang/build/release/arm64-macosx-llvm/core/include/core.hh" + +#if defined(_WIN32) || defined(WIN32) || defined(_WIN64) || defined(WIN64) +# include +#elif defined(__unix__) || defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) || \ + defined(__OpenBSD__) || defined(__bsdi__) || defined(__DragonFly__) +# include +#elif defined(__APPLE__) || defined(__MACH__) +# include +#else +# error "unsupported platform" +#endif + +namespace helix { +// This function retrieves the path of the currently running executable. +// It uses platform-specific methods to ensure compatibility across different systems. +// The implementation is platform-dependent and may vary based on the operating system. +inline libcxx::filesystem::path get_executable_path() { +#if defined(_WIN32) || defined(WIN32) || defined(_WIN64) || defined(WIN64) + DWORD bufferSize = 256; + libcxx::vector buffer(bufferSize); + + while (true) { + DWORD result = GetModuleFileNameA(NULL, buffer.data(), bufferSize); + if (result == 0) { + throw libcxx::runtime_error("Failed to retrieve executable path"); + } + if (result < bufferSize) { + return libcxx::filesystem::path(buffer.data()); + } + bufferSize *= 2; + buffer.resize(bufferSize); + } +#elif defined(__unix__) || defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) || \ + defined(__OpenBSD__) || defined(__bsdi__) || defined(__DragonFly__) + size_t bufferSize = 256; + libcxx::vector buffer(bufferSize); + + while (true) { + ssize_t count = readlink("/proc/self/exe", buffer.data(), bufferSize); + if (count == -1) { + throw libcxx::runtime_error("Failed to retrieve executable path"); + } + if (static_cast(count) < bufferSize) { + return libcxx::filesystem::path(libcxx::string(buffer.data(), count)); + } + bufferSize *= 2; + buffer.resize(bufferSize); + } +#elif defined(__APPLE__) || defined(__MACH__) + uint32_t bufferSize = 256; + libcxx::vector buffer(bufferSize); + + while (true) { + if (_NSGetExecutablePath(buffer.data(), &bufferSize) == 0) { + return libcxx::filesystem::path(buffer.data()); + } + + buffer.resize(bufferSize); // _NSGetExecutablePath updates bufferSize if it was too small + } +#else + throw std::runtime_error("unsupported platform"); +#endif +} +} // namespace helix + +#endif // __EXE_H__ \ No newline at end of file diff --git a/vial/rt/exec.hh b/vial/rt/exec.hh new file mode 100644 index 00000000..f5817c23 --- /dev/null +++ b/vial/rt/exec.hh @@ -0,0 +1,168 @@ +///--- The Helix Project ------------------------------------------------------------------------/// +/// /// +/// Part of the Helix Project, under the Attribution 4.0 International license (CC BY 4.0). /// +/// You are allowed to use, modify, redistribute, and create derivative works, even for /// +/// commercial purposes, provided that you give appropriate credit, and indicate if changes /// +/// were made. /// +/// /// +/// For more information on the license terms and requirements, please visit: /// +/// https://creativecommons.org/licenses/by/4.0/ /// +/// /// +/// SPDX-License-Identifier: CC-BY-4.0 /// +/// Copyright (c) 2024 The Helix Project (CC BY 4.0) /// +/// /// +///-------------------------------------------------------------------------------------- C++ ---/// + +/// uncomment only for lsp support otherwise there will be build errors. +// #include "/Volumes/Development/Projects/Helix/helix-lang/build/release/arm64-macosx-llvm/core/include/core.hh" + +#ifndef __EXEC_H__ +#define __EXEC_H__ + +namespace helix { + +inline string s2ws(const libcxx::string &str) { + using convert_typeX = libcxx::codecvt_utf8; + libcxx::wstring_convert converterX; + + return converterX.from_bytes(str); +} + +inline libcxx::string ws2s(const string &wstr) { + using convert_typeX = libcxx::codecvt_utf8; + libcxx::wstring_convert converterX; + + return converterX.to_bytes(wstr); +} + +struct ExecResult { + string output; + i32 return_code{}; +}; + +#if defined(_WIN32) || defined(WIN32) || defined(_WIN64) || defined(WIN64) + +inline ExecResult exec(const string &wcmd) { + SECURITY_ATTRIBUTES sa = {sizeof(SECURITY_ATTRIBUTES), nullptr, TRUE}; + HANDLE hReadPipe = nullptr; + HANDLE hWritePipe = nullptr; + libcxx::string cmd = ws2s(wcmd); + + if (CreatePipe(&hReadPipe, &hWritePipe, &sa, 0) == 0) { + throw libcxx::runtime_error("CreatePipe failed! Error: " + + libcxx::to_string(GetLastError())); + } + + if (SetHandleInformation(hReadPipe, HANDLE_FLAG_INHERIT, 0) == 0) { + CloseHandle(hReadPipe); + CloseHandle(hWritePipe); + throw libcxx::runtime_error("SetHandleInformation failed! Error: " + + libcxx::to_string(GetLastError())); + } + + PROCESS_INFORMATION pi = {}; + STARTUPINFO si = {}; + si.cb = sizeof(si); + si.hStdOutput = hWritePipe; + si.hStdError = hWritePipe; + si.dwFlags |= STARTF_USESTDHANDLES; + + if (!CreateProcess(nullptr, + const_cast(cmd.c_str()), + nullptr, + nullptr, + TRUE, + CREATE_NO_WINDOW, + nullptr, + nullptr, + &si, + &pi)) { + CloseHandle(hReadPipe); + CloseHandle(hWritePipe); + throw libcxx::runtime_error("CreateProcess failed! Error: " + + libcxx::to_string(GetLastError())); + } + + CloseHandle(hWritePipe); + + libcxx::string result; + libcxx::array buffer{}; + + DWORD bytesRead = 0; + + libcxx::thread readerThread([&]() { + while (true) { + if (ReadFile(hReadPipe, buffer.data(), buffer.size(), &bytesRead, nullptr) == 0) { + if (GetLastError() == ERROR_BROKEN_PIPE) { + break; + } + throw libcxx::runtime_error("ReadFile failed! Error: " + + libcxx::to_string(GetLastError())); + } + result.append(buffer.data(), bytesRead); + } + }); + + DWORD waitResult = WaitForSingleObject(pi.hProcess, 10000); // 10 sec timeout + if (waitResult == WAIT_TIMEOUT) { + TerminateProcess(pi.hProcess, 1); + readerThread.join(); + CloseHandle(hReadPipe); + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + throw libcxx::runtime_error("Process timed out!"); + } + + readerThread.join(); + + DWORD exitCode = 0; + if (GetExitCodeProcess(pi.hProcess, &exitCode) == 0) { + CloseHandle(hReadPipe); + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + throw libcxx::runtime_error("GetExitCodeProcess failed! Error: " + + libcxx::to_string(GetLastError())); + } + + CloseHandle(hReadPipe); + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + + return {.output = s2ws(result), .return_code = static_cast(exitCode)}; +} + +#elif defined(__unix__) || defined(__APPLE__) || defined(__linux__) || defined(__FreeBSD__) || \ + defined(__NetBSD__) || defined(__OpenBSD__) || defined(__bsdi__) || defined(__DragonFly__) || \ + defined(__MACH__) + +inline ExecResult exec(const string &wcmd) { + libcxx::array buffer{}; + libcxx::string result; + libcxx::string cmd = ws2s(wcmd); + + FILE *pipe = popen(cmd.c_str(), "r"); + + if (pipe == nullptr) { + throw libcxx::runtime_error("popen() failed to initialize command execution."); + } + + try { + while (feof(pipe) == 0) { + if (fgets(buffer.data(), buffer.size(), pipe) != nullptr) { + result += buffer.data(); + } + } + } catch (...) { + pclose(pipe); + throw; + } + + int rc = pclose(pipe); + return {.output = s2ws(result), .return_code = rc}; +} + +#endif + +} // namespace helix + +#endif // __EXEC_H__ \ No newline at end of file diff --git a/vial/rt/fs.hlx b/vial/rt/fs.hlx new file mode 100644 index 00000000..4e1bc972 --- /dev/null +++ b/vial/rt/fs.hlx @@ -0,0 +1,99 @@ +import types; + +const eval if defined(_WIN32) { + static var PATH_SEP: string = ";"; + static var exts = [".exe", ".bat", ".cmd", ""]; +} else { + static var PATH_SEP: string = ":"; + static var exts = [""]; +} + +inline fn get_cwd() -> types::Path { + return libcxx::filesystem::current_path(); +} + +inline fn normalize_path(path: types::Path) -> types::Path { + return libcxx::filesystem::canonical(path); +} + +inline fn find_build_file() -> types::Path? { + var filename: string = "build.hlx"; + var path: types::Path = get_cwd(); + + for (file : types::File in libcxx::filesystem::directory_iterator(path)) { + if (file.is_regular_file() && (file.path().filename() == filename.raw_string())) { + return normalize_path(file.path()); + } + } + + // If not found, return null + return null; +} + +inline fn find_tool(name: string) -> string? { + static var cache: map::; + static var initialized = false; + + if initialized { + var it = cache.find(name); + + if it != cache.end() { + return it->second; + } + + return null; + } + + const var path_env = libcxx::getenv(r"PATH"); + + if !path_env { + return null; + } + + var path: string = std::to_string(path_env); + var start: usize = 0; + + var populate_cache = fn (dir: types::Path) { + for entry in (libcxx::filesystem::directory_iterator(dir, libcxx::filesystem::directory_options::skip_permission_denied)) { + if !entry.is_regular_file() { continue; } + + var name = entry.path().filename().wstring(); + + for ext in exts { + if !(name.size() >= ext.size() && name.ends_with(ext.raw_string())) { continue; } + + var base = name.substr(0, name.size() - ext.size()); + cache.emplace(base, entry.path().wstring()); + } + } + }; + + while start < path.size() { + var end_q: usize? = path.lfind(PATH_SEP, start); + var end: usize = 0; + + end = (path.size()) if (end_q == null) else (*end_q); + + var dir = types::Path(path.subslice(start, end - start).raw_string()); + + if libcxx::filesystem::exists(dir) && libcxx::filesystem::is_directory(dir) { + try { + populate_cache(dir); + } catch { + // silently skip directories that cannot be read + } + } + + start = end + 1; + } + + initialized = true; + + var it = cache.find(name); + + if it != cache.end() { + return it->second; + } + + return null; +} diff --git a/vial/rt/linkage.hlx b/vial/rt/linkage.hlx new file mode 100644 index 00000000..8e010ee2 --- /dev/null +++ b/vial/rt/linkage.hlx @@ -0,0 +1,4 @@ +enum Linkage { + Object, + Binary, +} \ No newline at end of file diff --git a/vial/rt/logger.hlx b/vial/rt/logger.hlx new file mode 100644 index 00000000..34c8aace --- /dev/null +++ b/vial/rt/logger.hlx @@ -0,0 +1,70 @@ +class Logger { + fn Logger(self) {} + + fn timestamp(self) -> string { + var now = libcxx::chrono::system_clock::now(); + var time_t = libcxx::chrono::system_clock::to_time_t(now); + var tm = *libcxx::localtime(&time_t); + return f"{tm.tm_year + 1900:04}-{tm.tm_mon + 1:02}-{tm.tm_mday:02} " + + f"{tm.tm_hour:02}:{tm.tm_min:02}:{tm.tm_sec:02}"; + } + + fn color_wrap(self, s: string, color_code: string) -> string { + return f"{color_code}{s}\033[0m"; + } + + fn format_box(self, msg: string, level: string) -> string { + var lines = msg.split("\n"); + var max_len = 0; + + for (line in lines) { + if (line.length() > max_len) { + max_len = line.length(); + } + } + + var pad_len = max_len + 4; + var top = "*" + string(pad_len, '-') + "*"; + var bottom = "*" + string(pad_len, '-') + "*"; + + var content = ""; + for (line in lines) { + var padded = line + string((max_len - line.length()), ' '); + content += f"| {padded} |\n"; + } + + return f"{level} {self.timestamp()}\n{top}\n{content}{bottom}"; + } + + fn info(self, msg: string) { + var label = self.color_wrap("[INFO]", "\033[1;34m"); + print(f"{label}\t{self.timestamp()}\t {msg}"); + // if (msg.contains("\n")) { + // print(self.format_box(msg, label)); + // } + } + + fn warn(self, msg: string) { + var label = self.color_wrap("[WARN]", "\033[1;33m"); + print(f"{label}\t{self.timestamp()}\t {msg}"); + // if (msg.contains("\n")) { + // print(self.format_box(msg, label)); + // } + } + + fn error(self, msg: string) { + var label = self.color_wrap("[ERROR]", "\033[1;31m"); + print(f"{label}\t{self.timestamp()}\t {msg}"); + // if (msg.contains("\n")) { + // print(self.format_box(msg, label)); + // } + } + + fn debug(self, msg: string) { + var label = self.color_wrap("[DEBUG]", "\033[1;90m"); + print(f"{label}\t{self.timestamp()}\t {msg}"); + // if (msg.contains("\n")) { + // print(self.format_box(msg, label)); + // } + } +} \ No newline at end of file diff --git a/vial/rt/target.hlx b/vial/rt/target.hlx new file mode 100644 index 00000000..0fa4651f --- /dev/null +++ b/vial/rt/target.hlx @@ -0,0 +1,32 @@ +import types; +import fs; +import linkage::*; + +struct Target { + var name: string; + var linkage: Linkage; + var sources: vec::; + var includes: vec::; + var links: vec::; + var builddir: types::Path = types::Path(fs::get_cwd() / "build".raw_string()); + var cxxflags: vec::; + var defines: vec::; + var before_build: fn (*Target) -> void; + var after_build: fn (*Target) -> void; + + fn op as (self) -> string { + // nice format + return "Target {\n" + + f" name: {name},\n" + + f" linkage: {linkage},\n" + + f" sources: {sources},\n" + + f" includes: {includes},\n" + + f" links: {links},\n" + + f" builddir: {builddir},\n" + + f" cxxflags: {cxxflags},\n" + + f" defines: {defines},\n" + + f" before_build: {before_build},\n" + + f" after_build: {after_build}\n" + + "}"; + } +} \ No newline at end of file diff --git a/vial/rt/thread.hh b/vial/rt/thread.hh new file mode 100644 index 00000000..ff6b8caf --- /dev/null +++ b/vial/rt/thread.hh @@ -0,0 +1,121 @@ +///--- The Helix Project ------------------------------------------------------------------------/// +/// /// +/// Part of the Helix Project, under the Attribution 4.0 International license (CC BY 4.0). /// +/// You are allowed to use, modify, redistribute, and create derivative works, even for /// +/// commercial purposes, provided that you give appropriate credit, and indicate if changes /// +/// were made. /// +/// /// +/// For more information on the license terms and requirements, please visit: /// +/// https://creativecommons.org/licenses/by/4.0/ /// +/// /// +/// SPDX-License-Identifier: CC-BY-4.0 /// +/// Copyright (c) 2024 The Helix Project (CC BY 4.0) /// +/// /// +///-------------------------------------------------------------------------------------- C++ ---/// + +#ifndef __THREAD_H__ +#define __THREAD_H__ + +/// uncomment only for lsp support otherwise there will be build errors. +#include "/Volumes/Development/Projects/Helix/helix-lang/build/release/arm64-macosx-llvm/core/include/core.hh" + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace helix { + +class ThreadPool { + public: + explicit ThreadPool(usize thread_count = libcxx::thread::hardware_concurrency()); + ~ThreadPool(); + + ThreadPool(const ThreadPool &) = delete; + ThreadPool &operator=(const ThreadPool &) = delete; + + // Disable move semantics + ThreadPool(ThreadPool &&) = delete; + ThreadPool &operator=(ThreadPool &&) = delete; + + template + auto submit(Func &&f, Args &&...args) -> libcxx::future>; + + private: + using Task = libcxx::function; + + void worker_loop(); + + libcxx::vector workers_; + libcxx::queue tasks_; + libcxx::mutex queue_mutex_; + libcxx::condition_variable condition_; + libcxx::atomic stop_; +}; + +inline ThreadPool::ThreadPool(usize thread_count) + : stop_(false) { + thread_count = (thread_count == 0) ? 1 : thread_count; + workers_.reserve(thread_count); + for (usize i = 0; i < thread_count; ++i) { + workers_.emplace_back([this] { worker_loop(); }); + } +} + +inline ThreadPool::~ThreadPool() { + stop_.store(true, libcxx::memory_order_release); + condition_.notify_all(); + for (auto &thread : workers_) { + if (thread.joinable()) { + thread.join(); + } + } +} + +inline void ThreadPool::worker_loop() { + while (!stop_.load(libcxx::memory_order_acquire)) { + Task task; + { + libcxx::unique_lock lock(queue_mutex_); + condition_.wait( + lock, [this] { return stop_.load(libcxx::memory_order_relaxed) || !tasks_.empty(); }); + + if (stop_.load(libcxx::memory_order_relaxed) && tasks_.empty()) { + return; + } + + task = libcxx::move(tasks_.front()); + tasks_.pop(); + } + task(); + } +} + +template +auto ThreadPool::submit(Func &&f, Args &&...args) + -> libcxx::future> { + using ReturnType = libcxx::invoke_result_t; + + auto bound = libcxx::bind(libcxx::forward(f), libcxx::forward(args)...); + auto task_ptr = libcxx::make_shared>(libcxx::move(bound)); + + { + libcxx::lock_guard lock(queue_mutex_); + if (stop_.load(libcxx::memory_order_relaxed)) { + throw libcxx::runtime_error("ThreadPool is stopped"); + } + + tasks_.emplace([task_ptr]() { (*task_ptr)(); }); + } + + condition_.notify_one(); + return task_ptr->get_future(); +} + +} // namespace helix + +#endif // __THREAD_H__ \ No newline at end of file diff --git a/vial/rt/triple.hlx b/vial/rt/triple.hlx new file mode 100644 index 00000000..423958d3 --- /dev/null +++ b/vial/rt/triple.hlx @@ -0,0 +1,89 @@ +inline fn get_arch() -> string { + const eval if defined(__x86_64__) || defined(_M_X64) { + return "x86_64"; + } else if defined(__i386__) || defined(_M_IX86) { + return "i686"; + } else if defined(__aarch64__) || defined(_M_ARM64) { + return "arm64"; + } else if defined(__arm__) || defined(_M_ARM) { + return "arm"; + } else if defined(__powerpc64__) || defined(__PPC64__) { + return "powerpc64"; + } else if defined(__powerpc__) || defined(__PPC__) { + return "powerpc"; + } else if defined(__riscv) && defined(__riscv_xlen) && (__riscv_xlen == 64) { + return "riscv64"; + } else if defined(__riscv) && defined(__riscv_xlen) && (__riscv_xlen == 32) { + return "riscv32"; + } else if defined(__mips__) { + return "mips"; + } else if defined(__sparc__) { + return "sparc"; + } else if defined(__s390x__) { + return "systemz"; + } else if defined(__wasm__) { + return "wasm"; + } else if defined(__loongarch__) { + return "loongarch"; + } else if defined(__hexagon__) { + return "hexagon"; + } else if defined(__xtensa__) { + return "xtensa"; + } else if defined(__m68k__) { + return "m68k"; + } else if defined(__sh__) { + return "sh"; + } else if defined(__alpha__) { + return "alpha"; + } else if defined(__ia64__) { + return "ia64"; + } else if defined(__vax__) { + return "vax"; + } else if defined(__avr__) { + return "avr"; + } else { + return "unknown"; + } +} + +inline fn get_vendor() -> string { + const eval if defined(_WIN32) || defined(_WIN64) { + return "pc-win32"; + } else if defined(__APPLE__) { + return "apple"; + } else if defined(__linux__) { + return "linux"; + } else if defined(__FreeBSD__) { + return "freebsd"; + } else if defined(__NetBSD__) { + return "netbsd"; + } else if defined(__OpenBSD__) { + return "openbsd"; + } else { + return "unknown"; + } +} + +inline fn get_system() -> string { + const eval if defined(_MSC_VER) { + return "msvc"; + } else if defined(__clang__) || defined(__llvm__) { + return "llvm"; + } else if defined(__GLIBC__) { + return "gnu"; + } else if defined(__UCLIBC__) { + return "uclibc"; + } else if defined(__BIONIC__) { + return "bionic"; + } else if defined(__NEWLIB__) { + return "newlib"; + } else if defined(__MUSL__) { + return "musl"; + } else if defined(__dietlibc__) { + return "dietlibc"; + } else if defined(__wasi__) { + return "wasi"; + } else { + return "unknown"; + } +} \ No newline at end of file diff --git a/vial/rt/types.hlx b/vial/rt/types.hlx new file mode 100644 index 00000000..16aa1313 --- /dev/null +++ b/vial/rt/types.hlx @@ -0,0 +1,4 @@ +/// P.S: Current compiler necessitates the use of +/// `;;` for type declarations and some unknown other things. +type Path = libcxx::filesystem::path;; +type File = libcxx::filesystem::directory_entry;; \ No newline at end of file diff --git a/vial/runtime.hlx b/vial/runtime.hlx new file mode 100644 index 00000000..24616ace --- /dev/null +++ b/vial/runtime.hlx @@ -0,0 +1,98 @@ +#[trivially_import(true)] + +import rt::api as api; + +__inline_cpp("extern i32 build(void);"); + +/* Target: +var name: string; +var linkage: Linkage; +var sources: vec::; +var includes: vec::; +var links: vec::; +var builddir: types::Path = types::Path(fs::get_cwd() / "build".raw_string()); +var cxxflags: vec::; +var defines: vec::; +var before_build: fn (*Target) -> void; +var after_build: fn (*Target) -> void; +*/ + +fn compile_cmd(target: *api::Target) -> string { + // api::HELIX_COMPILER::value() + var cmd = f"{api::HELIX_COMPILER::value()}"; + + return cmd; +} + +fn set_up_globals() { + api::VERSION::set("0.1.0"); + api::IN_TARGET::set(false); + api::TARGET_TRIPLE::set(f"{api::get_arch()}-{api::get_vendor()}-{api::get_system()}"); + api::BUILD_DIR::set(api::types::Path(api::get_cwd() / r"build" / api::TARGET_TRIPLE::value().raw_string())); +} + +fn main(argc: i32, argv: *(*helix::std::Legacy::char)) -> i32 { + var args = vec::(); + + for (i in 0..argc) { + args.push_back(std::to_string(argv[i])); + } + + // the following paths must be provided by the user + // helix compiler path, and c++ compiler path, compiler type (msvc or clang) + + var helix_compiler: string; + var cpp_compiler: string; + var is_msvc: bool = false; + + if argc != 4 { + api::log.error("Usage: vial "); + return 127; + } + + helix_compiler = args[1]; + cpp_compiler = args[2]; + is_msvc = args[3] == "true"; + + if !libcxx::filesystem::exists(helix_compiler.raw_string()) { + api::log.error(f"Helix compiler path does not exist: {helix_compiler}"); + return 1; // Helix compiler not found + } + + if !libcxx::filesystem::exists(cpp_compiler.raw_string()) { + api::log.error(f"C++ compiler path does not exist: {cpp_compiler}"); + return 1; // C++ compiler not found + } + + api::set_helix_compiler(helix_compiler.raw()); + api::set_cpp_compiler(cpp_compiler.raw()); + + finally { // ensure all targets are deleted + for target in api::TARGETS::value() { + std::forget(target); + } + } + + api::log.info("Starting build process..."); + + var ret = build(); + + api::log.info(f"Build process completed. targets: {api::TARGETS::value().size()}"); + + for target in api::TARGETS::value() { + target->before_build(target); + + var cmd = compile_cmd(target); + api::log.info(f"Compiling target: {target->name} with command: {cmd}"); + + // Here you would execute the command, e.g., using a system call + // For now, we just log it + // system(cmd.raw_string()); + + target->after_build(target); + } + + api::log.info("Build targets registered successfully."); + + return ret; +} \ No newline at end of file diff --git a/vial/vial.hlx b/vial/vial.hlx new file mode 100644 index 00000000..7ccbcead --- /dev/null +++ b/vial/vial.hlx @@ -0,0 +1,655 @@ +import rt::api as api; + +static var USING_MSVC: bool = false; + +// "⠈", "⠐", "⠠", "⢀", "⡀", "⠄", "⠂", "⠁" + + +fn in_vs_env() -> bool { + const eval if defined(_WIN32) || defined(_WIN64) { + return std::getenv("VSCMD_VER") != &null && std::getenv("VSINSTALLDIR") != &null; + } else { + return false; // Not applicable for non-Windows systems + } +} + +fn get_msvc_cl() -> string? { + const eval if !(defined(_WIN32) || defined(_WIN64)) { + return null; // Not applicable for non-Windows systems + } + + if in_vs_env() { + // we only need to find the cl.exe path + var cl_path: string? = api::find_tool("cl"); + + if cl_path != null { + return *cl_path; + } else { + return null; // cl.exe not found + } + } + + // If not in VS environment, try to use vswhere and find cl.exe + var vswhere_path: string? = api::find_tool("vswhere"); + + if vswhere_path == null { + var mingw_path: string? = api::find_tool("mingw"); + + if mingw_path != null { + return *mingw_path; // Use MinGW if available + } + + var any_cpp: string? = api::find_tool("c++"); + if any_cpp != null { + return *any_cpp; // Use any C++ compiler found + } + + return null; // No C++ compiler found + } + + // Use vswhere to find the latest Visual Studio installation + var vswhere_cmd: string = f"{*vswhere_path} -latest -products * -requires" + "Microsoft.VisualStudio.Component.VC.Tools.x86.x64" + "-property installationPath"; + + var res: ExecResult = exec(vswhere_cmd); + + if res.return_code != 0 { + api::log.error(f"Error finding Visual Studio installation: {res.output}"); + return null; // vswhere failed + } + + var vs_path: string = res.output.strip(); + + if !libcxx::filesystem::exists(vs_path.raw_string()) { + api::log.error(f"Visual Studio installation path does not exist: {vs_path}"); + return null; // Invalid path + } + + var msvc_tools: api::types::Path = api::types::Path(vs_path.raw_string()) + / r"VC" / r"Auxiliary" / r"Build" / r"vcvars64.bat"; + + if !libcxx::filesystem::exists(msvc_tools) { + api::log.error(f"MSVC tools path does not exist: {msvc_tools}"); + return null; // MSVC tools not found + } + + // Construct the command to run vcvars64.bat and find cl.exe + var compile_cmd = f"cmd.exe /c \"call \"{msvc_tools.generic_wstring()}\" >nul 2>&1 && cl"; + + USING_MSVC = true; + return compile_cmd; +} + +fn get_cpp_compiler() -> string? { + const eval if defined(_WIN32) || defined(_WIN64) { + return get_msvc_cl(); + } else { + var clang_path: string? = api::find_tool("clang++"); + + if clang_path != null { + return f"\"{clang_path}\""; // Use Clang++ if available + } + + var cpp_path: string? = api::find_tool("c++"); + + if cpp_path != null { + return f"\"{cpp_path}\""; // Use any C++ compiler found + } + + return null; // No C++ compiler found + } +} + +fn get_helix_compiler() -> string? { + var helixc = api::find_tool("helix"); + + if helixc != null { + return f"\"{helixc}\""; // Return helix compiler path + } + + return null; // No helix compiler found +} + +fn find_runtime() -> string? { + var exec_dir: api::types::Path = get_executable_path().parent_path(); + var file_dir: api::types::Path = api::types::Path(std::to_string(__FILE__).raw_string()).parent_path(); + + if libcxx::filesystem::exists(exec_dir.parent_path() / r"pkgs" / r"vial" / r"runtime.hlx") { + return string((exec_dir.parent_path() / r"pkgs" / r"vial" / r"runtime.hlx").generic_wstring()); + } else if libcxx::filesystem::exists(file_dir / r"runtime.hlx") { + return string((file_dir / r"runtime.hlx").generic_wstring()); + } + + api::log.error("vial runtime not found, please make sure it is in the current directory or in the pkgs/vial directory."); + return null; // Runtime not found +} + +fn setup_build_environment() -> i32 { + var build_file = api::find_build_file(); + if build_file == null { + api::log.error("no build.hlx file found in the current directory."); + return 1; + } + + api::log.info("build.hlx file found."); + api::BUILD_FILE::set(*build_file); + + libcxx::filesystem::create_directories(api::BUILD_DIR::value() / r".resolver" / r".cache"); + libcxx::filesystem::create_directories(api::BUILD_DIR::value() / r"release" / r"bin"); + libcxx::filesystem::create_directories(api::BUILD_DIR::value() / r"debug" / r"bin"); + libcxx::filesystem::create_directories(api::BUILD_DIR::value() / r".shared" / r"libs"); + libcxx::filesystem::create_directories(api::BUILD_DIR::value() / r".shared" / r".cache"); + + api::log.debug(f"created build directory: {api::BUILD_DIR::value()}"); + + return 0; +} + +fn prepare_build_file() -> i32 { + var new_build_file = api::BUILD_DIR::value() / r".resolver" / r"build.hlx"; + libcxx::filesystem::remove(new_build_file); + + // open the file and add #[trivially_import(true)] to the first line + var input = libcxx::wifstream(api::BUILD_FILE::value(), __inline_cpp("libcxx::ios::in")); + + if !input.is_open() { + api::log.error("Failed to open original build file."); + return 1; + } + + var output = libcxx::wofstream(new_build_file, libcxx::ios::out | libcxx::ios::trunc); + + if !output.is_open() { + api::log.error("Failed to create new build file."); + return 1; + } + + output << r"#[trivially_import(true)]\n"; + + var line: libcxx::wstring; + + while (libcxx::getline(input, line)) { + output << line << '\n'; + } + + input.close(); + output.close(); + + api::BUILD_FILE::set(new_build_file); + return 0; +} + +fn compile_helix_sources() -> i32 { + var helixc = get_helix_compiler(); + + if helixc == null { + api::log.error("helix compiler not found. install it or add it to your PATH."); + return 1; + } + + api::log.info("compiling build.hlx..."); + + var o_ext: string; + + const eval if defined(_WIN32) || defined(_WIN64) { + o_ext = ".obj"; + } else { + o_ext = ".o"; + } + + var runtime_path = find_runtime(); + + if runtime_path == null { + api::log.error("vial runtime not found in working dir or pkgs/vial."); + return 1; + } + + var full_o_path = api::BUILD_DIR::value() / r".resolver" / r".cache"; + var build_artifact = (full_o_path / (f"build_artifact{o_ext}".raw_string())); + var rt_artifact = (full_o_path / (f"vial_runtime{o_ext}".raw_string())); + + libcxx::filesystem::remove(build_artifact); + libcxx::filesystem::remove(rt_artifact); + + var helix_build_cmd = f"{helixc} \"{api::BUILD_FILE::value().generic_wstring()}\" -o \"{build_artifact.generic_wstring()}\" --lib"; + var helix_rt_cmd = f"{helixc} \"{runtime_path}\" -o \"{rt_artifact.generic_wstring()}\" --lib"; + + const eval if defined(DEBUG) { + helix_build_cmd += " --debug"; + helix_rt_cmd += " --debug"; + } + + helix_build_cmd += " 2>&1"; + helix_rt_cmd += " 2>&1"; + + api::log.debug(f"compiling build.hlx with: {helix_build_cmd}"); + api::log.debug(f"compiling runtime with: {helix_rt_cmd}"); + + var pool: ThreadPool = ThreadPool(3); + var rt_future = pool.submit(exec, helix_rt_cmd); + var build_future = pool.submit(exec, helix_build_cmd); + + var res = build_future.get(); + + if res.return_code != 0 { + api::log.error(f"build.hlx compilation failed:\n{res.output}"); + return 1; + } + + api::log.info("build.hlx compiled successfully."); + + res = rt_future.get(); + + if res.return_code != 0 { + api::log.error(f"vial runtime compilation failed:\n{res.output}"); + return 1; + } + + api::log.info("runtime compiled successfully."); + return 0; +} + +fn link_artifacts() -> i32 { + var cpp_compiler = get_cpp_compiler(); + + if cpp_compiler == null { + api::log.error("c++ compiler not found. install one or add it to your PATH."); + return 1; + } + + api::log.info("linking build resolver..."); + + var output_exe: api::types::Path; + var o_ext: string; + + const eval if defined(_WIN32) || defined(_WIN64) { + output_exe = api::BUILD_DIR::value() / f"build.exe".raw_string(); + o_ext = ".obj"; + } else { + output_exe = api::BUILD_DIR::value() / r".shared" / f"build".raw_string(); + o_ext = ".o"; + } + + var full_o_path = api::BUILD_DIR::value() / r".resolver" / r".cache"; + var flags: string; + + if USING_MSVC { + (flags = f"/OUT:\"{output_exe.generic_wstring()}\" " + "/EHsc /MT "); + } else { + (flags = f"-o \"{output_exe.generic_wstring()}\" " + "-std=c++20 -lpthread -lm "); + } + + var link_cmd = f"{cpp_compiler} {flags}" + + f"\"{(full_o_path / f"build_artifact{o_ext}".raw()).generic_wstring()}\" " + + f"\"{(full_o_path / f"vial_runtime{o_ext}".raw()).generic_wstring()}\" " + + "2>&1"; + + var res = exec(link_cmd); + api::log.debug(f"linking with: {link_cmd}"); + + if res.return_code != 0 { + api::log.error(f"linking failed:\n{res.output}"); + return 1; + } + + api::log.info(f"build successful! executable created at: {output_exe}"); + return 0; +} + +fn execute_build_artifact() -> i32 { + var helixc = get_helix_compiler(); + var cpp_compiler = get_cpp_compiler(); + + if helixc == null || cpp_compiler == null { + api::log.error("Required compilers not available for execution."); + return 1; + } + + api::log.info("executing build artifact..."); + + var output_exe: api::types::Path; + + const eval if defined(_WIN32) || defined(_WIN64) { + output_exe = api::BUILD_DIR::value() / f"build.exe".raw_string(); + } else { + output_exe = api::BUILD_DIR::value() / r".shared" / f"build".raw_string(); + } + + var exec_cmd = f"{output_exe} {helixc} {cpp_compiler} \"{"true" if USING_MSVC else "false"}\""; + var res = exec(exec_cmd); + + api::log.debug(f"running build with: {exec_cmd}"); + + if res.return_code != 0 { + api::log.error(f"build execution failed:\n{res.output}"); + return res.return_code; + } + + api::log.info(res.output); + return 0; +} + +fn main() -> i32 { + api::VERSION::set("0.1.0"); + api::IN_TARGET::set(false); + api::TARGET_TRIPLE::set(f"{api::get_arch()}-{api::get_vendor()}-{api::get_system()}"); + api::BUILD_DIR::set(api::types::Path(api::get_cwd() / r"build" / api::TARGET_TRIPLE::value().raw_string())); + + api::log.debug(f"build dir {api::BUILD_DIR::value()}"); + + var result = setup_build_environment(); + + if result != 0 { + return result; + } + + api::log.debug(f"build dir {api::BUILD_DIR::value()}"); + result = prepare_build_file(); + + if result != 0 { + return result; + } + + api::log.debug(f"build dir {api::BUILD_DIR::value()}"); + result = compile_helix_sources(); + + if result != 0 { + return result; + } + + api::log.debug(f"build dir {api::BUILD_DIR::value()}"); + result = link_artifacts(); + + if result != 0 { + return result; + } + + api::log.debug(f"build dir {api::BUILD_DIR::value()}"); + return execute_build_artifact(); +} + +/* +import rt::api as api; + +var log = api::Logger(); +static var USING_MSVC: bool = false; + +fn in_vs_env() -> bool { + const eval if defined(_WIN32) || defined(_WIN64) { + return std::getenv("VSCMD_VER") != &null && std::getenv("VSINSTALLDIR") != &null; + } else { + return false; // Not applicable for non-Windows systems + } +} + +fn get_msvc_cl() -> string? { + const eval if !(defined(_WIN32) || defined(_WIN64)) { + return null; // Not applicable for non-Windows systems + } + + if (in_vs_env()) { + // we only need to find the cl.exe path + var cl_path: string? = api::find_tool("cl"); + if (cl_path != null) { + return *cl_path; + } else { + return null; // cl.exe not found + } + } + + // If not in VS environment, try to use vswhere and find cl.exe + + var vswhere_path: string? = api::find_tool("vswhere"); + if (vswhere_path == null) { + var mingw_path: string? = api::find_tool("mingw"); + if (mingw_path != null) { + return *mingw_path; // Use MinGW if available + } + + var any_cpp: string? = api::find_tool("c++"); + if (any_cpp != null) { + return *any_cpp; // Use any C++ compiler found + } + + return null; // No C++ compiler found + } + + // Use vswhere to find the latest Visual Studio installation + var vswhere_cmd: string = f"{*vswhere_path} -latest -products * -requires" + "Microsoft.VisualStudio.Component.VC.Tools.x86.x64" + "-property installationPath"; + + var res: ExecResult = exec(vswhere_cmd); + + if (res.return_code != 0) { + api::log.error(f"Error finding Visual Studio installation: {res.output}"); + return null; // vswhere failed + } + + var vs_path: string = res.output.strip(); + if !libcxx::filesystem::exists(vs_path.raw_string()) { + api::log.error(f"Visual Studio installation path does not exist: {vs_path}"); + return null; // Invalid path + } + + var msvc_tools: api::types::Path = api::types::Path(vs_path.raw_string()) + / r"VC" / r"Auxiliary" / r"Build" / r"vcvars64.bat"; + + if !libcxx::filesystem::exists(msvc_tools) { + api::log.error(f"MSVC tools path does not exist: {msvc_tools}"); + return null; // MSVC tools not found + } + + // Construct the command to run vcvars64.bat and find cl.exe + var compile_cmd = f"cmd.exe /c \"call \"{msvc_tools.generic_wstring()}\" >nul 2>&1 && cl"; + USING_MSVC = true; + return compile_cmd; +} + +fn get_cpp_compiler() -> string? { + const eval if defined(_WIN32) || defined(_WIN64) { + return get_msvc_cl(); + } else { + var clang_path: string? = api::find_tool("clang++"); + if (clang_path != null) { + return f"\"{clang_path}\""; // Use Clang++ if available + } + + var cpp_path: string? = api::find_tool("c++"); + if (cpp_path != null) { + return f"\"{cpp_path}\""; // Use any C++ compiler found + } + + return null; // No C++ compiler found + } +} + +fn get_helix_compiler() -> string? { + var helixc = api::find_tool("helix"); + if (helixc != null) { + return f"\"{helixc}\""; // Return helix compiler path + } + + return null; // No helix compiler found +} + +fn find_runtime() -> string? { + var exec_dir: api::types::Path = get_executable_path().parent_path(); + var file_dir: api::types::Path = api::types::Path(std::to_string(__FILE__).raw_string()).parent_path(); + + if (libcxx::filesystem::exists(exec_dir.parent_path() / r"pkgs" / r"vial" / r"runtime.hlx")) { + return string((exec_dir.parent_path() / r"pkgs" / r"vial" / r"runtime.hlx").generic_wstring()); + } else if (libcxx::filesystem::exists(file_dir / r"runtime.hlx")) { + return string((file_dir / r"runtime.hlx").generic_wstring()); + } + + api::log.error("vial runtime not found, please make sure it is in the current directory or in the pkgs/vial directory."); + return null; // Runtime not found +} + +fn main() -> i32 { + var build_file = api::find_build_file(); + if (build_file == null) { + api::log.error("no build.hlx file found in the current directory."); + return 1; + } + + api::log.info("build.hlx file found."); + + api::BUILD_FILE = *build_file; + + if !libcxx::filesystem::exists(api::BUILD_DIR) { + libcxx::filesystem::create_directories(api::BUILD_DIR / r".resolver" / r".cache"); + api::log.debug(f"created build directory: {api::BUILD_DIR}"); + } + + var full_o_path = api::BUILD_DIR / r".resolver" / r".cache"; + var new_build_file = api::BUILD_DIR / r".resolver" / r"build.hlx"; + + libcxx::filesystem::remove(new_build_file); + + // open the file and add #[trivially_import(true)] to the first line + var input = libcxx::wifstream(api::BUILD_FILE, __inline_cpp("libcxx::ios::in")); + if (!input.is_open()) { + api::log.error("Failed to open original build file."); + return 1; + } + + var output = libcxx::wofstream(new_build_file, libcxx::ios::out | libcxx::ios::trunc); + if (!output.is_open()) { + api::log.error("Failed to create new build file."); + return 1; + } + + output << r"#[trivially_import(true)]\n"; + + var line: libcxx::wstring; + while (libcxx::getline(input, line)) { + output << line << '\n'; + } + + input.close(); + output.close(); + + api::BUILD_FILE = new_build_file; + + // Compile build.hlx + var helixc = get_helix_compiler(); + if (helixc == null) { + api::log.error("helix compiler not found. install it or add it to your PATH."); + return 1; + } + + api::log.info("compiling build.hlx..."); + + var o_ext: string; + const eval if defined(_WIN32) || defined(_WIN64) { + o_ext = ".obj"; + } else { + o_ext = ".o"; + } + + var runtime_path = find_runtime(); + if (runtime_path == null) { + api::log.error("vial runtime not found in working dir or pkgs/vial."); + return 1; + } + + var build_artifact = (full_o_path / (f"build_artifact{o_ext}".raw_string())); + var rt_artifact = (full_o_path / (f"vial_runtime{o_ext}".raw_string())); + + libcxx::filesystem::remove(build_artifact); + libcxx::filesystem::remove(rt_artifact); + + var helix_build_cmd = f"{helixc} \"{api::BUILD_FILE.generic_wstring()}\" -o \"{build_artifact.generic_wstring()}\" --lib"; + var helix_rt_cmd = f"{helixc} \"{runtime_path}\" -o \"{rt_artifact.generic_wstring()}\" --lib"; + + const eval if defined(DEBUG) { + helix_build_cmd += " --debug"; + helix_rt_cmd += " --debug"; + } + + helix_build_cmd += " 2>&1"; + helix_rt_cmd += " 2>&1"; + + api::log.debug(f"compiling build.hlx with: {helix_build_cmd}"); + api::log.debug(f"compiling runtime with: {helix_rt_cmd}"); + + var pool: ThreadPool = ThreadPool(3); + var rt_future = pool.submit(exec, helix_rt_cmd); + var build_future = pool.submit(exec, helix_build_cmd); + + var res = build_future.get(); + if (res.return_code != 0) { + api::log.error(f"build.hlx compilation failed:\n{res.output}"); + return 1; + } + + api::log.info("build.hlx compiled successfully."); + + res = rt_future.get(); + if (res.return_code != 0) { + api::log.error(f"vial runtime compilation failed:\n{res.output}"); + return 1; + } + + api::log.info("runtime compiled successfully."); + + // Link + var cpp_compiler = get_cpp_compiler(); + if (cpp_compiler == null) { + api::log.error("c++ compiler not found. install one or add it to your PATH."); + return 1; + } + + api::log.info("linking build resolver..."); + + var output_exe: api::types::Path; + + const eval if defined(_WIN32) || defined(_WIN64) { + output_exe = api::BUILD_DIR / f"build.exe".raw_string(); + } else { + output_exe = api::BUILD_DIR / r".shared" / f"build".raw_string(); + } + + var flags: string; + if USING_MSVC { + (flags = f"/OUT:\"{output_exe.generic_wstring()}\" " + "/EHsc /MT "); + } else { + flags = f"-o \"{output_exe.generic_wstring()}\" " + "-std=c++20 -lpthread -lm "; + } + + var link_cmd = f"{cpp_compiler} {flags}" + + f"\"{(full_o_path / f"build_artifact{o_ext}".raw()).generic_wstring()}\" " + + f"\"{(full_o_path / f"vial_runtime{o_ext}".raw()).generic_wstring()}\" " + + "2>&1"; + + res = exec(link_cmd); + api::log.debug(f"linking with: {link_cmd}"); + + if (res.return_code != 0) { + api::log.error(f"linking failed:\n{res.output}"); + return 1; + } + + api::log.info(f"build successful! executable created at: {output_exe}"); + + // Run executable + api::log.info("executing build artifact..."); + + var exec_cmd = f"{output_exe} {helixc} {cpp_compiler} \"{"true" if USING_MSVC else "false"}\""; + res = exec(exec_cmd); + + api::log.debug(f"running build with: {exec_cmd}"); + + if (res.return_code != 0) { + api::log.error(f"build execution failed:\n{res.output}"); + return res.return_code; + } + + api::log.info(res.output); + + return 0; +} +*/ \ No newline at end of file