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.


Topics - bas

Pages: [1] 2 3
1
Ideas / Macro-system design
« on: January 30, 2019, 09:17:13 AM »
Since C2 wants to avoid the pitfalls of the C textual-replacement based macro system (and C2 doesn't have includes), a replacement macro system is needed.
Macros have a various, valid use-cases. I'll focus on one case here and try to find a nice syntax + semantics.

What I want is to be able to put a loop over a data structure in a macro, so that changing the underlying data structure maybe doesn't need any 'api' change
for users. So something like

(in some function)
Code: [Select]
   // there is a list.List* mylist that points to the list (let's say a linked-list)
   list.foreach(mylist, i) {     // i is the name of the Element*
      // use Element* i here
      if (i.value == 10) break;
  }

So:
  • list.foreach is the macro
  • When changing the data structure from a linked list to a vector, nothing much has to change in the fragment.
  • The break statement should break out of the macro loop even if  the macro itself has multiple, nested loops.
Note that this is generally not possible in C, since the macro probably needs to add code before and after the user code inside the loop.

The definition could be roughly something like:
Code: [Select]
public macro foreach(List* thelist, iname) {
   Element* iname = thelist.first;
   while (iname != nil) {
       // macro body should be here, How to specify?
      iname = iname.next;
   }
}
So this brings up a lot of questions:
  • how to let a user specify the name of the iterator?
  • Do we required types for macro arguments?
  • How can we specify the place for the macro body?
  • Do we allow using variables from the calling scope (and how to specify)
  • How can we implement the break in user code
  • Do we allow nested macro calling?

For C macro's all these questions are easy, since it's just replacing text. But that gives rise to a lot of issues, so a new system
is definitely needed. But I think a non-textual system will have a lot more restrictions (which can be good), so when designing a
new system, the goal should not be to find a replacement for every possibility in C macros. This won't work. My plan is to
start creating a set of use-cases and build on top of that. The foreach example is just one of those use-cases.

2
Ideas / Basic list of improvement points
« on: October 26, 2018, 09:44:15 AM »
There have been some really good ideas and improvement points on this forum. The problem is that there are so many.
I would hate to miss or lose some ideas simply because there is no way to implement them all at once. So I'm looking at
some way to organize this.

More matured languages (Python for example) seem to have formalized this process by using PIPs (Python Improvement Proposals).
That is very nice for gradual improvements, but not handy when the base is not complete yet. Maybe we can have a list of items here,
that point to various other posts on the forum, along with their status. I'll update the list (and maybe prioritize a bit).

3
Implementation Details / Rebased on clang-60
« on: March 28, 2018, 08:43:13 PM »
C2C is now based on LLVM/Clang 6.0. The port was rather easy and took 20 minutes (excluding time to build LLVM/Clang).
The only nasty thing was a shift in arguments in include/clang/Basic/DiagnosticSemaKinds.def. This gives runtime asserts,
but luckily one of the unit tests broke, so it was very easy to detect and locate.

The performance seems to be the same and I've noticed no other differences. The clang binary does seem to have grown
another 5MB (87 -> 92 MB), all nice features I'm sure ;)

4
Implementation Details / cross-compilation using build file
« on: March 22, 2018, 10:27:54 PM »
I've recently added the concept of a build file to C2. I would like to explain some design choices here.

The C2 compiler used to use a single configuration file called the recipe. The recipe describes:
  • which targets and type (executable or library
  • which source files for each target
  • the configuration options
  • some output options (whether to generate-c, deps, refs, etc)
  • which libraries the targets depend on
So the recipe only specifies which dependencies, not where to find them. Because C2 is also meant
for embedded systems where cross-compilation is a basic need, it should be easy to do. To handle
this, there now is a build file that specifies:
  • The target triple (eg arm-unknown-linux-gnueabi or x86_64-unknown-linux-gnu)
  • A list of library search directories
  • toolchain settings for the C-backend
When using a build file, C2C will only use paths from that build file. This makes it trivial
to isolate cross-builds between different targets. No need for sysroot like constructs.

The general idea is that the recipe file is written by the develops of the project. If the project has a lot
of configuration options (which could change the files that need to be compiled), a 'make menuconfig'
like tool could generate this.

The build file is either written by users or generated by a 3rd party build system like yocto/buildroot etc
The same recipe could be used to build for different targets by using a different build file for each.

To demonstrate this, the C2-examples archive has a working example for Ubuntu 17.04. See the README.md
for instructions.

Future work
Currently C2 uses a <triple> subdirectory in each library directory, so it can hold libraries for multiple targets.
This is handy for now, but in a real system, an (external) build system will already have populated a sysroot-like
directory with all required libs. So this directory will be removed in the future.

Another step will be to add a Cargo-like (from Rust) tool that reads the recipe, downloads all required libraries and
generates the build file.

Cross-compilation for ansi-C is quire a hassle. Hopefully C2 can make this a lot easier.
For more documentation see http://c2lang.org/site/build_system/intro/

As always, other ideas/feedback is welcome.

5
Ideas / Struct Function declaration
« on: April 15, 2017, 09:22:04 PM »
With the current syntax, I think it's quite unclear when a function is a struct function. I would
like to try to make this more explicit. So hereby some ideas.

Code: [Select]
func void point_add(Point* p, int32 x) { .. }    // current syntax

func void Point_add(Point* p, int32 x) { .. }   // allow Upper-case as first char, so looks more like type Point

func void point_add[Point](Point* p, int32 x) { ..} /// hmm, 3 points in a row..no

func void point_add(Point* p, int32 x) @{structfunc} { ..} // via attribute
func void point_add(Point* p, int32 x) @{struct='Point'} { ..} // via attribute, nr 2

func void Point.add(Point* p, int32 x) { .. }   // most explicit, via member operator

..

I like the last option best. The two arguments against it are:
  • it's harder to see what the c- function name is (point_add in all cases). The is actually currently
        the same for module attributes as well (Foo.bar() -> foo_bar)
  • The syntax breaks more with the ansiC style

What do you think?

6
Ideas / Struct Functions - part 2
« on: November 28, 2016, 09:08:33 AM »
I've been implementing some code using struct functions and must say I'm completely happy with the calling side. On the receiving side (the function itself), a patterns is appearing that might be avoided. Currently struct functions look like this:

Code: [Select]
func void myStruct_init(MyStruct* this) {
   this.a = 1;
   this.b = 2;
   // ..
}

This is just a small piece of code, but the thing that starts to itch in bigger functions is the frequent appearance of the this.
To make it feel more like (for example) a C++ class, we could add the struct members in the function namespace. Then all the
this keywords could go (unless ambiguous). This would have some consequences:

  • name duplicates between module and struct members is allowed (since can distinguish with module.a and this.a)
  • name duplication between struct members and function arguments is not allowed (cannot distinguish somehow)

I don't think since is a problem, since it's not a problem in object-oriented languages as well. Any feedback is welcome,

Cheers,
Bas

7
General Discussion / Light-weight classes
« on: April 28, 2016, 09:38:34 PM »
There was recently a discussion on Embedded.com (http://www.embedded.com/electronics-blogs/break-points/4441819/Fixing-C).
On of the often heard ideas was C++, but without all the fluff. But something like lightweight classes.

C++ has (multiple)inheritance, no real interfaces (like Java), operator overloading, method overloading etc etc
If you just want the basics, eg a struct with functions, that should be possible to create. It's just syntactic sugar,
s
Code: [Select]
func void MyStruct::myfunction() {..}
just becomes:
Code: [Select]
func void myfunction(MyStruct* A) {..}

Of course, when you also want interfaces, it becomes harder.
And when you also want to use the same function name with different parameter types, it again becomes harder..

My conclusion is that any 'lightweight' class construction will quickly lead to a lot of fluff, because people except
all those features in languages with classes. So it might just be easier to just leave them out altogether...

8
General Discussion / Pthreads
« on: November 20, 2015, 09:49:30 PM »
Now that library support is in place, I've been trying out pthreads. This is quite a weird library it seems.
On OS X, compilation doesn't need a special linker flag. If you pass '-pthread', clang gives a warning about
'unused flag'. On linux, clang needs '-pthread' to build, otherwise you get an 'undefined reference to..' warning.

Nasty..

9
Ideas / Enum classes
« on: July 20, 2015, 04:43:25 PM »
Recently C++11 introduced the concept of enum classes. This puts the enum constants
in their own enum's namespace. So:

Code: [Select]
enum class State {
   START,
   MIDDLE,
   END
};

This allows the compiler to detect clashes between different enums and some other tricks. A second advantage
is avoiding name clashes between the MIDDLE of one enum type with MIDDLE of the second enum type.

In C2 there are already modules to avoid clashes, but currently the enum constants (like START, MIDDLE, etc)
end up in that modules namespace. I'm toying with the idea to always put enum constants in their own namespace.
The declaration will still look exactly the same, but the usage now uses an enum namespace, like

Code: [Select]
module test;

type State enum int32 {
  Start,
  Middle,
  End
}

func void foo() {
   State s1 = Start;               // current situation (full: test.Start)
   State s2 = State::Start;     // option 1 (full: test.State::Start)
   State s3 = State.Start;      // option 2 (full: test.State.Start)

}

Note that the module member operator is already a dot (.). I prefer test.State::Start over
test.State.Start, since it distinguishes the enum namespace and a.b is always either a module
or struct member. Once again I look for the experience and wisdom of the forum members :)

10
General Discussion / Modules
« on: May 04, 2015, 11:41:09 AM »
The default C/C++ language communities are gaining momentum to fix the Header file problem.
Here is another interesting proposal for Modules in C++:

http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2015/n4465.pdf

What I noticed was that they also state macro's as a big problem and also use the word Evolution.
So more indications we're moving in the right direction  :D

There are some differences howevery:
  • In C2, you can choose which declarations to export outside the source (in the recipe file)
  • The paper exports all symbols put in the module, C2 distinguishes between public in a Module and exporting from the compilation unit
  • The paper proposal sub-modules, while C2 only allows a single layer to avoid having to mangle the symbols
  • In C2 you only type-once, so no forward decls, etc. The paper requires exporting the symbol, separate from defining it.

11
General Discussion / Another programming language initiative
« on: April 19, 2015, 09:11:48 PM »
I recently came across a lot of videos by Jonathan Blow, a game designer/developer who's
apparantly designing a language specifically for game programming. It seems to be called Jai.

if you have time (20+ hours on video... 8) )
https://www.youtube.com/user/jblow888/videos

12
Ideas / one big design item: Macros
« on: April 11, 2015, 10:27:17 PM »
One of the big remaining design issues (next to countless smaller ones  :) ) is the design
of a new macro system that is not based on textual substitution by the preprocessor.

Let's start by looking at the goal of macros:
  • Feature selection
Code: [Select]
#ifdef HAVE_FOO_FEATURE
..
#endif
  • Constants
Code: [Select]
#define MAX_ITEMS 10
  • code expansion
Code: [Select]
#define print_if_positive(x) \
   if (x > 0) printf("value is %d\n", x);

I think each of these is a valid goal. So the new macro system should provide
a solution for each (in some way or another). For the Feature selection, C2 can
use the same way as C; since the C2 compiler also has a preprocessor, it's completely
identical.

The Constants goals is attained in C2 by using const of Numeric types:
Code: [Select]
const int32 MAX_ITEMS = 10;This will just 'replace' all references with 10.

The Code-expansion is the hardest. Since there is no textual replacement, the macro system
has to be language aware. This means that when parsing the macro definition, the parser must
understand what's happening. This results in 2 types of macros: local and non-local. Local macros
can be used inside functions, while non-local macros can only be used outside function
bodies. So local macros are parsed as a series of Statements, while non-local macros are parsed
as a list of global declarations.

The syntax I currently think of is:
Code: [Select]
local macro(x) {
   io.printf("value of "$$x" = %d", x);
}
macro(x) {
func gen_$x() {
}

Open issues:
  • Q: are public macros in module X allowed to access non-public Decls in X?
  • Q: what to allow as macro arguments?
  • Q: what syntax to use for argument replacement, concatenation and stringify?


13
Implementation Details / Milestone reached!
« on: March 06, 2015, 10:26:41 PM »
C2 just reached another milestone: completely building a (static) library and genarting the c header file!   ;D
The library itself is created by generating c-files and a makefile and then running the makefile. This is of course not the final path, but will definately help in fleshing out more details. Also I extended the unit-test framework to test generated C files easily.

One detail that came up was the name 'mangling'. The generated names of module.decl used to be __module_decl. But the linker couldn't find these in the static library. So the mangling now just consists of module_decl, also a bit easier on the eyes.

The next step will be playing with the options to control object visibility.

14
Ideas / Array types
« on: January 20, 2015, 09:29:42 AM »
I recently read
http://www.drdobbs.com/architecture-and-design/cs-biggest-mistake/228701625
where the author states that the (one of) the biggest mistakes made in C was
converting an array to a pointer when calling a function, so (C-code)
Code: [Select]
int array[4];
int* ip = &array[0];
myfunction(array);
myfunction(ip);
the two calls are equal. One idea would be to make this distinction. One limit would be
that the array would be a known size (in function prototype), eg (C2 code)
Code: [Select]
func void setMacnr(char[4] macnr) { .. }
A function that would take a variable sized array (like string functions) would still require a pointer and a size
argument.


15
Implementation Details / BitOffsets
« on: December 04, 2014, 08:11:44 PM »
Hi,

I've just pushed a new feature called BitOffsets. This allows easy copying of certain bit-ranges, like often
done in drivers. So if your chip's reference guide says:   Counter Bit 14-10, you used to do (in Plain-Old-C)

Code: [Select]
int counter = ((value >> 10) & 0x1F);
Which is not too readable. Off course you could use a fancy macro like REG_BITS(value, 10, 0x1F),
but that's not really nice either.

Because C2 targets kernels, bootloaders and other low-level programs, accessing register values should
be easy. Enters BitOffsets:

Code: [Select]
int counter = value[14:10];
Which does the same as the example above. Additionally it can check possible overflows (like assigned a 8+ width
value to a char etc)

Currently BitOffsets can only be used in RHS (Right-hand side) expressions. I'm still debating whether
to also allow this on LHS expressions.

Pages: [1] 2 3