Skip to content

Instantly share code, notes, and snippets.

@raymyers
Last active August 25, 2023 01:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save raymyers/0f248dee8d3c251d15e42e8a255f6edf to your computer and use it in GitHub Desktop.
Save raymyers/0f248dee8d3c251d15e42e8a255f6edf to your computer and use it in GitHub Desktop.
Mend Demo - August 2023

Mend demo

Introducing the mend command as a way to drive reproducable code refactoring. It takes a Menderfile, inspired by Makefile and Dockerfile but also having a lot in common with DataBase migrations in tools like ActiveRecord and Liquibase.

The files in this gist are an example run - mend takes Menderfile to turn main.c into main-after.c leaving the trail of commits in mend.log.

This is useful both for human operators as as a safe abstraction for an LLM to drive changes in accordance with the principles of the Mechanized Mending Manifesto.

Other stuff in play

Untangler

Untangler is a tool I'm writing to provide a unified CLI to refactoring steps for a variety of languages.

TCR - Kent Beck's TCR = Test && Commit || Revert

Kind of like a more strict TDD designed to force small steps. If you run the tests and they fail, you blow away your changes. For docs and formatting, I'm also adding a stricter variant, BICR = BinaryIdentical && Commit || Revert.

Obfuscated C Code

IOCCC = International Obfuscated C Code Contest. Here I'm using an entry from 2020 that prints a starwars intro style scroll in ascii art

Provable refactoring

Arlo's Commit Notation gives a rationale for having a tracable set tiny commits of known risk on a legacy codebase. Mend would be set up to make it very easy to follow this convention for commit prefixes.

See also Naming as a Process, showing the flexibility of refactoring steps targeting capturing insight incrementally in names.

#include <complex.h>/**/ /*Oh,my_dear_friend.How_I've_missed_you.--C-3PO***/
#include <stdio.h>
#include <time.h>/**** ****/
int *d;
int D[9999];
int N = 20;
int L = 4;
int n;
int pixel_index;
int color_value;
int a[3];
int i;
char *p;
char *q;
char screen_buffer[2000] = "L@X-SGD-HNBBB-AD-VHSG-\
-XNT\x1b[2J\x1b[H";
char *s = screen_buffer;
char *G = "r2zZX!.+@KBK^yh.!:%Bud!.+Jyh.!6.BHBhp!6.BHBh!:%Bu\
v{VT!.hBJ6p!042ljn!284b}`!.hR6Dp!.hp!h.T6p!h.p6!2/LilqP72!h.+@QB!~}lqP72/Lil!\
h.+@QBFp!:)0?F]nwf!,82v!.sv{6!.l6!,j<n8!.xN6t!&NvN*!.6hp";
typedef complex double(c);
c (control_points)[3];
c point_value;
c offset;
c calculate_value(double t) {
double s = 1 - t;
double u;
point_value = s * s * control_points[1] + 2 * s * t * *control_points + t * t * control_points[2] + offset;
u = I * point_value;
return +48 * ((s = point_value) + 48 * I) / (1 < u ? u : 1);
}
void evaluate_and_draw_interval(double t, double u) {
double s = point_value = calculate_value(t) - calculate_value(u);
(s = point_value * (2 * s - point_value)) < 1 ? pixel_index = point_value = calculate_value((t + u) / 2), color_value = -I * point_value,
pixel_index > -41 &&pixel_index < 39 && 9 < color_value &&color_value < 48
? pixel_index += color_value / 2 * 80 + 73,
screen_buffer[pixel_index] = screen_buffer[pixel_index] - 73 ? color_value % 2 ? screen_buffer[pixel_index] - 94 ? 95 : 73
: screen_buffer[pixel_index] - 95 ? 94
: 73
: 73
: 1
: (evaluate_and_draw_interval(t, (t + u) / 2), evaluate_and_draw_interval((t + u) / 2, u), 0);
}
int(main)(int(x), char **V) {
;
clock_t(c) = clock();
;
;
for (d = D; pixel_index < 26; pixel_index++, s++)
*s > 63 ? *d++ = pixel_index % 7 * 16 - 7 * 8, *d++ = pixel_index / 7 * 25, *d++ = *s - 64 : 0;
;
if (V[1]) {
;
;
;
FILE *F = fopen(V[+1], "r");
for (d = D, L = N = pixel_index = 0; (x = fgetc(F)) > 0 || fclose(F);)
if (x > 13 ? 64 < x &&x < 91 ? *d++ = pixel_index * 16, *d++ = L * 25,
*d++ = x % 26 : 0, pixel_index++, 0 : 1)
for (++L; d - D > N * 3 || (pixel_index = 0); N++)
D[N * 3] -= pixel_index * 8;
}
for (; i < 200 + L * 25; i++) {
for (n = 0, p = screen_buffer + 33; n<1920; *p++ = n++ % 80> 78 ? 10 : 32) {
}
for (*p = x = 0, d = D; x < N; x++, d += 3) {
offset = (d[1] - i - 40) * I + *d;
n = d[2];
p = G;
for (; n--;)
for (; *p++ > 33;)
;
*a = a[1] = *p++;
for (; *p > 33; p++)
if (*p % 2 ? *a = *p, 0 : 1) {
a[2] = *p;
for (pixel_index = 0; pixel_index < 3; pixel_index++) {
color_value = a[pixel_index] / 2 - 18;
q = "/&&&##%%##.+),A$$$$'&&'&&((%-((#'/#%%#&#\
&&#D&";
for (n = 2; color_value--;)
n += *q++ - 34;
control_points[pixel_index] = n % 13 + n / 13 * I;
}
evaluate_and_draw_interval(0, 1);
*a = a[1] = *p;
}
}
for (puts(s), s = screen_buffer + 30; (clock() - c) * 10 < i * CLOCKS_PER_SEC;)
;
}
return 0;
}
/* A long time ago in a galaxy far, far away.... */
int*d,D[9999],N=20,L=4,n,m,k,a[3],i;char*p,*q,S[2000]="L@X-SGD-HNBBB-AD-VHSG-\
-XNT\x1b[2J\x1b[H",*s=S,*G="r2zZX!.+@KBK^yh.!:%Bud!.+Jyh.!6.BHBhp!6.BHBh!:%Bu\
v{VT!.hBJ6p!042ljn!284b}`!.hR6Dp!.hp!h.T6p!h.p6!2/LilqP72!h.+@QB!~}lqP72/Lil!\
h.+@QBFp!:)0?F]nwf!,82v!.sv{6!.l6!,j<n8!.xN6t!&NvN*!.6hp";/*Stay_on_target.**/
#include/**/<complex.h>/**//*Oh,my_dear_friend.How_I've_missed_you.--C-3PO***/
typedef/**/complex/**/double(c);c(X)[3],P,O;c/**/B(double t){double s=1-t,u;P=
s*s*X[1] +2 *s*t* *X+t *t*X [2]+O;u=I*P;
return+48*(( s=P)+ 48*I )/( 1<u? u: 1);} /* 1977 IOCCC2020*/
#include/** Do.Or do_not . There_is_ no_try... --Yoda**/<stdio.h>
void/**/b( double t,/*** * **/double u){double s=P=B(t)-B(u);(s=P
*(2*s-P)) <1?m=P=B ((t+ u)/ 2),k =- I*P, m> -41&&m<39&&9<k&&k
<48? m+=k/ 2*80+ 73,S [m]= S[m]
-73?k%2?S[m]-94?95:73:S[m]-95?94:73:73:1:(b(t,(t+u)/2),b((t+u)/2,u),0);}/*<oo>
_No. _I_am_ IOCCC 1977 ***/
#include/***** your father.. --DarthVader **/ <time.h>/**** ****/
int(main)(int (x), char**V){; clock_t(c)= /* */clock();;; for(d=D
;m<26;m++,s ++)*s> 63?*d++=m% 7* 16-7 *8,*d++=m/ 7*25,*d++
=*s-64:0;; if(V[1]) {;;;FILE *F =fopen(V[+1], "r");for (d=D,L=N=m
=0;(x=/** * *** **/ fgetc(F))>0
||fclose(F);)if(x>13?64<x&&x<91?*d++=m*16,*d++=L*25,*d++=x%26:0,m++,0:1)for(++
L;d-D>N*3||(m=0);N++)D[N*3]-=m*8;}for(;i<200+L*25;i++){for(n=0,p=S+33;n<1920;*
p++=n++%80>78?10:32){}for(*p=x=0,d=D;x<N;x++,d+=3){O=(d[1]-i-40)*I+*d;n=d[2];p
=G;for(;n--;)for(;*p++>33;);*a=a[1]=*p++;for(;*p>33;p++)if(*p%2?*a=*p,0:1){a[2
]=*p;for(m=0;m<3;m++){k=a[m]/2-18;q="/&&&##%%##.+),A$$$$'&&'&&((%-((#'/#%%#&#\
&&#D&";for(n=2;k--;)n+=*q++-34;X[m]=n%13+n/13*I;}b(0,1);*a=a[1]=*p;}}for(puts(
s),s=S+30;(clock()-c)*10<i*CLOCKS_PER_SEC;);}return 0;}/*Nevertellmetheodds*/
> mend
╭────┬─────────┬─────────────────────────────────────┬─────────╮
│ # │ status │ name │ sha │
├────┼─────────┼─────────────────────────────────────┼─────────┤
│ 0 │ Done │ remove_comments_in_includes │ 0d6db40 │
│ 1 │ Done │ remove_comments │ 46b6bd4 │
│ 2 │ Done │ format │ 43347cb │
│ 3 │ Done │ move_includes_to_top │ 4132788 │
│ 4 │ Done │ split_declarations │ d9c35f2 │
│ 5 │ Running │ rename B calculate_value │ │
│ 6 │ Pending │ rename b evaluate_and_draw_interval │ │
│ 7 │ Pending │ rename X control_points │ │
│ 8 │ Pending │ rename P point_value │ │
│ 9 │ Pending │ rename O offset │ │
│ 10 │ Pending │ rename m pixel_index │ │
│ 11 │ Pending │ rename k color_value │ │
│ 12 │ Pending │ rename S screen_buffer │ │
╰────┴─────────┴─────────────────────────────────────┴─────────╯
# ...
╭────┬────────┬─────────────────────────────────────┬─────────╮
│ # │ status │ name │ sha │
├────┼────────┼─────────────────────────────────────┼─────────┤
│ 0 │ Done │ remove_comments_in_includes │ 0d6db40 │
│ 1 │ Done │ remove_comments │ 46b6bd4 │
│ 2 │ Done │ format │ 43347cb │
│ 3 │ Done │ move_includes_to_top │ 4132788 │
│ 4 │ Done │ split_declarations │ d9c35f2 │
│ 5 │ Done │ rename B calculate_value │ f09024a │
│ 6 │ Done │ rename b evaluate_and_draw_interval │ 57f2551 │
│ 7 │ Done │ rename X control_points │ 48a9261 │
│ 8 │ Done │ rename P point_value │ 0de9cb1 │
│ 9 │ Done │ rename O offset │ 4679cc0 │
│ 10 │ Done │ rename m pixel_index │ b53b537 │
│ 11 │ Done │ rename k color_value │ 3a1736b │
│ 12 │ Done │ rename S screen_buffer │ 052fc4e │
╰────┴────────┴─────────────────────────────────────┴─────────╯
# Done
FROM 43a3a253
ENV DEFAULT_FILE=main.c
ENV UNTANGLER_DEFAULT_FILE=main.c
ENV JAVA_HOME=/Library/Java/JavaVirtualMachines/graalvm-jdk-20.0.2+9.1/Contents/Home/
ENV PATH="$PATH:/Users/rmyers/dev/untangler/build/install/untangler/bin"
## Aliases
RECIPE move_includes_to_top \
grep "^#include" $UNTANGLER_DEFAULT_FILE > a.tmp && \
grep -v "^#include" $UNTANGLER_DEFAULT_FILE >> a.tmp && \
mv a.tmp $UNTANGLER_DEFAULT_FILE
RECIPE format clang-format -i $UNTANGLER_DEFAULT_FILE
RECIPE rename untangler rename $1 $2 -w
RECIPE remove_comments_in_includes \
perl -pi -e 's{^#include */\*((?!\*/).)*\*/}{#include}gs' $UNTANGLER_DEFAULT_FILE
RECIPE remove_comments \
untangler remove comment "*" --sub=" " -w
RECIPE split_declarations \
untangler misc split-declaration "*" -w
BEFORE_ALL make clean && make test
# Phase 1
# BICR: Binary Identical && Commit || Revert
RECIPE BeforeEach make && cp a.out a.bak
RECIPE BeforeEach diff a.out a.out.bak
RUN remove_comments_in_includes
RUN remove_comments
RUN format
RUN move_includes_to_top
RUN split_declarations
# Phase 2
# TCR: Test && Commit || Revert
RECIPE BeforeEach make && cp a.out a.bak
RECIPE AfterEach make test
RUN rename B calculate_value
RUN rename b evaluate_and_draw_interval
## Need to select a line range for these to work because multiple functions use them
# RUN rename t start_t
# RUN rename u end_t
# RUN rename s difference
RUN rename X control_points
RUN rename P point_value
RUN rename O offset
RUN rename m pixel_index
RUN rename k color_value
RUN rename S screen_buffer
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment