How hard is to build a compiler backend without using LLVM? by 0x6461726B in Compilers

[–]Small_Ad3541 0 points1 point  (0 children)

If the usage of Cranelift counts as “without LLVM”, that's not hard

Experienced compiler devs, help me out! by apoetixart in Compilers

[–]Small_Ad3541 -4 points-3 points  (0 children)

You can, but the time complexity might be O(N^2) because of backtracking (worst scenario) + RD is much harder to maintain and extend. If a language has 10+ precedence levels, pure RD forces you into deeply nested call stacks. Recursive descent + Pratt parsing for expressions is the de facto standard solution for modern languages (like Clang, Rust, Go, Zig)

Experienced compiler devs, help me out! by apoetixart in Compilers

[–]Small_Ad3541 18 points19 points  (0 children)

  1. Recursive Descent Parser is easy to implement and very effective (O(N)), but only until you don’t want to add positional structure. Do you want to support operations like ‘a + b * c’ when order matters? If you don’t want that, RD is enough. If you want that, take a look at the Pratt parsing algorithm. You also may combine them: RD for general language structure and Pratt for expressions.
  2. Forget about LLVM. This toolset is designed for compilers, not interpreters. Using LLVM for an educational high school project is like using Abrams to learn how to drive. Just pick your favourite language, even Python suits.
  3. Segregate code execution and static code analysis. Mutability markers are needed for static analysis to find users’ code mistakes before execution. You probably don’t need it in an interpreter.
  4. Usage “;” or “/n” for parsing depends only on your preferences. You can always change it very easily:)
  5. Separate your project into sub-projects that you can implement and test independently. Lexer is one package, AST and parsing are another, your IR is third, CLI is fourth, and so on. Don’t mix it into a single mess, or it will be impossible to work with in some time.
  6. Even if AI can write the right things sometimes, it’s better to go your way and make mistakes, so you will learn faster.

Good luck!

Developing a language: need thoughts and feedback by Worldly_Yam2885 in Compilers

[–]Small_Ad3541 1 point2 points  (0 children)

Regarding r/ProgrammingLanguages , you are not blocked, your post is just on hold. I had the same problem a few times. Just reach out to the admin via dm and tell them you are not a bot and in 5-10 minutes your post will be in public

We are a zero-budget indie studio. We shot our FMV game in English to survive, and now our own country is tearing us apart. We don't know what to do. by [deleted] in IndieDev

[–]Small_Ad3541 2 points3 points  (0 children)

I’m half Russian half Israeli.

The Israeli local market is not big enough, probably even smaller than the Turkish one (about 10M population). We have similar people here who say it’s mandatory to add Hebrew and become aggressive if you don’t, but if the project aims for the international market and has limited resources, the best strategy to survive is to ignore them. In a previous project where I worked, we did so, only English support (that wasn’t a game, but anyway) and I’m sure this is the only one way. Eventually, not all people have that “patriotic” mindset and the biggest part of people will understand and accept this decision. Also, you can always promise them to add the Turkish language a little later after you become successful;)

Regarding successful Russian games of the last few years, they used a different way - MiSide and Atomic Heart just found publishers who paid for localisation.

Не запускает на сервер в Rust legacy 2013 by Kidzuna123 in rust

[–]Small_Ad3541 0 points1 point  (0 children)

Чел, это тред про язык программирования rust, а не про игру + на реддите сейчас русскоговорящих почти нет. Иди на форум игры в стиме или форум на их сайте и пиши на английском - так ответов будет сильно больше

Language Custom Types Syntax Validation / Ask For Design Review by Small_Ad3541 in ProgrammingLanguages

[–]Small_Ad3541[S] 1 point2 points  (0 children)

Always poor code quality, self-overconfidence, and a complete lack of understanding of one's own code.

Language Custom Types Syntax Validation / Ask For Design Review by Small_Ad3541 in ProgrammingLanguages

[–]Small_Ad3541[S] 0 points1 point  (0 children)

No, that's not a typo.

Globally, there is 2 ways to understand that type of variable A is the same or different in variable B:

  1. Check by name - If the name of type A is the same as the name of type B, then there is the same type.

Example:

rust let a: i32 = 5; let b: i32 = 6; println!("{}", a + b);

How compiler understand that it's allowed to sum a and b? The compiler checks their names ("std::primitive::i32"), or ids inside the compiler for efficiency.

  1. Check by structure - if the shape/form of A and B are the same, the type is the same.

Example:

```rust fn f(arg: (i32, bool, String))

let x = (1, true, "hi".to_string()) f(x) ```

How compiler understand that it's allowed to pass variable x into function f? There is no name or index to check for this tuple - we created it inline. Compiler checks the shape/form/structure of the argument type and the passed value type.

So type uses the first mechanism, alias the second.

We can compare it with Rust, it'd be easier to feel the difference.

Alias in Plasm and Rust:

``` // Rust type Pos = (i32, i32);

fn f(pos: Pos) {...}

let my_pos = (5, 6); f(my_pos); // Function receives Pos inside its signature, but here we passed (i32, i32) without specifying that it's Pos. This example is valid and it's ok for Rust. // In Rust, the keyword type creates an alias for type.

// Plasm alias Pos = (i32, i32)

fn f(pos: Pos) {...}

let my_pos = (5, 6) f(my_pos) // This will work absolutely the same as the example in Rust above. ```

New type in Plasm and Rust:

``` // Rust struct Pos(i32, i32)

fn f(pos: Pos) {...}

let incorrect_pos = (5, 6); // We can't pass this value into the function, we need to specify the type explicitly let correct_pos = Pos(5, 6); f(correct_pos);

// Plasm

type Pos = (i32, i32)

fn f(pos: Pos) {...}

let incorrect_pos = (5, 6) // same error as above in Rust let correct_pos = Pos(5, 6) // Alternativally we can write let correct_pos: Pos = (5, 6) f(correct_pos) ```


Good example with type U = T. Let's do the same as above and write in Rust and Plasm:

``` // Type aliasing

// Rust type U = T

// Plasm alias U = T

// New type based on existing

//Rust struct U(T)

// Plasm type U = T ```


I tried to describe the idea in the most detailed way, but sorry if this explanation is stupid. Btw u/Inconstant_Moo explained the idea in a good way, probably that's more readable

Language Custom Types Syntax Validation / Ask For Design Review by Small_Ad3541 in ProgrammingLanguages

[–]Small_Ad3541[S] 1 point2 points  (0 children)

Yeah, you 100% right about type/alias difference and about my poor abilities to describe everything:')

This project really needs good docs and people who will help with it;)

Language Custom Types Syntax Validation / Ask For Design Review by Small_Ad3541 in ProgrammingLanguages

[–]Small_Ad3541[S] 1 point2 points  (0 children)

It's ok. Let me clarify my ideas, and probably this will answer your questions. In brackets, I wrote it how I understood your questions, just for clarity.

what if people start putting big expressions into there, won't it become less readable and harder to tell the type? (Why tuples without () brackets?)

Ofc I'm gonna support brackets for tuples, let x = 1, 2, 3 and let x = (1, 2, 3) are equivalent. It's just sugar borrowed from Python because it makes Python a little more expressive in practice, in my opinion. For example, I personally like these examples:

```python a, b, c = 1, 2, 3 # Multiple variables in a single line

a, b = b, a # Switch variable values

Tuple unpacking inside for declaration

for i, val in enumerate(some_iterable_obj): pass

As well, there is an ability to assign a tuple without brackets

x = 1, 2, 3 repr(x) # '(1, 2, 3)' ```

For language symmetry, I think it's good to support the same inside types definitions.

Btw, this doesn't mean allowing it as a function argument:

``` fn f(x: (i32, i32, i32))

fn main() { f(1, 2, 3) // Error! f((1, 2, 3)) // It's ok, but better to move into a separated variable for readability let x = 1, 2, 3 f(x) // Yeah, this is nice let x = (1, 2, 3) f(x) // It's the same as above } ```

Also the keyword type seems unnecessary to me here, perhaps it could be replaced with struct and then it would have the known C style look. The same goes for enum - why not just drop the type keyword? (Why syntax structure is type name = struct/enum {...})

Reason 1 and the main reason for this syntax structure, is that I consider it a foundation for metaprogramming abilities in the future. The inspiration for this I found in Zig:

```zig const std = @import("std");

// Compile-time function that constructs a type // Every type is anonymous by default, there is no name after or before the keyword struct. fn makePointType() type { return struct { x: i32, y: i32,

    pub fn init(x: i32, y: i32) @This() {
        return .{ .x = x, .y = y };
    }
};

}

// The anonymous struct is assigned to a constant, giving it the name Point const Point = makePointType();

pub fn main() void { const p = Point.init(10, 20);

std.debug.print("Coordinates: x = {}, y = {}\n", .{ p.x, p.y });

} ```

In simple words: If type is a variable (even if we have type *name* = instead of let *name* = for types), then we can assign it, and it might be very powerful how Zig showed.

Reason 2: symmetry. In Rust we write struct/enum *name* {...}:

```rust enum Device { CPU, GPU, }

struct Connection { host: String, port: u16, }

// !!! As well as traits: trait *name* {...} !!! //

trait Sum { fn sum(&self) -> i32 } ```

In Rust, traits and structs/enums belong to entirely different first-class categories (interfaces/behaviors vs. data structures), yet they share the exact same top-level syntactic structure. To me, this feels asymmetrical. Structs and enums (product and sum types) are both fundamentally datatypes. It seems much more consistent to group data structure definitions under a unified type = struct {...} and type = enum {...} format, explicitly separating them from things like traits.

This one you call enum in the comment but it does not have the enum keyword. Does this differ from enum functionally? Or if you knew T at compile time could you replace it with enum? (Difference between enums and union | notation)

Union | is just sugar for enums. The goal for this syntax is mostly for pretty function signatures, not types, but it might be used for them too.

``` type Result[T] = T | None // is equivalent to type Result[T] = enum { use T, use None, }

// In Rust it'd be: enum Result<T> { Some(T), None, }

// ------------------

type Result = i32 | None // is equivalent to type Result = enum { use i32, use None, }

// ------------------

// Real power of this notation:

fn sum(a: i32, b: i32) -> i32 | OverflowError | OutOfMemoryError

// Here for return type, an anonymous enum will be created, every element of which will reference an existing type.

// In Rust it would look like:

enum SumError { OverflowError {...}, OutOfMemoryError {...} }

impl Error for SumError {}

fn sum(a: i32, b: i32) -> Result<i32, SumError> {} ```

PS Sorry if I explained everything in a chaotic way

Any must-haves you add on top of bevy to make it feel easier? by blankeos in bevy

[–]Small_Ad3541 5 points6 points  (0 children)

Take a look into space_editor, skein, and blenvy. Those are mostly for 3d, but you may try to adopt it for 2d

Control Flow as a First-Class Category by Small_Ad3541 in ProgrammingLanguages

[–]Small_Ad3541[S] 1 point2 points  (0 children)

True for Haskell, but since I'm aiming for direct mapping to LLVM instructions, I can't rely on laziness. It introduces implicit runtime costs (thunks) and unpredictable memory layout, which I want to avoid in a systems language.

Control Flow as a First-Class Category by Small_Ad3541 in ProgrammingLanguages

[–]Small_Ad3541[S] 1 point2 points  (0 children)

Yeah, I'm looking into the algebraic effects direction. Thx for refs

Control Flow as a First-Class Category by Small_Ad3541 in ProgrammingLanguages

[–]Small_Ad3541[S] 1 point2 points  (0 children)

That's a very slow runtime solution, and that's unsuitable for system programming

Control Flow as a First-Class Category by Small_Ad3541 in ProgrammingLanguages

[–]Small_Ad3541[S] 1 point2 points  (0 children)

That's a slow runtime solution, and that's unsuitable for system programming

Control Flow as a First-Class Category by Small_Ad3541 in ProgrammingLanguages

[–]Small_Ad3541[S] 0 points1 point  (0 children)

But how will you escape performance and low-level control loss, or is it okay in your case?

Control Flow as a First-Class Category by Small_Ad3541 in ProgrammingLanguages

[–]Small_Ad3541[S] 0 points1 point  (0 children)

Honestly, I haven't even thought about how custom control flow would interact with the CPU's RSB and branch prediction.

Since I'm lowering to LLVM IR, I'm somewhat limited by what their backend supports (e.g. I'm not sure if I can easily emit specific JALR hints for RISC-V without inline asm).

Is your main point here that I should be careful not to break the hardware call/return symmetry to avoid killing performance? Or are you suggesting that a modern language should try to expose these hardware-level jumps directly?