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 ... 13 14 [15] 16 17
Ideas / Re: Macros again
« on: October 22, 2018, 09:22:08 PM »
If you look at Ruby, that uses blocks extensively, you need to be rather ad hoc in your implementation if you want to make it useful with closures.

Consider the following Ruby code:

Code: [Select]
def x(foo)
  3.times do |x|
    return x if foo[x] == x
  return -1

def y(foo)
  l = ->(x) { return x if foo[x] == x }
  return -1

In this case x([2, 2, 2]) would return 2 and y([2, 2, 2]) returns -1.

So even though the "times" method is defined using a lambda, there is special handling (return exits the outer scope) if the block is defined inline.

The closure/first class function version in y cannot express the same semantics. And usually "escaping the outer scope" is exactly what's so useful in many cases! This is also separate from the added complexity of optimizing away the lambdas, which is necessary for good performance.

Ideas / Re: char / int / i8 / i32 and so on
« on: October 22, 2018, 08:58:23 PM »
I was assuming C2 would use something like c_int, c_short, c_unsigned_short etc för C?

Also, since the bit widths would be same or equal to width of the corresponding values it should not run into issues.

So my argument is roughly:

  • If one supports more than 8/16/32/64/128 widths, sure use i / u prefix. It's fine.
  • If we only support 8/16/32/64/128 we're fine with a name that in C corresponds to a type with at least the given width

If we look at the types we have from c:

unsigned char = u8
signed char = i8
char = u8 OR i8 depending on architecture
short, short int = i16
int = min i16, i16 Win16, i32 on Win32, Win64, Unix, i64 on legacy Unix 64 bit
long, long int = i32 on Win16/32/64/ARM, i64 on Unix
long long = i64 on all platforms

Sorting away legacy systems we basically have:

char = u8 or i8
short = i16
int = i32
long = i32 or i64
long long = i64

The only place where we run into problems with assuming a bit width is "long" here. But since the proposal of long = i64 it's still at least the min size encountered *and* replaces long long for the most cases. "long" is a rather unnecessary type in C for most platforms today.

If char is kept then we have to decide on signed or unsigned anyway. Plus *java* has already standardized on these sizes with these names, as have C#.

If we want stuff like i24, i48 etc then sure, the bit size makes a lot of sense.

Ideas / Re: Macros again
« on: October 22, 2018, 07:01:21 PM »
Sorry, that was just an error in my example  :-[ d should be passed in as well of course!

Ideas / Re: Add defer
« on: October 22, 2018, 05:42:38 PM »
I'm thinking quite straightforward:

Code: [Select]
File *file = open("foo.txt");
if (!file) return false;
defer {
if (something) return false;
return true;

This is transformed to the following code (can be done when parsing):

Code: [Select]
File *file = open("foo.txt");
if (!file) return false;
if (something)
  return false;
return true;

(It can also be handled with a jump obviously)

I would suggest the following:

  • It's possible to use a plain return within a defer, it only exits the defer early (the defer acts as an anonymous function / closure).
  • It's possible to stack defers

One thing worth discussing is what happens when you actually put a defer in a block.

Consider this:

Code: [Select]
defer { ... } // A
    defer { ... } // B
} // C
return; // D

In this case it's unclear whether the best thing would be defer B to act on exit of the block (at point C) or (point D, before defer at A is executed.

But if we pick point C (which would be nice), we have some choices to make. For example, when is the defer called here:

Code: [Select]
if (some_condition) defer { .... }; // A
if (some_condition) {
   defer { .... }; // B
if (some_condition) {
   defer { .... }; // C
   x = y;
} // D

return; // E

One probably wants the defer of both A and B to occur at E, but the defer at C is probably intended for D.

Go makes the decision to use E for all defers, while Swift would allow execute A and B directly, just like defer at C.

Zig follows Swift and adds an "errdefer" as a special case defer on error exit.

Ideas / Re: Preprocessor and Macros
« on: October 22, 2018, 05:08:38 PM »
I agree that it is a mess. For a meta language I'd consider something very simple that could easily be statically analysed. That's why I don't suggest going the Zig route. For both code reading and code parsing we always want to read as few lines as possible to understand the execution context, compile time execution makes that much harder.

Ideas / Re: Readability of keywords "uint32", "float64" etc
« on: October 22, 2018, 05:04:33 PM »
I would use it exclusively for loops BTW, for any data structure I'd use explicit sizes.

I did a little research and seems like the question is a bit deeper than I first thought. int/i32/i64 will affect register allocation, so performance depends a bit on the processor.

Something "good to have" out of the box is getting information form the compiler directly into the macro system to know things like data width, # and types of registers available, endianness and so on.

Ideas / Add defer
« on: October 22, 2018, 10:16:33 AM »
I suggest adding defer statements to C2. In the vein of the same feature in Zig, Jai, Go etc

General Discussion / How should I set up the project for dev?
« on: October 21, 2018, 03:49:29 PM »
I like to set up things in an IDE for nice code reading. Are there any instructions (or ideas) how to set it up in a way that could work on Mac OS X (in XCode / AppCode) without actually pulling the Clang / LLVM sources in? (Needless to say, the size of those will break most AST building IDEs)

Implementation Details / Why do we need Clang and LLVM?
« on: October 21, 2018, 12:28:43 AM »
LLVM and Clang are huge codebases that takes forever to compile whenever you want to play around with C2. That is bad.
LLVM offers excellent optimizations. That is good.

Here is an idea:

- For debug builds, initially use TinyCC (Tiny C Compiler)
- For optimized builds, use LLVM / Clang (and possibly later LLVM IR directly)

TinyCC is... tiny compared to LLVM. It's also very, very fast. TinyCC is also built to be fed C code directly.

Ideas / Macros again
« on: October 20, 2018, 07:14:06 PM »
I just wanted to discuss some possible ways of doing it that I was playing with.

First, we have to consider macros as always expanding where they are referenced to keep it simple.

Code: [Select]
macro @foo(int v) {
  if (v > 10) return 10;
  return v;
int a = 10;

This code would then be exactly equal to:

Code: [Select]
int a = 10;
if (a > 10) return 10;

Macros simply expand in place.

Secondly, we can have macros returning values:

Code: [Select]
macro int @foo2(int v, int w) {
  if (v > 10) return 10;
  w += 3;
  return 0;
d = 0;
a = 10;
int b = @foo(a, d);

This expands to:
a = 10
int b;
if (a > 10) {
  b = 10;
} else {
  b = 0;
  d += 3;

Note that I'm using a sigil to indicate the code expansion. I think this is a good idea, even if @ collides with current C2 use and we need to pick another sigil. It makes it very obvious that macro expansion occurs.

We can allow the macro to take a body (yeah, I'm calling the type of the body "{}" it should be something better obviously!):

Code: [Select]
macro int @foo3(int a, {} body) {
   while (a > 10) {

b = 20;
@foo3(b) {

We expand this to:
Code: [Select]
b = 20;
while (b > 10) {

Ideas / char / int / i8 / i32 and so on
« on: October 20, 2018, 06:59:46 PM »
I talked a bit about readability for int/floats in another thread. Reading through the sample code, I notice that "char" is actually there as i8. Can we perhaps take a page from java and introduce named types with fixed sizes?

bool = u1
byte = u8
char = i8
short = i16
ushort = u16
int = i32
uint = u32
long = i64
ulong = u64
float = f32
double = f64

I suggest that the bit-named types still remain (so char is alias to i8, it doesn't replace it). This allows us to postpone naming of f128 and allows easy insert of middle sized types like i24 if that ever would be interesting.

I'm not entirely sure that this is a good idea, but I think that either keep it the bits explicit for everything (including char) or use well defined aliases for all basic types.

Ideas / Re: Preprocessor and Macros
« on: October 20, 2018, 05:07:45 PM »
I have been thinking about this a bit. I actually prefer a meta language rather than using the same language to express more complex macros. Compare Jai, Zig and some others that use compile time execution of the normal code. I find this both hard to read and problematic for static analysis. Also, it would tempt overuse quite a bit.

In a simple meta lang we would need:

  • To loop over a range or a statically declared set (often some enums)
  • Access some information about the stack
  • Parameterize code with different (a) type, (b) struct member accessed (c) function called.
  • conditionals (if/else) with simple integer values.
  • string generation (basically a static printf)

Any suggestions?

Ideas / Restricted pointers (and the analogue with threads)
« on: October 20, 2018, 02:42:27 AM »
In C we can now use "restricted" to declare that a pointer cannot be aliased. I will try to argue that the "restricted" should be default and you should need to explicitly have to declare a pointer as not restricted.

My experience after both writing correct threaded code and fixing incorrectly written threaded code is that attempts to create an illusion of "this is now thread safe" without having to actually understand threading is doomed. Well meant things like the keyword "synchronized" or so-called "thread safe" libraries (that actually still required you to understand threading to use it properly in a multi-threaded environment) are increasingly understood to be a bad thing. These are leaky abstractions that often just makes things worse (creating deadlocks etc)

I suggest a similar approach for C2. Basically say that "in C2 all pointers are considered restricted, so if you want to tell ensure the data is always loaded from memory, you need to explicitly mark it so". This is, incidentally, pretty similar to how volatile works, so the same keyword could actually be reused.

Ideas / Re: Readability of keywords "uint32", "float64" etc
« on: October 20, 2018, 01:41:03 AM »
Hmm maybe. It sort of depends on how the macro system looks later on.

Ideas / Allocators and context
« on: October 19, 2018, 02:19:33 PM »
As is possibly well known, Jai uses a "context" that can be pushed / popped.

If the standard malloc and libraries then uses the context we gain some very powerful abilities.

For example, consider handling a web request (I'm going to use C syntax here)

Code: [Select]
int process_request(Request *request)
{ ... }

int handle_request(Request *request)
    Context *context = push_context();
    BumpAllocator allocator = BumpAllocator.create(MAX_MEM);
    context.allocator = allocator;
    int err = process_request(request);
    return err;

That is we emulate the php memory allocator. :D

Pages: 1 ... 13 14 [15] 16 17