Compare commits

...
Sign in to create a new pull request.

71 commits

Author SHA1 Message Date
0c05d51ecd
morphisms: fix syntax error in angle.morphism-base 2025-07-02 08:44:55 +02:00
43dcb13f62
search viz: implement MapStruct/MapEnum & correctly set extent 2025-07-02 08:43:49 +02:00
3938985b83
add delta seq morphism 2025-06-30 14:07:26 +02:00
688ee303c4
adapt morphisms to new syntax 2025-06-30 14:06:23 +02:00
bbbe54149e
adapt layout in search pane 2025-06-30 13:59:49 +02:00
7ed2936024
add type defs for nativeInt* 2025-06-30 08:32:09 +02:00
410ba4019f
target morph: strip already substituted variables from context when baking MorphismInstance::Specialize 2025-06-29 20:22:21 +02:00
557d345412
viz: improve search pane 2025-06-29 17:40:45 +02:00
7375d14fcb
fixup theme 2025-06-29 02:41:11 +02:00
1de4c7e7ad
dont emit code when no instacne s available 2025-06-28 07:07:40 +02:00
61ae353105
search viz: emit svg for nodes in search graph 2025-06-28 07:06:16 +02:00
b83771594a
viz: search pane 2025-06-26 23:22:24 +02:00
bbaec58c83
rename ctx; TypeTerm::Seq: items->item 2025-06-26 16:03:38 +02:00
dbe1a2c002
gen_lib: use sugared sequence syntax 2025-06-21 03:05:04 +02:00
793ade4709
implement get_c_repr_type for struct/enum/seq
(since we removed desugared terms)
2025-06-21 02:51:47 +02:00
06b607eb6f
parser adaptions; remove desugared terms 2025-06-20 17:13:46 +02:00
89b730ea7d
substitutions & contexts 2025-06-19 16:11:39 +02:00
4674e75e99
morphism base: new syntax 2025-06-14 13:22:36 +02:00
64e8b96c92
adapt to changes in lib-laddertypes 2025-06-12 07:08:25 +02:00
6897b5e274
adapt to changed MorphismInstance in lib-laddertypes 2025-05-30 20:19:45 +02:00
0564845951
fix angle morphism name 2025-05-30 18:28:09 +02:00
1a3c194ec2
morphisms: posint le/be fixes & distinguished morphism names 2025-05-29 13:03:30 +02:00
0d6378c72e
morphism base: digits as ℕ 2025-05-29 13:03:30 +02:00
cd53199dfb
remove syntax experiments from morphism base 2025-05-29 13:03:30 +02:00
080e8b756c
add farbfeld example 2025-05-29 13:03:30 +02:00
f8ef2d08a2
add more morphisms 2025-05-29 13:03:30 +02:00
e5df94ebd1
visualization: alsow use arrowhead2 with edges outside of module <g> 2025-05-29 13:02:01 +02:00
74fc706c4b
visualization: fix edge positions in both pane modes 2025-05-29 13:02:01 +02:00
3d121fb57b
visualization: improve layouting 2025-05-29 13:02:01 +02:00
d5fab37fca
visualization: add seq map 2025-05-29 13:02:01 +02:00
f03ebb2d2c
visualization: improve style of struct morphisms 2025-05-29 13:02:01 +02:00
8d7c60cfe0
visualization: improve struct layout & add opening bracket symbol 2025-05-29 13:02:00 +02:00
76b64e583f
improve morph instance viz 2025-05-29 13:02:00 +02:00
7c17302f8b
vizualize chain morphisms 2025-05-29 13:02:00 +02:00
f9b0759f32
improve js string generation 2025-05-29 13:02:00 +02:00
ff0fe2c877
viz: fix some html/js stuff 2025-05-29 13:02:00 +02:00
3c04c7909c
cleanup viz code 2025-05-29 13:02:00 +02:00
5f944fb403
wip viz 2025-05-29 13:02:00 +02:00
9276336655
viz: dummy for adding morphism instances in addition to the overall morphism base 2025-05-29 13:02:00 +02:00
c40efda83b
initial html output 2025-05-29 13:01:59 +02:00
cff14cf096
add MorphismInstance::Id 2025-05-29 13:01:23 +02:00
edd3441511
todo.md 2025-05-29 13:01:11 +02:00
dfa364bc0f
length prefix morphism: generic calls to functions with C macro 2025-05-16 13:55:36 +02:00
62f0ff9eac
add array morphisms : StaticLength <--> LengthPrefix 2025-05-16 13:53:19 +02:00
76c005ac8b
morph lenpfx to valterm: make item type generic 2025-05-16 13:51:45 +02:00
0eb8074644
digit morphism: also print Radix in warning 2025-05-16 02:34:18 +02:00
0c9a4ccc8b
example: use -o option in makefile 2025-05-16 02:33:17 +02:00
24f9a0f5b5
C code gen: rename include block to header block 2025-05-14 10:28:31 +02:00
7dd6352760
code generation: only use scanf/printf on newline('\n') terminated strings 2025-05-14 10:20:55 +02:00
692a34c257
main: when loading morphism base, remove . from path suffix variable 2025-05-14 10:19:48 +02:00
ea2504ff07
morphisms: fix bitshift in lenpfx to msb-cont 2025-05-14 10:18:42 +02:00
6f0a0f5927
when generating #defines for values of type variables, distinguish between variables of type 'Type' and others 2025-05-10 16:28:58 +02:00
4a705241fe
manage include blocks & only output required blocks once 2025-05-10 16:27:56 +02:00
efa584dfb5
update test.sh to new cli features 2025-05-10 16:25:36 +02:00
783c70b2e2
add output option in cli arguments 2025-05-10 15:40:49 +02:00
a58ac1a69c
in generation of main function, fix case that input/output buffer is a nullterm string 2025-05-10 15:16:51 +02:00
1116457bcc
add example C program which generates (de)marshaling with ldmc 2025-05-10 15:00:54 +02:00
27ea365755
improve integration
* add command line parsing
* read morphism base directory from environment
* generate C library of multiple morphisms
2025-05-09 03:33:51 +02:00
923fe987dc
fix typedefs 2025-05-07 16:29:45 +02:00
3ed7a19270
adapt changes in lib-laddertypes 2025-05-06 00:39:32 +02:00
6787e607a6
wip 2025-05-01 06:38:17 +02:00
ef819b4711
morphisms: remove 'return 0;' at end since it is now added by default 2025-04-03 16:37:46 +02:00
3f1397735c
improve code generation
- improve usage of FUSE
- implement seq-map for different representations
- fix C-types in morph-function arguments
- collect active types and put typedef into header
- add generate_main() to create stdio-morphism
2025-04-02 23:32:06 +02:00
67cec64dfc
fix codegen for seq-map 2025-04-02 15:01:45 +02:00
98592aa596
fuse results of sub- morphisms in complex morphisms 2025-04-02 14:37:31 +02:00
35b747ea15
fix local src/dst types in complex morphisms 2025-04-02 14:31:47 +02:00
39cba1ee57
improve symbol names in generated c code 2025-04-02 14:11:17 +02:00
583892f10d
wip struct morphisms 2025-04-01 18:23:18 +02:00
d98afc294d
exemplaric platform definitions 2025-03-31 10:29:57 +02:00
bce52e9fcf
replace native.Float/native.Double with native.Float32/native.Float64 2025-03-25 19:38:56 +01:00
21bb33193a
wip struct morphisms 2025-03-24 10:14:25 +01:00
50 changed files with 4580 additions and 1014 deletions

View file

@ -8,3 +8,5 @@ chumsky = "0.9.0"
ariadne = "0.2"
laddertypes = { path = "../lib-laddertypes", features = ["pretty"] }
tiny-ansi = { version = "0.1.0" }
clap = { version = "4.5.37", features = ["derive"] }
walkdir = "2.5.0"

1
examples/01-uint-example/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
build/

View file

@ -0,0 +1,14 @@
#include "morphisms.h"
int main(int argc, char* argv[]) {
if( argc > 1 ) {
uint64_t value;
demarshal( argv[1], &value );
uint64_t result = value * value;
char buf[256];
marshal( &result, buf );
printf("%s\n", buf);
}
}

View file

@ -0,0 +1,23 @@
all: build/square
.PHONY: build clean
build:
mkdir -p build
build/morphisms.h: build
ldmc \
-m "marshal: \
~ <_ 0> ~ native.UInt64 \
--> ~ <PosInt 10 BigEndian> ~ [~<ValueTerminated 0> <Digit 10>~Char~Ascii~native.UInt8 ] \
" \
-m "demarshal: \
~ <PosInt 10 BigEndian> ~ [~<ValueTerminated 0> <Digit 10>~Char~Ascii~native.UInt8 ] \
--> ~ <_ 0> ~ native.UInt64 \
" \
-o build/morphisms.h
build/square: build build/morphisms.h
gcc -Os -I../../morphisms/runtime/include/ -Ibuild main.c -o build/square
clean:
rm build/ -rf

185
examples/02-farbfeld/main.c Normal file
View file

@ -0,0 +1,185 @@
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <morphisms.h>
char * size = "\
size ImageSize = Λwidth: Λheight: (width * height);\
";
typedef float f32x4 __attribute__ ((vector_size (16)));
struct ColorRGBAu16 { uint16_t red; uint16_t green; uint16_t blue; uint16_t alpha; };
struct ColorHSVAf32 { float hue; float saturation; float value; float alpha; };
struct Image_SoA_HSV_f32x4 {
uint64_t size;
f32x4 * hue;
f32x4 * saturation;
f32x4 * value;
f32x4 * alpha;
};
uint64_t div_by_four(uint64_t x) {
return (x/4) + (x&0b11);
}
void filter_saturate(struct Image_SoA_HSV_f32x4 * image, float saturation_factor) {
uint64_t vector_size = div_by_four(image->size);
for( size_t i = 0; i < vector_size; ++i) {
image->saturation[i] = image->saturation[i] * saturation_factor;
}
}
struct Farbfeld {
uint64_t magic;
uint32_t width;
uint32_t height;
uint16_t data[];
};
struct Farbfeld *read_farbfeld(const char *filename) {
FILE *f = fopen(filename, "rb");
if (!f) return NULL;
char magic_bytes[8];
if (fread(magic_bytes, 1, 8, f) != 8 || memcmp(magic_bytes, "farbfeld", 8) != 0) {
fclose(f);
return NULL;
}
uint32_t width, height;
if (fread(&width, 4, 1, f) != 1 || fread(&height, 4, 1, f) != 1) {
fclose(f);
return NULL;
}
width = __builtin_bswap32(width);
height = __builtin_bswap32(height);
size_t npixels = (size_t)width * height;
size_t datasize = npixels * 4 * sizeof(uint16_t); // RGBA, 4 channels
struct Farbfeld *img = malloc(sizeof(struct Farbfeld) + datasize);
if (!img) {
fclose(f);
return NULL;
}
img->magic = 0x6661726266656c64ULL; // "farbfeld"
img->width = width;
img->height = height;
if (fread(img->data, 1, datasize, f) != datasize) {
free(img);
fclose(f);
return NULL;
}
// Convert from big-endian to host byte order
for (size_t i = 0; i < npixels * 4; ++i)
img->data[i] = __builtin_bswap16(img->data[i]);
fclose(f);
return img;
}
int write_farbfeld(const char *filename, const struct Farbfeld *img) {
FILE *f = fopen(filename, "wb");
if (!f) return -1;
// Write magic bytes
if (fwrite("farbfeld", 1, 8, f) != 8) {
fclose(f);
return -1;
}
// Convert and write width and height as big-endian
uint32_t w_be = __builtin_bswap32(img->width);
uint32_t h_be = __builtin_bswap32(img->height);
if (fwrite(&w_be, 4, 1, f) != 1 || fwrite(&h_be, 4, 1, f) != 1) {
fclose(f);
return -1;
}
// Convert and write pixel data as big-endian
size_t npixels = (size_t)img->width * img->height;
for (size_t i = 0; i < npixels * 4; ++i) {
uint16_t be_val = __builtin_bswap16(img->data[i]);
if (fwrite(&be_val, 2, 1, f) != 1) {
fclose(f);
return -1;
}
}
fclose(f);
return 0;
}
int main(int argc, char* argv[]) {
/* read Farbfeld Format */
struct Farbfeld * farbfeld;
if( argc > 1 )
farbfeld = read_farbfeld(argv[1]);
else {
fprintf(stderr, "Error: no input file specified!\n");
return -1;
}
/* morph into SIMD friendly HSVf32 image */
struct Image_SoA_HSV_f32x4 hsv_image;
hsv_image.size = farbfeld->width * farbfeld->height;
uint64_t vector_size = 1 + (hsv_image.size-1)/4 ;
hsv_image.hue = malloc( vector_size * sizeof(f32x4) );
hsv_image.saturation = malloc( vector_size * sizeof(f32x4) );
hsv_image.value = malloc( vector_size * sizeof(f32x4) );
hsv_image.alpha = malloc( vector_size * sizeof(f32x4) );
#define VECTOR_SIZE 4
for(uint64_t i = 0; i < farbfeld->width*farbfeld->height; i += VECTOR_SIZE) {
struct ColorHSVAf32 hsv[VECTOR_SIZE];
uint64_t vector_idx = i / VECTOR_SIZE;
for( unsigned j = 0; j < VECTOR_SIZE; ++j ) {
if( (i+j) < farbfeld->width*farbfeld->height ) {
morph_RGBAu16_to_HSVAf32( (void*)&farbfeld->data[4*(i+j)], (void*)&hsv[j] );
// morph AoS to SoA
hsv_image.hue[vector_idx][j] = hsv[j].hue;
hsv_image.saturation[vector_idx][j] = hsv[j].saturation;
hsv_image.value[vector_idx][j] = hsv[j].value;
hsv_image.alpha[vector_idx][j] = hsv[j].alpha;
}
else break;
}
}
/* apply filter */
filter_saturate(&hsv_image, 0.1);
/* morph back to RGBu16 */
//memset(source_image.data, 0, source_image.size * sizeof(struct ColorRGBAu16));
for(uint64_t i = 0; i < farbfeld->width*farbfeld->height; ++i) {
// morph SoA to AoS
struct ColorHSVAf32 hsv = {
.hue = hsv_image.hue[i/4][i%4],
.saturation = hsv_image.saturation[i/4][i%4],
.value = hsv_image.value[i/4][i%4],
.alpha = hsv_image.alpha[i/4][i%4],
};
// morph Hsv to Rgb
morph_HSVAf32_to_RGBAu16( (void*)&hsv, (void*)& farbfeld->data[i*4] );
}
/* write farbfeld */
if( argc > 2 ) {
fprintf(stderr, "write farbfeld to %s\n", argv[2]);
write_farbfeld(argv[2], farbfeld);
} else {
fprintf(stderr, "Error: no output file specified!");
}
return 0;
}

View file

@ -0,0 +1,60 @@
all: build/color-filter
.PHONY: all clean
build:
mkdir -p build
build/morphisms.h: build
ldmc \
-m "morph_RGBAu16_to_HSVAf32 : \
RgbaColor ~ RGB ~ { \
red: ~ <QuantizedLinear 0 1 65535> \
~ ~ <PosInt 256 BigEndian> \
~ [~<StaticLength 2> <Digit 256>~<_ 256>~native.UInt8 ]; \
green: ~ <QuantizedLinear 0 1 65535> \
~ ~ <PosInt 256 BigEndian> \
~ [~<StaticLength 2> <Digit 256>~<_ 256>~native.UInt8 ]; \
blue: ~ <QuantizedLinear 0 1 65535> \
~ ~ <PosInt 256 BigEndian> \
~ [~<StaticLength 2> <Digit 256>~<_ 256>~native.UInt8 ]; \
alpha: ~ <QuantizedLinear 0 1 65535> \
~ ~ <PosInt 256 BigEndian> \
~ [~<StaticLength 2> <Digit 256>~<_ 256>~native.UInt8 ]; \
} \
--> RgbaColor ~ HSV ~ { \
hue: Angle ~ Turns ~ ~ native.Float32; \
saturation: ~ native.Float32; \
value: ~ native.Float32; \
alpha: ~ native.Float32; \
} \
" \
-m "morph_HSVAf32_to_RGBAu16 : \
RgbaColor ~ HSV ~ { \
hue: Angle~Turns~~native.Float32; \
saturation: ~native.Float32; \
value: ~native.Float32; \
alpha: ~ native.Float32; \
} \
--> RgbaColor ~ RGB ~ <Struct \
red: ~<QuantizedLinear 0 1 65535> \
~ ~ <PosInt 256 BigEndian> \
~ [~<StaticLength 2> <Digit 256>~<_ 256>~native.UInt8]; \
green: ~<QuantizedLinear 0 1 65535> \
~ ~ <PosInt 256 BigEndian> \
~ [~<StaticLength 2> <Digit 256>~<_ 256>~native.UInt8]; \
blue: ~<QuantizedLinear 0 1 65535> \
~ ~ <PosInt 256 BigEndian> \
~ [~<StaticLength 2> <Digit 256>~<_ 256>~native.UInt8]; \
alpha: ~<QuantizedLinear 0 1 65535> \
~ ~ <PosInt 256 BigEndian> \
~ [~<StaticLength 2> <Digit 256>~<_ 256>~native.UInt8]; \
> \
" \
-o build/morphisms.h
build/color-filter: build/morphisms.h
gcc -g -O1 -lm -Ibuild -I../../morphisms/runtime/include/ main.c -o build/colorfilter
clean:
rm build/ -rf

View file

@ -2,71 +2,45 @@
#define PHI 6.28318530718
```
morph_angle_as_degrees_to_turns_float ()
Angle ~ Degrees ~ ~ native.Float
--> Angle ~ Turns ~ ~ native.Float
```
*dst = *src / 360.0;
return 0;
```
morph angle_as_degrees_to_turns_float :
Angle ~ Degrees ~ ~ native.Float32
--> Angle ~ Turns ~ ~ native.Float32
= ```*dst = *src / 360.0;```;
morph_angle_as_degrees_to_turns_double ()
Angle ~ Degrees ~ ~ native.Double
--> Angle ~ Turns ~ ~ native.Double
```
*dst = *src / 360.0;
return 0;
```
morph angle_as_degrees_to_turns_double :
Angle ~ Degrees ~ ~ native.Float64
--> Angle ~ Turns ~ ~ native.Float64
= ```*dst = *src / 360.0;```;
morph_angle_as_turns_to_degrees_float ()
Angle ~ Turns ~ ~ native.Float
--> Angle ~ Degrees ~ ~ native.Float
```
*dst = *src * 360.0;
return 0;
```
morph angle_as_turns_to_degrees_float :
Angle ~ Turns ~ ~ native.Float32
--> Angle ~ Degrees ~ ~ native.Float32
= ```*dst = *src * 360.0;```;
morph_angle_as_turns_to_degrees_double ()
Angle ~ Turns ~ ~ native.Double
--> Angle ~ Degrees ~ ~ native.Double
```
*dst = *src * 360.0;
return 0;
```
morph angle_as_turns_to_degrees_double :
Angle ~ Turns ~ ~ native.Float64
--> Angle ~ Degrees ~ ~ native.Float64
= ```*dst = *src * 360.0;```;
morph angle_as_radians_to_turns_float :
Angle ~ Radians ~ ~ native.Float32
--> Angle ~ Turns ~ ~ native.Float32
= ```*dst = *src / PHI;```;
morph angle_as_radians_to_turns_double :
Angle ~ Radians ~ ~ native.Float64
--> Angle ~ Turns ~ ~ native.Float64
= ```*dst = *src / PHI;```;
morph_angle_as_radians_to_turns_float ()
Angle ~ Radians ~ ~ native.Float
--> Angle ~ Turns ~ ~ native.Float
```
*dst = *src / PHI;
return 0;
```
morph angle_as_turns_to_radians_float :
Angle ~ Turns ~ ~ native.Float32
--> Angle ~ Radians ~ ~ native.Float32
= ```*dst = *src * PHI;```;
morph_angle_as_radians_to_turns_double ()
Angle ~ Radians ~ ~ native.Double
--> Angle ~ Turns ~ ~ native.Double
```
*dst = *src / PHI;
return 0;
```
morph_angle_as_turns_to_radians_float ()
Angle ~ Turns ~ ~ native.Float
--> Angle ~ Radians ~ ~ native.Float
```
*dst = *src * PHI;
return 0;
```
morph_angle_as_degrees_to_radians_double ()
Angle ~ Turns ~ ~ native.Double
--> Angle ~ Radians ~ ~ native.Double
```
*dst = *src * PHI;
return 0;
```
morph angle_as_turns_to_radians_double :
Angle ~ Turns ~ ~ native.Float64
--> Angle ~ Radians ~ ~ native.Float64
= ```*dst = *src * PHI;```;

View file

@ -0,0 +1,79 @@
```
#include <math.h>
```
morph color_as_rgb_to_hsv :
RgbaColor ~ RGB ~ {
red: ~ native.Float32;
green: ~ native.Float32;
blue: ~ native.Float32;
alpha: ~ native.Float32;
}
--> RgbaColor ~ HSV ~ {
hue: Angle ~ Turns ~ ~ native.Float32;
saturation: ~ native.Float32;
value: ~ native.Float32;
alpha: ~ native.Float32;
}
= ```
float max = fmaxf(fmaxf(src->red, src->green), src->blue);
float min = fminf(fminf(src->red, src->green), src->blue);
float delta = max - min;
float hue_turns;
if (delta == 0) {
hue_turns = 0;
} else if (max == src->red) {
hue_turns = fmodf(((src->green - src->blue) / delta + 6), 6) / 6.0;
} else if (max == src->green) {
hue_turns = (((src->blue - src->red) / delta) + 2) / 6.0;
} else {
hue_turns = (((src->red - src->green) / delta) + 4) / 6.0;
}
dst->hue = hue_turns;
dst->saturation = (max == 0) ? 0 : (delta / max);
dst->value = max;
dst->alpha = src->alpha;
```;
morph color_as_hsv_to_rgb :
RgbaColor ~ HSV ~ {
hue: Angle ~ Turns ~ ~ native.Float32;
saturation: ~ native.Float32;
value: ~ native.Float32;
alpha: ~ native.Float32;
}
--> RgbaColor ~ RGB ~ {
red: ~ native.Float32;
green: ~ native.Float32;
blue: ~ native.Float32;
alpha: ~ native.Float32;
}
= ```
float C = src->value * src->saturation;
float X = C * (1 - fabsf(fmodf(src->hue * 6.0, 2) - 1));
float m = src->value - C;
float r, g, b;
if (src->hue >= 0 && src->hue < 1.0 / 6.0) {
r = C; g = X; b = 0;
} else if (src->hue >= 1.0 / 6.0 && src->hue < 2.0 / 6.0) {
r = X; g = C; b = 0;
} else if (src->hue >= 2.0 / 6.0 && src->hue < 3.0 / 6.0) {
r = 0; g = C; b = X;
} else if (src->hue >= 3.0 / 6.0 && src->hue < 4.0 / 6.0) {
r = 0; g = X; b = C;
} else if (src->hue >= 4.0 / 6.0 && src->hue < 5.0 / 6.0) {
r = X; g = 0; b = C;
} else {
r = C; g = 0; b = X;
}
dst->red = r + m;
dst->green = g + m;
dst->blue = b + m;
dst->alpha = src->alpha;
```;

View file

@ -0,0 +1,32 @@
```
```
morph complex_as_polar_to_cartesian :
~ Polar
~ {~Aligned
phi: Angle~Radians~~native.Float64;
val: ~native.Float64;
}
--> ~ Cartesian
~ {~Aligned
x: ~native.Float64;
y: ~native.Float64;
}
= ```
return 0;
```;
morph complex_as_cartesian_to_polar :
~ Cartesian
~ {~Aligned
x: ~native.Float64;
y: ~native.Float64;
}
--> ~ Polar
~ {~Aligned
phi: Angle~Radians~~native.Float64;
val: ~native.Float64;
}
= ```
return 0;
```;

View file

@ -0,0 +1,32 @@
```
#include <array/length-prefix.h>
```
morph delta_seq_encode :
[~<LengthPrefix native.UInt64> ~native.UInt64 ]
--> [~DeltaSeq ]
~ [~<LengthPrefix native.UInt64> ~native.Int64 ]
= ```
PRESCAN_LENGTH_PREFIX_CALL(nativeUInt64, __nativeInt64_, clear)( dst );
for( size_t i = 0; i < src->len; ++i ) {
uint64_t cur = src->items[i];
uint64_t prev = 0;
if (i > 0) {
prev = src->items[i-1];
}
PRESCAN_LENGTH_PREFIX_CALL(nativeUInt64, __nativeInt64_, push)( dst, cur-prev );
}
```;
morph delta_seq_decode :
[~DeltaSeq ]
~ [~<LengthPrefix native.UInt64> ~native.Int64 ]
--> [~<LengthPrefix native.UInt64> ~native.UInt64 ]
= ```
PRESCAN_LENGTH_PREFIX_CALL(nativeUInt64, __nativeUInt64_, clear)( dst );
uint64_t cur = 0;
for( size_t i = 0; i < src->len; ++i ) {
cur += src->items[i];
PRESCAN_LENGTH_PREFIX_CALL(nativeUInt64, __nativeUInt64_, push)( dst, cur );
}
```;

View file

@ -0,0 +1,10 @@
```
```
morph ref_to_val8 : ∀T (T :<= native.UInt8)
< Ref~Ptr T >
~ native.Address
--> T
=```
*dst = **src ;
```;

View file

@ -1,11 +1,10 @@
```
#include <stdio.h>
```
morph_digit_as_char_to_uint8 (Radix:)
morph digit_as_char_to_uint8 : ∀Radix:
<Digit Radix> ~ Char ~ Ascii ~ native.UInt8
--> <Digit Radix> ~ native.UInt8
```
--> <Digit Radix> ~ <_ Radix> ~ native.UInt8
= ```
if( *src >= '0' && *src <= '9' )
*dst = *src - '0';
else if( *src >= 'a' && *src <= 'f')
@ -23,35 +22,13 @@ morph_digit_as_char_to_uint8 (Radix:)
fprintf(stderr, "digit %u is out of range for radix %u\n", *dst, Radix);
return -1;
}
```
```;
morph_digit_as_char_to_uint64 (Radix:)
<Digit Radix> ~ Char ~ Ascii ~ native.UInt8
--> <Digit Radix> ~ native.UInt64
```
if( *src >= '0' && *src <= '9' )
*dst = *src - '0';
else if( *src >= 'a' && *src <= 'f')
*dst = 0xa + *src - 'a';
else if( *src >= 'A' && *src <= 'F')
*dst = 0xa + *src - 'A';
else {
fprintf(stderr, "invalid digit 0x%x\n", *src);
return -1;
}
if( *dst < Radix ) {
return 0;
} else {
fprintf(stderr, "digit %u is out of range for radix %u\n", *dst, Radix);
return -1;
}
```
morph_digit_as_uint8_to_char (Radix:_16)
<Digit Radix> ~ native.UInt8
morph digit_as_uint8_to_char : ∀Radix:
<Digit Radix> ~ <_ Radix> ~ native.UInt8
--> <Digit Radix> ~ Char ~ Ascii ~ native.UInt8
```
= ```
if ( *src < 10 )
*dst = *src + '0';
else if( *dst < 16 )
@ -60,22 +37,4 @@ morph_digit_as_uint8_to_char (Radix:_16)
fprintf(stderr, "digit %u is out of rage for char\n", *dst);
return -1;
}
return 0;
```
morph_digit_as_uint64_to_char (Radix:_16)
<Digit Radix> ~ native.UInt64
--> <Digit Radix> ~ Char ~ Ascii ~ native.UInt8
```
if ( *src < 10 )
*dst = *src + '0';
else if( *dst < 16 )
*dst = *src - 0xa + 'a';
else {
fprintf(stderr, "digit %u is out of rage for char\n", *dst);
return -1;
}
return 0;
```
```;

View file

@ -0,0 +1,16 @@
```
```
morph energy_as_wh_to_joule :
Energy ~ Wh ~ ~ native.Float64
--> Energy ~ Ws ~ ~ native.Float64
= ```
*dst = *src * 3600.0;
```;
morph energy_as_joule_to_wh :
Energy ~ Ws ~ ~ native.Float64
--> Energy ~ Wh ~ ~ native.Float64
= ```
*dst = *src / 3600.0;
```;

View file

@ -3,45 +3,61 @@
#include <array/length-prefix.h>
```
morph_array_as_valterm_to_lenpfx (Terminator:native.UInt8)
<Seq~<ValueTerminated Terminator> native.UInt8>
--> <Seq~<LengthPrefix native.UInt64> native.UInt8>
```
length_prefix_uint64_t_array_uint8_t_clear(dst);
morph array_as_static_to_lenpfx : ∀Len: ∀T
[~<StaticLength Len> T]
--> [~<LengthPrefix native.UInt64> T]
= ```
//PRESCAN_LENGTH_PREFIX_CALL(nativeUInt64, T, clear)( dst );
length_prefix_nativeUInt64_array_##T##_clear(dst);
for( nativeUInt64 i = 0; i < Len; ++i )
length_prefix_nativeUInt64_array_##T##_push(dst, src->items[i]);
//PRESCAN_LENGTH_PREFIX_CALL(nativeUInt64, T, push)( dst, src->items[i] );
```;
morph array_as_lenpfx_to_static : ∀Len: ∀T
[~<LengthPrefix native.UInt64> T ]
--> [~<StaticLength Len> T ]
= ```
nativeUInt64 i;
for( i = 0; i < Len && i < src->len; ++i )
dst->items[i] = src->items[i];
if( i < Len )
memset( &dst[i], 0, (Len-i) * sizeof(T) );
```;
morph array_as_valterm_to_lenpfx : ∀T ∀Terminator:T
[~<ValueTerminated Terminator> T ]
--> [~<LengthPrefix native.UInt64> T ]
= ```
PRESCAN_LENGTH_PREFIX_CALL(nativeUInt64, T, clear)( dst );
while( *src != Terminator )
length_prefix_uint64_t_array_uint8_t_push(dst, *src++);
PRESCAN_LENGTH_PREFIX_CALL(nativeUInt64, T, push)( dst, *src++ );
```;
return 0;
```
morph_array_as_lenpfx_to_valterm (Terminator:native.UInt8)
<Seq~<LengthPrefix native.UInt64> native.UInt8>
--> <Seq~<ValueTerminated Terminator> native.UInt8>
```
morph array_as_lenpfx_to_valterm : ∀T ∀Terminator:T
[~<LengthPrefix native.UInt64> T ]
--> [~<ValueTerminated Terminator> T ]
=```
for( uint64_t i = 0; i < src->len; ++i )
*dst++ = src->items[i];
*dst = Terminator;
```;
return 0;
```
morph_array_as_lenpfx_to_continuation_bit (T:Type)
<Seq~<LengthPrefix native.UInt64> T>
--> <Seq~MsbCont T>
```
morph array_as_lenpfx_to_continuation_bit : ∀T
[~<LengthPrefix native.UInt64> T ]
--> [~MsbCont T ]
= ```
for( uint64_t i = 0; i < src->len; ++i ) {
const size_t n_bits = 8*sizeof(T);
if( src->items[i] & (1<<(n_bits-1)) ) {
fprintf(stderr, "error: value to high for MsbContinuation\n");
if( src->items[i] & ((uint64_t)1<<(n_bits-1)) ) {
fprintf(stderr, "error: value has MSB set, while being used in MsbContinuation sequence!\n");
return -1;
}
dst[i] = src->items[i];
if( i+1 < src->len )
dst[i] |= (1<<(n_bits-1));
dst[i] |= ((uint64_t)1<<(n_bits-1));
}
return 0;
```
```;

View file

@ -2,37 +2,47 @@
#include <array/length-prefix.h>
```
morph_nat_as_u64_to_pos ()
morph nat_as_u64_to_pos :
~ <_ 0>
~ native.UInt64
-->
~ <PosInt 0 LittleEndian>
~ <Seq~<LengthPrefix native.UInt64> <Digit 0>~native.UInt64>
```
~ [~<LengthPrefix native.UInt64> <Digit 0> ~<_ 0>~ native.UInt64 ]
= ```
dst->len = 1;
dst->items[0] = *src;
return 0;
```
```;
morph_nat_as_pos_to_u64 (Endianness:Type)
morph nat_as_pos_to_u64 : ∀Endianness
~ <PosInt 0 Endianness>
~ <Seq~<LengthPrefix native.UInt64> <Digit 0>~native.UInt64>
-->
~ [~<LengthPrefix native.UInt64> <Digit 0> ~<_ 0>~ native.UInt64 ]
-->
~ <_ 0>
~ native.UInt64
```
*dst = src->items[0];
return 0;
```
= ```
if( src->len == 0 )
*dst = 0;
else if ( src->len == 1 )
*dst = src->items[0];
else
{
fprintf(stderr, "Variable Length Integer too large for UInt64");
return -1;
}
```;
morph_posint_radix_le (SrcRadix:, DstRadix:)
morph posint_radix_le : ∀SrcRadix: ∀DstRadix:
~ <PosInt SrcRadix LittleEndian>
~ <Seq~<LengthPrefix native.UInt64> <Digit SrcRadix>~native.UInt64>
-->
~ [~<LengthPrefix native.UInt64> <Digit SrcRadix> ~<_ SrcRadix>~ native.UInt64 ]
-->
~ <PosInt DstRadix LittleEndian>
~ <Seq~<LenghtPrefix native.UInt64> <Digit DstRadix>~native.UInt64>
```
~ [~<LenghtPrefix native.UInt64> <Digit DstRadix> ~<_ DstRadix>~ native.UInt64 ]
= ```
uint64_t value = 0;
for( uint64_t i = 0; i < src->len; ++i ) {
@ -40,30 +50,28 @@ morph_posint_radix_le (SrcRadix:, DstRadix:)
value += src->items[src->len - i - 1];
}
length_prefix_uint64_t_array_uint64_t_clear( dst );
length_prefix_nativeUInt64_array_nativeUInt64_clear( dst );
#if DstRadix==0
length_prefix_uint64_t_array_uint64_t_push( dst, value );
length_prefix_nativeUInt64_array_nativeUInt64_push( dst, value );
#else
if( value == 0 ) {
length_prefix_uint64_t_array_uint64_t_push( dst, 0 );
length_prefix_nativeUInt64_array_nativeUInt64_push( dst, 0 );
} else while( value > 0 ) {
length_prefix_uint64_t_array_uint64_t_push( dst, value % DstRadix );
length_prefix_nativeUInt64_array_nativeUInt64_push( dst, value % DstRadix );
value /= DstRadix;
}
#endif
```;
return 0;
```
morph_posint_radix_be (SrcRadix:, DstRadix:)
morph posint_radix_be : ∀SrcRadix: ∀DstRadix:
~ <PosInt SrcRadix BigEndian>
~ <Seq~<LengthPrefix native.UInt64> <Digit SrcRadix>~native.UInt64>
~ [~<LengthPrefix native.UInt64> <Digit SrcRadix> ~<_ SrcRadix>~ native.UInt64 ]
-->
~ <PosInt DstRadix BigEndian>
~ <Seq~<LengthPrefix native.UInt64> <Digit DstRadix>~native.UInt64>
```
~ [~<LengthPrefix native.UInt64> <Digit DstRadix> ~<_ DstRadix>~ native.UInt64 ]
= ```
uint64_t value = 0;
for( uint64_t i = 0; i < src->len; ++i ) {
@ -91,28 +99,48 @@ morph_posint_radix_be (SrcRadix:, DstRadix:)
value /= DstRadix;
}
#endif
```;
return 0;
```
morph_posint_endianness (Radix:)
morph posint_endianness_le_to_be : ∀Radix:
~ <PosInt Radix LittleEndian>
~ <Seq~<LengthPrefix native.UInt64> <Digit Radix> ~ native.UInt64>
~ [~<LengthPrefix native.UInt64> <Digit Radix> ~<_ Radix>~ native.UInt64 ]
-->
~ <PosInt Radix BigEndian>
~ <Seq~<LengthPrefix native.UInt64> <Digit Radix> ~ native.UInt64>
```
return length_prefix_uint64_t_array_uint64_t_reverse( src, dst );
```
~ [~<LengthPrefix native.UInt64> <Digit Radix> ~<_ Radix>~ native.UInt64 ]
= ```
return length_prefix_nativeUInt64_array_nativeUInt64_reverse( (void*)src, (void*)dst );
```;
morph_posint_endianness (Radix:)
morph posint_endianness_be_to_le : ∀Radix:
~ <PosInt Radix BigEndian>
~ <Seq~<LengthPrefix native.UInt64> <Digit Radix> ~ native.UInt64>
~ [~<LengthPrefix native.UInt64> <Digit Radix> ~<_ Radix>~ native.UInt64 ]
-->
~ <PosInt Radix LittleEndian>
~ <Seq~<LengthPrefix native.UInt64> <Digit Radix> ~ native.UInt64>
```
return length_prefix_uint64_t_array_uint64_t_reverse( src, dst );
```
~ [~<LengthPrefix native.UInt64> <Digit Radix> ~<_ Radix>~ native.UInt64 ]
= ```
return length_prefix_nativeUInt64_array_nativeUInt64_reverse( (void*)src, (void*)dst );
```;
morph posint_uint32_endianness_be_to_le :
~ <PosInt 256 BigEndian>
~ [~<StaticLength 8> <Digit 256> ~<_ 256>~ native.UInt8 ]
-->
~ <PosInt 256 LittleEndian>
~ [~<StaticLength 8> <Digit 256> ~<_ 256>~ native.UInt8 ]
= ```
*dst = __builtin_bswap32(*src);
```;
morph posint_uint32_endianness_le_to_be :
~ <PosInt 256 LittleEndian>
~ [~<StaticLength 8> <Digit 256> ~<_ 256>~ native.UInt8 ]
-->
~ <PosInt 256 BigEndian>
~ [~<StaticLength 8> <Digit 256> ~<_ 256>~ native.UInt8 ]
= ```
*dst = __builtin_bswap32(*src);
```;

View file

@ -2,104 +2,87 @@
#include <stdio.h>
```
morph_real_as_decimalstr_to_float ()
~ <PosInt 10 BigEndian> ~ <Seq~<ValueTerminated 0> <Digit 10>~Char~Ascii~native.UInt8>
--> ~ native.Float
```
sscanf(src, "%f", dst);
return 0;
```
morph real_as_decimalstr_to_float :
~ <PosInt 10 BigEndian> ~ [~<ValueTerminated 0> <Digit 10>~Char~Ascii~native.UInt8 ]
--> ~ native.Float32
= ```sscanf(src, "%f", dst);```;
morph_real_as_decimalstr_to_double ()
~ <PosInt 10 BigEndian> ~ <Seq~<ValueTerminated 0> <Digit 10>~Char~Ascii~native.UInt8>
--> ~ native.Double
```
sscanf(src, "%lf", dst);
return 0;
```
morph real_as_decimalstr_to_double :
~ <PosInt 10 BigEndian> ~ [~<ValueTerminated 0> <Digit 10>~Char~Ascii~native.UInt8 ]
--> ~ native.Float64
= ```sscanf(src, "%lf", dst);```;
morph_real_as_float_to_decimalstr ()
~ native.Float
--> ~ <PosInt 10 BigEndian> ~ <Seq~<ValueTerminated 0> <Digit 10>~Char~Ascii~native.UInt8>
```
sprintf(dst, "%f", *src);
return 0;
```
morph real_as_float_to_decimalstr :
~ native.Float32
--> ~ <PosInt 10 BigEndian> ~ [~<ValueTerminated 0> <Digit 10>~Char~Ascii~native.UInt8 ]
= ```sprintf(dst, "%f", *src);```;
morph_real_as_double_to_decimalstr ()
~ native.Double
--> ~ <PosInt 10 BigEndian> ~ <Seq~<ValueTerminated 0> <Digit 10>~Char~Ascii~native.UInt8>
```
sprintf(dst, "%f", *src);
return 0;
```
morph real_as_double_to_decimalstr :
~ native.Float64
--> ~ <PosInt 10 BigEndian> ~ [~<ValueTerminated 0> <Digit 10>~Char~Ascii~native.UInt8 ]
= ```sprintf(dst, "%f", *src);```;
morph real_as_float_to_double :
~ native.Float32
--> ~ native.Float64
= ```*dst = *src;```;
morph_real_as_float_to_double ()
~ native.Float
--> ~ native.Double
```
*dst = *src;
return 0;
```
morph_real_as_double_to_float ()
~ native.Double
--> ~ native.Float
```
morph real_as_double_to_float :
~ native.Float64
--> ~ native.Float32
= ```
fprintf(stderr, "Warning: morphin Double -> Float. Precision loss!");
*dst = *src;
return 0;
```
```;
morph_real_as_u64_to_float ()
morph real_as_u64_to_float :
~ ~ native.UInt64
--> ~ native.Float
```
--> ~ native.Float32
= ```
fprintf(stderr, "Warning: morphin UInt64 -> Float. Precision loss!");
*dst = *src;
return 0;
```
```;
morph_real_as_u64_to_double ()
morph real_as_u64_to_double :
~ ~ native.UInt64
--> ~ native.Double
```
--> ~ native.Float64
= ```
fprintf(stderr, "Warning: morphin UInt64 -> Double. Precision loss!");
*dst = *src;
return 0;
```
```;
morph_real_as_quantized_linear_to_float (Begin: , End: , Steps: )
morph real_as_nat_to_quantized_linear :
~ ~ native.UInt64
--> ~ <QuantizedLinear 0 1 1> ~ ~ native.UInt64
= ```
*dst = *src;
```;
morph real_as_quantized_linear_to_float : ∀Begin: ∀End: ∀Steps:
~ <QuantizedLinear Begin End Steps> ~ ~ native.UInt64
--> ~ native.Float
```
--> ~ native.Float32
= ```
*dst = (float)Begin + ( *src * ((float)End - (float)Begin) ) / (float)Steps;
return 0;
```
```;
morph_real_as_float_to_quantized_linear (Begin: , End: , Steps: )
~ native.Float
morph real_as_float_to_quantized_linear : ∀Begin: ∀End: ∀Steps:
~ native.Float32
--> ~ <QuantizedLinear Begin End Steps> ~ ~ native.UInt64
```
= ```
*dst = ((*src - (float)Begin) * (float)Steps) / ((float)End - (float)Begin);
return 0;
```
```;
morph_real_as_quantized_linear_to_double (Begin: , End: , Steps: )
morph real_as_quantized_linear_to_double : ∀Begin: ∀End: ∀Steps:
~ <QuantizedLinear Begin End Steps> ~ ~ native.UInt64
--> ~ native.Double
```
*dst = (double)Begin + ( *src * ((double)End - (double)Begin) ) / (double)Steps;
return 0;
```
--> ~ native.Float64
= ```
*dst = (double)Begin + ( *src * ((double)End - (double)Begin) ) / (double)Steps;
```;
morph_real_as_double_to_quantized_linear (Begin: , End: , Steps: )
~ native.Double
morph real_as_double_to_quantized_linear : ∀Begin: ∀End: ∀Steps:
~ native.Float64
--> ~ <QuantizedLinear Begin End Steps> ~ ~ native.UInt64
```
= ```
*dst = ((*src - (double)Begin) * (double)Steps) / ((double)End - (double)Begin);
return 0;
```
```;

View file

@ -2,27 +2,37 @@
#include <stdio.h>
#include <stdint.h>
/*
typedef struct { \
LEN_TYPE len; \
ITEM_TYPE items[]; \
} __Seq__LengthPrefix_##LEN_TYPE##___##ITEM_TYPE##_; \
*/ \
#define LENGTH_PREFIX_ARRAY_TYPE(LEN_TYPE, ITEM_TYPE) \
_Seq___LengthPrefix_##LEN_TYPE##___##ITEM_TYPE##_
#define LENGTH_PREFIX_ARRAY_CALL(LEN_TYPE, ITEM_TYPE, METHOD) \
length_prefix_##LEN_TYPE##_array__##ITEM_TYPE##__ ## METHOD
#define PRESCAN_LENGTH_PREFIX_CALL(LEN, ITEM, METHOD) \
LENGTH_PREFIX_ARRAY_CALL(LEN, ITEM, METHOD)
#define DEFINE_LENGTH_PREFIX_ARRAY(LEN_TYPE, ITEM_TYPE) \
typedef struct { \
LEN_TYPE len; \
ITEM_TYPE items[]; \
} LengthPrefix_##LEN_TYPE##_Array_##ITEM_TYPE; \
\
static inline void length_prefix_##LEN_TYPE##_array_##ITEM_TYPE##_clear( \
LengthPrefix_##LEN_TYPE##_Array_##ITEM_TYPE *data) { \
static inline void PRESCAN_LENGTH_PREFIX_CALL(LEN_TYPE, ITEM_TYPE, clear) \
(_Seq___LengthPrefix_##LEN_TYPE##___##ITEM_TYPE##_ *data) { \
data->len = 0; \
} \
\
static inline void length_prefix_##LEN_TYPE##_array_##ITEM_TYPE##_push( \
LengthPrefix_##LEN_TYPE##_Array_##ITEM_TYPE *data, \
static inline void PRESCAN_LENGTH_PREFIX_CALL(LEN_TYPE, ITEM_TYPE, push) \
(_Seq___LengthPrefix_##LEN_TYPE##___##ITEM_TYPE##_ *data, \
ITEM_TYPE value) { \
data->items[data->len++] = value; \
} \
\
static inline int length_prefix_##LEN_TYPE##_array_##ITEM_TYPE##_reverse( \
LengthPrefix_##LEN_TYPE##_Array_##ITEM_TYPE const * restrict src, \
LengthPrefix_##LEN_TYPE##_Array_##ITEM_TYPE *restrict dst) { \
static inline int PRESCAN_LENGTH_PREFIX_CALL(LEN_TYPE, ITEM_TYPE, reverse) \
( _Seq___LengthPrefix_##LEN_TYPE##___##ITEM_TYPE##_ const * restrict src, \
_Seq___LengthPrefix_##LEN_TYPE##___##ITEM_TYPE##_ *restrict dst) { \
for (LEN_TYPE i = 0; i < src->len; i++) { \
dst->items[i] = src->items[src->len - 1 - i]; \
} \
@ -30,60 +40,11 @@
return 0; \
} \
\
static inline void length_prefix_##LEN_TYPE##_array_##ITEM_TYPE##_dump( \
LengthPrefix_##LEN_TYPE##_Array_##ITEM_TYPE const * data) { \
static inline void PRESCAN_LENGTH_PREFIX_CALL(LEN_TYPE, ITEM_TYPE, dump) \
( _Seq___LengthPrefix_##LEN_TYPE##___##ITEM_TYPE##_ const * data) { \
printf("Length: %llu\n", (unsigned long long) data->len); \
for (LEN_TYPE i = 0; i < data->len; i++) { \
printf("%llu ", (unsigned long long) data->items[i]); \
} \
printf("\n"); \
}
#define DEFINE_ALL_LENGTH_PREFIX_ARRAYS(LEN_TYPE) \
DEFINE_LENGTH_PREFIX_ARRAY(LEN_TYPE, uint8_t) \
DEFINE_LENGTH_PREFIX_ARRAY(LEN_TYPE, uint16_t) \
DEFINE_LENGTH_PREFIX_ARRAY(LEN_TYPE, uint32_t) \
DEFINE_LENGTH_PREFIX_ARRAY(LEN_TYPE, uint64_t)
DEFINE_ALL_LENGTH_PREFIX_ARRAYS(uint8_t)
DEFINE_ALL_LENGTH_PREFIX_ARRAYS(uint16_t)
DEFINE_ALL_LENGTH_PREFIX_ARRAYS(uint32_t)
DEFINE_ALL_LENGTH_PREFIX_ARRAYS(uint64_t)
#define DEFINE_LENGTH_PREFIX_ARRAY_MAP(LEN_TYPE, SRC_ITEM_TYPE, DST_ITEM_TYPE) \
static inline int length_prefix_##LEN_TYPE##_array_map_##SRC_ITEM_TYPE##_to_##DST_ITEM_TYPE( \
int (*f)(SRC_ITEM_TYPE const * restrict, DST_ITEM_TYPE * restrict), \
LengthPrefix_##LEN_TYPE##_Array_##SRC_ITEM_TYPE const * restrict src, \
LengthPrefix_##LEN_TYPE##_Array_##DST_ITEM_TYPE * restrict dst) \
{ \
if (dst->len < src->len) return -1; /* Ensure enough space */ \
for (LEN_TYPE i = 0; i < src->len; i++) { \
if (f(&src->items[i], &dst->items[i]) != 0) return -1; \
} \
dst->len = src->len; \
return 0; \
}
#define DEFINE_ALL_MAPS(LEN_TYPE) \
DEFINE_LENGTH_PREFIX_ARRAY_MAP(LEN_TYPE, uint8_t, uint8_t) \
DEFINE_LENGTH_PREFIX_ARRAY_MAP(LEN_TYPE, uint8_t, uint16_t) \
DEFINE_LENGTH_PREFIX_ARRAY_MAP(LEN_TYPE, uint8_t, uint32_t) \
DEFINE_LENGTH_PREFIX_ARRAY_MAP(LEN_TYPE, uint8_t, uint64_t) \
DEFINE_LENGTH_PREFIX_ARRAY_MAP(LEN_TYPE, uint16_t, uint8_t) \
DEFINE_LENGTH_PREFIX_ARRAY_MAP(LEN_TYPE, uint16_t, uint16_t) \
DEFINE_LENGTH_PREFIX_ARRAY_MAP(LEN_TYPE, uint16_t, uint32_t) \
DEFINE_LENGTH_PREFIX_ARRAY_MAP(LEN_TYPE, uint16_t, uint64_t) \
DEFINE_LENGTH_PREFIX_ARRAY_MAP(LEN_TYPE, uint32_t, uint8_t) \
DEFINE_LENGTH_PREFIX_ARRAY_MAP(LEN_TYPE, uint32_t, uint16_t) \
DEFINE_LENGTH_PREFIX_ARRAY_MAP(LEN_TYPE, uint32_t, uint32_t) \
DEFINE_LENGTH_PREFIX_ARRAY_MAP(LEN_TYPE, uint32_t, uint64_t) \
DEFINE_LENGTH_PREFIX_ARRAY_MAP(LEN_TYPE, uint64_t, uint8_t) \
DEFINE_LENGTH_PREFIX_ARRAY_MAP(LEN_TYPE, uint64_t, uint16_t) \
DEFINE_LENGTH_PREFIX_ARRAY_MAP(LEN_TYPE, uint64_t, uint32_t) \
DEFINE_LENGTH_PREFIX_ARRAY_MAP(LEN_TYPE, uint64_t, uint64_t)
DEFINE_ALL_MAPS(uint8_t)
DEFINE_ALL_MAPS(uint16_t)
DEFINE_ALL_MAPS(uint32_t)
DEFINE_ALL_MAPS(uint64_t)

View file

@ -1,34 +1,49 @@
```
```
morph_celsius_to_kelvin ()
Temperature ~ Celsius ~ ~ native.Float
--> Temperature ~ Kelvin ~ ~ native.Float
```
*dst = *src + 273.15;
return 0;
```
morph celsius_to_kelvin :
Temperature ~ Celsius ~ ~ native.Float32
--> Temperature ~ Kelvin ~ ~ native.Float32
= ```*dst = *src + 273.15;```;
morph_kelvin_to_celsius ()
Temperature ~ Kelvin ~ ~ native.Float
--> Temperature ~ Celsius ~ ~ native.Float
```
*dst = *src - 273.15;
return 0;
```
morph kelvin_to_celsius :
Temperature ~ Kelvin ~ ~ native.Float32
--> Temperature ~ Celsius ~ ~ native.Float32
= ```*dst = *src - 273.15;```;
morph_celsius_to_fahrenheit ()
Temperature ~ Celsius ~ ~ native.Float
--> Temperature ~ Fahrenheit ~ ~ native.Float
```
*dst = (*src * 9.0 / 5.0) + 32.0;
return 0;
```
morph celsius_to_fahrenheit :
Temperature ~ Celsius ~ ~ native.Float32
--> Temperature ~ Fahrenheit ~ ~ native.Float32
= ```*dst = (*src * 9.0 / 5.0) + 32.0;```;
morph_fahrenheit_to_celsius ()
Temperature ~ Fahrenheit ~ ~ native.Float
--> Temperature ~ Celsius ~ ~ native.Float
```
*dst = (*src - 32.0) * 5.0 / 9.0;
return 0;
```
morph fahrenheit_to_celsius :
Temperature ~ Fahrenheit ~ ~ native.Float32
--> Temperature ~ Celsius ~ ~ native.Float32
=```*dst = (*src - 32.0) * 5.0 / 9.0;```;
morph pt100_resistance_to_celsius :
Temperature ~ PT100 ~ Resistance ~ Ohms ~ ~ native.Float64
--> Temperature ~ Celsius ~ ~ native.Float64
= ```
double resistance = *src;
// Constants for PT100 (ITS-90 standard)
#define R0 100.0
#define A 3.9083e-3
#define B -5.775e-7
// Solve the quadratic equation: R = R0 * (1 + A*T + B*T^2)
// Rearranged: B*T^2 + A*T + (1 - R/R0) = 0
double a = B;
double b = A;
double c = 1.0 - (resistance / R0);
double discriminant = b * b - 4 * a * c;
if (discriminant < 0) {
fprintf(stderr, "invalid resistance");
return -1;
}
*dst = (-b + sqrt(discriminant)) / (2 * a); // use positive root
```;

View file

@ -2,14 +2,13 @@
#include <time.h>
```
morph_unixtime_to_iso ()
morph unixtime_to_iso :
TimePoint ~ <TimeSince UnixEpoch> ~ Duration ~ Seconds ~ ~ <QuantizedLinear 0 1 1> ~ ~ native.UInt64
--> TimePoint ~ ISO8601 ~ <Seq~<ValueTerminated 0> Char~Ascii~native.UInt8>
```
--> TimePoint ~ ISO8601 ~ [~<ValueTerminated 0> Char~Ascii~native.UInt8 ]
= ```
time_t rawtime = (time_t)(*src);
struct tm *timeinfo = gmtime(&rawtime);
if (!timeinfo) return -1;
strftime((char*)dst, 20, "%Y-%m-%dT%H:%M:%SZ", timeinfo);
return 0;
```
```;

View file

@ -0,0 +1,20 @@
```
```
morph int_as_u8_to_u64 : ∀N:
<_ N> ~ native.UInt8
--> <_ N> ~ native.UInt64
= ```*dst = *src;```;
morph int_as_u64_to_u8 : ∀N:
<_ N> ~ native.UInt64
--> <_ N> ~ native.UInt8
= ```
if ( N < 256 ) {
*dst = *src % N;
}
else {
fprintf(stderr, "radix %u is out of rage for UInt8\n", N);
return -1;
}
```;

View file

@ -2,23 +2,19 @@
#include <stdio.h>
```
morph_string_as_ascii_to_utf8 ()
<Seq ~ <ValueTerminated 0> Char~Ascii~native.UInt8>
--> <Seq Char~Unicode>
~ UTF-8
~ <Seq~<ValueTerminated 0> native.UInt8>
```
morph string_as_ascii_to_utf8 :
[~<ValueTerminated 0> Char~Ascii~native.UInt8 ]
--> [Char~Unicode] ~ UTF-8 ~ [~<ValueTerminated 0> native.UInt8 ]
= ```
while( *src ) { *dst++ = *src++; }
*dst = 0;
return 0;
```
```;
morph_string_as_utf8_to_ascii ()
<Seq Char~Unicode>
~ UTF-8
~ <Seq~<ValueTerminated 0> native.UInt8>
--> <Seq ~ <ValueTerminated 0> Char~Ascii~native.UInt8>
```
morph string_as_utf8_to_ascii :
[Char~Unicode] ~ UTF-8 ~ [~<ValueTerminated 0> native.UInt8 ]
--> [~<ValueTerminated 0> Char~Ascii~native.UInt8 ]
= ```
while( *src ) {
if( *src < 128 ) {
*dst++ = *src++;
@ -28,31 +24,20 @@ morph_string_as_utf8_to_ascii ()
}
}
*dst = 0;
return 0;
```
```;
morph_string_as_ascii_to_utf32 ()
<Seq ~ <ValueTerminated 0> Char~Ascii~native.UInt8>
--> <Seq Char~Unicode>
~ UTF-32
~ <Seq~<ValueTerminated 0> native.UInt32>
```
morph string_as_ascii_to_utf32 :
[~<ValueTerminated 0> Char~Ascii~native.UInt8 ]
--> [Char~Unicode] ~ UTF-32 ~ [~<ValueTerminated 0> native.UInt32 ]
= ```
while( *src ) { *dst++ = *src++; }
*dst = 0;
return 0;
```
```;
morph_string_as_utf8_to_utf32 ()
<Seq Char~Unicode>
~ UTF-8
~ <Seq~<ValueTerminated 0> native.UInt8>
--> <Seq Char~Unicode>
~ UTF-32
~ <Seq~<ValueTerminated 0> native.UInt32>
```
morph string_as_utf8_to_utf32 :
[Char~Unicode] ~ UTF-8 ~ [~<ValueTerminated 0> native.UInt8 ]
--> [Char~Unicode] ~ UTF-32 ~ [~<ValueTerminated 0> native.UInt32 ]
= ```
bool has_multibyte = false;
uint32_t val = 0;
while( *src ) {
@ -89,6 +74,4 @@ morph_string_as_utf8_to_utf32 ()
*dst++ = val;
*dst++ = 0;
return 0;
```
```;

View file

@ -3,97 +3,93 @@
#include <stdlib.h>
```
morph_seqseq_valsep_uint8 (T: Type, SrcDelim: T, DstDelim: T)
< Seq <Seq T> >
morph valsep_delim : ∀T ∀SrcDelim:T ∀DstDelim:T
[[T]]
~ < ValueSep SrcDelim T >
~ < Seq~<LengthPrefix native.UInt64> T >
~ [~<LengthPrefix native.UInt64> T ]
--> < Seq <Seq T> >
--> [[T]]
~ < ValueSep DstDelim T >
~ < Seq~<LengthPrefix native.UInt64> T >
```
length_prefix_uint64_t_array_uint8_t_clear( dst );
~ [~<LengthPrefix native.UInt64> T ]
=```
PRESCAN_LENGTH_PREFIX_CALL(nativeUInt64, T, clear)( dst );
uint8_t * dst_items = dst->items;
for( uint64_t i = 0; i < src->len; ++i ) {
if( src->items[i] == SrcDelim ) {
length_prefix_uint64_t_array_uint8_t_push( dst, DstDelim );
PRESCAN_LENGTH_PREFIX_CALL(nativeUInt64, T, push)( dst, DstDelim );
} else if( src->items[i] == DstDelim ) {
if( DstDelim == '\n' ) {
length_prefix_uint64_t_array_uint8_t_push( dst, '\\' );
length_prefix_uint64_t_array_uint8_t_push( dst, 'n' );
PRESCAN_LENGTH_PREFIX_CALL(nativeUInt64, T, push)( dst, '\\' );
PRESCAN_LENGTH_PREFIX_CALL(nativeUInt64, T, push)( dst, 'n' );
} else {
PRESCAN_LENGTH_PREFIX_CALL(nativeUInt64, T, push)( dst, '\\' );
PRESCAN_LENGTH_PREFIX_CALL(nativeUInt64, T, push)( dst, DstDelim );
}
} else {
length_prefix_uint64_t_array_uint8_t_push( dst, src->items[i] );
PRESCAN_LENGTH_PREFIX_CALL(nativeUInt64, T, push)( dst, src->items[i] );
}
}
```;
return 0;
```
morph_seqseq_as_valsep_to_lenpfx (T: Type, Delim: T, EscKey: T)
< Seq <Seq T> >
morph seqseq_as_valsep_to_lenpfx : ∀T ∀Delim:T ∀EscKey:T
[[T]]
~ < ValueSep T Delim >
~ < Seq~<LengthPrefix native.UInt64> T >
~ [~<LengthPrefix native.UInt64> T ]
--> < Seq~<LengthPrefix native.UInt64>
<Seq~<LengthPrefix native.UInt64> T >
~ <RefMut < Seq~<LengthPrefix native.UInt64> T>>
--> [~<LengthPrefix native.UInt64>
[~<LengthPrefix native.UInt64> T ]
~ <RefMut [~<LengthPrefix native.UInt64> T ] >
~ native.Address
~ native.UInt64
>
```
length_prefix_uint64_t_array_uint64_t_clear( dst );
]
= ```
length_prefix_nativeUInt64_array_nativeUInt64_clear( dst );
struct LengthPrefix_uint64_t_Array_uint8_t * cur_item = NULL;
LENGTH_PREFIX_ARRAY_TYPE( nativeUInt64, T ) * cur_item = NULL;
uint8_t const * start = &src->items[0];
uint8_t const * cur = start;
uint8_t const * end = &src->items[src->len];
T const * start = &src->items[0];
T const * cur = start;
T const * end = &src->items[src->len];
while( cur < end ) {
if( *cur == Delim || cur+1 == end ) {
uint64_t len = cur - start;
cur_item = malloc( sizeof(uint64_t) + sizeof(uint8_t) * len );
cur_item = malloc( sizeof(uint64_t) + sizeof(T) * len );
cur_item->len = len;
memcpy( cur_item->items, start, len );
length_prefix_uint64_t_array_uint64_t_push( dst, (uint64_t)cur_item );
length_prefix_nativeUInt64_array_nativeUInt64_push( dst, (uint64_t)cur_item );
start = ++cur;
} else {
cur++;
}
}
```;
return 0;
```
morph_seqeq_as_lenpfx_to_valsep (T: Type, Delim: T, EscKey: T)
< Seq~<LengthPrefix native.UInt64>
<Seq~<LengthPrefix native.UInt64> T >
~ <RefMut < Seq~<LengthPrefix native.UInt64> T>>
morph seqeq_as_lenpfx_to_valsep : ∀T ∀Delim:T ∀EscKey:T
[~<LengthPrefix native.UInt64>
[~<LengthPrefix native.UInt64> T ]
~ <RefMut [~<LengthPrefix native.UInt64> T ]>
~ native.Address
~ native.UInt64
>
--> < Seq <Seq T> >
]
--> [[T]]
~ < ValueSep T Delim >
~ < Seq~<LengthPrefix native.UInt64> T >
```
length_prefix_uint64_t_array_uint8_t_clear( dst );
~ [~<LengthPrefix native.UInt64> T ]
= ```
PRESCAN_LENGTH_PREFIX_CALL(nativeUInt64, T, clear)( dst );
for( uint64_t i = 0; i < src->len; ++i ) {
LengthPrefix_uint64_t_Array_uint8_t * item = src->items[i];
LENGTH_PREFIX_ARRAY_TYPE( nativeUInt64, T ) * item = src->items[i];
for( uint64_t j = 0; j < item->len; ++j ) {
length_prefix_uint64_t_array_uint8_t_push( items->items[j] );
PRESCAN_LENGTH_PREFIX_CALL( nativeUInt64, T, push )( items->items[j] );
}
if( i+1 < src->len ) {
length_prefix_uint64_t_array_uint8_t_push( Delim );
PRESCAN_LENGTH_PREFIX_CALL( nativeUInt64, T, push )( Delim );
}
}
return 0;
```
```;

View file

@ -0,0 +1,39 @@
```
```
morph wheatstone_reading_to_ohms :
∀Vcc: Potential ~ Volts ~
∀R1: Resistance ~ Ohms ~
∀R2: Resistance ~ Ohms ~
∀R3: Resistance ~ Ohms ~
Resistance ~ <WheatstoneBridge Vcc R1 R2 R3> ~ Potential ~ Volts ~ ~ native.Float64
--> Resistance ~ Ohms ~ ~ native.Float64
= ```
// Voltage at midpoints of two voltage dividers:
// o Vcc
// _/ \_
// R1 | | | | R3
// |_| |_|
// / \
// v1 o(*src)o v2
// \_ _/
// R2 | | | | Rx = (*dst)
// |_| |_|
// \ /
// o GND
constexpr double v1 = Vcc * ((double)R2 / ((double)R1 + (double)R2));
double v2 = v1 + *src;
printf("v1 = %f\n", v1);
printf("v2 = %f\n", v2);
if( v2 < (double)Vcc ) {
// voltage divider equation:
// v2/Vcc = Rx/(Rx + R3)
// Rx = (v2*R3) / (Vcc-v2)
*dst = (v2 * (double)R3) / (Vcc - v2);
printf("R = %f Ω \n", *dst);
} else {
fprintf(stderr, "Error");
}
```;

View file

@ -1,26 +1,24 @@
```
```
morph_i64_as_twos_complement_to_zigzag ()
morph i64_as_twos_complement_to_zigzag :
~ native.Int64
--> ~ ZigZagInt ~ ~ native.UInt64
```
= ```
if( *src >= 0 ) {
*dst = (2 * (uint64_t)*src)
} else {
*dst = (2 * (uint64_t)(- *src)) - 1;
}
```;
return 0;
```
morph_i64_as_zigzag_to_twos_complement ()
morph i64_as_zigzag_to_twos_complement :
~ ZigZagInt ~ ~ native.UInt64
--> ~ native.Int64
```
= ```
if( *src % 2 == 0 ) {
*dst = *src / 2;
} else {
*dst = - ((*src+1) / 2);
}
```
```;

100
platforms/json.lt Normal file
View file

@ -0,0 +1,100 @@
type UTF8-String = <Seq Char~Unicode> ~ UTF-8 ~ <Seq~<ValueTerminated 0> native.UInt8> ;
type SerializedJson.Value = json.Value
~ <Enum
<Null json.Null ~ SerializedJson.Null >
<String json.String ~ SerializedJson.String >
<Number json.Number ~ SerializedJson.Number >
<Bool json.Bool ~ SerializedJson.Bool >
<Array json.Array ~ SerializedJson.Array >
<Object json.Object ~ SerializedJson.Object >>
~ <Seq Char> ;
type SerializedJson.Null = < Struct~<Seq Char> "\"null\"" > ;
type SerializedJson.String = <Seq Char>
~ <Struct~<Seq Char> "\"" <Seq Char> "\"">
~ <Seq Char> ;
type SerializedJson.Number =
~ <PosInt 10 BigEndian>
~ <Seq <Digit 10> ~ Char> ;
type SerializedJson.Bool = Bool
~ <Enum~<Seq Char>
<True~"\"true\"" <>>
<False~"\"false\"" <>>>
~ <Seq Char> ;
type SerializedJson.Array = <Struct~<Seq Char>
"["
<Seq json.Value>
~ <ValueSep ',' Char>
~ <Seq Char>
"]" >
~ <Seq Char> ;
type SerializedJson.Object = <Struct~<Seq Char>
"{"
<members <Seq
<Struct~<Seq Char>
<key json.String>
":"
<value json.Value>>>>
"}"
>
~ <Seq Char>;
type parsedJson.Value = json.Value ~ <Enum~native.UInt8
< parsedJson_Null~0 <> >
< parsedJson_String~1 <Ref UTF8-String> ~ native.Address >
< parsedJson_Int~2 ~~native.UInt64 >
< parsedJson_Float~2 ~native.Float64 >
< parsedJson_Bool~3 Bool ~ native.UInt8 >
< parsedJson_Array~4 <Ref <Seq ~ <LengthPrefix native.UInt64>
parsedJsonValue
>
>
~ native.Address >
< parsedJson_Object~5 <Ref <Seq ~ <LengthPrefix native.UInt64>
<Struct
<key <Ref UTF8-String> ~ native.Address>
<val parsedJsonValue>
>
>
~ native.Address >>
~ <Struct
<tag native.UInt8>
native.UInt64 >;
parse_json_value ()
json.Value ~ UTF8-String
--> json.Value ~ parsedJsonValue
```
{
dst->tag = PARSED_JSON_NULL;
dst->parsedJson_Null;
}
{
dst->tag = PARSED_JSON_STRING;
dst->parsedJson_String = malloc(len);
strncpy(dst->parsedJson_String, src, len);
src += len;
}
{
dst->tag = PARSED_JSON_NUMBER;
MORPH(
" ~ ~ <PosInt 10 BigEndian> ~ <Seq <Digit 10> ~ Char> ~ UTF8-String",
" ~ ~ native.UInt64",
src,
&dst->parsedJson_Number
);
}
```

0
platforms/json5.lt Normal file
View file

3
platforms/protobuf.lt Normal file
View file

@ -0,0 +1,3 @@
type protobuf.Varint = ~ <PosInt 128 LittleEndian> ~ <Seq~MsbCont <Digit 128> ~ _128 ~ native.UInt8> ;
type protobuf.String = <Seq Char ~ Unicode> ~ UTF-8 ~ <Seq ~ <LengthPrefix protobuf.Varint> Byte> ;

0
platforms/spapod.lt Normal file
View file

5
platforms/x86.lt Normal file
View file

@ -0,0 +1,5 @@
type x86.UInt64 = _2/\64 ~ <PosInt 256 LittleEndian> ~ <Seq~<StaticLength 8> <Digit 256> ~ x86.UInt8 >;
type x86.UInt32 = _2/\32 ~ <PosInt 256 LittleEndian> ~ <Seq~<StaticLength 4> <Digit 256> ~ x86.UInt8 >;
type x86.UInt16 = _2^16 ~ <PosInt 256 LittleEndian> ~ <Seq~<StaticLength 2> <Digit 256> ~ x86.UInt8 >;
type x86.UInt8 = _256 ~ <Posint 2 BigEndian> ~ <Seq~<StaticLength 8> <Digit 2> ~ Bit>;

View file

@ -1,419 +0,0 @@
use {
laddertypes::{TypeDict, TypeTerm, parser::*, unparser::*, morphism::{Morphism, MorphismInstance}},
crate::morphism::{LdmcPrimCMorphism, LdmcMorphism}
};
/*
*/
pub fn get_c_repr_kind(kind: String) -> Option<String> {
match kind.as_str() {
"" => Some("uint64_t".into()),
_ => None
}
}
/* for a given ladder type `t`, get the corresponding C type
*/
pub fn get_c_repr_type(dict: &mut impl TypeDict, t: laddertypes::TypeTerm, skip_pointer: bool) -> Option<String> {
let lnf = t.normalize().decurry().get_lnf_vec();
match lnf.last() {
Some(t) => {
if t == &dict.parse("Byte").expect("parse")
|| t == &dict.parse("native.UInt8").expect("parse")
|| t == &dict.parse("<StaticLength 8 Bit>").expect("parse")
{
Some("uint8_t".into())
} else if t == &dict.parse("native.UInt16").expect("parse") {
Some("uint16_t".into())
} else if t == &dict.parse("native.UInt32").expect("parse") {
Some("uint32_t".into())
} else if t == &dict.parse("native.UInt64").expect("parse") {
Some("uint64_t".into())
} else if t == &dict.parse("native.Float").expect("parse") {
Some("float".into())
} else if t == &dict.parse("native.Double").expect("parse") {
Some("double".into())
} else {
match t {
laddertypes::TypeTerm::App(args) => {
if args[0] == laddertypes::TypeTerm::TypeID(dict.get_typeid(&"LengthPrefix".into()).unwrap())
{
let length_c_type : String = get_c_repr_type(dict, args[1].clone(), false)?;
let item_c_type : String = get_c_repr_type(dict, args[2].clone(), false)?;
match length_c_type.as_str() {
"uint8_t" |
"uint16_t" |
"uint32_t" |
"uint64_t" => {}
_ => {
eprintln!("invalid length type!");
return None;
}
}
match item_c_type.as_str() {
"uint8_t" |
"uint16_t" |
"uint32_t" |
"uint64_t" => {}
_ => {
eprintln!("invalid item type!");
return None;
}
}
Some(format!("LengthPrefix_{}_Array_{}", length_c_type, item_c_type))
}
else if args[0] == laddertypes::TypeTerm::TypeID(dict.get_typeid(&"ValueTerminated".into()).unwrap())
{
let _delim_val = args[1].clone();
let c_type = get_c_repr_type(dict, args[2].clone(), false)?;
if skip_pointer {
Some(c_type)
} else {
Some(format!("{} *", c_type))
}
}
else if args[0] == laddertypes::TypeTerm::TypeID(dict.get_typeid(&"MsbCont".into()).unwrap())
{
let c_type = get_c_repr_type(dict, args[1].clone(), false)?;
if skip_pointer {
Some(c_type)
} else {
Some(format!("{} *", c_type))
}
}
else {
None
}
}
_ => None
}
}
}
None => None
}
}
pub fn encode_type_to_symbol(dict: &mut impl TypeDict, t: &laddertypes::TypeTerm)-> String {
match t {
laddertypes::TypeTerm::Char(c) => {
match c {
'.' => format!("_dot_"),
_ =>
format!("{}", (*c as u64))
}
},
laddertypes::TypeTerm::Num(n) => {
format!("{}", n)
}
laddertypes::TypeTerm::TypeID(ty_id) => {
if let Some(name) = dict.get_typename(ty_id) {
format!("{}", name.replace(".", "_dot_"))
} else {
format!("")
}
}
laddertypes::TypeTerm::Ladder(rs) |
laddertypes::TypeTerm::App(rs) => {
let mut s = String::new();
for r in rs {
s.push('_');
s.push_str(&encode_type_to_symbol(dict, r));
}
s
}
}
}
pub fn encode_type_to_value(dict: &mut impl TypeDict, t: &laddertypes::TypeTerm) -> String {
dict.unparse(t)
}
pub fn generate_main(type_dict: &mut impl TypeDict, path: Vec<MorphismInstance<LdmcMorphism>>, src_type: TypeTerm, dst_type: TypeTerm) {
let mut i = 0;
/* todo: collect include files from morphism base */
println!(r#"
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
"#);
let mut existing_instantiations = Vec::new();
for morph_inst in path.iter() {
match &morph_inst.m {
LdmcMorphism::Primitive( item_morph ) => {
let name = item_morph.instantiated_symbol_name(type_dict, &morph_inst.σ);
if ! existing_instantiations.contains(&name) {
if let Some(s) = item_morph.generate_instantiation(type_dict, &morph_inst.σ) {
println!("{}", s);
} else {
eprintln!("couldnt generate instance {}", name);
}
existing_instantiations.push( name );
}
}
LdmcMorphism::LengthPrefixMap { length_prefix_type, item_morph } => {
let name = item_morph.instantiated_symbol_name(type_dict, &morph_inst.σ);
if ! existing_instantiations.contains(&name) {
if let Some(s) = item_morph.generate_instantiation(type_dict, &morph_inst.σ) {
println!("{}", s);
} else {
eprintln!("couldnt generate instance {}", name);
}
existing_instantiations.push( name );
}
}
_ => {}
}
}
println!(r#"
int main() {{
uint8_t bufA[128];
uint8_t bufB[128];
memset(bufA, 0, sizeof(bufA));
memset(bufB, 0, sizeof(bufB));
char in_str[] = "read :: {} \n";
char out_str[]= "write:: {} \n";
write(2, in_str, strlen(in_str));
write(2, out_str, strlen(out_str));
int l = read(0, bufA, sizeof(bufA));
//fprintf(stderr, "read %d bytes\n", l);
"#,
type_dict.unparse(&src_type).replace("\\", "\\\\"),
type_dict.unparse(&dst_type).replace("\\", "\\\\") );
for morph_inst in path.iter() {
println!(r#"
/* morph to {}
...with
morph {}
---> {},
subst σ = {:?},
halo Ψ = {}
*/"#,
type_dict.unparse(&morph_inst.get_type().dst_type.param_normalize().decurry()),
type_dict.unparse(&morph_inst.m.get_type().src_type),
type_dict.unparse(&morph_inst.m.get_type().dst_type),
morph_inst.σ,
type_dict.unparse(&morph_inst.halo),
);
morph_inst.m.generate_call(type_dict, &morph_inst.σ, i);
i += 1;
}
let out_buf = if i%2==0 { "bufA" } else { "bufB" };
let is_string = false;
if let Ok((halo, σ)) = laddertypes::subtype_unify(
&dst_type,
&type_dict.parse("<Seq~<ValueTerminated 0> native.UInt8>").unwrap()
) {
println!(r#"
printf("%s\n", {});"#, out_buf);
} else if let Ok((halo, σ)) = laddertypes::subtype_unify(
&dst_type,
&type_dict.parse("<Seq~<LengthPrefix native.UInt64> native.UInt8>").unwrap()
) {
println!(r#"
/* write output
*/
{{
LengthPrefix_uint64_t_Array_uint8_t * buf = (void*){};
write(1, {}, sizeof(uint64_t) + buf->len);
}}"#, out_buf, out_buf);
} else {
println!(r#"
write(1, {}, sizeof({}));"#,
out_buf,
out_buf
);
}
println!(r#"
return 0;
}}
"#);
eprintln!("Success: generated C code");
}
impl LdmcPrimCMorphism {
pub fn instantiated_symbol_name(&self, dict: &mut impl TypeDict,
σ: &std::collections::HashMap<laddertypes::TypeID, laddertypes::TypeTerm>) -> String {
let mut s = self.symbol.clone();
for (k_id,v) in self.type_args.iter() {
if let Some(val_trm) = σ.get(k_id) {
if let laddertypes::TypeID::Var(var_id) = k_id {
let name = dict.get_varname(*var_id).unwrap();
let val_str = encode_type_to_symbol(dict, val_trm);
s.push_str(&format!("_{}_{}", name, val_str));
}
} else {
if let laddertypes::TypeID::Var(var_id) = k_id {
let k = dict.get_varname(*var_id).unwrap();
s.push_str(&format!("_{:?}_MISSING", k));
eprintln!("INCOMPLETE MORPHISM INSTANTIATION, missing {} ({})", k, var_id);
}
}
}
s
}
pub fn expected_c_type_signature(&self, dict: &mut impl TypeDict,
σ: &std::collections::HashMap<laddertypes::TypeID, laddertypes::TypeTerm>) -> String {
format!("int {} ({} const * restrict src, {} * restrict dst);",
self.instantiated_symbol_name(dict, σ),
get_c_repr_type(dict, self.src_type.clone(), true).expect("cant get c-repr type for src type"),
get_c_repr_type(dict, self.dst_type.clone(), true).expect("cant get c-repr type for dst type"))
}
pub fn generate_instantiation(&self, dict: &mut impl TypeDict, σ: &std::collections::HashMap<laddertypes::TypeID, laddertypes::TypeTerm>) -> Option<String> {
let mut s = String::new();
s.push_str(&format!(r#"
int {} ( {} const * restrict src, {} * restrict dst ) {{
"#,
self.instantiated_symbol_name(dict, &σ),
get_c_repr_type(dict, self.src_type.clone().apply_substitution(&|k| σ.get(k).cloned()).clone(), true).expect("cant get c-repr-type"),
get_c_repr_type(dict, self.dst_type.clone().apply_substitution(&|k| σ.get(k).cloned()).clone(), true).expect("cant get c-repr-type"),
));
for (ty_id, kind) in self.type_args.iter() {
if let Some(val) = σ.get(ty_id) {
eprintln!("val = {}", dict.unparse(&val));
let type_var_value =
if let Some(c_repr_type) = get_c_repr_type(dict, val.clone(), true) {
c_repr_type
} else {
encode_type_to_value(dict, val)
};
s.push_str(&format!(" #define {} {}\n", dict.get_typename(&ty_id).unwrap(), type_var_value));
}
}
s.push_str(&self.c_source);
for (ty_id, kind) in self.type_args.iter() {
s.push_str(&format!("\n #undef {}", dict.get_typename(&ty_id).unwrap()));
}
s.push_str(&format!(r#"
}}
"#));
Some(s)
}
}
impl LdmcMorphism {
pub fn generate_call(&self, dict: &mut impl TypeDict, σ: &std::collections::HashMap<laddertypes::TypeID, laddertypes::TypeTerm>, i: u64) {
match self {
LdmcMorphism::Primitive(prim_morph) => {
let src_c_type = get_c_repr_type(dict, prim_morph.src_type.clone().apply_substitution(&|k| σ.get(k).cloned()).clone(), true).expect("cant get c-repr type for src type");
let dst_c_type = get_c_repr_type(dict, prim_morph.dst_type.clone().apply_substitution(&|k| σ.get(k).cloned()).clone(), true).expect("cant get c-repr type for dst type");
let src_buf = if i%2 == 0 { "bufA" } else { "bufB" };
let dst_buf = if i%2 == 0 { "bufB" } else { "bufA" };
println!(r#"
{}
{} const * restrict src = (void*) {};
{} * restrict dst = (void*) {};
{} ( src, dst );
{}"#,
'{',
src_c_type, src_buf,
dst_c_type, dst_buf,
prim_morph.instantiated_symbol_name(dict, σ),
'}');
}
LdmcMorphism::LengthPrefixMap { length_prefix_type, item_morph } => {
let src_type = self.get_type().src_type.clone().apply_substitution(&|k| σ.get(k).cloned()).clone();
let dst_type = self.get_type().dst_type.clone().apply_substitution(&|k| σ.get(k).cloned()).clone();
eprintln!("length prefix type ={:?}", length_prefix_type);
eprintln!("src_type ={:?}", src_type);
eprintln!("dst_type = {:?}", dst_type);
dict.add_varname("S".into());
let γ_src = laddertypes::unify(
&dict.parse("<Seq~<LengthPrefix S> T>").expect("parse template"),
&src_type
).expect("cant get src item type");
let γ_dst = laddertypes::unify(
&dict.parse("<Seq~<LengthPrefix S> T>").expect("parse template"),
&dst_type
).expect("cant get dst item type");
let src_length_type = γ_src.get(&dict.get_typeid(&"S".into()).unwrap()).expect("cant get src-length type").clone();
let dst_length_type = γ_src.get(&dict.get_typeid(&"S".into()).unwrap()).expect("cant get dst-length type").clone();
let length_type = src_length_type;
let src_item_type = γ_src.get(&dict.get_typeid(&"T".into()).unwrap()).expect("cant get src-item type").clone();
let dst_item_type = γ_dst.get(&dict.get_typeid(&"T".into()).unwrap()).expect("cant get src-item type").clone();
let length_c_type = get_c_repr_type(dict, length_type, true).expect("cant c-repr type for array length");
let src_item_c_type = get_c_repr_type(dict, src_item_type, true).expect("cant c-repr type for src item");
let dst_item_c_type = get_c_repr_type(dict, dst_item_type, true).expect("cant c-repr type for dst item");
let src_c_type = get_c_repr_type(dict, src_type, true).expect("cant get c-repr type for src type");
let dst_c_type = get_c_repr_type(dict, dst_type, true).expect("cant get c-repr type for dst type");
let map_fn = format!("length_prefix_{}_array_map_{}_to_{}",
length_c_type, src_item_c_type, dst_item_c_type
);
let src_buf = if i%2 == 0 { "bufA" } else { "bufB" };
let dst_buf = if i%2 == 0 { "bufB" } else { "bufA" };
println!(r#"
{}
{} const * restrict src = (void*) {};
{} * restrict dst = (void*) {};
{} ( {}, src, dst );
{}"#,
'{',
src_c_type, src_buf,
dst_c_type, dst_buf,
map_fn,
item_morph.instantiated_symbol_name(dict, σ),
'}');
}
LdmcMorphism::ValueDelimMap { delim, item_morph } => {
let src_c_type = get_c_repr_type(dict, item_morph.src_type.clone(), false).expect("cant get c-repr type for src type");
let dst_c_type = get_c_repr_type(dict, item_morph.dst_type.clone(), false).expect("cant get c-repr type for dst type");
let src_buf = if i%2 == 0 { "bufA" } else { "bufB" };
let dst_buf = if i%2 == 0 { "bufB" } else { "bufA" };
println!(r#"
{}
{} const * restrict src = (void*) {};
{} * restrict dst = (void*) {};
value_delim_array_map_8_to_64( {}, src, dst );
{}"#,
'{',
src_c_type, src_buf,
dst_c_type, dst_buf,
item_morph.instantiated_symbol_name(dict, σ),
'}');
}
}
}
}

85
src/c_gen/gen_lib.rs Normal file
View file

@ -0,0 +1,85 @@
use {
super::types::get_c_repr_arg_type, crate::{c_gen::LdmcCTargetMorph, LdmcPrimMorph},
laddertypes::{morphism::MorphismInstance, parser::*, Context, ContextPtr, LayeredContext, MorphismType, TypeDict},
std::{collections::HashMap, sync::{Arc, RwLock}}
};
pub fn generate_lib(
ctx: &ContextPtr,
include_blocks: Vec< String >,
include_dependencies: HashMap< MorphismType, usize >,
morphisms: Vec< (String, MorphismInstance<LdmcPrimMorph>) >
) -> Result<String, ()> {
let mut root_ctx = ctx.scope(laddertypes::AddressingMode::StackUp);
let mut target = LdmcCTargetMorph::new( root_ctx.clone(), include_blocks, include_dependencies );
let mut wrappers = String::new();
for (name, morph) in morphisms {
match target.add_instantiation(HashMap::new(), morph) {
Ok((σs, inst)) => {
if name == "main" {
let mut c_source = String::new();
c_source.push_str(&format!(r#"
#include <unistd.h>
int main() {{
uint8_t bufIn[4096];
uint8_t bufOut[4096];"#));
if let Ok(_) = laddertypes::subtype_unify(
&inst.ty.apply_subst(&σs).src_type,
&root_ctx.parse("[~<ValueTerminated '\\n'> Char~Ascii~native.UInt8]").expect("")
) {
c_source.push_str("scanf(\"%s\", bufIn);\n");
} else {
c_source.push_str("read(0, bufIn, sizeof(bufIn));\n");
}
c_source.push_str(
&format!(r#"FUSE( {}, (void const*)bufIn, (void*)bufOut );
"#,
inst.instantiated_symbol_name(&root_ctx, &σs)
));
if let Ok(ψ) = laddertypes::subtype_unify(
&inst.ty.dst_type,
&root_ctx.parse("[~<ValueTerminated 0> native.UInt8]").expect("")
) {
c_source.push_str("printf(\"%s\\n\", bufOut);\n");
} else {
c_source.push_str("write(1, bufOut, sizeof(bufOut));\n");
}
c_source.push_str("
return 0;
}");
wrappers.push_str(&c_source);
} else {
target.add_type(inst.ty.src_type.clone());
target.add_type(inst.ty.dst_type.clone());
wrappers.push_str(&format!("
int {} (
{} const * restrict src,
{} * restrict dst
) {{
return {}( (void*)src, (void*)dst );
}}
", name,
get_c_repr_arg_type(root_ctx.clone(), &inst.ty.src_type),
get_c_repr_arg_type(root_ctx.clone(), &inst.ty.dst_type),
inst.instantiated_symbol_name(&root_ctx, &σs)
));
}
}
Err(_err) => {
eprintln!("failed to create morphism instatiation");
return Err(());
}
}
}
let mut c_source = target.into_c_source();
c_source.push_str(&wrappers);
Ok(c_source)
}

7
src/c_gen/mod.rs Normal file
View file

@ -0,0 +1,7 @@
pub mod types;
pub mod morph;
pub mod gen_lib;
pub use {
morph::target_morph::LdmcCTargetMorph
};

61
src/c_gen/morph/mod.rs Normal file
View file

@ -0,0 +1,61 @@
pub mod target_morph;
use {
crate::{
c_gen::types::{
get_c_repr_arg_type,
get_c_repr_definition,
get_c_repr_type
},
morphism::LdmcPrimMorph
},
laddertypes::{unparser::UnparseLadderType, ContextPtr, HashMapSubst, Morphism, Substitution, TypeDict},
};
impl LdmcPrimMorph {
pub fn instantiated_symbol_name(&self, ctx: &ContextPtr, σs: &HashMapSubst) -> String {
let mut s = self.symbol.clone();
for (i,entry) in self.get_type().Γ.iter().enumerate() {
if let Some(mut t) = σs.get(&(i as u64)).cloned() {
t.apply_subst(ctx);
let val_str = get_c_repr_type(ctx, &t);
s.push_str(&format!("_{}_{}", entry.symbol, val_str));
}
}
s
}
pub fn expected_c_type_signature(&self, ctx: &ContextPtr, σs: &HashMapSubst) -> String {
let ty = self.get_type().apply_subst(σs).apply_subst(ctx);
format!("int {} ({} const * restrict src, {} * restrict dst);",
self.instantiated_symbol_name(ctx, σs),
get_c_repr_definition(ctx.clone(), ty.src_type, true).expect("cant get c-repr type for src type"),
get_c_repr_definition(ctx.clone(), ty.dst_type, true).expect("cant get c-repr type for dst type"))
}
pub fn generate_instantiation(&mut self, ctx: &ContextPtr, σs: &HashMapSubst) -> Option<String> {
let mut s = String::new();
let symbol = self.instantiated_symbol_name(ctx, σs);
eprintln!("generate instantiation:");
let ty = self.ty.clone().apply_subst(ctx);
eprintln!("full type: {} ----> {}", ty.src_type.pretty(&mut ctx.clone(), 0), ty.dst_type.pretty(&mut ctx.clone(),0));
//let ty = ty.strip_common_rungs();
//eprintln!("stripped type: {} ----> {}", ty.src_type.pretty(&mut Γ.clone(), 0), ty.dst_type.pretty(&mut Γ.clone(),0));
let src_c_symbol = get_c_repr_arg_type(ctx.clone(), &ty.src_type);
let dst_c_symbol = get_c_repr_arg_type(ctx.clone(), &ty.dst_type);
s.push_str(&format!(r#"
int {} ( {} const * restrict src, {} * restrict dst ) {{
"#,
symbol, src_c_symbol, dst_c_symbol,
));
s.push_str(&self.c_source);
s.push_str(&format!(r#"
return 0;
}}
"#));
Some(s)
}
}

View file

@ -0,0 +1,419 @@
use {
crate::{
c_gen::types::{
encode_morph_type_to_symbol, encode_type_to_value, get_c_repr_definition, get_c_repr_type
},
morphism::LdmcPrimMorph
},
laddertypes::{
parser::*, ContextEntry, ContextPtr, HashMapSubst, LayeredContext, Morphism, MorphismInstance, MorphismType, SubstitutionMut, TypeDict, TypeKind, TypeTerm
}, std::{collections::HashMap, ops::Deref}
};
pub struct LdmcCTargetMorph {
ctx: ContextPtr,
header_blocks: Vec< String >,
header_dependencies: HashMap< MorphismType, usize >,
active_headers: Vec< usize >,
active_types: Vec< TypeTerm >,
active_morphisms: Vec< (HashMapSubst, LdmcPrimMorph) >,
active_lenpfx_types: Vec< (TypeTerm, TypeTerm) >,
typedefs: Vec< String >,
macro_calls: Vec< String >
}
impl LdmcCTargetMorph {
pub fn add_required_header_block(&mut self, block: String) {
if ! self.header_blocks.contains(&block) {
let i = self.header_blocks.len();
self.active_headers.push(i);
self.header_blocks.push(block);
}
}
pub fn new(
Γ: ContextPtr,
include_blocks: Vec< String >,
include_dependencies: HashMap< MorphismType, usize >
) -> Self {
let mut m = LdmcCTargetMorph {
ctx: Γ,
header_blocks: include_blocks,
header_dependencies: include_dependencies,
active_headers: Vec::new(),
active_morphisms: Vec::new(),
active_types: Vec::new(),
active_lenpfx_types: Vec::new(),
typedefs: Vec::new(),
macro_calls: Vec::new()
};
m.add_required_header_block(
"/* default ldmc header */
#include <stdint.h>
#define FUSE(morph, src, dst) { int result = morph(src,dst); if(result) return result; }
".into()
);
m
}
pub fn add_type(&mut self, ty: TypeTerm) {
let ty = ty.strip();
if ! self.active_types.contains(&ty) {
eprintln!("add type {}", ty.pretty(&mut self.ctx,0));
self.active_types.push(ty.clone());
let (ht,ft) = ty.get_floor_type();
if ht.is_empty() {
match &ft {
TypeTerm::Seq { seq_repr, item } => {
let item_type = item.deref().clone();
self.add_type(item_type.clone());
if let Some(seq_repr) = seq_repr {
let mut ctx = self.ctx.scope(laddertypes::AddressingMode::StackUp);
ctx.add_variable("LengthType", TypeKind::Type);
if let Ok(σ) =
laddertypes::constraint_system::unify(
seq_repr.as_ref(),
&ctx.parse("<LengthPrefix LengthType>").expect("")
)
{
let length_type = σ.get(&ctx.get_varid("LengthType").expect("")).expect("cant get Length type");
self.add_type(length_type.clone());
self.macro_calls.push(
format!("DEFINE_LENGTH_PREFIX_ARRAY({}, {})\n",
get_c_repr_type(&ctx, &length_type),
get_c_repr_type(&ctx, &item_type)
)
);
}
}
}
_ => {}
}
if let Some(type_def) = get_c_repr_definition(self.ctx.clone(), ft, false) {
let type_name = get_c_repr_type(&self.ctx, &ty);
self.typedefs.push(format!("typedef {} {};\n", type_def, type_name));
} else {
eprintln!("cant get c-repr type for type '{}'", ty.pretty(&mut self.ctx,0));
}
} else {
let type_name = get_c_repr_type(&self.ctx, &ty);
let type_def = get_c_repr_type(&self.ctx, &ft);
self.add_type(ft);
self.typedefs.push(format!("typedef {} {};\n", type_def, type_name));
}
}
}
pub fn add_instantiation(
&mut self,
σ: HashMapSubst,
morph: MorphismInstance<LdmcPrimMorph>,
) -> Result<(HashMapSubst, LdmcPrimMorph), ()> {
let (σs, new_inst) = self.bake_morphism(TypeTerm::unit(), σ, morph)?;
eprintln!("add inst: σs.len() = {}", σs.len());
if ! self.active_morphisms.contains(&(σs.clone(), new_inst.clone())) {
self.active_morphisms.push((σs.clone(), new_inst.clone()));
}
let ty = new_inst.get_type();//.apply_subst(&new_inst.Γ);
self.add_type(ty.src_type);
self.add_type(ty.dst_type);
Ok((σs, new_inst))
}
pub fn into_c_source(mut self) -> String {
let mut source = String::new();
self.active_headers.sort();
self.active_headers.dedup();
self.typedefs.dedup();
self.macro_calls.dedup();
for i in self.active_headers {
source.push_str( &self.header_blocks[i] );
source.push('\n');
}
source.push('\n');
for typedef in self.typedefs {
source.push_str(&typedef);
}
source.push('\n');
for m in self.macro_calls {
source.push_str(&m);
source.push('\n');
}
source.push('\n');
for (σs, mut m) in self.active_morphisms {
source.push_str(&m.generate_instantiation(&self.ctx, &σs).expect("cant create function"));
}
source
}
pub fn bake_morphism(
&mut self,
mut ψ: TypeTerm,
mut σ0: HashMapSubst,
morph_inst: MorphismInstance<LdmcPrimMorph>,
) -> Result<(HashMapSubst, LdmcPrimMorph), ()> {
let ty = morph_inst.get_type();
let symbol = encode_morph_type_to_symbol(&self.ctx, &ty.clone().apply_subst(&σ0));
eprintln!("BAKE {}", symbol);
match &morph_inst {
MorphismInstance::Id { τ } => {
self.add_required_header_block("#include <string.h>".into());
Ok((HashMap::new(), LdmcPrimMorph {
symbol,
ty: MorphismType {
Γ: Vec::new(),
bounds: Vec::new(),
src_type: τ.clone(),
dst_type: τ.clone()
},
c_source: String::from("memcpy(dst, src, sizeof(*src));")
}))
}
MorphismInstance::Sub { ψ:ψ1, m } => {
ψ = TypeTerm::Ladder(vec![ ψ1.clone(), ψ.clone() ]).normalize();
self.bake_morphism(ψ, σ0, m.deref().clone())
}
MorphismInstance::Specialize { σ, m } => {
σ0.append(&σ);
self.bake_morphism(ψ, σ0.clone(), m.deref().clone())
}
MorphismInstance::Primitive { σs, m: morph } => {
if let Some(i) = self.header_dependencies.get(&morph.get_type()) {
self.active_headers.push(*i);
}
let mut c_source = String::new();
for (i,entry) in morph.get_type().Γ.iter().enumerate() {
if let Some(mut val) = σs.get(&(i as u64)).cloned() {
val.apply_subst(&σ0);
let type_var_value =
match &entry.kind {
TypeKind::Type => { get_c_repr_type(&self.ctx, &val) },
TypeKind::Value(t) => { encode_type_to_value(&self.ctx, &val) },
_ => "/* ERROR */".into()
};
c_source.push_str(&format!(" #define {} {}\n", entry.symbol, type_var_value));
} else {
// todo: add variable to new_ctx
c_source.push_str(&format!("// Error: cannot '#define': missing variable name for Var({})", i));
}
}
c_source.push_str(&morph.c_source);
for entry in morph.get_type().Γ.iter() {
c_source.push_str(&format!("\n #undef {}", entry.symbol));
}
let mut ty = morph_inst.get_type().apply_subst(&σ0);
ty.Γ = ty.Γ.iter().enumerate().filter_map(|(i,entry)| {
if ty.src_type.contains_var(i as u64)
|| ty.dst_type.contains_var(i as u64)
{
Some(entry.clone())
} else {
None
}
}).collect();
Ok((σs.clone(), LdmcPrimMorph{
symbol: morph.instantiated_symbol_name(&self.ctx, σs),
ty,
c_source
}))
},
MorphismInstance::Chain { path } => {
let mut c_source = String::new();
if path.len() > 1 {
c_source.push_str(r#"
uint8_t bufA[4096];
uint8_t bufB[4096];
"#);
}
for (i,morph) in path.iter().enumerate() {
if let Ok((σs, inst)) = self.add_instantiation(σ0.clone(), morph.clone()) {
//let ty = inst.get_type().apply_subst(&inst.Γ).apply_subst(Γ0);
let morph_symbol = inst.instantiated_symbol_name(&self.ctx, &σs);
let src_buf = if i == 0 { "(void*)src" } else if i%2 == 0 { "(void*)bufA" } else { "(void*)bufB" };
let dst_buf = if i+1 == path.len() { "(void*)dst" } else if i%2 == 0 { "(void*)bufB" } else { "(void*)bufA" };
c_source.push_str(&format!(r#"
FUSE( {}, {}, {} );"#,
morph_symbol, src_buf, dst_buf,
));
} else {
c_source.push_str(&format!("/* ERROR: missing morphism */"));
eprintln!("failed to add instantiation of item morphism");
}
}
Ok((HashMap::new(), LdmcPrimMorph {
symbol,
ty: ty.apply_subst(&σ0),
c_source
}))
}
MorphismInstance::MapSeq { seq_repr, item_morph } => {
if let Ok((σs, item_morph_inst)) = self.add_instantiation(σ0, item_morph.as_ref().clone()) {
let item_type = item_morph_inst.ty.src_type.clone();
if let Some(seq_repr) = seq_repr {
let mut ctx = self.ctx.scope(laddertypes::AddressingMode::StackUp);
ctx.add_variable("Length", TypeKind::Value(self.ctx.clone().parse("").expect("")));
ctx.add_variable("LengthType", TypeKind::Type);
ctx.add_variable("TerminatorValue", TypeKind::Value(item_type));
if let Ok(γ) = laddertypes::constraint_system::unify(
&ctx.parse("<StaticLength Length>").expect("parse type template"),
seq_repr.as_ref()
) {
let length = γ.get(&ctx.get_varid("Length").expect("")).expect("cant get Length");
match length {
TypeTerm::Num(l) => {
let item_morph_symbol = item_morph_inst.instantiated_symbol_name(&self.ctx, &σs);
let mty = morph_inst.get_type().strip_common_rungs().apply_subst(&σs);
self.add_type( mty.src_type );
self.add_type( mty.dst_type );
let c_source = format!(r#"
for( size_t i = 0; i < {}; ++i ) {{
FUSE( {}, &src->items[i], &dst->items[i] );
}}
"#,
l,
item_morph_symbol,
);
Ok((σs, LdmcPrimMorph{
symbol, ty,
c_source
}))
}
_ => {
eprintln!("invalid length '{}'", length.pretty(&mut ctx, 0));
Err(())
}
}
}
else if let Ok(γ) = laddertypes::constraint_system::unify(
&ctx.parse("<LengthPrefix LengthType>").expect("parse type template"),
seq_repr.as_ref()
) {
// todo: assert that length type is native.UIntX
//let length_type = γ.get(&dict.get_typeid(&"LengthType".into()).expect("")).expect("cant get LengthType");
let item_morph_symbol = item_morph_inst.instantiated_symbol_name(&self.ctx, &σs);
self.add_type( morph_inst.get_type().strip_common_rungs().src_type );
self.add_type( morph_inst.get_type().strip_common_rungs().dst_type );
let c_source = format!(r#"
for( size_t i = 0; i < src->len; ++i ) {{
FUSE( {}, &src->items[i], &dst->items[i] );
}}
dst->len = src->len;
"#,
item_morph_symbol,
);
Ok((σs, LdmcPrimMorph{
symbol, ty,
c_source
}))
}
else if let Ok(γ) = laddertypes::constraint_system::unify(
&ctx.parse("<ValueTerminated TerminatorValue>").expect("parse type template"),
seq_repr.as_ref()
) {
let terminator_value = γ.get(&ctx.get_varid("TerminatorValue").expect("")).expect("cant get TerminatorValue");
let item_morph_symbol = item_morph_inst.instantiated_symbol_name(&self.ctx, &σs);
let terminator = encode_type_to_value(&self.ctx, terminator_value);
let c_source = format!(r#"
while( *src != {} ) {{
FUSE( {}, src, dst );
++src;
++dst;
}}
*dst = {};"#,
terminator,
item_morph_symbol,
terminator
);
Ok((σs.clone(), LdmcPrimMorph{
symbol, ty,
c_source
}))
}
else {
eprintln!("Error: Unknown Seq- Representation!!");
Err(())
}
} else {
eprintln!("Error: missing Seq- Representation!!");
Err(())
}
} else {
eprintln!("failed to add item-morph instantiation");
Err(())
}
},
MorphismInstance::MapStruct { struct_repr, member_morph } => {
let mut c_source = String::new();
let mut σs0 = HashMap::new();
for (name, morph) in member_morph.iter() {
if let Ok((σs, inst)) = self.add_instantiation(σ0.clone(), morph.clone()) {
σs0.append(&σs);
let name = name.replace("-", "_").replace(".","_dot_");
c_source.push_str(
&format!("
FUSE( {}, (void*)&src->{}, (void*)&dst->{} );",
inst.symbol,
name, name
));
} else {
c_source.push_str(&format!("/* ERROR: missing morphism for struct member '{}' */", name));
eprintln!("failed to create morphism instantiation for struct member");
return Err(());
}
}
Ok((σs0, LdmcPrimMorph{
symbol,
ty,
c_source
}))
}
MorphismInstance::MapEnum { enum_repr, variant_morph } => {
todo!();
}
}
}
}

326
src/c_gen/types/mod.rs Normal file
View file

@ -0,0 +1,326 @@
use {
crate::struct_layout::LayoutType,
laddertypes::{
morphism::MorphismType, parser::*, unparser::*, ContextPtr, EnumVariant, LayeredContext, StructMember, TypeDict, TypeID, TypeKind, TypeTerm
},
std::{ops::Deref}
};
/* for a given ladder type `t`, get the corresponding C type
*/
pub fn get_c_repr_definition(mut root_ctx: ContextPtr, t: TypeTerm, skip_pointer: bool) -> Option<String> {
match t {
TypeTerm::Id(tyid) => {
if TypeID::Fun(tyid) == root_ctx.get_typeid_creat("native.UInt8") {
Some("uint8_t".into())
} else if TypeID::Fun(tyid) == root_ctx.get_typeid_creat("native.UInt16") {
Some("uint16_t".into())
} else if TypeID::Fun(tyid) == root_ctx.get_typeid_creat("native.UInt32") {
Some("uint32_t".into())
} else if TypeID::Fun(tyid) == root_ctx.get_typeid_creat("native.UInt64") {
Some("uint64_t".into())
} else if TypeID::Fun(tyid) == root_ctx.get_typeid_creat("native.Int8") {
Some("int8_t".into())
} else if TypeID::Fun(tyid) == root_ctx.get_typeid_creat("native.Int16") {
Some("int16_t".into())
} else if TypeID::Fun(tyid) == root_ctx.get_typeid_creat("native.Int32") {
Some("int32_t".into())
} else if TypeID::Fun(tyid) == root_ctx.get_typeid_creat("native.Int64") {
Some("int64_t".into())
} else if TypeID::Fun(tyid) == root_ctx.get_typeid_creat("native.Address") {
Some("void*".into())
} else if TypeID::Fun(tyid) == root_ctx.get_typeid_creat("native.Float32") {
Some("float".into())
} else if TypeID::Fun(tyid) == root_ctx.get_typeid_creat("native.Float64") {
Some("double".into())
} else {
None
}
}
TypeTerm::Seq{ seq_repr, item } => {
if let Some(seq_repr) = seq_repr {
let item_type = item.deref().clone();
let item_c_type : String = get_c_repr_type(&mut root_ctx, &item_type);
let mut ctx = root_ctx.scope(laddertypes::AddressingMode::StackUp);
ctx.add_variable("Length", TypeKind::Value(root_ctx.parse("").expect("")));
ctx.add_variable("LengthType", TypeKind::Type);
ctx.add_variable("TermValue", TypeKind::Value(item_type.clone()));
if let Ok((ψ, σ)) = laddertypes::constraint_system::subtype_unify(
seq_repr.as_ref(),
&ctx.parse("<StaticLength Length>").expect("parse template type")
) {
let length = match
σ.get(
&ctx.get_varid("Length").expect("no Length ID")
).expect("no length specified!")
{
TypeTerm::Num(l) => l,
_ => {
eprintln!("invalid Length!");
return None;
}
};
Some(format!("struct {{ {} items[{}]; }} ", item_c_type, length))
}
else if let Ok((ψ, σ)) = laddertypes::constraint_system::subtype_unify(
seq_repr.as_ref(),
&ctx.parse("<LengthPrefix LengthType>").expect("parse template type")
) {
let length_type = σ.get(
&ctx.get_varid("LengthType").expect("no LengthType ID")
).expect("no length type specified");
let length_c_type = get_c_repr_type(&ctx, &length_type);
Some(format!("struct {{ {} len; {} items[]; }} ", length_c_type, item_c_type))
}
else if let Ok((ψ, σ)) = laddertypes::constraint_system::subtype_unify(
seq_repr.as_ref(),
&ctx.parse("<ValueTerminated TermValue>").expect("parse template type")
) {
if skip_pointer {
Some(item_c_type)
} else {
Some(format!("{} *", item_c_type))
}
}
else if let Ok((ψ, σ)) = laddertypes::constraint_system::subtype_unify(
seq_repr.as_ref(),
&ctx.parse("MsbCont").expect("parse template type")
) {
if skip_pointer {
Some(item_c_type)
} else {
Some(format!("{} *", item_c_type))
}
}
else {
eprintln!("can't get C-repr for sequence type `{}`!", seq_repr.pretty(&mut ctx.clone(), 0));
None
}
} else {
eprintln!("no sequence-representation type specified!");
None
}
}
TypeTerm::Struct{ struct_repr, members } => {
let c_struct_attribute =
if let Some(sr) = struct_repr {
if sr.deref() == &TypeTerm::Id(0x59742) {
// native repr., no attributes
""
} else {
match LayoutType::parse(&mut root_ctx, &sr).expect("layout type") {
LayoutType::Aligned => "",
LayoutType::Packed => "__attribute((packed))__"
}
}
} else {""};
let mut c_type = format!("struct {} {{\n", c_struct_attribute);
for StructMember{ symbol, ty } in members {
let field_c_type = get_c_repr_definition(root_ctx.clone(), ty, false).expect("");
c_type.push_str(&format!(" {} {};\n",
field_c_type,
symbol.replace("-", "_").replace(".","_dot_")
));
}
c_type.push_str("}");
Some(c_type)
}
TypeTerm::Enum { enum_repr, variants } => {
let mut c_type = format!("
struct {{
enum {{
");
for (i, EnumVariant{ symbol, ty }) in variants.iter().enumerate() {
c_type.push_str(&format!(
" {} = {}", symbol.replace("-", "_").replace(".","_dot_").to_uppercase(), i
));
if i+1 < variants.len() {
c_type.push_str(",\n");
}
}
c_type.push_str("
} tag;
union {
");
for EnumVariant{ symbol, ty } in variants {
let variant_c_type = get_c_repr_definition(root_ctx.clone(), ty, false).expect("cant get C-Repr type for variant");
c_type.push_str(&format!(
" {} {};\n", variant_c_type, symbol.replace("-", "_").replace(".","_dot_")
));
}
c_type.push_str("
};
}");
Some(c_type)
}
TypeTerm::Ladder(rungs) => {
if let Some(t) = rungs.last() {
get_c_repr_definition(root_ctx, t.clone(), false)
} else {
None
}
}
_ => { None }
}
}
pub fn get_c_repr_type(Γ: &ContextPtr, t: &laddertypes::TypeTerm)-> String {
let t = t.clone().strip();
match t {
TypeTerm::Char(c) => {
match c {
'.' => format!("_dot_"),
'-' => format!("_minus_"),
_ =>
format!("{}", (c as u64))
}
},
TypeTerm::Num(n) => {
format!("{}", n)
}
TypeTerm::Id(ty_id) => {
if let Some(name) = Γ.get_typename(ty_id) {
name
.replace("-", "_")
.replace(".", "")
} else {
format!("")
}
}
TypeTerm::Ladder(rs) |
TypeTerm::Spec(rs) => {
let mut s = String::new();
s.push('_');
for r in rs {
s.push_str(&get_c_repr_type(Γ, &r));
s.push('_');
}
s
}
TypeTerm::Struct { struct_repr, members } => {
let mut s = String::new();
s.push_str("_Struct_");
if let Some(sr) = struct_repr.as_ref() {
s.push('_');
s.push_str(&get_c_repr_type(Γ, &sr));
s.push('_');
}
for member in members.iter() {
s.push('_');
s.push_str(&member.symbol);
s.push('_');
s.push_str(&get_c_repr_type(Γ, &member.ty));
s.push('_');
}
s
},
TypeTerm::Enum { enum_repr, variants } => {
let mut s = String::new();
s.push_str("_Enum_");
if let Some(sr) = enum_repr.as_ref() {
s.push('_');
s.push_str(&get_c_repr_type(Γ, &sr));
s.push('_');
}
for member in variants.iter() {
s.push('_');
s.push_str(&member.symbol);
s.push('_');
s.push_str(&get_c_repr_type(Γ, &member.ty));
s.push('_');
}
s
},
TypeTerm::Seq { seq_repr, item } => {
let mut s = String::new();
s.push_str("_Seq_");
if let Some(sr) = seq_repr.as_ref() {
s.push('_');
s.push_str(&get_c_repr_type(Γ, &sr));
s.push('_');
}
s.push('_');
s.push_str(&get_c_repr_type(Γ, &item.deref()));
s.push('_');
s
},
// ??
TypeTerm::Func(type_terms) => todo!(),
TypeTerm::Morph(type_term, type_term1) => todo!(),
// err
TypeTerm::Var(_) => {
"_VAR_".into()
},
TypeTerm::Univ{ Γ, bounds, τ } => unreachable!(),
}
}
pub fn get_c_repr_arg_type(mut ctx: ContextPtr, t: &laddertypes::TypeTerm)-> String {
let t = t.clone().strip().get_floor_type().1;
let mut ctx = ctx.scope(laddertypes::AddressingMode::StackUp);
ctx.add_variable("TerminatorValue", TypeKind::Value(TypeTerm::unit()));
match &t {
TypeTerm::Seq { seq_repr, item } => {
if let Some(seq_repr) = seq_repr.as_ref() {
if let Ok(ψ) = laddertypes::constraint_system::subtype_unify(
seq_repr,
&ctx.parse("<ValueTerminated TerminatorValue>").expect("")
) {
return get_c_repr_type(&ctx, &item.clone().strip().get_floor_type().1);
}
else if let Ok(ψ) = laddertypes::constraint_system::subtype_unify(
seq_repr,
&ctx.parse("MsbCont").expect("")
) {
return get_c_repr_type(&ctx, &item.clone().strip().get_floor_type().1);
}
}
}
_ => {}
}
get_c_repr_type(&ctx, &t)
}
pub fn encode_morph_type_to_symbol(ctx: &ContextPtr, t: &MorphismType) -> String {
eprintln!("encode morph_type_to symbol:");
eprintln!("t = {} -> {}", t.src_type.pretty(&mut ctx.clone(), 0), t.dst_type.pretty(&mut ctx.clone(), 0));
eprintln!("Γ = {}", ctx.pretty());
//let t = t.apply_subst(&c);
format!(
"morph__{}___TO__{}",
get_c_repr_type(&ctx, &t.src_type),
get_c_repr_type(&ctx, &t.dst_type)
)
}
pub fn encode_type_to_value(ctx: &ContextPtr, t: &laddertypes::TypeTerm) -> String {
ctx.unparse(&t)
}

View file

@ -3,50 +3,113 @@
mod morphism;
mod parser;
mod c_gen;
mod struct_layout;
mod viz;
use {
ariadne::{Color, Label, Report, ReportKind, Source},
chumsky::prelude::*,
laddertypes::{
parser::ParseLadderType, BimapTypeDict, MorphismType
},
std::sync::{Arc, RwLock},
tiny_ansi::TinyAnsi,
crate::{
morphism::LdmcMorphism,
morphism::LdmcPrimMorph,
parser::morphism_base_parser,
}
viz::{MorphbaseVisualizationPane, Visualization},
},
ariadne::{Color, Label, Report, ReportKind, Source}, clap::Parser, laddertypes::{
morphism::*, BimapTypeDict, Context, Morphism
},
parser::morphism_type_parser, std::{io::Write, path::PathBuf, str::FromStr, sync::{Arc, RwLock}},
tiny_ansi::TinyAnsi,
walkdir::WalkDir,
};
#[derive(Parser, Debug)]
#[command(version, about, long_about = None)]
struct Args {
#[arg(short='b', long)]
morphism_base: Vec<PathBuf>,
#[arg(short='m', long="morph")]
morphisms: Vec<String>,
#[arg(short='o', long)]
output: Option<PathBuf>,
#[arg(long)]
html: Option<PathBuf>
}
fn main() {
let mut type_dict = Arc::new(RwLock::new(BimapTypeDict::new()));
let mut morphism_base = laddertypes::MorphismBase::<LdmcMorphism>::new(vec![
//type_dict.parse("Seq~MsbCont").expect(""),
//type_dict.parse("Seq~<ValueTerminated '\\0'>").expect(""),
type_dict.parse("Seq~<LengthPrefix native.UInt64>").expect("")
]);
let args = Args::parse();
let mut ctx = Context::new();
let mut morphism_base = laddertypes::morphism::base::MorphismBase::<LdmcPrimMorph>::new(ctx.clone());
let mut args = std::env::args().skip(1);
let src_type_arg = args.next().expect("src type expected");
let dst_type_arg = args.next().expect("dst type expected");
// 1. load morphism base
let mut mb_paths = args.morphism_base;
for mb_path in args {
let src = std::fs::read_to_string(mb_path.clone()).expect("read");
let result = morphism_base_parser(type_dict.clone()).parse(src.clone());
// 1.1. pfade ermitteln
let env_var = "MORPHISM_BASE";
let suffix = "morphism-base";
match std::env::var(env_var) {
Ok(path_str) => {
let path = std::path::Path::new(&path_str);
if path.is_dir() {
for entry in WalkDir::new(path)
.into_iter()
.filter_map(Result::ok)
.filter(|e|
e.path().is_file() &&
e.path().extension().map_or(false, |ext| ext == suffix)
)
{
mb_paths.push(entry.path().into());
}
} else {
eprintln!("morphism-base path is not a directory: {:?}", path);
}
}
Err(e) => {
eprintln!("failed to read environment variable {}: {}", env_var, e);
}
}
// 1.2. read files
let mut include_blocks = Vec::new();
let mut include_dependencies = std::collections::HashMap::new();
let mut viz = viz::Visualization::new();
let mut morphbase_pane = MorphbaseVisualizationPane::new(mb_paths.len());
for mb_path in mb_paths {
let modname = mb_path.file_stem().unwrap().to_str().unwrap().to_string();
viz.add_module(modname.clone());
morphbase_pane.add_module(modname.clone());
let src = std::fs::read_to_string(mb_path.clone()).expect("failed to read morphism base");
use chumsky::Parser;
let result = morphism_base_parser(ctx.clone()).parse(src.clone());
match result {
Ok((includes, morphisms)) => {
eprintln!("[{}] parse ok.", mb_path.bright_yellow());
println!("{}", includes);
eprintln!("[{}] parse ok.", mb_path.to_str().unwrap().bright_yellow());
let include_idx = include_blocks.len();
let mut includes_prefixed = format!("/* from `{}` */", mb_path.display());
includes_prefixed.push_str(&includes);
include_blocks.push(includes_prefixed);
for m in morphisms {
morphism_base.add_morphism(LdmcMorphism::Primitive(m));
include_dependencies.insert( m.get_type(), include_idx );
morphbase_pane.add_morphism( &modname, &m );
morphism_base.add_morphism(m);
}
}
Err(errs) => {
eprintln!("Error in {}:", mb_path.to_str().unwrap().bright_yellow());
errs.into_iter().for_each(|e| {
Report::build(ReportKind::Error, (), e.span().start)
.with_message(e.to_string())
.with_message(format!("in {} : {}", mb_path.display(), e.to_string()))
.with_label(
Label::new(e.span())
.with_message(e)
@ -60,22 +123,53 @@ fn main() {
}
}
eprintln!("done loading");
morphbase_pane.build_svg(&mut ctx);
viz.add_pane( morphbase_pane );
let src_type = type_dict.parse( src_type_arg.as_str() ).expect("");
let dst_type = type_dict.parse( dst_type_arg.as_str() ).expect("");
// 2. Generate Morphisms
let mut instances = Vec::new();
let mut morph_graph = MorphismGraph::new(morphism_base);
for morph_decl in args.morphisms.iter() {
use chumsky::Parser;
if let Ok((name, src_type, dst_type)) = morphism_type_parser(ctx.clone()).parse(morph_decl.as_str()) {
let ty = MorphismType{ Γ: Vec::new(), bounds: vec![], src_type, dst_type };
let (morph_inst,search) = morph_graph.search( ty );
let path = morphism_base.find_morphism_path(MorphismType {
src_type: src_type.clone(),
dst_type: dst_type.clone(),
});
if let Ok(morph_inst) = morph_inst {
eprintln!("===========");
eprintln!("{}", morph_inst.pretty(&ctx));
eprintln!("===========");
match path {
Some(path) => {
c_gen::generate_main(&mut type_dict, path, src_type, dst_type);
}
None => {
eprintln!("Error: could not find morphism path");
std::process::exit(-1);
viz.add_module(name.clone());
viz.add_morph_inst(name.clone(), morph_inst.clone(), ctx.clone());
instances.push( (name.clone(), morph_inst) );
}
let search_name = format!("search-{}",name);
viz.add_module(search_name.clone());
viz.add_search_graph(ctx.clone(), search_name, &search);
}
}
if instances.len() > 0 {
let c_source = crate::c_gen::gen_lib::generate_lib(&ctx, include_blocks, include_dependencies, instances).expect("failed to generate library");
if let Some(out_path) = args.output {
let mut file = std::fs::File::create(out_path).expect("failed to open output file");
file.write_all(c_source.as_bytes()).expect("failed to write output file");
} else {
println!("{}", c_source);
}
} else {
eprintln!("No instances");
}
if let Some(html_path) = args.html {
let html_src = viz.into_html(&mut ctx, vec![
PathBuf::from_str("file:///home/micha/projects/syntaxAlchemist/ldmc/example-layout.csv").unwrap()
]);
let mut output = std::fs::File::create(html_path).expect("couldnt open file");
write!(output, "{}", html_src);
}
}

View file

@ -1,76 +1,26 @@
use laddertypes::morphism::Morphism;
use {
laddertypes::{
context::{ContextPtr},
morphism::{Morphism, MorphismType},
}
};
#[derive(Clone, Debug)]
pub struct LdmcPrimCMorphism {
pub struct LdmcPrimMorph {
pub symbol: String,
pub type_args: Vec<(laddertypes::TypeID, String)>,
pub src_type: laddertypes::TypeTerm,
pub dst_type: laddertypes::TypeTerm,
pub ty: MorphismType,
pub c_source: String
}
#[derive(Clone)]
pub enum LdmcMorphism {
Primitive( LdmcPrimCMorphism ),
LengthPrefixMap{
length_prefix_type: laddertypes::TypeTerm,
item_morph: Box<LdmcPrimCMorphism>
},
ValueDelimMap{
delim: u64,
item_morph: Box<LdmcPrimCMorphism>
impl PartialEq for LdmcPrimMorph {
fn eq(&self, other: &Self) -> bool {
self.symbol == other.symbol
}
}
impl Morphism for LdmcMorphism {
fn weight(&self) -> u64 {
1
}
fn get_type(&self) -> laddertypes::MorphismType {
match self {
LdmcMorphism::Primitive(prim_morph) =>
laddertypes::MorphismType {
src_type: prim_morph.src_type.clone().normalize(),
dst_type: prim_morph.dst_type.clone().normalize()
},
LdmcMorphism::LengthPrefixMap{ length_prefix_type, item_morph } => {
laddertypes::MorphismType {
src_type: laddertypes::TypeTerm::App(vec![ length_prefix_type.clone(), item_morph.src_type.clone() ]),
dst_type: laddertypes::TypeTerm::App(vec![ length_prefix_type.clone(), item_morph.dst_type.clone() ]),
}
},
LdmcMorphism::ValueDelimMap{ delim, item_morph } => {
let value_delim_type = laddertypes::TypeTerm::App(vec![]);
laddertypes::MorphismType {
src_type: laddertypes::TypeTerm::App(vec![ value_delim_type.clone(), item_morph.src_type.clone() ]),
dst_type: laddertypes::TypeTerm::App(vec![ value_delim_type.clone(), item_morph.dst_type.clone() ]),
}
}
}.normalize()
}
fn map_morphism(&self, seq_type: laddertypes::TypeTerm) -> Option< Self > {
match self {
LdmcMorphism::Primitive(prim) => {
let item_morph = Box::new(prim.clone());
/*
if seq_type == self.length_prefix_type {
*/
Some(LdmcMorphism::LengthPrefixMap{
length_prefix_type: seq_type,
item_morph,
})
/*
} else if seq_type == self.value_delim_type {
Some(LdmcMorphism::ValueDelimMap { delim, item_morph })
} else {
None
}
*/
}
_ => None
}
impl Morphism for LdmcPrimMorph {
fn get_type(&self) -> MorphismType {
self.ty.clone()
}
}

View file

@ -1,15 +1,15 @@
use {
chumsky::{
prelude::*, text::*
},
std::sync::{Arc, RwLock},
laddertypes::{TypeDict, BimapTypeDict, parser::*},
crate::morphism::LdmcPrimCMorphism
crate::morphism::LdmcPrimMorph, chumsky::{
error::SimpleReason, prelude::*, text::*
}, laddertypes::{morphism::MorphismType, parser::*, BimapTypeDict, ContextPtr, LayeredContext, TypeKind, TypeTerm}, std::sync::{Arc, RwLock}
};
// todo: lexer with ladder-type tokens,
// todo: parse type definitions
/* morphism-base text format:
* NAME '(' [TYPE-ARG-NAME ':' KIND] ')'
* NAME '(' [TYPE-ARG-NAME ':' Kind] ')'
* where
* SRC-TYPE
* '-->' DST-TYPE
* ```
@ -17,64 +17,116 @@ use {
* ```
*/
pub fn morphism_base_parser(
type_dict: Arc<RwLock< BimapTypeDict >>
) -> impl Parser<char, (String, Vec<LdmcPrimCMorphism>), Error = Simple<char>> {
root_ctx: ContextPtr
) -> impl Parser<char, (String, Vec<LdmcPrimMorph>), Error = Simple<char>> {
/* header block */
just("```")
.then(take_until(just("```")))
.map(
move |(a, (b, c))| {
move |(_a, (b, _c))| {
b.iter().collect()
}
)
// morph name
.then(
ident().padded()
// type args
.then(
ident().padded()
.then_ignore(just(":").padded())
.then(none_of(",)").repeated().padded())
.separated_by(just(",").padded())
.delimited_by(just("("), just(")"))
just("morph").padded()
// morph name
.then(ident().padded()
// type
.then(
just(":").padded()
.then(none_of("=").repeated().padded())
)
.then_ignore(just('=').padded())
.then_ignore(just("```"))
// c sourcecode template
.then(take_until(just("```")))
.then_ignore(just(";").padded())
)
// newline
.then_ignore(just('\n'))
// src type
.then(take_until(just("-->").ignored()))
// dst_type
.then(take_until(just("```")))
// c sourcecode template
.then(take_until(just("```")))
.map(
move |((((symbol, type_args), (src_type, _)), (dst_type, _)), (c_source, _))| {
move |(morph_keyword, ((symbol, (_dot, morphtype)), (c_source, _)))| {
let mut ctx = root_ctx.clone();
let c_source = c_source.iter().collect();
let mut type_dict = type_dict.write().unwrap();
let type_args : Vec<_> = type_args.into_iter().map(|(v,k)| (v,k.into_iter().collect())).collect();
let mut ty_args = Vec::new();
for (var, kind) in type_args.into_iter() {
let var_id = type_dict.add_varname(var.clone());
ty_args.push((var_id, kind));
}
let morphtype : String = morphtype.iter().collect();
let src_type = type_dict.parse(&src_type.iter().collect::<String>()).expect("couldnt parse src type");
let dst_type = type_dict.parse(&dst_type.iter().collect::<String>()).expect("couldnt parse dst type");
let ty = match ctx.parse(&morphtype) {
Ok(ty) => ty,
Err(err) => {
eprintln!("failed to parse morph type of `{}`", symbol);
TypeTerm::unit()
//return Err(SimpleReason::Custom("cant parse morph type".into()));
}
};
eprintln!("read {}", ty.pretty(&ctx, 0));
let ty = ty.into_morphism_type().expect("invalid morphism type");
LdmcPrimCMorphism {
LdmcPrimMorph {
symbol,
type_args: ty_args,
src_type,
dst_type,
ty,
c_source
}
})
.separated_by(text::newline())
}
/*
move |((((symbol, type_args), (src_type, _)), (dst_type, _)), (c_source, _))| {
let c_source = c_source.iter().collect();
let mut ctx = root_ctx.scope();
let type_args : Vec<_> = type_args.into_iter().map(|(v,k)| (v,k.into_iter().collect::<String>())).collect();
for (var, bound) in type_args.into_iter() {
let var_id = ctx.add_variable(var.as_str(),
if bound == "Type" {
TypeKind::Type
} else if bound == "" {
TypeKind::ValueUInt
} else {
unreachable!()
}
);
}
let src_type = ctx.parse(&src_type.iter().collect::<String>()).expect("couldnt parse src type");
let dst_type = ctx.parse(&dst_type.iter().collect::<String>()).expect("couldnt parse dst type");
LdmcPrimMorph {
symbol,
Γ: ctx,
ty: MorphismType{
bounds: Vec::new(),
src_type,
dst_type
},
c_source
}
*/
)
.repeated()
)
//.separated_by(text::newline())
}
pub fn morphism_type_parser(
ctx: ContextPtr
) -> impl Parser<char, (String, TypeTerm, TypeTerm), Error = Simple<char>> {
ident()
.padded()
.then(just(":").ignored())
.then(
take_until(just("-->").ignored())
)
.then(take_until(end()))
.map({
let ctx = ctx.clone();
move |((name, src_type), dst_type)| {
(
name.0,
ctx.clone().parse(&src_type.0.iter().collect::<String>()).expect("parse type"),
ctx.clone().parse(&dst_type.0.iter().collect::<String>()).expect("parse type")
)
}
})
}

43
src/platform/x86.rs Normal file
View file

@ -0,0 +1,43 @@
use std::collections::HashMap;
pub fn example_substitution() {
let mut γ = HashMap::new();
γ.insert(
type_dict.get_typeid_creat(&"Byte"),
type_dict.parse("<Seq~<StaticLength 8> Bit>").expect("parse")
.apply_subst(&γ)
.clone()
);
γ.insert(
type_dict.get_typeid_creat(&"x86.UInt8"),
type_dict.parse("_2^8 ~ <PosInt 2 LittleEndian> ~ <Seq~<StaticLength 8> <Digit 2> ~ Bit>").expect("parse")
.apply_subst(&γ)
.clone()
);
γ.insert(
type_dict.get_typeid_creat(&"x86.UInt16"),
type_dict.parse("_2^16 ~ <PosInt 256 LittleEndian> ~ <Seq~<StaticLength 2> <Digit 256> ~ x86.UInt8 >").expect("parse")
.apply_subst(&γ)
.clone()
);
γ.insert(
type_dict.get_typeid_creat(&"x86.UInt32"),
type_dict.parse("_2^32 ~ <PosInt 256 LittleEndian> ~ <Seq~<StaticLength 4> <Digit 256> ~ x86.UInt8 >").expect("parse")
.apply_subst(&γ).clone()
);
γ.insert(
type_dict.get_typeid_creat(&"x86.UInt64"),
type_dict.parse("_2^64 ~ <PosInt 256 LittleEndian> ~ <Seq~<StaticLength 8> <Digit 256> ~ x86.UInt8 >").expect("parse")
.apply_subst(&γ).clone()
);
γ.insert(
type_dict.get_typeid_creat(&"x86.Float32"),
type_dict.parse(" ~ IEEE-754.Float32 ~ <Seq~<StaticLength 4> Byte>").expect("parse")
.apply_subst(&γ).clone()
);
γ.insert(
type_dict.get_typeid_creat(&"x86.Float64"),
type_dict.parse(" ~ IEEE-754.Float64 ~ <Seq~<StaticLength 4> Byte>").expect("parse")
.apply_subst(&γ).clone()
);
}

245
src/struct_layout.rs Normal file
View file

@ -0,0 +1,245 @@
use std::ops::Deref;
use laddertypes::{parser::*, ContextPtr, LayeredContext, StructMember, TypeDict, TypeID, TypeTerm};
#[derive(Clone, Copy, Debug)]
pub enum ObjectSize {
Static(u64),
Dynamic(/* function to get size at runtime */),
Unknown
}
impl std::ops::Add for ObjectSize {
type Output = Self;
fn add(self, rhs: Self) -> Self {
match (self, rhs) {
(ObjectSize::Unknown, _) |
(_, ObjectSize::Unknown)
=> ObjectSize::Unknown,
(ObjectSize::Dynamic(), _) |
(_, ObjectSize::Dynamic())
=> ObjectSize::Dynamic(),
(ObjectSize::Static(s1), ObjectSize::Static(s2)) => ObjectSize::Static(s1 + s2),
}
}
}
impl std::ops::Mul for ObjectSize {
type Output = Self;
fn mul(self, rhs: Self) -> Self {
match (self, rhs) {
(ObjectSize::Unknown, _) |
(_, ObjectSize::Unknown)
=> ObjectSize::Unknown,
(ObjectSize::Dynamic(), _) |
(_, ObjectSize::Dynamic())
=> ObjectSize::Dynamic(),
(ObjectSize::Static(s1), ObjectSize::Static(s2)) => ObjectSize::Static(s1 * s2),
}
}
}
pub fn get_type_size( dict: &mut ContextPtr, ty: TypeTerm ) -> ObjectSize {
match &ty {
TypeTerm::Id(tyid) => {
if TypeID::Fun(*tyid) == dict.get_typeid_creat("native.UInt8") { ObjectSize::Static(1) }
else if TypeID::Fun(*tyid) == dict.get_typeid_creat("native.UInt16") { ObjectSize::Static(2) }
else if TypeID::Fun(*tyid) == dict.get_typeid_creat("native.UInt32") { ObjectSize::Static(4) }
else if TypeID::Fun(*tyid) == dict.get_typeid_creat("native.UInt64") { ObjectSize::Static(8) }
else if TypeID::Fun(*tyid) == dict.get_typeid_creat("native.Address") { ObjectSize::Static(8) }
else if TypeID::Fun(*tyid) == dict.get_typeid_creat("native.Float32") { ObjectSize::Static(4) }
else if TypeID::Fun(*tyid) == dict.get_typeid_creat("native.Float64") { ObjectSize::Static(8) }
else {
ObjectSize::Unknown
}
},
TypeTerm::Ladder(rungs) => {
get_type_size(dict, rungs.last().unwrap().clone() )
}
TypeTerm::Struct { struct_repr, members } => {
let layout = StructLayout::parse_sugared(dict, ty).expect("invalid struct");
layout.get_size()
}
TypeTerm::Seq { seq_repr, item } => {
if let Some(seq_repr) = seq_repr {
dict.add_typename("Length".into());
dict.add_typename("LengthType".into());
dict.add_typename("TerminatorValue".into());
if let Ok(σ) = laddertypes::unify(
&seq_repr.clone(),
&dict.parse("<StaticLength Length>").expect("")
) {
if let Some(TypeTerm::Num(len)) = σ.get(&dict.get_varid("Length").expect("")) {
ObjectSize::Static(*len as u64) * get_type_size(dict, item.deref().clone())
} else {
ObjectSize::Unknown
}
}
else if let Ok(σ) = laddertypes::unify(
&seq_repr.clone(),
&dict.parse("<LengthPrefix LengthType>").expect("")
) {
ObjectSize::Dynamic()
}
else if let Ok(σ) = laddertypes::unify(
&seq_repr.clone(),
&dict.parse("<ValueTerminated TerminatorValue>").expect("")
) {
ObjectSize::Dynamic()
}
else {
ObjectSize::Unknown
}
} else {
ObjectSize::Unknown
}
}
_ => ObjectSize::Unknown
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum LayoutType {
Aligned,
Packed
}
impl LayoutType {
pub fn parse( dict: &mut impl LayeredContext, ty: &TypeTerm ) -> Option< LayoutType > {
if ty == &dict.parse("Aligned").expect("parse") {
Some(LayoutType::Aligned)
} else if ty == &dict.parse("Packed").expect("parse") {
Some(LayoutType::Packed)
} else {
None
}
}
pub fn unparse(&self, dict: &mut impl LayeredContext) -> TypeTerm {
match self {
LayoutType::Aligned => dict.parse("Aligned").unwrap(),
LayoutType::Packed => dict.parse("Packed").unwrap()
}
}
}
#[derive(Clone)]
pub struct StructLayout {
pub ty: laddertypes::TypeTerm,
pub layout: LayoutType,
pub members: Vec< StructMember >,
offsets: std::collections::HashMap< String, u64 >,
size: ObjectSize
}
impl StructLayout {
pub fn get_type(&self) -> TypeTerm {
self.ty.clone()
}
pub fn get_size(&self) -> ObjectSize {
self.size
}
pub fn get_offset(&self, name: &String) -> u64 {
*self.offsets.get(name).expect("")
}
pub fn init_offsets(&mut self, dict: &mut ContextPtr) {
let mut offset = 0;
for StructMember{symbol: field_name, ty: field_type} in self.members.iter() {
//let field_c_type = crate::c_gen::get_c_repr_type(dict, field_type.clone(), true).expect("cant get c-repr for struct field");
match get_type_size( dict, field_type.clone() ) {
ObjectSize::Static(field_size) => {
match self.layout {
LayoutType::Aligned => {
// add padding to get natural alignment of current member
if offset % field_size > 0 {
offset = field_size * ((offset / field_size)+1);
}
}
LayoutType::Packed => {
// no padding
}
}
// take offset
self.offsets.insert(field_name.clone(), offset);
// advance by current size
offset += field_size;
}
ObjectSize::Dynamic() => {
eprintln!("dynamically sized Field in struct!");
self.offsets.insert(field_name.clone(), offset);
self.size = ObjectSize::Dynamic();
return;
}
ObjectSize::Unknown => {
eprintln!("Struct member `{}` has unknown size!", field_name);
self.offsets.insert(field_name.clone(), offset);
self.size = ObjectSize::Unknown;
return;
}
}
}
match self.size {
ObjectSize::Dynamic() => {
}
_ => {
self.size = ObjectSize::Static(offset);
}
}
}
pub fn parse(dict: &mut ContextPtr, struct_type: &TypeTerm) -> Option <Self> {
let st = struct_type.clone().strip().normalize();
Self::parse_sugared(dict, st)
}
pub fn parse_sugared(dict: &mut ContextPtr, st: TypeTerm) -> Option <Self> {
eprintln!("{}", st.pretty(dict, 0));
match st.clone() {
TypeTerm::Struct{ struct_repr, members } => {
let mut sl = StructLayout {
ty: st,
layout: if let Some(sr) = struct_repr {
LayoutType::parse( dict, &sr ).expect("invalid struct layout type")
} else {
eprintln!("choose default struct repr");
LayoutType::Aligned
},
members,
offsets: std::collections::HashMap::new(),
size: ObjectSize::Unknown
};
sl.init_offsets(dict);
Some(sl)
}
_ => {
eprintln!("not a struct");
None
}
}
}
}

502
src/viz/gen_html.rs Normal file
View file

@ -0,0 +1,502 @@
use std::path::PathBuf;
use laddertypes::TypeDict;
use crate::{viz::Visualization};
impl Visualization {
pub fn into_html(
self,
dict: &mut impl TypeDict,
position_files: Vec<PathBuf>
) -> String {
let mut position_file_dropdown_options = String::new();
for path in position_files {
let name = path.file_stem().expect("cant get basename").to_str().unwrap();
position_file_dropdown_options.push_str(&format!(r##"
<option value="{}" data-src="{}">{}</option>
"##, name,
path.to_str().unwrap(),
name
));
}
let mut html = String::new();
html.push_str(
r##"
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Morphism Base</title>
"##
);
html.push_str(&self.style());
html.push_str(
r##"
</head>
<body>
"##
);
html.push_str(&self.toolbar());
for pane in self.panes.iter() {
html.push_str(&pane.svg());
}
html.push_str(&self.script());
html.push_str(
r##"
</body>
</html>
"##);
html
}
pub fn pane_selector( &self ) -> String {
let mut html = String::new();
html.push_str(r##"<select id="paneSelector">"##);
for (i, pane) in self.panes.iter().enumerate() {
if i == 0 {
html.push_str(&format!(r##"<option value="{}" selected>{}</option>"##,
pane.id(),
pane.display_name()));
} else {
html.push_str(&format!(r##"<option value="{}">{}</option>"##,
pane.id(),
pane.display_name()));
}
}
html.push_str(r##"</select>"##);
html
}
pub fn toolbar(&self) -> String {
format!(r##"
<div id="toolbar">
<select id="layoutSelector">
<option value="" disabled selected>Choose layout</option>
<option value="browse">Browse local file</option>
</select>
<button id="saveCsvBtn">💾 Save</button>
<label id="themeToggle">
<input type="checkbox" id="themeCheckbox" />
🌙
</label>
{}
</div>
<input type="file" id="csvInput" accept=".csv" style="display:none;" />
"##,
self.pane_selector()
)
}
pub fn script(&self) -> String {
let mut js = String::new();
js.push_str(r##"
<script>
// --- Helpers ---
function parseTranslate(transform) {
const match = /translate\(\s*([-\d.]+)\s*,\s*([-\d.]+)\s*\)/.exec(transform);
if (!match) return { x: 0, y: 0 }; // fallback if transform is malformed or missing
return {
x: parseFloat(match[1]),
y: parseFloat(match[2])
};
}
function getNodePosition(node) {
const match = /translate\(([^,]+),([^)]+)\)/.exec(
node.getAttribute("transform"),
);
return { x: parseFloat(match[1]), y: parseFloat(match[2]) };
}
function setNodePosition(node, x, y) {
node.setAttribute("transform", `translate(${x},${y})`);
}
function getCanvasTransform(canvasGroup) {
const match =
/translate\(([^,]+),([^)]+)\)\s*scale\(([^)]+)\)/.exec(
canvasGroup.getAttribute("transform"),
);
return {
tx: parseFloat(match[1]),
ty: parseFloat(match[2]),
scale: parseFloat(match[3]),
};
}
function getCanvasScale(canvasGroup) {
return getCanvasTransform(canvasGroup).scale;
}
// --- Update Edges and Labels ---
function updateAllEdges() {
const edges = document.querySelectorAll('line.arrow');
const labels = document.querySelectorAll('.edge-label');
edges.forEach(line => {
const sourceId = line.getAttribute('data-source');
const targetId = line.getAttribute('data-target');
const source = document.getElementById(sourceId);
const target = document.getElementById(targetId);
if (!source || !target) return;
const sourceParentPos = parseTranslate(source.parentNode.getAttribute('transform'));
const targetParentPos = parseTranslate(target.parentNode.getAttribute('transform'));
const sourcePos = parseTranslate(source.getAttribute('transform'));
const targetPos = parseTranslate(target.getAttribute('transform'));
const offsetX = 200;
const offsetY = 60;
const x1 = sourceParentPos.x + sourcePos.x + 390;
const y1 = sourceParentPos.y + sourcePos.y + 80;
const x2 = targetParentPos.x + targetPos.x + 10;
const y2 = targetParentPos.y + targetPos.y + 50;
// Set edge line position
line.setAttribute('x1', x1);
line.setAttribute('y1', y1);
line.setAttribute('x2', x2);
line.setAttribute('y2', y2);
// Update matching label
const label = findLabel(labels, sourceId, targetId);
if (label) {
const midX = (x1 + x2) / 2;
const midY = (y1 + y2) / 2;
label.setAttribute('transform', `translate(${midX}, ${midY})`);
}
});
}
// Find label by matching source and target
function findLabel(labelElements, sourceId, targetId) {
return Array.from(labelElements).find(label =>
label.getAttribute('data-source') === sourceId &&
label.getAttribute('data-target') === targetId
);
}
function centerPane(pane, targetWidth) {
const canvasGroup = pane.getElementById("canvasGroup");
const svgWidth = pane.clientWidth;
const svgHeight = pane.clientHeight;
const scale = svgWidth / targetWidth;
// Center the canvasGroup at the middle of the screen
const offsetX = svgWidth / 2;
const offsetY = svgHeight / 2;
canvasGroup.setAttribute('transform', `translate(${offsetX},${offsetY}) scale(${scale})`);
}
//-- Event Listener Setup --
function setupEventListenersForPane(pane) {
let selectedNode = null;
let dragStartScreen = null;
let nodeStartPos = null;
let panStart = null;
const canvasGroup = pane.getElementById("canvasGroup");
// --- Dragging Nodes ---
function onMouseDown(e) {
const node = e.target.closest(".draggable");
if (node) {
selectedNode = node;
dragStartScreen = { x: e.clientX, y: e.clientY };
nodeStartPos = getNodePosition(selectedNode);
pane.style.cursor = "grabbing";
window.addEventListener("mousemove", onMouseMoveNode);
window.addEventListener("mouseup", onMouseUpNode);
} else {
panStart = { x: e.clientX, y: e.clientY };
pane.style.cursor = "grabbing";
window.addEventListener("mousemove", onPanMove);
window.addEventListener("mouseup", endPan);
}
}
function onMouseMoveNode(e) {
if (!selectedNode) return;
const dx = e.clientX - dragStartScreen.x;
const dy = e.clientY - dragStartScreen.y;
const scale = getCanvasScale(canvasGroup);
const newX = nodeStartPos.x + dx / scale;
const newY = nodeStartPos.y + dy / scale;
setNodePosition(selectedNode, newX, newY);
updateAllEdges();
}
function onMouseUpNode(e) {
pane.style.cursor = "grab";
window.removeEventListener("mousemove", onMouseMoveNode);
window.removeEventListener("mouseup", onMouseUpNode);
selectedNode = null;
}
// --- Panning ---
function onPanMove(e) {
const dx = e.clientX - panStart.x;
const dy = e.clientY - panStart.y;
const { tx, ty, scale } = getCanvasTransform(canvasGroup);
canvasGroup.setAttribute(
"transform",
`translate(${tx + dx},${ty + dy}) scale(${scale})`,
);
panStart = { x: e.clientX, y: e.clientY };
}
function endPan(e) {
pane.style.cursor = "grab";
window.removeEventListener("mousemove", onPanMove);
window.removeEventListener("mouseup", endPan);
}
// -- Dragging --
pane.addEventListener("mousedown", onMouseDown);
// --- Zoom ---
pane.addEventListener("wheel", (e) => {
e.preventDefault();
const zoomIntensity = 0.1;
const { tx, ty, scale } = getCanvasTransform(canvasGroup);
const pt = pane.createSVGPoint();
pt.x = e.clientX;
pt.y = e.clientY;
const cursorpt = pt.matrixTransform(
pane.getScreenCTM().inverse(),
);
const direction = e.deltaY > 0 ? -1 : 1;
const factor = 1 + direction * zoomIntensity;
const newScale = scale * factor;
const dx = cursorpt.x - tx;
const dy = cursorpt.y - ty;
const newTx = cursorpt.x - dx * factor;
const newTy = cursorpt.y - dy * factor;
canvasGroup.setAttribute(
"transform",
`translate(${newTx},${newTy}) scale(${newScale})`,
);
// Adjust module-label font sizes
const labels = document.querySelectorAll('tspan.module-label');
labels.forEach(label => {
const fontScale = 1.0/ newScale;
// Scale inversely so labels shrink as you zoom in, grow as you zoom out
label.setAttribute("transform",
`scale(${fontScale})`,
);
});
});
}
// -- Load Positions --
function updatePositionsFromCSV(csvText) {
const lines = csvText.trim().split('\n');
const [header, ...rows] = lines;
const [idHeader, xHeader, yHeader] = header.split(',').map(h => h.trim());
if (idHeader !== 'id' || xHeader !== 'x' || yHeader !== 'y') {
console.warn('CSV header must be: id,x,y');
return;
}
rows.forEach(row => {
const [id, xStr, yStr] = row.split(',').map(s => s.trim());
const x = parseFloat(xStr);
const y = parseFloat(yStr);
if (!id || isNaN(x) || isNaN(y)) {
console.warn(`Skipping invalid row: "${row}"`);
return;
}
const node = document.getElementById(id);
if (node) {
node.setAttribute('transform', `translate(${x},${y})`);
} else {
console.warn(`Node not found: ${id}`);
}
});
}
const layoutSelector = document.getElementById('layoutSelector');
const fileInput = document.getElementById('csvInput');
layoutSelector.addEventListener('input', (e) => {
const selected = e.target.selectedOptions[0];
const value = selected.value;
if (value === 'browse') {
fileInput.click(); // Open file picker
layoutSelector.selectedIndex = 0; // Reset to "Choose layout..."
return;
}
const src = selected.getAttribute('data-src');
if (src) {
fetch(src)
.then(response => {
if (!response.ok) throw new Error(`Failed to fetch: ${src}`);
return response.text();
})
.then(csvText => {
updatePositionsFromCSV(csvText);
updateAllEdges();
layoutSelector.selectedIndex = 0; // Reset to placeholder
})
.catch(err => {
alert('Could not load layout: ' + err.message);
console.error(err);
layoutSelector.selectedIndex = 0;
});
}
});
// Also reset after loading a local file
fileInput.addEventListener('change', function (event) {
const file = event.target.files[0];
if (!file) return;
const reader = new FileReader();
reader.onload = function (e) {
updatePositionsFromCSV(e.target.result);
updateAllEdges();
layoutSelector.selectedIndex = 0; // Reset dropdown after use
};
reader.readAsText(file);
});
// -- Save Positions --
document.getElementById('saveCsvBtn').addEventListener('click', function () {
const draggableNodes = document.querySelectorAll('g.draggable');
let csvContent = 'id,x,y\n';
draggableNodes.forEach(node => {
const id = node.id;
const transform = node.getAttribute('transform');
const match = /translate\(([-\d.]+),\s*([-\d.]+)\)/.exec(transform);
if (match) {
const x = parseFloat(match[1]);
const y = parseFloat(match[2]);
csvContent += `${id},${x},${y}\n`;
}
});
// Trigger download
const blob = new Blob([csvContent], { type: 'text/csv' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'morphgraph-layout.csv';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
});
// -- Theme --
const themeCheckbox = document.getElementById('themeCheckbox');
function setTheme(mode) {
document.body.classList.remove('light', 'dark');
document.body.classList.add(mode);
themeCheckbox.checked = (mode === 'dark');
localStorage.setItem('theme', mode);
}
// Load saved theme or default to dark
const savedTheme = localStorage.getItem('theme');
if (savedTheme) {
setTheme(savedTheme);
} else {
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
setTheme(prefersDark ? 'dark' : 'light', false);
}
themeCheckbox.addEventListener('change', () => {
setTheme(themeCheckbox.checked ? 'dark' : 'light');
});
// -- Pane Selector --
function setPane(paneid) {
// deactivate all
const panes = document.querySelectorAll('svg');
panes.forEach(pane => pane.style.display = 'none');
// activate current
currentPane = document.getElementById(paneid);
if (currentPane) currentPane.style.display = 'block';
localStorage.setItem('current-pane', paneid);
}
document.getElementById('paneSelector').value = 'morphism-base';
document.getElementById('paneSelector').addEventListener('change', function() {
setPane(this.value);
});
// Load saved pane selection
const savedPaneId = localStorage.getItem('current-pane');
if( savedPaneId ) {
// selector.value = savedPaneId;
//setPane(savedPaneId);
}
"##);
for pane in self.panes.iter() {
let target_width = 4000;
js.push_str(&format!(r##"
pane = document.getElementById('{}');
centerPane( pane, {} );
setupEventListenersForPane( pane );
"##, pane.id(), target_width));
}
js.push_str(r##"
updateAllEdges();
</script>
"##);
js
}
}

13
src/viz/layout.rs Normal file
View file

@ -0,0 +1,13 @@
use std::collections::HashMap;
pub struct PaneLayout {
pub modules: HashMap< String, ModulePos >,
}
pub struct ModulePos {
pub x: f64,
pub y: f64,
pub max_morphisms: u32,
pub next_pos_count: u32,
}

61
src/viz/mod.rs Normal file
View file

@ -0,0 +1,61 @@
pub mod type_to_svg;
pub mod layout;
pub mod morphbase_pane;
pub mod morphinst_pane;
pub mod search_pane;
pub mod theme;
pub mod gen_html;
pub use type_to_svg::unparse_type_to_svg;
pub use layout::PaneLayout;
pub use morphbase_pane::MorphbaseVisualizationPane;
pub use morphinst_pane::MorphinstVizPane;
pub use search_pane::SearchPane;
use std::collections::HashMap;
use laddertypes::{unparser::*, ContextPtr, GraphSearch, Morphism, MorphismInstance, StructMember, TypeDict, TypeID, TypeTerm};
use crate::morphism::LdmcPrimMorph;
pub struct Visualization {
pub modules: Vec< String >,
pub panes: Vec< Box<dyn VisualizationPane> >
}
pub trait VisualizationPane {
fn id(&self) -> String;
fn display_name(&self) -> String;
fn svg(&self) -> String;
}
impl Visualization {
pub fn new() -> Visualization {
Visualization {
modules: Vec::new(),
panes: Vec::new(),
}
}
pub fn add_module(&mut self, modname: String) {
self.modules.push(modname);
}
pub fn add_pane(&mut self, pane: impl VisualizationPane + 'static) {
self.panes.push(Box::new(pane));
}
pub fn add_morph_inst(&mut self, name: String, morph: MorphismInstance<LdmcPrimMorph>, Γ0: ContextPtr) {
let mut morph_pane = MorphinstVizPane::new(name, morph);
morph_pane.build_svg(Γ0);
self.modules.append( &mut morph_pane.get_subnames() );
self.add_pane( morph_pane );
}
pub fn add_search_graph(&mut self, ctx: ContextPtr, name: String, graph: &GraphSearch<LdmcPrimMorph>) {
let mut search_pane = SearchPane::new(ctx, name);
search_pane.build_svg(graph);
self.modules.append( &mut search_pane.get_subnames() );
self.add_pane( search_pane );
}
}

192
src/viz/morphbase_pane.rs Normal file
View file

@ -0,0 +1,192 @@
pub use {
laddertypes::TypeDict,
crate::morphism::LdmcPrimMorph,
super::{VisualizationPane, PaneLayout, unparse_type_to_svg},
std::collections::HashMap,
};
pub struct MorphbaseVisualizationPane {
n_modules: usize,
nodes: Vec< VizNode >,
layout: PaneLayout,
svg: Option< String >
}
pub struct VizNode {
pub module: String,
pub morph: LdmcPrimMorph
}
impl VisualizationPane for MorphbaseVisualizationPane {
fn id(&self) -> String { "morphism-base".into() }
fn display_name(&self) -> String { "Morphism Base".into() }
fn svg(&self) -> String {
self.svg.clone().unwrap_or(String::new())
}
}
impl MorphbaseVisualizationPane {
pub fn new(n_modules: usize) -> MorphbaseVisualizationPane {
MorphbaseVisualizationPane {
n_modules,
nodes: Vec::new(),
layout: PaneLayout { modules: HashMap::new() },
svg: None
}
}
pub fn add_module( &mut self, modname: String ) {
let i = self.layout.modules.len();
let rot = 6.283 * (i as f64) / (self.n_modules as f64);
let radius = 1000.0 + 500.0 * f64::sin(3.0*rot);
let modpos = (2.0*radius*rot.cos(), radius*rot.sin());
self.layout.modules.insert(
modname,
super::layout::ModulePos {
x: modpos.0,
y: modpos.1,
max_morphisms: 0,
next_pos_count: 0
});
}
pub fn add_morphism(
&mut self,
modname: &String,
m: &LdmcPrimMorph
) {
self.nodes.push(VizNode{ module: modname.clone(), morph: m.clone() });
if let Some(modpos) = self.layout.modules.get_mut(modname) {
modpos.max_morphisms += 1;
}
}
pub fn get_edges(&self) -> Vec<(String, String, String)> {
let mut edges = vec![];
for n1 in self.nodes.iter() {
for n2 in self.nodes.iter() {
if let Ok((σ,ψ)) = laddertypes::subtype_unify(&n1.morph.ty.dst_type, &n2.morph.ty.src_type) {
edges.push((
n1.morph.symbol.clone(),
n2.morph.symbol.clone(),
"".into()
/*
format!(r##"
&leq;
"##)
*/
));
}
}
}
edges
}
pub fn build_svg(&mut self, dict: &mut impl TypeDict) {
let mut svg = String::new();
svg.push_str(&format!(r##"
<svg id="{}">
<defs>
<marker
id="arrowhead"
markerWidth="10"
markerHeight="7"
refX="10"
refY="3.5"
orient="auto"
markerUnits="strokeWidth"
>
<polygon points="0 0, 10 3.5, 0 7" fill="#555" />
</marker>
</defs>
"##, self.id()));
svg.push_str(r##"<g id="canvasGroup" transform="translate(800,500) scale(0.4)">"##);
for edge in self.get_edges() {
let srcid = edge.0;
let dstid = edge.1;
let text = edge.2;
svg.push_str(&format!(r##"
<line class="arrow" data-source="{}" data-target="{}" />
<text class="edge-label" data-source="{}" data-target="{}">
{}
</text>
"##, srcid, dstid, srcid, dstid, text));
}
for (modname, modpos) in self.layout.modules.iter_mut() {
svg.push_str(&format!(r##"
<g id="mod-{}" class="draggable" transform="translate({}, {})">
<circle r="300" class="module-bg-{}" />
<!-- Module Label -->
<text
class="module-label module-{}"
id="module-{}"
text-anchor="middle"
>
{}
</text>
<!-- Nodes in module Group -->
"##, modname, modpos.x as i32, modpos.y as i32, modname, modname, modname, modname));
for node in self.nodes.iter().filter(|n| &n.module == modname) {
let mut radius = 400.0 + (modpos.max_morphisms as f64)*10.0;
let rot = 6.283 * (modpos.next_pos_count as f64 / modpos.max_morphisms as f64);
modpos.next_pos_count += 1;
radius = radius + 50.0*(rot*modpos.max_morphisms as f64 / 3.0).sin();
let posx = radius * rot.cos() - 200.0;
let posy = radius * rot.sin() - 100.0;
svg.push_str(&format!(r##"<g class="draggable" id="{}" transform="translate({},{})">"##,
node.morph.symbol,
posx as i32,
posy as i32
));
svg.push_str(&format!(r##"
<rect
class="node-box module-{}"
width="400"
height="120"
/>
<text
class="morph-name module-{}"
x="60"
y="25"
text-anchor="left"
> {} </text>
<text x="50" y="50"> {} </text>
<text x="10" y="80"> <tspan class="arrow"></tspan> </text>
<text x="50" y="80"> {} </text>
"##,
node.module,
node.module,
node.morph.symbol,
unparse_type_to_svg(dict, &node.morph.ty.src_type),
unparse_type_to_svg(dict, &node.morph.ty.dst_type)
));
svg.push_str(r##"</g>"##);
}
svg.push_str(r##"</g>"##);
}
svg.push_str(r##"</g>"##);
svg.push_str(r##"</svg>"##);
self.svg = Some(svg);
}
}

460
src/viz/morphinst_pane.rs Normal file
View file

@ -0,0 +1,460 @@
use std::ops::Deref;
use laddertypes::{unparser::*, AddressingMode, ContextPtr, LayeredContext, Morphism, MorphismInstance, TypeDict, TypeTerm};
use crate::morphism::LdmcPrimMorph;
use crate::viz::{type_to_svg, unparse_type_to_svg, VisualizationPane};
pub struct Edge {
src_id: String,
dst_id: String,
label: String,
width: f32,
height: f32
}
pub struct Node {
id: String,
pos: (f32, f32),
ctx: ContextPtr,
ψ: TypeTerm,
τ: TypeTerm,
}
pub struct MorphinstVizPane {
name: String,//todo: displayname + ID
inst: MorphismInstance<LdmcPrimMorph>,
svg: Option< String >,
extent: Extent,
subnames: Vec<String>,
}
impl MorphinstVizPane {
pub fn new( name: String, inst: MorphismInstance<LdmcPrimMorph>) -> Self {
MorphinstVizPane { name, inst, svg: None, subnames: vec![ ], extent: Extent::empty() }
}
pub fn get_subnames(&self) -> Vec<String> {
self.subnames.clone()
}
pub fn get_extent(&self) -> Extent {
self.extent.clone()
}
pub fn build_module_group(&mut self,
svg: &mut String,
mut ctx: ContextPtr,
pos: (f32, f32)
) {
let mut layout = Layout::new(self.name.clone());
let mut nodes = Vec::new();
let mut edges = Vec::new();
layout.add_type(&mut nodes, &ctx, TypeTerm::unit(), self.inst.get_type().src_type);
let mut ctx0 = layout.add_morph_inst(&mut nodes, &mut edges, &self.inst, &ctx, TypeTerm::unit(), &mut self.subnames);
self.extent = layout.extent.clone();
// add some padding
self.extent.begin.0 -= 100.0;
self.extent.begin.1 -= 100.0;
self.extent.end.0 += 100.0;
self.extent.end.1 += 100.0;
svg.push_str(&format!(r##"
<g id="mod-{}" class="draggable" transform="translate({}, {})">
<rect x="{}" y="{}" width="{}" height="{}" class="module-bg-{}" />
<!-- Module Label -->
<text
class="module-label module-{}"
id="module-{}"
text-anchor="left"
>
{}
</text>
<!-- Nodes in module Group -->
"##, self.name, pos.0, pos.1,
self.extent.begin.0, self.extent.begin.1,
self.extent.end.0 - self.extent.begin.0, self.extent.end.1 - self.extent.begin.1,
self.name, self.name, self.name, self.display_name()));
for n in nodes.iter() {
svg.push_str(&format!(r##"<g id="{}" class="draggable type-node" transform="translate({},{})" width=200 height=100 >"##,
n.id,
n.pos.0,
n.pos.1
));
svg.push_str(&format!(r##"<rect class="node-box module-{}" x="0" y="0" width=800 height=200 />"##, self.name));
svg.push_str(r##"<text x="50" y="80">"##);
svg.push_str(&unparse_type_to_svg(&mut n.ctx.clone(), &n.ψ.clone().apply_subst(&n.ctx).clone()));
svg.push_str(r##"</text>"##);
svg.push_str(r##"<text x="50" y="150">"##);
if !n.ψ.is_empty() {
svg.push_str(r##"<tspan class="infix">~</tspan>"##);
}
svg.push_str(&unparse_type_to_svg(&mut n.ctx.clone(), &n.τ.clone().apply_subst(&n.ctx).clone()));
svg.push_str(r##"</text>"##);
svg.push_str(r##"</g>"##);
}
svg.push_str(r##"</g>"##);
for e in edges.iter() {
svg.push_str(&format!(r##"
<line class="arrow arrow2" data-source="{}" data-target="{}" />
<g class="edge-label" data-source="{}" data-target="{}">
<g class="draggable" transform="translate({},{})" >
{}
</g>
</g>
"##, e.src_id, e.dst_id, e.src_id, e.dst_id, 0., -e.height/2., e.label));
}
}
pub fn build_svg(&mut self, Γ: ContextPtr) {
let mut svg = String::new();
svg.push_str(&format!(r##"
<svg id="{}">
<defs>
<marker
id="arrowhead2"
markerWidth="10"
markerHeight="7"
refX="10"
refY="3.5"
orient="auto"
markerUnits="strokeWidth"
>
<polygon points="0 0, 10 3.5, 0 7" fill="#555" />
</marker>
</defs>
"##, self.id()));
svg.push_str(r##"<g id="canvasGroup" transform="translate(400,200) scale(1)">"##);
self.build_module_group(&mut svg, Γ, (0.0, 0.0));
svg.push_str(r##"</g>"##);
svg.push_str(r##"</svg>"##);
self.svg = Some(svg);
}
}
#[derive(Clone)]
pub struct Extent {
begin: (f32, f32),
end: (f32, f32)
}
impl Extent {
fn empty() -> Self {
Extent {
begin: (0.,0.),
end: (0.,0.)
}
}
fn max(e1: &Extent, e2: &Extent) -> Self {
Extent {
begin: (f32::min(e1.begin.0, e2.begin.0), f32::min(e1.begin.1, e2.begin.1)),
end: (f32::max(e1.end.0, e2.end.0), f32::max(e1.end.1, e2.end.1))
}
}
fn offset(&self, offset: (f32, f32)) -> Self {
Extent {
begin: (self.begin.0 + offset.0, self.begin.1 + offset.1),
end: (self.end.0 + offset.0, self.end.1 + offset.1),
}
}
}
pub struct Layout {
i: u32,
pos: (f32, f32),
nameprefix: String,
extent: Extent
}
impl Layout {
pub fn new(nameprefix: String) -> Self {
Layout {
i: 0,
pos: (50.0, 400.0),
nameprefix,
extent: Extent::empty()
}
}
pub fn add_type(&mut self,
nodes: &mut Vec<Node>,
Γ: &ContextPtr,
ψ: TypeTerm, mut τ: TypeTerm)
{
τ.apply_subst(Γ);
nodes.push(Node {
id: format!("type-{}-{}", self.nameprefix, self.i),
pos: self.pos,
ctx: Γ.clone(),
ψ, τ
});
self.extent = Extent::max(&self.extent, &Extent { begin: (0., 0.), end: (600.0, 400.0) }.offset(self.pos));
}
pub fn add_chained_type(
&mut self,
nodes: &mut Vec<Node>,
edges: &mut Vec<Edge>,
σ: Vec<(u64, TypeTerm)>,
ψ: TypeTerm,
τ: TypeTerm,
morphname: String,
mut Γ0: ContextPtr,
) {
let mut σ_str = "σ = {".to_string();
for (n,t) in σ {
σ_str.push_str(&format!("{} -> {}",
Γ0.get_varname(n).unwrap_or("??".into()),
unparse_type_to_svg(&mut Γ0, &t)));
}
σ_str.push_str("}");
let ψ_str =
if ψ.is_empty() {
"ψ = ε".into()
} else {
let mut ψ = ψ.clone();
ψ.apply_subst(&Γ0);
format!("ψ = {}", unparse_type_to_svg(&mut Γ0.clone(), &ψ))
};
edges.push(Edge {
src_id: format!("type-{}-{}", &self.nameprefix, self.i),
dst_id: format!("type-{}-{}", &self.nameprefix, self.i+1),
label: format!(r##"
<text y="-80" class="halo" text-anchor="center">{}</text>
<text y="0" class="morph-name" text-anchor="center" >{}</text>
<text y="80" class="Γ" text-anchor="center">{}</text>
"##, ψ_str, morphname, σ_str),
width: 0.0,
height: 0.,
});
self.i += 1;
self.pos.0 += 850.0;
self.pos.1 += if self.pos.1 < 0. { 400.0} else {-400.0};
self.extent.begin.0 = f32::min( self.extent.begin.0, self.pos.0 );
self.extent.begin.1 = f32::min( self.extent.begin.1, self.pos.1 );
self.extent.end.0 = f32::max( self.extent.end.0, self.pos.0 );
self.extent.end.1 = f32::max( self.extent.end.1, self.pos.1 );
self.add_type(nodes, &Γ0, ψ, τ);
}
pub fn sub_layout(&self, pfx: String, yoff: f32) -> Self {
Layout {
i: 0,
nameprefix: format!("{}-{}", self.nameprefix, pfx),
pos: (
self.pos.0 + 600.0,
self.pos.1 + yoff
),
extent: Extent::empty()
}
}
pub fn add_morph_inst(
&mut self,
nodes: &mut Vec<Node>,
edges: &mut Vec<Edge>,
inst: &MorphismInstance<LdmcPrimMorph>,
Γ0: &ContextPtr,
ψ: TypeTerm,
subnames: &mut Vec<String>,
) -> ContextPtr {
match inst {
MorphismInstance::Id { τ } => {
}
MorphismInstance::Sub { ψ:ψ1, m } => {
self.add_morph_inst(nodes, edges, m, Γ0,
TypeTerm::Ladder(vec![ ψ1.clone(), ψ.clone() ]).normalize(),
subnames
);
}
MorphismInstance::Specialize { σ, m } => {
//let mut c = Γ0.scope(AddressingMode::StackUp);
for (v,t) in σ.iter() {
Γ0.bind(*v,t.clone());
}
self.add_morph_inst(nodes, edges, m, &Γ0, ψ, subnames);
return Γ0.clone();
}
MorphismInstance::Primitive { σs, m: morph } => {
subnames.push( morph.symbol.clone() );
let gamma = σs.iter().map(|(v,t)| (match t {
TypeTerm::Var(v) => *v,
_ => unreachable!()
}, t.clone().apply_subst(Γ0).clone())).collect::<Vec<_>>();
Γ0.shift_variables(&morph.get_type().Γ);
self.add_chained_type(nodes, edges,
gamma,
ψ,
inst.get_type().dst_type,
morph.symbol.clone(),
Γ0.clone());
},
MorphismInstance::Chain { path } => {
for m in path.iter() {
self.add_morph_inst(nodes, edges, m, Γ0, ψ.clone(), subnames);
}
},
MorphismInstance::MapSeq { seq_repr, item_morph } => {
let mut map_group_svg = String::new();
let name = format!("{}-{}-SeqItem", self.nameprefix, self.i);
let mut pane = MorphinstVizPane::new( name.clone(), item_morph.deref().clone() );
pane.build_module_group(&mut map_group_svg, Γ0.clone(), (200.0, 200.0) );
subnames.append(&mut pane.get_subnames());
subnames.push(name);
let height = pane.get_extent().end.1 - pane.get_extent().begin.1 + 200.0;
map_group_svg.push_str(&format!(r##"
<path d="M0,0 C0,{} -50,{} 50,{}
M0,0 C0,{} -50,{} 50,{}"
fill="none" stroke-width="18" transform="translate(0,{})"/>
"##,
-height/4.,
-height/2.,
-height/2.,
height/4.,
height/2.,
height/2.,
pane.get_extent().begin.1 + height/2. + 100.0
));
map_group_svg.insert_str(0, &format!(r##"<g class="map-group" transform="translate({},{})">"##,
0,
0
));
map_group_svg.push_str(r##"</g>"##);
edges.push(Edge {
src_id: format!("type-{}-{}", &self.nameprefix, self.i),
dst_id: format!("type-{}-{}", &self.nameprefix, self.i+1),
label: map_group_svg,
width: pane.get_extent().end.0 - pane.get_extent().begin.0,
height: pane.get_extent().end.1 - pane.get_extent().begin.1
});
self.i += 1;
self.extent = Extent::max(&self.extent, &pane.get_extent());
self.pos.0 += 400.0;
self.pos.1 += pane.get_extent().end.1 - pane.get_extent().begin.1 + 500.0;
self.add_type(nodes, Γ0, ψ.clone(), inst.get_type().dst_type);
self.pos.0 += ( pane.get_extent().end.0 - pane.get_extent().begin.0 );
},
MorphismInstance::MapStruct { struct_repr, member_morph } => {
let mut map_group_svg = String::new();
map_group_svg.push_str(&format!(r##"<g class="map-group" transform="translate({},{})">"##, self.pos.0, self.pos.1 ));
let mut members_extent = Extent::empty();
let mut local_pos = (0.,0.);
for (i,m) in member_morph.iter().enumerate() {
let name = format!("{}-{}-{}", self.nameprefix, self.i, m.0);
let mut pane = MorphinstVizPane::new( name.clone(), m.1.clone() );
pane.build_module_group( &mut map_group_svg, Γ0.clone(), local_pos );
subnames.append(&mut pane.get_subnames());
subnames.push(name.clone());
members_extent = Extent::max(&members_extent, &pane.get_extent().offset(local_pos));
local_pos.1 += pane.get_extent().end.1 - pane.get_extent().begin.1;
local_pos.1 += 200.0; // padding
}
let height = members_extent.end.1 - members_extent.begin.1 + 100.0;
map_group_svg.push_str(&format!(r##"
<path d="M0,-50 C0,{} -50,{} 50,{}
M0,-50 C0,0 -100,-25 -100,0
M0,50 C0,0 -100,25 -100,0
M0,50 C0,{} -50,{} 50,{}"
fill="none" stroke-width="18" transform="translate(0,{})"/>
"##,
-height/4.,
-height/2. -30.,
-height/2. -15.,
height/4.,
height/2. - 30.,
height/2. - 15.,
height/2.
));
map_group_svg.push_str(r##"</g>"##);
edges.push(Edge {
src_id: format!("type-{}-{}", &self.nameprefix, self.i),
dst_id: format!("type-{}-{}", &self.nameprefix, self.i+1),
label: map_group_svg,
width: members_extent.end.0 - members_extent.begin.0,
height: members_extent.end.1 - members_extent.begin.1
});
self.i += 1;
self.extent = Extent::max(&self.extent, &members_extent.offset(self.pos));
self.pos.0 += 400.;
self.pos.1 += members_extent.end.1 - members_extent.begin.1 + 500.;
self.add_type(nodes, Γ0, ψ.clone(), inst.get_type().dst_type);
self.pos.0 += ( members_extent.end.0 - members_extent.begin.0 );
},
MorphismInstance::MapEnum { enum_repr, variant_morph } => {
todo!()
},
}
Γ0.clone()
}
}
impl VisualizationPane for MorphinstVizPane {
fn id(&self) -> String {
format!("morph-{}", self.name.clone())
}
fn display_name(&self) -> String {
self.name.clone()
}
fn svg(&self) -> String {
self.svg.clone().unwrap_or(String::new())
}
}

443
src/viz/search_pane.rs Normal file
View file

@ -0,0 +1,443 @@
use std::collections::HashMap;
use std::f32::consts::TAU;
use std::ops::Deref;
use laddertypes::search_node::SearchNodeExt;
use laddertypes::{unparser::*, AddressingMode, Context, ContextPtr, EnumVariant, GraphSearch, HashMapSubst, LayeredContext, Morphism, MorphismInstance, StructMember, TypeDict, TypeTerm};
use crate::morphism::LdmcPrimMorph;
use crate::viz::{type_to_svg, unparse_type_to_svg, VisualizationPane};
pub struct Edge {
src_id: String,
dst_id: String,
label: String,
width: f32,
height: f32
}
pub struct Node {
id_str: String,
pos: (f32, f32),
ctx: ContextPtr,
ψ: TypeTerm,
τ: TypeTerm,
}
pub struct SearchPane {
ctx: ContextPtr,
name: String,//todo: displayname + ID
svg: Option< String >,
extent: Extent,
subnames: Vec<String>,
}
impl SearchPane {
pub fn new( ctx: ContextPtr, name: String ) -> Self {
SearchPane { name, ctx, svg: None, subnames: vec![ ], extent: Extent::empty() }
}
pub fn get_subnames(&self) -> Vec<String> {
self.subnames.clone()
}
pub fn get_extent(&self) -> Extent {
self.extent.clone()
}
pub fn build_module_group(&mut self,
svg: &mut String,
pos: (f32, f32),
graph_search: &GraphSearch<LdmcPrimMorph>
) {
let mut layout = Layout::new(self.name.clone());
let mut nodes = Vec::new();
let mut edges :Vec<Edge> = Vec::new();
self.subnames.push(format!("module-{}",self.name.clone()));
layout.add_graph_search(&mut nodes, &mut edges, &mut self.subnames, graph_search);
self.extent = layout.extent.clone();
// add some padding
self.extent.begin.0 -= 100.0;
self.extent.begin.1 -= 100.0;
self.extent.end.0 += 100.0;
self.extent.end.1 += 100.0;
svg.push_str(&format!(r##"
<g id="mod-{}" class="draggable" transform="translate({}, {})">
<rect x="{}" y="{}" width="{}" height="{}" class="module-bg-{}" />
<!-- Module Label -->
<text
class="module-label module-{}"
id="module-{}"
text-anchor="left"
>
{}
</text>
<!-- Nodes in module Group -->
"##, self.name, pos.0, pos.1,
self.extent.begin.0, self.extent.begin.1,
self.extent.end.0 - self.extent.begin.0, self.extent.end.1 - self.extent.begin.1,
self.name, self.name, self.name, self.display_name()));
for n in nodes.iter() {
svg.push_str(&format!(r##"<g id="{}" class="draggable type-node" transform="translate({},{})" width=200 height=100 >"##,
n.id_str,
n.pos.0,
n.pos.1
));
svg.push_str(&format!(r##"<rect class="node-box module-{}" x="0" y="0" width=800 height=200 />"##, self.name));
svg.push_str(r##"<text x="50" y="80">"##);
svg.push_str(&unparse_type_to_svg(&mut n.ctx.clone(), &n.ψ.clone().apply_subst(&n.ctx).clone()));
svg.push_str(r##"</text>"##);
svg.push_str(r##"<text x="50" y="150">"##);
if !n.ψ.is_empty() {
svg.push_str(r##"<tspan class="infix">~</tspan>"##);
}
svg.push_str(&unparse_type_to_svg(&mut n.ctx.clone(), &n.τ.clone().apply_subst(&n.ctx).clone()));
svg.push_str(r##"</text>"##);
svg.push_str(r##"</g>"##);
}
svg.push_str(r##"</g>"##);
for e in edges.iter() {
svg.push_str(&format!(r##"
<line class="arrow arrow3" data-source="{}" data-target="{}" />
<g class="edge-label" data-source="{}" data-target="{}">
<g class="draggable" transform="translate({},{})" >
{}
</g>
</g>
"##, e.src_id, e.dst_id, e.src_id, e.dst_id, 0.0, -e.height/2., e.label));
}
}
pub fn build_svg(&mut self, graph_search: &GraphSearch<LdmcPrimMorph>) {
let mut svg = String::new();
svg.push_str(&format!(r##"
<svg id="{}">
<defs>
<marker
id="arrowhead3"
markerWidth="10"
markerHeight="7"
refX="10"
refY="3.5"
orient="auto"
markerUnits="strokeWidth"
>
<polygon points="0 0, 10 3.5, 0 7" fill="#555" />
</marker>
</defs>
"##, self.id()));
svg.push_str(r##"<g id="canvasGroup" transform="translate(0,0) scale(1)">"##);
self.build_module_group(&mut svg, (0.0, 0.0), graph_search);
svg.push_str(r##"</g>"##);
svg.push_str(r##"</svg>"##);
self.svg = Some(svg);
}
}
#[derive(Clone)]
pub struct Extent {
begin: (f32, f32),
end: (f32, f32)
}
impl Extent {
fn empty() -> Self {
Extent {
begin: (0.,0.),
end: (0.,0.)
}
}
fn max(e1: &Extent, e2: &Extent) -> Self {
Extent {
begin: (f32::min(e1.begin.0, e2.begin.0), f32::min(e1.begin.1, e2.begin.1)),
end: (f32::max(e1.end.0, e2.end.0), f32::max(e1.end.1, e2.end.1))
}
}
fn offset(&self, offset: (f32, f32)) -> Self {
Extent {
begin: (self.begin.0 + offset.0, self.begin.1 + offset.1),
end: (self.end.0 + offset.0, self.end.1 + offset.1),
}
}
}
pub struct LayoutNodeProperties {
pos: (f32, f32),
angle: f32,
next_child_angle: f32
}
pub struct Layout {
i: u32,
positions: HashMap<u64, LayoutNodeProperties>,
nameprefix: String,
extent: Extent
}
impl Layout {
pub fn new(nameprefix: String) -> Self {
Layout {
i: 0,
positions: HashMap::new(),
nameprefix,
extent: Extent::empty()
}
}
pub fn add_type(
&mut self,
nodes: &mut Vec<Node>,
ctx: &ContextPtr,
ψ: TypeTerm,
mut τ: TypeTerm,
id_str: String,
pos: (f32, f32)
)
{
τ.apply_subst(ctx);
nodes.push(Node {
id_str,
pos,
ctx: ctx.clone(),
ψ, τ
});
self.extent = Extent::max(&self.extent, &Extent { begin: (0., 0.), end: (600.0, 400.0) }.offset(pos));
}
pub fn sub_layout(&self, pfx: String, yoff: f32) -> Self {
Layout {
i: 0,
nameprefix: format!("{}-{}", self.nameprefix, pfx),
positions: HashMap::new(),
extent: Extent::empty()
}
}
pub fn add_graph_search(
&mut self,
nodes: &mut Vec<Node>,
edges: &mut Vec<Edge>,
subnames: &mut Vec<String>,
search: &GraphSearch<LdmcPrimMorph>
) -> ContextPtr {
let mut angle = 0.0;
for node in search.history.iter() {
let n = node.read().unwrap();
let mut path_len = 0;
{
let mut cur_node = Some(node.clone());
while let Some(n) = cur_node.clone() {
cur_node = n.read().unwrap().pred.clone();
path_len += 1;
}
}
let pos =
if let Some(parent) = n.pred.as_ref() {
let parent = parent.read().unwrap();
let parent_prop = self.positions.get_mut( &parent.id ).unwrap();
let angle = parent_prop.next_child_angle;
parent_prop.next_child_angle +=
0.35 *(1.5 / (path_len as f32)) * TAU;
let dist = 800.0 + 2000.0* (2.0/(1.0+2.0*path_len as f32));
(
parent_prop.pos.0 + dist*f32::cos(angle),
parent_prop.pos.1 + dist*f32::sin(angle),
)
} else {
(0.0, 0.0)
};
self.extent.begin.0 = f32::min( self.extent.begin.0, pos.0 );
self.extent.begin.1 = f32::min( self.extent.begin.1, pos.1 );
self.extent.end.0 = f32::max( self.extent.end.0, pos.0 );
self.extent.end.1 = f32::max( self.extent.end.1, pos.1 );
self.positions.insert( n.id, LayoutNodeProperties { pos, angle, next_child_angle:angle } );
let mut σ_str = "σ = {".to_string();
for (v,t) in n.ctx.0.read().unwrap().σ.iter() {
σ_str.push_str(&format!("{} -> {}",
n.ctx.get_varname(*v).unwrap_or("??".into()),
unparse_type_to_svg(&mut n.ctx.clone(), &t)));
}
σ_str.push_str("}");
let ψ_str =
if n.ψ.is_empty() {
"ψ = ε".into()
} else {
let mut ψ = n.ψ.clone();
ψ.apply_subst(&n.ctx);
format!("ψ = {}", unparse_type_to_svg(&mut n.ctx.clone(), &ψ))
};
let node_id = format!("{}-{}", self.nameprefix, n.id);
match &n.step {
laddertypes::search_node::Step::Id { τ:_ } |
laddertypes::search_node::Step::Prim { σs:_, m:_ } => {
self.add_type(
nodes,
&n.ctx,
n.ψ.clone(),
node.read().unwrap().ty.dst_type.clone(),
node_id.clone(),
pos
);
},
laddertypes::search_node::Step::MapSeq { seq_repr, item } => {
self.add_type(
nodes,
&n.ctx,
n.ψ.clone(),
TypeTerm::Seq { seq_repr: seq_repr.clone(), item: Box::new(item.goal.dst_type.clone()) },
node_id.clone(),
pos
);
},
laddertypes::search_node::Step::MapStruct { struct_repr, members } => {
self.add_type(
nodes,
&n.ctx,
n.ψ.clone(),
TypeTerm::Struct {
struct_repr: struct_repr.clone(),
members: members.iter().map(|(symbol,search)|
StructMember{ symbol:symbol.clone(), ty: search.goal.dst_type.clone() }
).collect()
},
node_id.clone(),
pos
);
},
laddertypes::search_node::Step::MapEnum { enum_repr, variants } => {
self.add_type(
nodes,
&n.ctx,
n.ψ.clone(),
TypeTerm::Enum {
enum_repr: enum_repr.clone(),
variants: variants.iter().map(|(symbol,search)|
EnumVariant{ symbol:symbol.clone(), ty: search.goal.dst_type.clone() }
).collect()
},
node_id.clone(),
pos
);
},
}
if let Some(parent) = n.pred.as_ref() {
let label = match &n.step {
laddertypes::search_node::Step::Id { τ } => "<text>Id</text>".into(),
laddertypes::search_node::Step::Prim { σs, m } => {
format!(r##"
<text y="-80" class="halo" text-anchor="center">{}</text>
<text y="0" class="morph-name" text-anchor="center" >{}</text>
<text y="80" class="Γ" text-anchor="center">{}</text>
"##, ψ_str, m.symbol, σ_str)
},
laddertypes::search_node::Step::MapSeq { seq_repr, item } => {
let label = format!("{}-MapSeq-{}-{}", self.nameprefix, parent.read().unwrap().id, n.id);
let mut map_pane = SearchPane::new(n.ctx.clone(), label.clone());
let mut svg = String::new();
subnames.push(label);
map_pane.build_module_group(&mut svg, (0.0, 0.0), item);
subnames.append(&mut map_pane.get_subnames());
self.extent = Extent::max(&self.extent, &map_pane.get_extent().offset(pos));
svg
},
laddertypes::search_node::Step::MapStruct { struct_repr, members } => {
let mut svg = String::new();
let mut members_extent = Extent::empty();
let mut local_pos = (0.,0.);
for (symbol, search) in members.iter() {
let label = format!("{}-MapStruct-{}-{}-{}", self.nameprefix, symbol, parent.read().unwrap().id, n.id);
let mut member_pane = SearchPane::new(n.ctx.clone(), label.clone());
subnames.push(label);
member_pane.build_module_group(&mut svg, local_pos, search);
subnames.append(&mut member_pane.get_subnames());
members_extent = Extent::max(&members_extent, &member_pane.get_extent().offset(local_pos));
local_pos.1 += member_pane.get_extent().end.1 - member_pane.get_extent().begin.1;
local_pos.1 += 200.0; // padding
}
self.extent = Extent::max(&self.extent, &members_extent.offset(pos));
svg
},
laddertypes::search_node::Step::MapEnum { enum_repr, variants } => {
let mut svg = String::new();
let mut variants_extent = Extent::empty();
let mut local_pos = (0.,0.);
for (symbol, search) in variants.iter() {
let label = format!("{}-MapEnum-{}-{}-{}", self.nameprefix, symbol, parent.read().unwrap().id, n.id);
let mut variant_pane = SearchPane::new(n.ctx.clone(), label.clone());
subnames.push(label);
variant_pane.build_module_group(&mut svg, local_pos, search);
subnames.append(&mut variant_pane.get_subnames());
variants_extent = Extent::max(&variants_extent, &variant_pane.get_extent().offset(local_pos));
local_pos.1 += variant_pane.get_extent().end.1 - variant_pane.get_extent().begin.1;
local_pos.1 += 200.0; // padding
}
self.extent = Extent::max(&self.extent, &variants_extent.offset(pos));
svg
}
};
edges.push(Edge {
src_id: format!("{}-{}", self.nameprefix, parent.read().unwrap().id),
dst_id: node_id,
label,
width: 0.0,
height: 0.,
});
}
}
Context::new()
}
}
impl VisualizationPane for SearchPane {
fn id(&self) -> String {
format!("search-{}", self.name.clone())
}
fn display_name(&self) -> String {
self.name.clone()
}
fn svg(&self) -> String {
self.svg.clone().unwrap_or(String::new())
}
}

314
src/viz/theme.rs Normal file
View file

@ -0,0 +1,314 @@
use crate::viz::Visualization;
fn generate_color(index: usize, total: usize) -> String {
let hue = (360.0 * (index as f32 / total as f32)) as u16;
let saturation = 60;
// Base lightness
let mut lightness = 50;
// Increase lightness for violet hues (260° - 320°)
if (260..=320).contains(&hue) {
lightness = 65; // lighter for better contrast on dark bg
}
format!("hsl({}, {}%, {}%)", hue, saturation, lightness)
}
fn generate_css_theme(identifiers: &Vec<String>) -> String {
let total = identifiers.len();
let mut css = String::new();
for (i, ident) in identifiers.iter().enumerate() {
let color = generate_color(i, total);
css.push_str(&format!(r##"
rect.module-bg-{ident}, circle.module-bg-{ident} {{
stroke: {color};
fill: {color};
opacity: 0.4;
filter: saturate(10%) blur(50px);
}}
text.module-{ident} {{
fill: {color};
stroke-width: 0.5;
}}
#mod-{ident} path {{
stroke: {color};
pointer-events: none;
}}
#mod-{ident} rect.struct-bg {{
opacity: 0.5;
fill: {color};
filter: brightness(85%) saturate(20%) blur(150px);
}}
body.dark text.module-{ident} {{
stroke: #ddd;
}}
body.light text.module-{ident} {{
stroke: #555;
}}
"##));
}
css
}
impl Visualization {
pub fn style(&self) -> String {
let mut css = String::new();
css.push_str(r##"<style>
/* Page */
body {
margin: 0;
overflow: hidden;
transition: background 0.3s ease;
}
#themeToggle {
display: flex;
align-items: center;
gap: 5px;
font-size: 18px;
cursor: pointer;
}
#themeToggle input {
margin: 0;
}
/* Light Theme */
body.light {
background: #f2f2f2;
}
body.light svg {
background-color: #ffffff;
}
body.light #toolbar {
background-color: #e0e0e0;
}
body.light select {
background-color: #fff;
color: #000;
}
body.light button {
background-color: #007acc;
color: white;
}
body.light button:hover {
background-color: #005fa3;
}
/* Dark Theme */
body.dark {
background: #121212;
}
body.dark svg {
background-color: #353535;
}
body.dark #toolbar {
background-color: #2b2b2b;
}
body.dark select {
background-color: #444;
color: #fff;
}
body.dark button {
background-color: #569cd6;
color: white;
}
body.dark button:hover {
background-color: #4a90c2;
}
/* SVG */
svg {
width: 100vw;
height: 100vh;
cursor: grab;
user-select: none;
transition: background 0.3s ease;
}
text {
font-family: sans-serif;
pointer-events: none;
}
line.arrow {
stroke: #111;
stroke-width: 4;
marker-end: url(#arrowhead);
pointer-events: none;
}
line.arrow2 {
stroke-width: 8;
marker-end: url(#arrowhead2);
pointer-events: none;
}
line.arrow3 {
stroke-width: 8;
marker-end: url(#arrowhead3);
pointer-events: none;
}
/* Nodes */
.node-box {
fill: #444;
stroke: #333;
stroke-width: 1.5;
rx: 6;
ry: 6;
pointer-events: all;
opacity: 0.8;
}
body.light .node-box {
fill: #eee;
}
.draggable {
cursor: grab;
transition: transform 0.5s ease-in-out;
}
.draggable:active {
transition: none !important;
}
.edge-label {
font-size: 32px;
fill: darkblue;
pointer-events: none;
}
.module-label {
font-weight: bold;
font-size: 80px;
fill: #222;
pointer-events: none;
}
line.arrow {
stroke: #777;
stroke-width: 2;
marker-end: url(#arrowhead);
pointer-events: none;
}
.morph-edge-label {
font-size: 42px;
font-family: monospace;
fill: darkblue;
pointer-events: none;
}
/* Types */
.halo {
font-size: 48px;
}
.Γ {
font-size: 48px;
}
.morph-name {
font-family: monospace;
font-size: 72px;
}
.infix, .bracket {
fill: #754000;
font-size: 64px;
}
.type {
fill: cadetblue;
font-size: 48px;
}
.typevar {
fill: #9cdcfe;
font-size: 48px;
font-style: smallcaps;
fill: #bbb;
}
.value {
fill: green;
font-size: 48px;
font-family: monospace;
}
.complex-opening {
font-size: 200px;
}
.type-box {
stroke: #777;
}
.type-node .type {
}
.type-node .typevar {
font-family: monospace;
}
.type-node .infix,
.type-node .bracket {
font-weight: bold;
}
/* Toolbar */
#toolbar {
position: absolute;
top: 10px;
left: 10px;
z-index: 1000;
border-radius: 8px;
padding: 8px 12px;
display: flex;
gap: 10px;
align-items: center;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2);
transition: background 0.3s ease;
}
#toolbar select,
#toolbar button,
#toolbar label {
font-size: 14px;
padding: 6px 10px;
border: none;
border-radius: 4px;
outline: none;
transition: background 0.3s ease, color 0.3s ease;
}
#toolbar select {
background-color: #444;
color: #fff;
}
#toolbar button {
background-color: #569cd6;
color: white;
cursor: pointer;
}
#toolbar button:hover {
background-color: #4a90c2;
}
#themeToggle {
background: #333;
}
"##);
css.push_str(&generate_css_theme(&self.modules));
css.push_str(r##"</style>"##);
css
}
}

110
src/viz/type_to_svg.rs Normal file
View file

@ -0,0 +1,110 @@
use std::ops::Deref;
use laddertypes::{TypeTerm, TypeID, TypeDict};
pub fn unparse_type_to_svg(
dict: &mut impl TypeDict,
t: &TypeTerm
) -> String {
match t {
TypeTerm::Char(c) => {
format!(r##"
<tspan class="value">&apos;{}&apos;</tspan>
"##, c)
}
TypeTerm::Num(n) => {
format!(r##"
<tspan class="value">{}</tspan>
"##, n)
}
TypeTerm::Id(id) => {
format!(r##"
<tspan class="type">{}</tspan>
"##, dict.get_typename(*id).unwrap_or("?".into()))
}
TypeTerm::Var(var_id) => {
format!(r##"
<tspan class="typevar">{}</tspan>
"##, dict.get_varname(*var_id).unwrap_or("?".into()))
}
TypeTerm::Spec(args) => {
let mut s = String::new();
s.push_str(&r##"<tspan class="bracket">⟨</tspan>"##);
for a in args.iter() {
s.push_str(&unparse_type_to_svg(dict, a));
s.push_str(" ");
}
s.push_str(&r##"<tspan class="bracket">⟩</tspan>"##);
s
}
TypeTerm::Func(args) => {
let mut s = String::new();
for (i,a) in args.iter().enumerate() {
s.push_str(&unparse_type_to_svg(dict, a));
if i+1 != args.len() {
s.push_str(&r##"<tspan class="arrow">⟶</tspan>"##);
}
}
s
}
TypeTerm::Seq { seq_repr, item } => {
let mut s = String::new();
s.push_str(&r##"<tspan class="bracket">[</tspan>"##);
if let Some(r) = seq_repr {
s.push_str(&r##"<tspan class="infix">~</tspan>"##);
s.push_str(&unparse_type_to_svg(dict, r));
}
s.push_str(&unparse_type_to_svg(dict, item.deref()));
s.push_str(&r##"<tspan class="bracket">]</tspan>"##);
s
}
TypeTerm::Struct { struct_repr, members } => {
let mut s = String::new();
s.push_str(&r##"<tspan class="bracket">{</tspan>"##);
if let Some(r) = struct_repr {
s.push_str(&r##"<tspan class="infix">~</tspan>"##);
s.push_str(&unparse_type_to_svg(dict, r));
}
for m in members.iter() {
s.push_str(&format!(r##"
<tspan class="typevar">{}</tspan>
<tspan class="infix">:</tspan>
{}
<tspan class="infix">;</tspan>
"##,
m.symbol,
unparse_type_to_svg(dict, &m.ty)
));
}
s.push_str(&r##"<tspan class="bracket">}</tspan>"##);
s
}
TypeTerm::Ladder(rungs) => {
let mut s = String::new();
for (i,a) in rungs.iter().enumerate() {
s.push_str(&unparse_type_to_svg(dict, a));
if i+1 != rungs.len() {
s.push_str(&r##"<tspan class="infix">~</tspan>"##);
}
}
s
}
_ => { "".into() }
}
}

View file

@ -8,13 +8,12 @@ run_test_case() {
-----------------------------------------------------------------------------
Running test case ${TEST_NAME}"
ldmc "${SRC_TYPE}" "${DST_TYPE}" ../morphisms/*.morphism-base 2>|.tmp/ldmc_err 1>| target/src/${TEST_NAME}.c \
ldmc -m "main : ${SRC_TYPE} --> ${DST_TYPE}" -o target/src/${TEST_NAME}.c 2>|.tmp/ldmc_err \
|| (echo "... error at generation:"; cat .tmp/ldmc_err; return -1);
gcc -I../morphisms/runtime/include target/src/${TEST_NAME}.c -o target/${TEST_NAME} \
|| (echo "... error at compilation:"; return -2);
LEN="$(echo -n "${EXPECT}" | wc -c)"
RESULT="$(echo -n ${INPUT} | ./target/${TEST_NAME} 2>.tmp/target_err | head -c ${LEN})"

76
todo.md Normal file
View file

@ -0,0 +1,76 @@
* BUG: -O3 kills some Morphisms
* BUG: erroneous code generation with Packed struct layout
* type Aliases / typedefs (load from input file)
-- platform specializations
-- shortcuts
* add highest-common-rung to generated morphism names
(to avoid potential ambiguities)
* add bounds to Morphism type:
- subtype bounds
- trait bounds
- value bounds
* data dependence:
- Each Struct creates a Context,
where a struct member becomes a type-variable,
available after it has been parsed from the input buffer.
- need some kind of partial Order over the member data fields,
to express parsing dependencies.
* bounds:
-> ~ UInt64 ---> ~ UInt8
- know that value fits into UInt8 ?
-> <Digit 128> ~ UInt64 --> <Digit 128> ~ UInt8
allows the following path:
<Digit 128> ~ UInt64
-> <Digit 128> ~ Char ~ Ascii ~ native.UInt8
-> <Digit 128> ~ native.UInt8
this will result in 'invalid digits' that are out of the ascii range
and is not wanted.
maybe the Morphism from digit to char should not exist for radix 128 (->value bounds on type vars)?
-fixed by adding explicit <Digit R>~UInt64 -morph-> <Digit R>~UInt8
* type descriptions: signed posints?
- Include minus sign '-' into the <Digit Radix> type ?
- or use [<Digit Radix> | Sign] ?
- or rather <SignedPosInt Radix Endianness> ~ {
sign: <Option Sign~Char>;
digits: <Seq <Digit 10>~Char>
};
- ?
* size estimation & memory allocation
- from each type definition, we need to derive
a form for an "allocation schema",
of which a value instance of that type defines
concrete length values for arrays and members
and thus the total required buffer size.
- in parallel to each morphism we need to know
the output-allocation schema in accordance to
a given allocation schema of the input data.
- Complex morphisms allocate a double buffering
with maximum required size at each point in the chain
- optional: allocation free morphisms for members in struct morphism ?
- In-Place Morphisms
* improve debugability of "no morphism path found"
- find a heuristic to guess "implementation overhead" of
a missing morphism by its type
- Step 1: Calculate Connected Components of src and dst Type ;
- Step 2: For each combination of a vertex-pair from src and dst Components,
calculate the implementation overhead heuristic ;
- Step 3: suggest to the developer the n smallest morphism types that would
bridge the two components
* allow to define (ladder-typed) functions, not just morphisms