Skip to content

RuriOSS/cwte

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

135 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

::::<

读完了依赖~我很快就离开~

WIP:

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, 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 for 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.

::::< Ascii logo:

         _-''''-._
       /`          `.
      /   .,~~~,     \
     |   /       \    |
     |   |   :>.,/    |
     \   \       ,___/::<
     `.   "-----"   /:::::<
       `.          /:::::::<
         '-.____. /::::::::::<

"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.

Version:

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.

::::< About cwte:

"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.

::::< The core:

:< 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

::::< The philosophy:

  • 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 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.
  • Cwte should not spread any bullshit, or human auditor should fire it.
  • 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.

::::< The goal:

 (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.
Anyway, trust the c code, not the cwte code and cwte-generator.

::::< The non-goal:

There's no silver bullet in C programming.
Cwte is never a .unwrap() or something like that, as C also has never provided a way to do that.
If you expect something like:

foo(bar() :<);

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. The generated code will be unreadable and un-auditable, and panic() is a dangerous side-effect, as an accuountable tail, cwte will never try that.
And if you expect complex AST parsing, cwte might never do that. The more complex features means the more complex bugs, and you will get many weird bugs if you don't have enough PLT knowledge and a good test coverage.

::::< Project structure:

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.

Why cwte:

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.

Why the :<:

  • 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.

The .hce header:

.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.

::::< cwte implementation:

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.

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, 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.

cwte design goals:

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) :>;

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(...);
}

And I'll do more refinement to make sure it's deterministica transformation.

Note:

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-number for internal variable name, so you will match generated code with .ce easily.

Future:

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?"

柔らかな皮膚しかない理由は

人が人の傷みを聴くためだ

急げ悲しみ、翼に変われ

急げ傷跡、羅針盤になれ

まだ飛べない雛たちみたいに

About

cute way to handle error ::::<

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors