thradams / cake Goto Github PK
View Code? Open in Web Editor NEWCake a C23 front end and transpiler written in C
Home Page: http://thradams.com/cake/index.html
License: GNU General Public License v3.0
Cake a C23 front end and transpiler written in C
Home Page: http://thradams.com/cake/index.html
License: GNU General Public License v3.0
Problem with empty line and
#define M \
\
B
Wrong error report here
void F() {
char (*s)[10];
*s + 1;
}
error: left operator is not scalar
The line number showed on errors are wrong by 1 in the web version and correct when real files are used.
The web version use a string with the source and the problem can be there, need to investigate.
The preprocessing emulation seems to get stuck on a first call to a macro:
typedef typeof(nullptr) nullptr_t;
#define giveit(X) _Generic(0UL, unsigned long: 1, void*: 2, nullptr_t: 2, default: 0)
#pragma expand giveit
int main(void)
{
int a0 = giveit(0UL);
int b0 = giveit(nullptr);
int c0 = giveit((void*)0);
int d0 = giveit(0);
}
Here the preprocessing output that is generated is
typedef typeof(nullptr) nullptr_t;
int main(void)
{
int a0 = _Generic(0UL,unsignedlong:1,void*:2,nullptr_t:2,default:0);
int b0 = _Generic(0UL,unsignedlong:1,void*:2,nullptr_t:2,default:0);
int c0 = _Generic(0UL,unsignedlong:1,void*:2,nullptr_t:2,default:0);
int d0 = _Generic(0UL,unsignedlong:1,void*:2,nullptr_t:2,default:0);
}
which just seems to reuse the argument for the first call to following calls.
(also the fact that this output has not enough space characters is a bit disturbing.)
With this sample
int main() {
int a5[5];
auto a[3] = &a5;
}
GCC emits
error: 'auto' requires a plain identifier, possibly with attributes, as declarator
Cake generates (similar what happens with typedefs)
int main()
{
int a5[5];
int (* a[3])[5] = &a5;
}
Also
int main()
{
int * p2;
auto * p2 = p1;
}
GCC
error: 'auto' requires a plain identifier, possibly with attributes, as declarator
Cake generates
int main()
{
int * p1;
auto * p2 = p1;
}
and warning: auto with pointer is UB in C23
Arrays and functions are not converted to pointers in expressions where they should.
A simple example
int main(void)
{
double ar[6];
extern int func(void);
auto a = ar;
auto f = func;
}
Both a
and f
should have pointer type.
I was reading this doc
and found the same problem in cake preprocessor for the following item
"Rescanning replacement list for macros"
#define CAT(a,b) a ## b
#define ECHO(...) __VA_ARGS__
// IMPL1 and IMPL2 are implementation details
#define IMPL1(prefix,value) do_thing_one( prefix, value)
#define IMPL2(prefix,value) do_thing_two( prefix, value)
// MACRO chooses the expansion behavior based on the value passed to macro_switch
#define DO_THING(macro_switch, b) CAT(IMPL, macro_switch) ECHO(( "Hello", b))
DO_THING(1, "World");
// Traditional preprocessor:
// do_thing_one( "Hello", "World");
// Conforming preprocessor:
// IMPL1 ( "Hello","World");
-n Check naming conventions (it is hardcoded for its own naming convention)
Today it is hardcode of it own style.
One of the features I miss the most in C is being able to define for example my own floating point data type and provide its arithmetic operators implementation. There's a proposal in WG14: https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3051.pdf (and I believe the LCC win32 compiler implements that).
But, aside from that proposal, given that Cake translates to C99, I believe the implementation could be quite straightforward, because the generated C99 code would just need to call the function pointers of the operators implementation provided by the user. Then maybe the data storage could be provided by an opaque struct defined by the user and its sizeof() result.
This code reproduces the problem
typedef void (*pf)(void* val);
pf a[10];
typeof(a[0]) b;
This code
void f()
{
const char* s1;
char* s2;
s2 - s1;
}
reports "error: incompatible types +-" that is wrong.
(the problem is caused comparing const x non-const)
Missing type information that a is 3 elements.
int a[] = { 1, 2, 3 };
static_assert(sizeof(a) == sizeof(int) * 3);
Today defer is implemented at code generation with little support from AST.
For instance, a stack of defers is built at code generation. (visit_ctx)
I am considering put this stack at the AST to have better support and to be able to
do static analysis considering the effects of defer happens at end of scope not where
defer is parsed.
To be able to check this effect the parser needs to evaluate defer at the end of scope
during compilation. Having this defer stack at AST may help.
Annotation feature also requires this analysis.
int fname()
{
const char * s = __func__;
_Static_assert(sizeof(__func__) == 6, "");
}
From build.h
it seems you program doesn't support MinGW compiler. MinGW usually used with the GCC compiler, but there are MinGW based LLVM Clang like this too: https://github.com/mstorsjo/llvm-mingw/releases
You only covered MSVC and MSVC based LLVM Clang. You left out the entire MinGW based world.
It is necessary to review semantics of each operator.
I did this for addition-expression only.
int main()
{
char s[2];
_Generic(s,
char *: "char*",
char [2]: "char [2]",
default: "?");
}
It is returning "char [2]" and should return "char*".
The idea is to keep the most complete information always, but we need to
convert to "lower information" when required by the standard.
maybe add something like
struct type type_lvalue_conversion(struct type*);
and call it on _Generic.
typedef const int T;
int main(){
T i;
i = 1; //must be an error
}
This sample shows the problem.
sizeof thinks struct is incomplete.
typedef struct X X;
struct X {
void* data;
};
static_assert(sizeof(X) == 1);
_Pragma is not implemented yet
void f() {
int a[] = { 1, 2 };
*(a + 1) = 1;
}
error: indirection requires pointer operand.
The problem is caused because x.s + 1 is not considered pointer.
Cake is doing static analysis while parsing (for instance ownership checks).
The problem is that it is impossible to handle gotos without the full AST.
We cannot see for instance, what are the scopes we are leaving without having
the goto destination. The we cannot check defers and end-of-life of variables.
To be able to do static analysis of defer, we also may need a secondary phase.
Another of the features I really miss in C is struct introspection (ie: having automatic getters and setters for struct fields accessed by their numerical index), which would open the door to automatic struct serialization and very useful related things.
For example, I imagine something like this:
struct myvector{
double x;
double y;
double z;
};
struct mydata{
uint32_t onevalue;
struct myvector v;
char id[5];
};
struct mydata datavalues={0};
size_t numfields = numfieldsof(struct mydata);
for(size_t f=0; f<numfields; f++) {
fread(&datavalues+offsetof(fieldof(struct mydata, f), sizeof(fieldof(struct mydata, f), 1, fileptr));
};
This would need two new operators: numfieldsof(), and fieldof(), and of course offsetof() should be available.
I also foresee the need of assigning flags or attributes to struct fields, so that the program could have for example an enum for the type of the field (which could be necessary in some kinds of serialization), or a flag for not serializing a field which shouldn't be serialized). Perhaps an extra operator returning the field name as a C string could be nice as well, something that could let you do printf("The name of the third field in struct mydata is: '%s'", fieldident(fieldof(struct mydata, 2)));
Things that might require some additional thought:
Note that nested structs shouldn't require additional thought: My simple example above has a nested struct.
In the middle of some windows header like
C:\Program Files (x86)\Windows Kits\10\include\10.0.19041.0\um\winnt.h:9763:2
We can find a strange character that produces an error:
Opening the file inside VC IDE is possible to see the character.
What is the problem?
This causes a surprise and build error for people trying cake.
(I delete this char myself)
If we repeat a try catch block:
#include <stdio.h>
int main()
{
FILE * f = NULL;
try
{
f = fopen("file.txt", "r");
if (f == NULL) throw;
/success here/
}
catch
{
/some error/
}
if (f)
fclose(f);
try
{
f = fopen("file.txt", "r");
if (f == NULL) throw;
/success here/
}
catch
{
/some error/
}
if (f)
fclose(f);
}
The compiled code, in the second try catch block, use the same label causing a compile error.
void F(int a1[2], int a2[const 2]) {
typeof(a1) b1;
typeof(a2) b2;
}
We need a version .. and -v option.
It will be something like 0.5.
Today #embed expand the sequence
For instance
static const char file_txt[] = {
#embed "stdio.h"
,0
};
becomes
static const char file_txt[] = {
35,112,113, /*... lot more ... */ 10
,0
};
The idea of this feature is add an option to generate a file with "embed_" prefix that is included
resulting in.
static const char file_txt[] = {
#include "embed_stdio.h"
,0
};
that looks much more like the original version.
sizeof/alignof does not affect transpilation but it may trigger some static asserts and it is on the roadmap
of the complete front end compiler.
#include <windows.h>
int a;
is generating
#include <windows.h>
888888888int a;
The following
int main(void)
{
auto a = nullptr;
}
is resolved to long
, even when the target language is C23. In general one of the points of nullptr
is to be able to do _Generic
with it an distinguish macro calls that receive nullptr
from other pointer values.
enum E {A =0, B =1, C=2};
void F(enum E e){}
int main() {
F(1);
F(3);
}
(Enumerators are int in C, this warning will not change this behavior)
For some reason "defined(C)" is disappearing.
#if defined(A)
#if defined(B)
#elif defined(C)
#endif
#endif
Is generating
#if defined(A)
#if defined(B)
#elif
#endif
#endif
I'd expect the following macro to work
#define empty(...) (__VA_OPT__(!)1)
And to result in (1)
if called with no arguments and (!1)
when called with arguments. Instead the expansion is (__VA_OPT__(!)1)
in both cases.
This is a regression. We didn't have test for this.
Lambdas are the most complicated code generation because local declarations have to be exported then renamed locally.
Its the only feature that requires two passes.
I am also considering to remove lambdas to focus on existing C23 features. Like enum and constexpr.
typeof(int [2]) *p1;
int main(){
auto p2 = (typeof(int [2]) *) p1 ;
}
Generating error code at line 3 (line 1 is correct)
int (* p2)[2] = (int[2] *) p1 ;
should be:
int (* p2)[2] = (int (*)[2]) p1 ;
This format could be implemented in cake
https://sarifweb.azurewebsites.net/
At file scope you can have multiple declarations.
typedef int I;
I i;
int i;
int i = 0;
In function scope, params only one. except typedefs.
typedef int I;
typedef int I;
typedef int I;
typedef int I;
I i;
int i;
int i = 0;
int main()
{
typedef int I;
typedef int I;
}
When multiple declaration are accept they must have the same type. This is missing
Qualifiers seem to be treated quite wrongly
int main()
{
double const x = 78.9;
double y = 78.9;
auto q = x;
auto const p = &x;
auto const r = &y;
}
Here this results in q
being const double
, which is wrong because the initializer is undergoing lvalue conversion and looses qualification.
p
and r
are even worse, because the const
doesn't land on the right side of the *
as it should. The const
in an auto
declaration applies to the whole type on the RHS, much as if it where encapsulated in a typedef
.
'''c
static_assert(sizeof("abc") == 4);
static_assert(sizeof("\nabc") == 5);
static_assert(sizeof(L"abc") == 16);
'''
The same function we use must accept function or function pointer
and be able to run this code
int (*(*F1)(void))(int, int*);
int (* F2(void) )(int, int*);
static_assert(_Generic(F1(), int (*)(int, int*) : 1));
static_assert(_Generic(F2(), int (*)(int, int*) : 1));
When building a type data struct we need remove all extra useless parentheses.
Like this etc..
'''c
char (((A));
is the same of
char A;
'''
type for auto should be const char* not array of chars.
int main()
{
auto s = "string";
static_assert(_is_same(const char*, typeof(s)));
}
typedef int A1[2];
typedef A1 *B1 [1];
static_assert(
(typeof(B1)) == (int (* [1]) [2])
);
typedef int A2[2];
typedef A2 *B2 ;
static_assert(
(typeof(B2)) == (int (*) [2])
);
This code
int a[2];
typeof(a) b;
is generating
int a[2];
int[2] b;
instead of
int a[2];
int b[2];
The following
int main(void)
{
auto a = nullptr;
}
is resolved to long
, even when the target language is C23. In general one of the points of nullptr
is to be able to do _Generic
with it an distinguish macro calls that receive nullptr
from other pointer values.
Ensure we have a guaranteed path to free and destroy
We should have a warning here
[[free]] void * malloc(int i){}
void free([[free]] void *p) {}
struct X {
int i;
};
[[free]] struct X* f() {
struct X * p = malloc(1);
struct X * p2;
p2 = p;
return p2; /*p2 is moved*/
}
int f(int runtime_condition) {
struct X * p = f();
if (runtime_condition)
{
free(p); /*not guaranteed path*/
}
}
int main() {}
Error 'i' not found in
void (*f(int i))(void) {
i = 1;
return 0;
}
The problem was the parameters scope was (void) instead of (correct one) (int i).
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.