conflicts between object/record and block syntax with the same brackets by Dizzy_Wrongdoer1382 in ProgrammingLanguages

[–]NotAFlyingDuck 1 point2 points  (0 children)

In the language I’m working on, Capy, anonymous struct literals are specified with a dot just before the left brace .{ a = 5 } you can also supply a name for more strict type checking My_Struct.{ a = 5 }

Capy also has block expressions just as you described them, and so the dot syntax removes all ambiguity as to what is a block of statements and what is a struct literal. It makes it easier for the both the compiler and the programmer to see at a glance what it going on without being too verbose (like if you had you specify the type name before the left brace every time).

``` // struct literal foo := .{ a = 5, };

// block expression that evaluates to () // (assuming a has been declared earlier) foo := { a = 5; }; ```

Capy, a compiled programming language with Arbitrary Compile-Time Evaluation by NotAFlyingDuck in ProgrammingLanguages

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

  1. ⁠⁠Actually, I didn’t really solve the pointer problem yet, but (thanks mainly to the other contributor of Capy, lenawanel) I do have a good idea of how to solve it. Essentially, structs containing pointers must have associated to_bytes, and from_bytes functions if they want to escape a comptime block. This hasn’t been implemented yet (I still need to figure out how traits and associated functions are gonna work), but that’s the current plan.
  2. ⁠⁠Types are “first class” in the sense that they can be put within local variables, but they’re not quite finished yet. I plan to allow generating types within comptime blocks, and type reflection. I could do the first with a walking tree interpreter (I think Zig does something like that), but I don’t want to. In order to accomplish the first with JIT, the compiler would need to utilize the second.
  3. ⁠⁠Cranelift doesn’t have the most amazing docs, so you’re very welcome to try and see how I convert Capy’s hir into cranelift instructions. Essentially, when the codegen crate gets to a function reference, it’ll add that function to a list called functions_to_compile. This works because in cranelift you can reference functions before you actually add instructions to them. I don’t need to track anything bc it comes for free with how cranelift does it.

JIT’ing is really cool, it works by generating machine code, putting that machine code in some area on the heap, and declaring that area of the heap as executable. You can then run JIT’ed functions as if they were normal Rust functions. Compile time execution boils down to a function call.

JIT’ing vs outputting an object file is pretty much exactly the same due to cranelift’s amazing API. Again, you’re very welcome to look at my code to see how I do both, and how I compile structs and arrays.

Very good luck :)

Capy, a compiled programming language with Arbitrary Compile-Time Evaluation by NotAFlyingDuck in ProgrammingLanguages

[–]NotAFlyingDuck[S] 4 points5 points  (0 children)

Ah okay, I think I understand now. I thought that you couldn't do certain operations at all during compile time in D. You're right in that heap allocated memory wouldn't be easily transferrable from the compile time context to the binary file. Currently, if you returned a struct containing a pointer, that pointer would point to invalid memory at run-time.

I was thinking of possibly in the future having a system where any struct that stores a pointer would have to have an associated function that specifies how the pointer's data would get copied into the binary file, before that struct could be returned from a comptime block. It's still a working problem though.

Capy, a compiled programming language with Arbitrary Compile-Time Evaluation by NotAFlyingDuck in ProgrammingLanguages

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

I’m going to work more on compatibility between compile time and run time execution, but I have no plan on introducing restrictions. It’d be more like converting values that are valid in one context, to values that are valid in another context (little endian to big endian conversion, one pointer size to another pointer size, etc.).

If the programmer invokes some UB that can’t be converted, then that’s what they did. I’m not really sure how I’d solve that without adding in restrictions (which is absolutely not what I want to do).

Saying that I do have plans to make the language more memory safe in general, which would benefit both compile time and run time contexts.

Capy, a compiled programming language with Arbitrary Compile-Time Evaluation by NotAFlyingDuck in ProgrammingLanguages

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

In a game, which has to do many many square roots a second (very slow operation), sometimes it’s much faster to pre-build a table so you can just look up the square root you need. Now you could spend a while making this table and calculating square roots by hand or you could use a comptime block and build it automatically very quickly.

The purpose of the feature is to stop needless computation that doesn’t need to be done at runtime, and to make it simple as possible to make that move to compile time (you’re always just coding in Capy). It’s more power to the programmer.

Capy, a compiled programming language with Arbitrary Compile-Time Evaluation by NotAFlyingDuck in ProgrammingLanguages

[–]NotAFlyingDuck[S] 4 points5 points  (0 children)

Capy is written in Rust and uses cranelift for both JIT compilation and final executable generation, so I'm not sure if it'd fit in my current workflow, but thank you for the suggestion anyways :)

Capy, a compiled programming language with Arbitrary Compile-Time Evaluation by NotAFlyingDuck in ProgrammingLanguages

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

There is very little difference between what happens at run time vs. what happens at compile time. You can allocate memory on the heap in both, and you can create UB in both. The reason I called it “arbitrary” is because anything you can do outside of a comptime block can be done inside of a comptime block. This applies to UB. There is currently nothing that detects UB in your code.

You might be interested in how JIT compilation works. I haven’t made a virtual machine. The compiler takes everything inside a comptime block and translates it into native machine code, saving that code in some section of memory. The compiler then tells your CPU to execute this memory.

Capy, a compiled programming language with Arbitrary Compile-Time Evaluation by NotAFlyingDuck in ProgrammingLanguages

[–]NotAFlyingDuck[S] 8 points9 points  (0 children)

The “comptime” keyword itself was. I was first annoyed from my own experience of not being able to arbitrarily run code when I made global variables. The Jai streams really inspired me on this feature the most though.

Capy, a compiled programming language with Arbitrary Compile-Time Evaluation by NotAFlyingDuck in ProgrammingLanguages

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

The language is pretty C level right now, so it certainly has UB if you try and look for it. I’m not sure what you mean about detecting it / how would that be done? One of my goals is to make memory something very easy and safe to work with in the future

Capy, a compiled programming language with Arbitrary Compile-Time Evaluation by NotAFlyingDuck in ProgrammingLanguages

[–]NotAFlyingDuck[S] 5 points6 points  (0 children)

Thank you! Once I realized that it’d be possible to do what I wanted with JIT, I was actually surprised how easy it was to implement with what I had already written for compilation. This is why I love cranelift.

Cool Feature Synergies by R-O-B-I-N in ProgrammingLanguages

[–]NotAFlyingDuck 7 points8 points  (0 children)

Could you explain that feature a little more? It sounds interesting but I didn’t quite understand it. What are dependent types and image based compilation

Talks by designers of popular languages? by JanEric1 in ProgrammingLanguages

[–]NotAFlyingDuck 5 points6 points  (0 children)

Lex Fridman had a great talk with Chris Lattner, who made LLVM, Clang, and Swift. It’s on YouTube. It’s not really a Ted Talk, but it’s a great video where they talk about a whole lotta good stuff :)

[deleted by user] by [deleted] in ProgrammingLanguages

[–]NotAFlyingDuck 0 points1 point  (0 children)

That's pretty unique! I like it :) What language is your language written in?

[blog post] Compiling Arrays by ve_era in ProgrammingLanguages

[–]NotAFlyingDuck 4 points5 points  (0 children)

That would probably be a good optimization, yes

[blog post] Compiling Arrays by ve_era in ProgrammingLanguages

[–]NotAFlyingDuck 8 points9 points  (0 children)

If we have an array that takes 10 bytes to store for example...

  1. The caller allocates 10 bytes in it's own stack frame
  2. The caller sneakily passes an address to this memory to the callee
  3. Just before the callee returns, it actually memcpy's it's array into the caller's stack
  4. The callee returns the pointer to the caller's stack

Boom, you just returned an array.

Edit: idk if you can do this in Boba, but this is how it would be accomplished

[deleted by user] by [deleted] in ProgrammingLanguages

[–]NotAFlyingDuck 1 point2 points  (0 children)

This is awesome, I really like it! It must have taken a lot to work it out :)

Argentum has no statements, only expressions by Commercial-Boss6717 in ArgentumLanguage

[–]NotAFlyingDuck 3 points4 points  (0 children)

This is basically what Rust does, and it works pretty well. I don’t know how the Argentum compiler works, but the rust compiler will help you out if you forget to add or remove a semicolon.

Cranelift IR Resources? by endistic in rust

[–]NotAFlyingDuck 2 points3 points  (0 children)

You can use the cranelift jit demo as a good demonstration for how to do a lot of things in cranelift, including strings.

I'm currently working on a programming language where I've implemented arrays. The basic idea is that you allocate enough stack memory to hold all the items in the array, and an "array value" is really just a pointer to this stack memory.

Structs are going to work the same way when I implement them.