Warning
Maybe not useful for you, just some design ideas.
This doc is not a spec or a commitment. It requires future refinement and implementation from the author, and all features remain to be determined at later stage.
But, if you throw this doc to LLM, let LLMs refine it to spec, and use LLM as your cwte tail, 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 :> And, the real cwte will just be a dev-stage-only code generator for ruri. Maybe I will not act on other unnecessary features not useful for ruri.
_-''''-._
/` `.
/ .,~~~,. \
| / \ |
| : :>.,/ |
\ \ ,___/:<
". "-----" /::::<
`. /::::::<
'-.____../:::::::::<
"Abstraction turns reality into a black box."
"When the black box springs a leak, out comes Cthulhu."
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 leave it to be a fossil QwQ
Cwte processing completed. Output written to ./test.c
I hope I'm just a cute tail ::::<
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.
Cwte will have two modes, one is gen, means auto-generate or general mode, this mode will use .hce files. And scmp, means seccomp mode in historical design, or strcmp mode in future design, this mode will be a json-drived code generator, and only act for json-specified code replacement.
"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 will 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.
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 can be auto expanded back, with zero-diff:
res = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(accept), 0);
ruri_check_seccomp_ret(res, container->no_warnings);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 call, 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.
:< 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 is never a silver bullet.
- Cwte is a postfix, a tail, but not the cat (C-lang).
- The tail can/should/will/must never wag the cat.
- Cwte is garbage-in-garbage-out, if the given rules are even wrong, cwte cannot be correct.
- Cwte should be reversible, if you don't like, thow it away and rollback to c.
- Human auditor should be ready to scream or even just fire cwte.
- 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.
- Always check where's your cat.
- Always be ready to fire cwte, and make a LLM code regen instead.
- Cwte should NEVER be a compile-time dependency for released code.
(original .c code)
|
v
zip something with `:<`
|
v
(.ce code)
|
v
cwte-generator
|
v
(generated .c code)
|
v
compile/run/debug
For scmp mode, the generated code should be nearly zero-diff with the original code. So that you can always fire cwte and rollback to the original code.
For gen mode, the generated code should be at least fully readable and auditable, and you can always get a diff check for the generated code to see if it's what you want.
You should never use cwte in CI or any production code, as cwte is just a dev-stage-only code generator.
Cwte should always be audited by human, it's just an agent.
In one word, cwte just defers :< from .ce to .c, and anyway :< should be expanded to C code.
Anyway, trust the c code, not the cwte code and cwte-generator.
There's no silver bullet in C programming.
Cwte is never a .unwrap() or even ownership analyzer, as C also has never provided a way to do that.
If you have 15 same open() with same error handling, cwte is good.
If you have just 1 open(), and Ahhh, cute tail, kawaii! we need it!, cwte is not good.
If there's no enough repetition, there's no tail to zip.
If you expect something like:
foo(bar() :<, buz());Then remove your brain. The only way to implement this is to use gnu extension ({ ... }) or even something like in-place ffi, and this is not a good idea. C does not define the order of evaluation of bar() and buz(), that's already a hell, and if bar() allocates a resource, will freeing it in :< conflict with foo()'s logic?
And the generated code will be unreadable and unauditable after that, considering the error handler is a dangerous side-effect, as an accuountable tail, cwte should never try that.
And if you expect complex AST parsing, cwte might not do that. More complex features mean more complex bugs, and you will get many weird bugs if you don't have enough PLT knowledge and a good test coverage.
A suggested 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.
You can use _CE_HAP for :> and _CE_SAD for :<, just recover with one sed, so your IDE and clang-format will not scream at it.
For foo() :<, :>, maybe 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.
.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)]]Note: #[[ce_reg()]] or scmp data is enforced, or ce will not know how to handle the error.
Note: cwte will use line-number for internal variable name, so you can match generated code with .ce easily.
Maybe we can also have a #[[ce_enforce(func)]] to enforce you catch result for func in cwte, and :D for ignoring the error anyway, and :o for only log when error.
.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.
Warning
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.
I didn't learn rust at all, so I also didn't trust thc code I/LLMs wrote. Each layer of cwte should be explicit and traceable, so I can always check the if the gray box give me the right code.
"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, as audit-enforced sandbox, 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.
- Gray-box testing: we will assume every layer is a gray box, so we can just check the input and output of each layer.
Rust users unhappy, fp users unhappy, linux users unhappy, but me happy, so leeme cook, with the 1980s style.
It's memefd in fact, just because I didn't learn rust's immutable data type and debug hooks well
Warning: draft only, never assume anything, and never trust the tail.
Warning: just some ideas, we will only focuse on :< now.
Symbols:
:< _CE_SAD :sad path when error
:> _CE_HAP :happy path when no error
:o _CE_LWE :log when error
::} _CE_NUS :just a todo mark
:D _CE_LAF :ignore error handler forever
:3 _CE_DFM :do that for me, an AI-native mark
In code:
// 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) :>;
// Only trigger log when fail.
int fd_6 = open("file6.txt", O_RDONLY) :o;
// Ignore error handling forever.
int fd_7 = open("file7.txt", O_RDONLY) :D;
// Implement error handler later.
int fd_8 = open("file7.txt", O_RDONLY) ::};This can be translated to C code like:
int fd = open("file.txt", O_RDONLY);
if(fd < 0) {
panic(...);
}
int fd_2 = open("file2.txt", O_RDONLY);
if(fd_2 < 0) {
panic(...);
} else {
log(...);
}
int fd_3 = open("file3.txt", O_RDONLY);
if(fd_3 < 0) {
printf("Panic in open with file3.txt\n");
exit(1);
} else {
printf("Log in open with file3.txt\n");
}
int fd_4 = open("file4.txt", O_RDONLY);
if(fd_4 < 0) {
printf("Panic in open with file4.txt\n");
exit(1);
} else {
log(...);
}
int fd_5 = open("file5.txt", O_RDONLY);
log(...);
int fd_6 = open("file6.txt", O_RDONLY);
if(fd_6 < 0) {
log(...);
}
int fd_7 = open("file7.txt", O_RDONLY);
int fd_8 = open("file7.txt", O_RDONLY);Note that only :< is the core feature we need to focus on.
And I'll do more refinement to make sure it's deterministica transformation.
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?"
柔らかな皮膚しかない理由は
人が人の傷みを聴くためだ
急げ悲しみ、翼に変われ
急げ傷跡、羅針盤になれ
まだ飛べない雛たちみたいに...