Warning
Maybe not useful for you, just some design ideas.
But, if you throw this doc to LLM, let LLM refine it to spec, and use LLM as your cwte, you have cwte right now.
"I'm a lazy dev, and I used :< sad face to mark the code that might fail, as my assistant, you should implement the :< mark as error handling logic for these code".
Or just fork this repo, and implement yourself, cwte and :< shouldn't be my own patent, and I have no time on it, I just want to have an ice cream.
And, the real cwte-generator will just be a dev-stage-only code generator for ruri. It will not act on other unnecessary features I will not use it in my code.
Warning
Cwte should and will always be a tail, I'm the developer of ruri, not a rust developer.
It should be stable and simple, even not perfect at all, and with many bullshit hacks.
Don't ask me why a f**king source-to-source code generator use so much kernel features, I had tons of syscalls in ruri, they are not ice cream at all.
As I didn't learned rust at all, so the code will be mostly generated by LLM, I'm only the designer of cwte syntax and project structure.
As cwte can bomb everything, it should be extremely careful to use.
We trust you have received the usual lecture from cwte project.
It usually boils down to these three things:
#1) The tail should never wag the cat.
#2) Your cat's tail can also make you copy-fail.
#3) Everything will become a fossil, nothing's absolutely evolved.
0.1.0, only general ::} implemented, and it's just a readable todo note, not a real code generator.
Cwte tail at ./test.ce line 10:
>>
>> t() ::};
>>
::} Here's a nautilus, have an ice cream and write a fix,
and don't left it to be a fossil QwQ
Cwte processing completed. Output written to ./test.ce.c
And I had a special rule for seccomp.c in ruri, so you can already see cwte in ruri.
Anyway I've successfully made a 1853->1210 line code reduction in seccomp.c, 34.7% code reduction, that's a good start.
"Let's zip the tail, and now we have a fuwafuwa cat".
Cwte (cute) stands for "C with tailed error-handler/Cute way to handle error/Cry to error/C-Way-To-Evolve (Oh no, never let tail wag cat)", it's a cute and concise error handling extension for C, with zero syntax breaking, and the tail will never wag the cat.
Just a cute error handling extension for C.
With no other syntax breaking, and the tail will never wag the cat.
We will just have a new sad face :< for error handling, and #[[ce_foo()]] for code generation.
These syntax will be translated to C code, you can use cwte for error handling, cwte-generator transform it to C, and you compile/run/debug the generated C code.
In short, cwte is just for zipping complex unhappy path logic, and make it more readable.
I just hope it can save some time, so I can have an ice cream.
:< Is the only core feature, it's a tail after func call, for error handling.
The tail should never wag the cat, this means sad path handler should never pollute the core logic, and cwte will also never pollute other c code.
The tail should never wag the cat also means tail command should not call |cat :D
- Cwte has no super cow powers.
- Cwte is a postfix, a tail, but not the cat (C-lang).
- The tail can/should/will/must never wag the cat.
- Cwte should be reversible, if you don't like, thow it away and rollback to c.
- We are c users, not cwte users.
- Cwte is dangerous, the tail can make everything cooked, so:
- Always check the generated code.
- Always make a backup to last working code.
- Always backup cwte itself.
- The longer tail you make, the less ice cream you have.
- Always check where's your cat.
- If cwte wrote the wrong code, the dev should scream.
- Always be ready to fire cwte, and make a LLM code regen instead.
- Cwte should NEVER be a compile-time dependency for released code.
- Never assume anything, your cat's tail can also make you copy-fail.
A cwte project should be like:
project
├── src // For C code, the ONLY code for testing and publishing.
│ └── foo.c // The ONLY code as true source code, for testing and publishing.
└── srce // For cwte code, only TEMPORARY code for developing.
├── foo.ce // Cwte code. For reading and developing.
└── foo.hce // Cwte definition, for registering func type and handler.
In ruri:
res = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(accept), 0);
ruri_check_seccomp_ret(res, container->no_warnings);
res = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(accept4), 0);
ruri_check_seccomp_ret(res, container->no_warnings);
res = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(access), 0);
ruri_check_seccomp_ret(res, container->no_warnings);Too ugly you see? I scream, eye scream, my small screen scream, my ADHD scream, my LLM scream, all scream.
seccomp_rule_add() uses va_args, so if you don't use these complex code, you can only use a macro. But in cross-arch CI, it will bomb to TLE, as the pre-compile expansion performance of macro is not good, and qemu is slow.
So, I want a:
#[[ce_reg(seccomp_rule_add, int, _<0)]]Then:
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(accept), 0) :<;
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(accept4), 0) :<;
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(access), 0) :<;Now we have ice cream, but no more I scream.
Dev happy, readers happy, PRs happy, LLMs happy (with prompt), all happy.
It will also be very useful in educational case, as you can use a :< to tell people "you should handle this error, but it's not the core logic for our code", and your example code will be more concise and readable.
And the above code will be auto expanded to code like this:
if(seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(accept), 0) != 0) {
warning("seccomp_rule_add", __FILE__, __LINE__, res, errno);
}So that's cwte, a cute tail.
The tail will never wag the cat.
So cwte will never break c syntax, except the old :> as ] design.
But as cwte will translate .ce to c, and if you only use :> as happy face in .ce, that's fine.
In one word, cwte makes a zipped error handling in C, and it's kawaii.
- Cute and readable: it's like a sad face.
- Zero syntax breaking: :< never affects C grammar.
- Explicit invalid-stat: it's illegal, if you leave a
:<,:>or]after a function, your compiler will definitely scream. - Enforced pre-compile code generation and error handling:
:<is not a todo comment, but it's easier to be done. With LLM or cwte.
.hce stands for happy c ending/handle c error, it's just a kv-map to register error expr and handler for funcs. maybe we can also have standard hce conf like posix.hce.
// Register function type and failure condition
#[[ce_reg(func, type, exp)]]
// For example:
#[[ce_reg(open, int, _<0)]]
// Register function's panic and default handlers
#[[ce_pan(func, panic)]]
#[[ce_dft(func, def)]]
// For example:
#[[ce_pan(open, panic)]]
#[[ce_dft(open, log)]].hce should only contain the three simple commands, and other definations, like #define panic(), #define log(), and typedef should be in .ce or your .h, as .hce is just happy c ending/handle c error delclaration file.
"Ohhhhh, memfd, silver bullet for saving data and IPC, so cool, so leeme cook."
cwte generator will be a fully memfd-based immutable artifact pipeline (so fd pipeline is also fp) design, we use memfd to save each layer, and make it immutable to the next layer, and each layer will only act on one feature, without other side effects to the generated code.
As the performance is always the tail, we should never let the tail wag the cat, so we can have a clear and trackable code generation process, and it's also easy to debug.
No Zero-Copy, no Copy-On-Write, no in-place modification, just a simple and clear pipeline. Stable and simple is the only goal.
We will use linux fd graph to implement:
- Layered structure: each layer will read from the last layer's memfd, and write to a new memfd, and pass it to the next layer.
- Immutability: each memfd will be read-only after it's written, this is enforced by the kernel but not my poor and stupid math knowledge.
- Side-effect ultra free: you cannot write a ro memfd anyway, as your kernel will definitely scream.
- Traceability: you can always check the content of each memfd, and see how the code evolves step by step.
- Observability: when panic, just dump the fd graph in shell, and you can see what's the last layer that caused the problem.
In security model:
- Least privilege: each layer can only have one lower layer to read, and one upper layer to write.
- Zero trust: we never assume any of the layers is correct, so we make the chain fully auditable and traceable.
Rust users unhappy, fp users unhappy, linux users unhappy, but me happy, so leeme cook, with the 1980s style.
Warning: draft only, never assume anything, and never trust the tail.
// Will call panic() if open returns < 0
int fd = open("file.txt", O_RDONLY) :<;
// Will call panic if open returns < 0,
// and call log() if open returns >= 0
int fd_2 = open("file2.txt", O_RDONLY) :<, :>;
// Will call user defined panic and log logic.
int fd_3 = open("file3.txt", O_RDONLY) :<
{
printf("Panic in open with file3.txt\n");
exit(1);
}
:>
{
printf("Log in open with file3.txt\n");
}
// Will call user defined panic logic, and default to log if not panic.
int fd_4 = open("file4.txt", O_RDONLY) :<
{
printf("Panic in open with file4.txt\n");
exit(1);
}
:>;
// Just add a default log handler for open, will be triggered even fail.
int fd_5 = open("file5.txt", O_RDONLY) :>;cwte just implements :< and :>, and #[[ce_foo()]], the rest is just C code, and every cwte feature will be translated to C code, You debug/run the generated C code, not the cwte code.
#[[ce_reg()]] is enforced, or ce will not know how to handle the error.
And, there will be many ubs, so always do a diff-check between .ce and .c, and make sure the generated code is what you want.
You can use _CE_DFT for :> and _CE_PAN for :<, just recover with one sed, so your IDE and clang-format will not scream at it. But for foo() :<, :>, your IDE will scream anyway, although these code are less in real-world case.
Cwte should be used step-by-step, and always check the generated code to make sure it's what you want. If it will be more ugly, immediately make a ctrl-z in your ide and rollback to the c way, we should never let the tail wag the cat.
cwte will use line-no for internal variable name, so you will match generated code with .ce easily.
Maybe we can have a #[[ce_enforce(func)]] to enforce you catch result for func in cwte, and :D for ignoring the error, and :o for only log when error, :~ { ... } for a custom handler, and even ::} to output a nautilus in cwte, and use ::} as a readable todo note.
Maybe one day it can be C-Way-To-Evolve, but at least these ideas shows that c is extensible, and cwte is also.
Cwte never assumes it won't become a fossil.
"But if we have to evolve, is there a trackable way?"
_-''''-._
/` `.
/ .'~~~, \
| / \ |
| | :>.,/ |
\ '. ,___/~~~
`. '-----`' /~~~~~~
`. /~~~~~~~~
'-.____. /~~~~~~~~~~~
We trust you have received the usual lecture from cwte project.
It usually boils down to these three things:
#1) The tail should never wag the cat.
#2) Your cat's tail can also make you copy-fail.
#3) Everything will become a fossil, nothing's absolutely evolved.
柔らかな皮膚しかない理由は
人が人の傷みを聴くためだ
急げ悲しみ、翼に変われ
急げ傷跡、羅針盤になれ
まだ飛べない雛たちみたいに