Show Posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.


Messages - lerno

Pages: 1 ... 9 10 [11] 12 13 ... 17
151
Implementation Details / Decoupling DiagnosticsEngine
« on: October 29, 2018, 05:34:47 PM »
It would be nice to be able to decouple the diagnostics engine. As far as I can tell it's almost always Diag.Report which is used.

Can we have some sort of convenience here?

1. A thread local global (access using macro)
2. A wrapper object (explicitly passed in like the DE object is used today)
3. Pull out the DE and push a copy into C2 that we can handle and update like we want.

152
Implementation Details / Panic type of errors.
« on: October 29, 2018, 03:58:45 PM »
I've added a pull req now to use macros FATAL_ERROR and TODO instead of assert(0) or assert(0 && "Todo") in the code. First of all it should always crash on fatal error! And secondly the TODOs will then actually break on builds of unsupported code instead of producing incorrect results...

153
Implementation Details / Re: Parsing numbers
« on: October 29, 2018, 09:46:17 AM »
Furthermore, we should consider whether summing literals should be made with numeric towers before being rendered (int/float-folding could be done before C emit).

If we take a page from Zig, then:

1. Use max available int/float size to calculate literals.
2. Coerce to right size (here we should error if the int size now exceed max/min limits.
3. Explicit casting, e.g. cast<i8>(123) invokes bit limited operations instead and the result is always the same bit size.

So:

Code: [Select]
i32 a = 1 + 257; // Fine.
i8 b = cast<i8>(4 + 254); // Will store 2 done with big int, then converted to i8
i8 c = 1 + 257; // Compile time error
i8 d = cast<i8>(4) + cast<i8>(254); // Also 2, done with 8 bit maths.
f32 e = 1.0 * 2.0; // calculated using f64, then converted to f32
f64 f = 1.0;
f32 g = f * 2.0; // Compile time error, needs truncating cast
f32 h = cast<f32>(f * 2.0);
f32 i = cast<f32>(92.23) * cast<f32>(281.323); // Done with f32 maths.

154
Ideas / Re: Switch statement
« on: October 29, 2018, 08:38:47 AM »
I thought of yet another way to do this:

- Introduce auto break like before create a ”fallthrough, empty case”

Maybe it could look like this:

Code: [Select]
switch (a) {
  case 1 |
  case 2 |
  case 3:
     foo();
  case 4 |
  case 5:
     bar();
     fallthrough;
  case 6:
     baz();
}

155
Ideas / Re: Support of Unicode ?
« on: October 29, 2018, 08:32:42 AM »
Personally, I think we should have first class strings in the language, and then C style strings are created using c”Hello World”.

156
General Discussion / Re: Complex numbers
« on: October 29, 2018, 08:29:17 AM »
It adds quite a bit of unpredictability to the code - that is usually the problem. Interestingly _Generic is exactly about emulating overloading behaviour (but admittedly it is more powerful than that)

157
Ideas / Re: Switch statement
« on: October 29, 2018, 01:45:02 AM »
Also, it could be possible to provide both switch-with-fallthrough and switch-with-break by having two different syntax constructs, one with a new name, like "select (foo)" "match (foo)"

158
Ideas / Re: One more big thing: Errors
« on: October 29, 2018, 01:35:20 AM »
It's a bit tricky getting good syntax for this. Go2 is trying a new syntax themselves. Zig has try/catch which is a bit weird and icky with try in front and catch after.

Something I was trying was this:

Code: [Select]
// init returns void + error
err != smtp.init("mail.example.com");
err !!= smtp.login(); // Conditionally execute what's on the right side if there is no error. Any error is put in err
err !!= smtp.sendMail(msg); // Same
err !!= smtp.close(); // Same

if (err) {
   printf("Failed to send mail: %s", err);
   exit(-1);
}

// With return value
file ! err = File.open_file("foo");
data ! err !!= file.read(b);

i32 value = get_may_throw() !! 0; // Returns 0 in case of error.

It might be good as a starting point for a discussion. It's a tweak of Zig.

Also have a look at the Go2 proposal which actually has echoes of the error handlers of (some versions of) BASIC as well as LISP:

Code: [Select]
type Parsed struct { ... }

func ParseJson(name string) (Parsed, error) {
    handle err {
        return fmt.Errorf("parsing json: %s %v", name, err)
    }

    // Open the file
    f := check os.Open(name)
    defer f.Close()

    // Parse json into p
    var p Parsed
    check json.NewDecoder(f).Decode(&p)

    return p
}

159
Ideas / Enum improvements
« on: October 29, 2018, 01:13:13 AM »
1. Allow an automated conversion of enum value => char*

90% of my X Macros are making a mapping of enums to strings for debugging purposes. The easiest would be to make this like a struct function.

TheEnum.to_string(my_enum_value)

Also, consider making enum_max / enum_min the same:

TheEnum.max_val

TheEnum.min_val

2. Allow associated values with enums.

This is a bigger feature. An example:

Code: [Select]
type enum State [char* name, i8 bit] i32  {
   START("begin!", 0x01) = 0,
   END("end it!", 0x10)
}

State.bit(State.START) // returns 1
State.bit(State.END) // returns 16
State.name(State.START) // returns "begin!"

The associated values simply generate these new functions which are switch cases.

3. Store a list of all enum values.

This relies on fixed arrays to be useful.

Code: [Select]
// Following the example above:
State.all // returns i32[] = { 0, 1 }


160
General Discussion / Re: Contribute / get into the code
« on: October 28, 2018, 11:36:46 PM »
Another thing, I'm used to making small, controlled changes when I work with existing code. I don't have much experience working in open source over github though.

Right now I have two pull requests filed and that's about as many as I like to keep "in the air" at the same time as the chance for more complex merges quickly increase.

On the other hand, writing something like defer (which touches several aspects of the code) might be difficult if it's split into multiple check-ins.

What is preferred? I don't know how long time it will take to get approval (or rejections) on pull requests either.

I will personally work in bursts, maybe claiming a few days to implement an entire feature, then spend the time on other things for a longer period of time. So it would be good if there could be some efficient workflow for that.

161
Ideas / Dynamic arrays & fixed arrays
« on: October 28, 2018, 10:07:49 PM »
In my proposal about "real arrays" I gave the following proposal:

Code: [Select]
type struct
{
   pointer_size length;
   some_type *start;
}

It might be better putting the pointer first, since that way we can convert to the pointer with a simple cast:

Code: [Select]
type struct
{
   some_type *start;
   pointer_size length;
}

I wrote that "All arrays are always passed by reference", meaning that the struct is actually passed by value, but since it is a fat pointer, it actually becomes pass by ref. So this makes sense for fixed arrays, but for dynamic arrays that breaks down.

Our dynamic array should look like this as a struct:

Code: [Select]
type struct
{
   some_type *start;
   pointer_size length;
   pointer_size allocated;
   Allocator *allocator;
}


But for this we need to pass the whole array by value!

Unfortunately, we can't then make slices in the same manner as with the fixed vector, a slice to a dynamic vector needs to look different from a slice to a normal vector. This is rather vexing since we would have preferred to let them convert into each other. Since the first two fields are structurally the same for the fixed array and the dynamic one, they could transform into each other.

This is safe:

Code: [Select]
func void foo(int[*] y) { ... };
func void bar(int[] x) { ... };

int[*] a = { 1, 2, 3 };
foo(a);
int[] b = cast<int[]>(a);
bar(a); // converts to foo(cast<struct FixedArray>(a))
printf("%d", b[0]);


These are unsafe:
Code: [Select]
func void foo(int[*] y); { ... }
func void bar(int[] x) { ... };

int[*] a = { 1, 2, 3 };
int[] b = cast<int[]>(a);
int[] c = a[0:1];
foo(a);
printf("%d", b[0]); // The pointer in b might have been freed.
printf("%d", c[0]); // The pointer in c might have been freed.

Either we simply accept these weaknesses... we see the errors as similar to the exceptions that occur in say, Java when getting a view of a map or a list that's later updated. OR we try to be clever. I'm somewhat for the less clever solution that might lead to errors when used incorrectly.

162
Ideas / Allocation strategies
« on: October 28, 2018, 07:01:28 PM »
One interesting thing would be to make it easy to use allocation transparently.

First of all we introduce a thread local global called "context". There are actually two ways of handling this: either context is a real thread local, or we keep an invisible context pointer locked in a register.

The context is a simple struct

Code: [Select]
type struct Context
{
   i64 thread_id;
   Context *previous;
   Allocator *allocator;
   void *data;
}

We then add func Context *push_context(), which basically creates a new Context, then pushes it on top. And the corresponding pop method that frees the data pointer (if non nil), and switches to the previous context. (Note that the lifetime of the Allocator is not tied to the Context)

The point of all this is that malloc uses the allocator in the current context and not a global allocator. By default the allocator can then be one-per-thread, which prevents need for locks in the allocator.

This functionality has a lot of "giving someone a bazooka to hunt mosquitos with", however it allows us fine grained control over growable arrays, maps and strings.

The usual problem with one of those, is that we need some kind of allocator – but we don't know exactly what is the best. If we want to play PHP we could push a bump allocator on the stack, then free everything when the page request ends. But we could still swap allocators if we needed long lived objects.

Look at Zig's problem... Zig declares "no default allocator" which is a huge problem. To see that, consider this:

Code: [Select]
// Initial design of library function:
char *getBazName(Baz *baz) {
  return baz ? baz.name : "Unknown baz";
}

// New design
char *getBazName(Baz *baz) {
  return baz ? uppercase(baz.name) : "Unknown baz";
}

Now we immediately realize that "uppercase" – by virtue of returning a char* – must allocate a new array. Consequently the real "uppercase" should be uppercase(char *, Allocator *). This also means that getBazName needs an Allocator:

Code: [Select]
char *getBazName(Baz *baz, Allocator *allocator) {
  return baz ? uppercase(baz.name, allocator) : "Unknown baz";
}

Thus the function needs to change when something internal changes. It's very bad. And if some library creator then decides to not explicitly expose the Allocator to configure? Well then you're out of luck trying to get consistent memory handling. Also, those Allocator functions pollutes the function profiles with something you're often not interested in.

The use of a thread local allocator context stack solves those problems.

163
Ideas / Re: Basic list of improvement points
« on: October 28, 2018, 05:46:05 PM »
Some more points (to be evaluated)

- Zero cost errors - discussion needed
- Nested comments - proposal exists (probably wait for a new lexer to do this)
- Extended switch/case - proposal exists
- C11 _Generic (type-switched macros)
- Real arrays (fat pointers) - discussion needed
- Expression blocks (like gcc's ({ }) expression statements) - proposal exists
- Defer - proposal exists
- Allocator push/pop functionality - discussion needed
- Ergonomics: native string, growable array, map - discussion needed
- Return native register size (32 / 64?) - discussion needed
- Custom C2 Lexer

164
General Discussion / Re: State of progress?
« on: October 28, 2018, 04:02:06 PM »
I imagine something along these lines: https://github.com/lerno/c2compiler/blob/lexer_example/c2c/Parser/Lexer.cpp

I realize that this one was very much like your TOML lexer.

165
Ideas / One more big thing: Errors
« on: October 28, 2018, 01:06:13 PM »
With Rust, Go and Zig we have alternatives to try-catch that makes sense in a low level language.

This approach is the one I think has the most things going for it:

http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2289.pdf

Truly zero overhead error handling.

Pages: 1 ... 9 10 [11] 12 13 ... 17