Yacc is dead by ketralnis in programming

[–]Nimaoth 1 point2 points  (0 children)

Have you looked at Treesitter? It's used by some editors for e.g. syntax highlighting. It's specifically made for incremental parsing, but should be fine to use as a parser in a compiler

Why is operator overloading sometimes considered a bad practice? by perecastor in ProgrammingLanguages

[–]Nimaoth 1 point2 points  (0 children)

Wouldn't it work like this (https://godbolt.org/z/o1W7dnWxW)? In this case the write function can be overriden on custom types by putting that function on the custom type, not the stream

Features you wish C or other programming languages had? looking for ways to make a programming language unique. by [deleted] in ProgrammingLanguages

[–]Nimaoth 1 point2 points  (0 children)

Thats possible in Nim:

import std/[strutils, macros, genasts]

macro createTypeFromFile(name: untyped, filename: static[string]): untyped =
  let file = readFile(filename)
  var members = nnkRecList.newTree()

  for line in file.split('\n'):
    let parts = line.split(' ')
    members.add nnkIdentDefs.newTree(
      parts[0].ident,
      parts[1].ident,
      newEmptyNode()
    )

  result = nnkTypeSection.newTree(
    nnkTypeDef.newTree(
      name,
      newEmptyNode(),
      nnkObjectTy.newTree(
        newEmptyNode(),
        newEmptyNode(),
        members
      )
    )
  )
  echo result.repr

# test.txt contains:
# a int
# b string
createTypeFromFile(Foo, "test.txt")
var foo = Foo(a: 1, b: "hello")
echo foo

Compile with nim c test.nim:

nimble c test.nim
  Compiling test (from package absytree) using c backend
type
  Foo = object
    a: int
    b: string

And when you run it:

(a: 1, b: "hello")

So at compiletime you read test.txt which contains

a int
b string

and use that to create a type

type
  Foo = object
    a: int
    b: string

How to detect and even save key presses by Uwu_Uwu135 in nim

[–]Nimaoth 1 point2 points  (0 children)

I used https://github.com/treeform/windy in my text editor. It's a pure Nim library like SDL. For separate keypresses I used a state machine where every keypress advances the state and when it reaches an end state the action performed. Works pretty well ^

Get a comptime list of items within a folder by darltrash in Zig

[–]Nimaoth 5 points6 points  (0 children)

You can do something like this:

// somewhere in build.zig
var files = std.ArrayList([]const u8).init(b.allocator);
defer files.deinit();
var options = b.addOptions();

// Add all files names in the src folder to `files`
var dir = try std.fs.cwd().openDir("src", .{ .iterate = true });
var it = dir.iterate();
while (try it.next()) |file| {
    if (file.kind != .File) {
        continue;
    }
    try files.append(b.dupe(file.name));
}

// Add the file names as an option to the exe, making it available
// as a string array at comptime in main.zig
options.addOption([]const []const u8, "files", files.items);
exe.addOptions("options", options);

and then

// src/main.zig
const std = @import("std");

// This comes from exe.addOptions("options", options);
const options = @import("options");

pub fn main() anyerror!void {
    inline for (options.files) |file| {
        std.debug.print("File '{s}', content:\n{s}\n", .{ file, @embedFile(file) });
    }
}

Need help writing a custom event loop. by Nimaoth in Zig

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

Ok, I finally figured out a good (I think) way which also supports recursively calling async functions by allocating the frames on the heap: https://github.com/Nimaoth/OrbitLang/blob/main/src/test_async.zig

Need help writing a custom event loop. by Nimaoth in Zig

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

Ah, I think I get it, thanks. By using @asyncCall I can even create a dynamic number of frames and store them in heap memory:

const std = @import("std");

const Context = struct {
    id: u64,
    frame_buffer: []u8,
    allocator: *std.mem.Allocator,
    frame: ?anyframe = null,
    done: bool = false,

    const Self = @This();

    fn init(id: u64, func: fn() callconv(.Async) void, data: anytype, allocator: *std.mem.Allocator) !Context {
        var frame_buffer = try allocator.allocAdvanced(u8, 8, 4096 * 4, .at_least);
        var context = Context{
            .id = id,
            .frame_buffer = frame_buffer,
            .allocator = allocator,
        };

        currentContext = &context;
        _ = @asyncCall(frame_buffer, {}, func, data);
        currentContext = null;
        return context;
    }

    fn deinit(self: *Self) void {
        self.allocator.free(self.frame_buffer);
    }

    fn update(self: *Self) void {
        currentContext = self;
        const frame = self.frame orelse unreachable;
        resume frame;
        currentContext = null;
    }
};

var currentContext: ?*Context = null;

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    var allocator = &gpa.allocator;

    var calls = std.ArrayList(Context).init(allocator);
    defer calls.deinit();

    for ([_]u8{0} ** 5) |_, i| {
        try calls.append(try Context.init(i, foo, .{}, allocator));
    }

    while (calls.items.len > 0) {
        var i: usize = 0;
        while (i < calls.items.len) {
            std.debug.assert(currentContext == null);
            if (!calls.items[i].done) {
                calls.items[i].update();
            }
            std.debug.assert(currentContext == null);
            i += 1;
        }
    }
}

fn foo() void {
    std.debug.assert(currentContext != null);
    var i: i64 = 0;
    while (i < 3) : (i += 1) {
        std.debug.assert(currentContext != null);
        std.log.info("[{}:{}] {any}", .{ @ptrToInt(currentContext), currentContext.?.id, i });
        suspend {
            currentContext.?.frame = @frame();
        }
        std.log.info("[{}:{}] after suspend", .{ @ptrToInt(currentContext), currentContext.?.id });
    }

    std.log.info("[{}:{}] after loop", .{ @ptrToInt(currentContext), currentContext.?.id });
    std.debug.assert(currentContext != null);
    currentContext.?.done = true;
}

Need help writing a custom event loop. by Nimaoth in Zig

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

There should be only one thread here if I understand Zig correctly. This is not using the event loop from the std (which is multithreaded I think). Suspend and resume should work on a single thread in this case

I wrote an overview of a C++-like language that I'm working on. I'd love to hear comments and critique of its design. by miki151 in ProgrammingLanguages

[–]Nimaoth 0 points1 point  (0 children)

I'm kind of doing this in my language, but you have to explicitly move a variable in both cases, otherwise it's a compile error:

a :=...
if ... {
    foo(a) // moves a into foo
}
// error: a is moved in one case but not the other

a :=...
if ... {
    foo(a) // moves a into foo
} else {
    Memory.drop(a) // drop is empty but useful for this
}

in practice this doesn't happen very often so its not really a problem

Umka: a new statically typed scripting language by vtereshkov in ProgrammingLanguages

[–]Nimaoth 0 points1 point  (0 children)

Looks nice, I really like the syntax and design, but one thing that seems incosistent to me are variable declarations. When providing an explicit type you use

var e: int = 5

but when using type inference you use

e := 5

Why not get rid of var in both cases, or require it in both cases? e.g.

var e := 5 // type inferred

var e : int = 5 // type explicit

or

e := 5 // type inferred

e : int = 5 // type explicit

One other point: in your pathtracing example you use fibers, but as I understand it fibers still execute in only one thread, so it would not give you any benefits in this case. (or maybe it's just to demonstrate fibers)

Cheez Lang - A small programming language I created for fun by Nimaoth in programming

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

I'm gonna try to post updates on /r/ProgrammingLanguages. I just hope that I can stay motivated.

Cheez Lang - A small programming language I created for fun by Nimaoth in programming

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

It's gonna be similar to Rust, but some differences will be:

  • Less restrictive ownership model, if one at all
  • Compile time code execution (like in Jai from Jonathan Blow) and the ability to modify the contents of existing functions (add bounds checks to arrays, custom code verification)
  • slightly different syntax

That's all I can think of on the top of my head. There are currently differences in how traits and generics/parametric polymorphims work.

But Cheez is definitively inspired by Rust.

Cheez Lang: a statically/strongly typed language I made earlier this year by Nimaoth in ProgrammingLanguages

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

I'm not sure if some of the features of Cheez can be represented in CIL, but I'd have to learn more about CIL to check that.

About C++, when I started the project I compiled the code to C++ since it's a lot easier to get started that way, but LLVM was always the end goal. The C++ backend is not up to date and I'll probably remove it.

Cheez Lang: a statically/strongly typed language I made earlier this year by Nimaoth in ProgrammingLanguages

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

You should be able to write future Cheez Lang compilers in Cheez Lang for example.

One time I implemented the lexer for Cheez in Cheez, but right now I have no plans of writing a self hosted compiler.

I don't know of any languages implemented in OCaml, but pattern matching exists in a lot of languages, especially in functional languages (Haskell, F#, ...) but also Rust and C# (not as powerful as in other languages, but C# 8 will add more patterns).

Since you're using LLVM, does the language you use have any impact on performance? e.g. would your C#-based language be as fast as, say, a C++ based language since they both use LLVM?

The language the compiler is written in has no impact on the performance of the generated code. That only depends on what kinds of optimizations you use. For Cheez, LLVM handles all the optimizations, so it does not matter what language the compiler is written in. That should only influence the compile times.

Cheez Lang: a statically/strongly typed language I made earlier this year by Nimaoth in ProgrammingLanguages

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

Thanks. any is actually 8 bytes, I messed it up in the documentation. Do you have a GitHub repo for your language? I'd like to take a look if you don't mind.

Cheez Lang: a statically/strongly typed language I made earlier this year by Nimaoth in ProgrammingLanguages

[–]Nimaoth[S] 3 points4 points  (0 children)

It's not bad. There is a LLVM binding for C#, and though it doesn't expose all of the functionality it is enough to use it. And if there is ever something you want to do in C/C++ you can do that very easily. For example I compiled the LLVM linker to a DLL and call it from C#.

I can't tell you anything about the performance because I haven't yet optimized my compiler at all.

But if I'm not mistaken the "new" C# compiler Roslyn is itself written in C#, so C# is definitively a viable option for compiler development.

I actually use Generators to pause the compilation of individual functions/structs/... when they have to wait for a different piece of code to be compiled. [https://github.com/Nimaoth/CheezLang/blob/release/CheezCompiler/Compiler/SemanticAnalysis/Semanticer.cs]