Commit: efd1b742e36a88a49d48cef56fe8dae05dfc0ef6
Parent: feca7d86eb20ea5a9b8b8e6c113ffc4374e449ee
Author: Randy Palamar
Date: Tue, 11 Nov 2025 13:59:12 -0700
meta: add conditional generation
this brings the @Emit directive closer to being able to do all
generation directly without supporting code in the build tool
parameter struct generation is now moved to metacode
Diffstat:
11 files changed, 922 insertions(+), 315 deletions(-)
diff --git a/beamformer.c b/beamformer.c
@@ -290,7 +290,7 @@ alloc_shader_storage(BeamformerCtx *ctx, u32 decoded_data_size, Arena arena)
/* NOTE(rnp): these are stubs when CUDA isn't supported */
cuda_register_buffers(cc->ping_pong_ssbos, countof(cc->ping_pong_ssbos), cc->rf_buffer.ssbo);
u32 decoded_data_dimension[3] = {pb->parameters.sample_count, pb->parameters.channel_count, pb->parameters.acquisition_count};
- cuda_init(pb->parameters.raw_data_dimensions, decoded_data_dimension);
+ cuda_init(pb->parameters.raw_data_dimensions.E, decoded_data_dimension);
}
function void
@@ -406,13 +406,11 @@ compute_cursor_finished(struct compute_cursor *cursor)
function m4
das_voxel_transform_matrix(BeamformerParameters *bp)
{
- v3 min = v3_from_f32_array(bp->output_min_coordinate);
- v3 max = v3_from_f32_array(bp->output_max_coordinate);
- v3 extent = v3_abs(v3_sub(max, min));
- v3 points = v3_from_iv3(make_valid_output_points(bp->output_points));
+ v3 extent = v3_abs(v3_sub(bp->output_max_coordinate, bp->output_min_coordinate));
+ v3 points = v3_from_iv3(make_valid_output_points(bp->output_points.E));
m4 T1 = m4_translation(v3_scale(v3_sub(points, (v3){{1.0f, 1.0f, 1.0f}}), -0.5f));
- m4 T2 = m4_translation(v3_add(min, v3_scale(extent, 0.5f)));
+ m4 T2 = m4_translation(v3_add(bp->output_min_coordinate, v3_scale(extent, 0.5f)));
m4 S = m4_scale(v3_div(extent, points));
m4 R;
@@ -649,9 +647,9 @@ plan_compute_pipeline(BeamformerComputePlan *cp, BeamformerParameterBlock *pb)
BeamformerShaderDASBakeParameters *db = &sd->bake.DAS;
BeamformerDASUBO *du = &cp->das_ubo_data;
- du->voxel_transform = das_voxel_transform_matrix(&pb->parameters);
- mem_copy(du->xdc_transform.E, pb->parameters.xdc_transform, sizeof(du->xdc_transform));
- mem_copy(du->xdc_element_pitch.E, pb->parameters.xdc_element_pitch, sizeof(du->xdc_element_pitch));
+ du->voxel_transform = das_voxel_transform_matrix(&pb->parameters);
+ du->xdc_transform = pb->parameters.xdc_transform;
+ du->xdc_element_pitch = pb->parameters.xdc_element_pitch;
db->sampling_frequency = sampling_frequency;
db->demodulation_frequency = pb->parameters.demodulation_frequency;
db->speed_of_sound = pb->parameters.speed_of_sound;
@@ -662,8 +660,8 @@ plan_compute_pipeline(BeamformerComputePlan *cp, BeamformerParameterBlock *pb)
db->channel_count = pb->parameters.channel_count;
db->acquisition_count = pb->parameters.acquisition_count;
db->interpolation_mode = pb->parameters.interpolation_mode;
- db->transmit_angle = pb->parameters.focal_vector[0];
- db->focus_depth = pb->parameters.focal_vector[1];
+ db->transmit_angle = pb->parameters.focal_vector.E[0];
+ db->focus_depth = pb->parameters.focal_vector.E[1];
db->transmit_receive_orientation = pb->parameters.transmit_receive_orientation;
if (pb->parameters.single_focus) sd->bake.flags |= BeamformerShaderDASFlags_SingleFocus;
@@ -853,11 +851,11 @@ beamformer_commit_parameter_block(BeamformerCtx *ctx, BeamformerComputePlan *cp,
if (cp->hadamard_order != (i32)cp->acquisition_count)
update_hadamard_texture(cp, (i32)cp->acquisition_count, arena);
- cp->min_coordinate = v3_from_f32_array(pb->parameters.output_min_coordinate);
- cp->max_coordinate = v3_from_f32_array(pb->parameters.output_max_coordinate);
+ cp->min_coordinate = pb->parameters.output_min_coordinate;
+ cp->max_coordinate = pb->parameters.output_max_coordinate;
- cp->output_points = make_valid_output_points(pb->parameters.output_points);
- cp->average_frames = pb->parameters.output_points[3];
+ cp->output_points = make_valid_output_points(pb->parameters.output_points.E);
+ cp->average_frames = pb->parameters.output_points.E[3];
GLenum gl_kind = cp->iq_pipeline ? GL_RG32F : GL_R32F;
if (cp->average_frames > 1 && !beamformer_frame_compatible(ctx->averaged_frames + 0, cp->output_points, gl_kind)) {
diff --git a/beamformer.meta b/beamformer.meta
@@ -86,6 +86,54 @@
[Cubic]
}
+@Table([name type]) ParametersHead
+{
+ [xdc_transform M4]
+ [xdc_element_pitch V2]
+ [raw_data_dimensions UV2]
+ [focal_vector V2]
+ [transmit_receive_orientation U32]
+ [sample_count U32]
+ [channel_count U32]
+ [acquisition_count U32]
+ [das_shader_id U32]
+ [time_offset F32]
+ [single_focus U8]
+ [single_orientation U8]
+ [decode_mode U8]
+ [sampling_mode U8]
+}
+
+@Table([name type]) ParametersUI
+{
+ [output_min_coordinate V3]
+ [output_max_coordinate V3]
+ [output_points SV4]
+ [sampling_frequency F32]
+ [demodulation_frequency F32]
+ [speed_of_sound F32]
+ [f_number F32]
+ [off_axis_pos F32]
+ [interpolation_mode U32]
+ [coherency_weighting U32]
+ [beamform_plane U32]
+ [decimation_rate U32]
+}
+
+// TODO(rnp): definable constants in meta code (or at least references to enum values)
+@Table([name type elements]) ParametersSimple
+{
+ [channel_mapping S16 256]
+ [sparse_elements S16 256]
+ [transmit_receive_orientations U8 256]
+ [steering_angles F32 256]
+ [focal_depths F32 256]
+ [compute_stages S32 16]
+ [compute_stage_parameters S32 16]
+ [compute_stages_count U32 1]
+ [data_kind S32 1]
+}
+
@Expand(AcquisitionKind) @Enumeration(AcquisitionKind `$(name)`)
@Expand(DataKind) @Enumeration(DataKind `$(name)`)
@Expand(FilterKind) @Enumeration(FilterKind `$(name)`)
@@ -94,6 +142,25 @@
@Emit
{
`typedef struct {`
+ @Expand(ParametersHead) ` $(%type)$(|)$(name);`
+ @Expand(ParametersUI) ` $(%type)$(|)$(name);`
+ `} BeamformerParameters;`
+ ``
+ `typedef struct {`
+ @Expand(ParametersHead) ` $(%type)$(|)$(name);`
+ `} BeamformerParametersHead;`
+ ``
+ `typedef struct {`
+ @Expand(ParametersUI) ` $(%type)$(|)$(name);`
+ `} BeamformerUIParameters;`
+ ``
+ `typedef struct {`
+ @Expand(ParametersHead) ` $(%type)$(|)$(name);`
+ @Expand(ParametersUI) ` $(%type)$(|)$(name);`
+ @Expand(ParametersSimple) ` $(%type)$(|)$(name)$(elements > 1 -> "[" elements "]");`
+ `} BeamformerSimpleParameters;`
+ ``
+ `typedef struct {`
` BeamformerEmissionKind kind;`
` union {`
` struct {`
@@ -234,3 +301,72 @@
{
@Shader(render_3d.frag.glsl) Render3D
}
+
+/////////////////
+// NOTE: Library
+
+@Emit(CLibrary)
+{
+ `typedef struct {`
+ @Expand(ParametersHead) ` $(%type)$(|)$(name)$(#type > 1 -> "[" #type "]");`
+ @Expand(ParametersUI) ` $(%type)$(|)$(name)$(#type > 1 -> "[" #type "]");`
+ @Expand(ParametersSimple) ` $(%type)$(|)$(name)$(elements > 1 -> "[" elements "]");`
+ `} BeamformerSimpleParameters;`
+ ``
+ `typedef struct {`
+ @Expand(ParametersUI) ` $(%type)$(|)$(name)$(#type > 1 -> "[" #type "]");`
+ `} BeamformerUIParameters;`
+ ``
+ `typedef struct {`
+ @Expand(ParametersHead) ` $(%type)$(|)$(name)$(#type > 1 -> "[" #type "]");`
+ `} BeamformerParametersHead;`
+ ``
+ `typedef struct {`
+ @Expand(ParametersHead) ` $(%type)$(|)$(name)$(#type > 1 -> "[" #type "]");`
+ @Expand(ParametersUI) ` $(%type)$(|)$(name)$(#type > 1 -> "[" #type "]");`
+ `} BeamformerParameters;`
+ ``
+}
+
+////////////////
+// NOTE: MATLAB
+
+@Emit(MATLAB) Parameters
+{
+ `classdef OGLBeamformerParameters`
+ ` properties`
+ @Expand(ParametersHead) ` $(name)(1,$(#type))$(|)$(%type)`
+ ` % UI Parameters`
+ @Expand(ParametersUI) ` $(name)(1,$(#type))$(|)$(%type)`
+ ` end`
+ `end`
+}
+
+@Emit(MATLAB) ParametersHead
+{
+ `classdef OGLBeamformerParametersHead`
+ ` properties`
+ @Expand(ParametersHead) ` $(name)(1,$(#type))$(|)$(%type)`
+ ` end`
+ `end`
+}
+
+@Emit(MATLAB) ParametersUI
+{
+ `classdef OGLBeamformerParametersUI`
+ ` properties`
+ @Expand(ParametersUI) ` $(name)(1,$(#type))$(|)$(%type)`
+ ` end`
+ `end`
+}
+
+@Emit(MATLAB) SimpleParameters
+{
+ `classdef OGLBeamformerSimpleParameters`
+ ` properties`
+ @Expand(ParametersHead) ` $(name)(1,$(#type)) $(%type)`
+ @Expand(ParametersUI) ` $(name)(1,$(#type)) $(%type)`
+ @Expand(ParametersSimple) ` $(name)(1,$(elements)) $(%type)`
+ ` end`
+ `end`
+}
diff --git a/beamformer_parameters.h b/beamformer_parameters.h
@@ -1,5 +1,4 @@
/* See LICENSE for license details. */
-#include <stdint.h>
/* TODO(rnp):
* [ ]: shader kinds have ballooned; shader stats table needs to be compressed
@@ -42,64 +41,6 @@ typedef enum {
typedef enum {BEAMFORMER_CONSTANTS_LIST} BeamformerConstants;
#undef X
-/* X(name, type, size, matlab_type, elements, comment) */
-#define BEAMFORMER_PARAMS_HEAD \
- X(xdc_transform, float, [16], single, 16, "IMPORTANT: column major order") \
- X(xdc_element_pitch, float, [2], single, 2, "[m] Transducer Element Pitch {row, col}") \
- X(raw_data_dimensions, uint32_t, [2], uint32, 2, "Raw Data Dimensions") \
- X(focal_vector, float, [2], single, 2, "[degree, m] focal point {angle, depth}") \
- X(transmit_receive_orientation, uint32_t, , uint32, 1, "") \
- X(sample_count, uint32_t, , uint32, 1, "") \
- X(channel_count, uint32_t, , uint32, 1, "") \
- X(acquisition_count, uint32_t, , uint32, 1, "") \
- X(das_shader_id, uint32_t, , uint32, 1, "") \
- X(time_offset, float, , single, 1, "pulse length correction time [s]") \
- X(single_focus, uint8_t, , uint8, 1, "") \
- X(single_orientation, uint8_t, , uint8, 1, "") \
- X(decode_mode, uint8_t, , uint8, 1, "") \
- X(sampling_mode, uint8_t, , uint8, 1, "")
-
-#define BEAMFORMER_UI_PARAMS \
- X(output_min_coordinate, float, [3], single, 3, "[m] Back-Top-Left corner of output region") \
- X(output_max_coordinate, float, [3], single, 3, "[m] Front-Bottom-Right corner of output region") \
- X(output_points, int32_t, [4], int32, 4, "Width * Height * Depth * (Frame Average Count)") \
- X(sampling_frequency, float, , single, 1, "[Hz]") \
- X(demodulation_frequency, float, , single, 1, "[Hz]") \
- X(speed_of_sound, float, , single, 1, "[m/s]") \
- X(f_number, float, , single, 1, "F# (set to 0 to disable)") \
- X(off_axis_pos, float, , single, 1, "[m] Position on screen normal to beamform in TPW/VLS/HERCULES") \
- X(interpolation_mode, uint32_t, , uint32, 1, "Nearest, Linear, or Cubic Interpolation of RF Samples") \
- X(coherency_weighting, uint32_t, , uint32, 1, "Apply coherency weighting to output data") \
- X(beamform_plane, uint32_t, , uint32, 1, "Plane to Beamform in TPW/VLS/HERCULES") \
- X(decimation_rate, uint32_t, , uint32, 1, "Number of times to decimate")
-
-#define BEAMFORMER_SIMPLE_PARAMS \
- X(channel_mapping, int16_t, [BeamformerMaxChannelCount], int16, BeamformerMaxChannelCount) \
- X(sparse_elements, int16_t, [BeamformerMaxChannelCount], int16, BeamformerMaxChannelCount) \
- X(transmit_receive_orientations, uint8_t, [BeamformerMaxChannelCount], uint8, BeamformerMaxChannelCount) \
- X(steering_angles, float, [BeamformerMaxChannelCount], single, BeamformerMaxChannelCount) \
- X(focal_depths, float, [BeamformerMaxChannelCount], single, BeamformerMaxChannelCount) \
- X(compute_stages, int32_t, [BeamformerMaxComputeShaderStages], int32, BeamformerMaxComputeShaderStages) \
- X(compute_stage_parameters, int16_t, [BeamformerMaxComputeShaderStages], int16, BeamformerMaxComputeShaderStages) \
- X(compute_stages_count, uint32_t, , uint32, 1) \
- X(data_kind, int32_t, , int32, 1)
-
-#define X(name, type, size, ...) type name size;
-typedef struct {BEAMFORMER_PARAMS_HEAD} BeamformerParametersHead;
-typedef struct {BEAMFORMER_UI_PARAMS} BeamformerUIParameters;
-
-typedef struct {
- BEAMFORMER_PARAMS_HEAD
- BEAMFORMER_UI_PARAMS
-} BeamformerParameters;
-
-typedef struct {
- BEAMFORMER_PARAMS_HEAD
- BEAMFORMER_UI_PARAMS
- BEAMFORMER_SIMPLE_PARAMS
-} BeamformerSimpleParameters;
-#undef X
-
#define BEAMFORMER_LIVE_IMAGING_DIRTY_FLAG_LIST \
X(ImagePlaneOffsets, 0) \
X(TransmitPower, 1) \
diff --git a/build.c b/build.c
@@ -925,14 +925,43 @@ meta_end_and_write_matlab(MetaprogramContext *m, char *path)
X(SubShader) \
X(Table)
+typedef enum {
+ #define X(k, ...) MetaEntryKind_## k,
+ META_ENTRY_KIND_LIST
+ #undef X
+ MetaEntryKind_Count,
+} MetaEntryKind;
+
+#define X(k, ...) #k,
+read_only global char *meta_entry_kind_strings[] = {META_ENTRY_KIND_LIST};
+#undef X
+
+#define META_EMIT_LANG_LIST \
+ X(C) \
+ X(CLibrary) \
+ X(MATLAB)
+
+typedef enum {
+ #define X(k, ...) MetaEmitLang_## k,
+ META_EMIT_LANG_LIST
+ #undef X
+ MetaEmitLang_Count,
+} MetaEmitLang;
+
#define META_KIND_LIST \
- X(F32, f32, float, single, 4) \
- X(S32, i32, int32_t, int32, 4) \
- X(S16, i16, int16_t, int16, 2) \
- X(S8, i8, int8_t, int8, 1) \
- X(U32, u32, uint32_t, uint32, 4) \
- X(U16, u16, uint16_t, uint16, 2) \
- X(U8, u8, uint8_t, uint8, 1)
+ X(M4, m4, float, single, 64, 16) \
+ X(SV4, iv4, int32_t, int32, 16, 4) \
+ X(UV4, uv4, uint32_t, uint32, 16, 4) \
+ X(UV2, uv2, uint32_t, uint32, 8, 2) \
+ X(V3, v3, float, single, 12, 3) \
+ X(V2, v2, float, single, 8, 2) \
+ X(F32, f32, float, single, 4, 1) \
+ X(S32, i32, int32_t, int32, 4, 1) \
+ X(S16, i16, int16_t, int16, 2, 1) \
+ X(S8, i8, int8_t, int8, 1, 1) \
+ X(U32, u32, uint32_t, uint32, 4, 1) \
+ X(U16, u16, uint16_t, uint16, 2, 1) \
+ X(U8, u8, uint8_t, uint8, 1, 1)
typedef enum {
#define X(k, ...) MetaKind_## k,
@@ -947,6 +976,12 @@ read_only global u8 meta_kind_byte_sizes[] = {
#undef X
};
+read_only global u8 meta_kind_elements[] = {
+ #define X(_k, _c, _b, _m, _by, elements, ...) elements,
+ META_KIND_LIST
+ #undef X
+};
+
read_only global s8 meta_kind_meta_types[] = {
#define X(k, ...) s8_comp(#k),
META_KIND_LIST
@@ -971,17 +1006,6 @@ read_only global s8 meta_kind_c_types[] = {
#undef X
};
-typedef enum {
- #define X(k, ...) MetaEntryKind_## k,
- META_ENTRY_KIND_LIST
- #undef X
- MetaEntryKind_Count,
-} MetaEntryKind;
-
-#define X(k, ...) #k,
-read_only global char *meta_entry_kind_strings[] = {META_ENTRY_KIND_LIST};
-#undef X
-
typedef struct { u32 line, column; } MetaLocation;
#define META_ENTRY_ARGUMENT_KIND_LIST \
@@ -1132,6 +1156,20 @@ meta_entry_print(MetaEntry *e, i32 indent, i32 caret)
fprintf(stderr, "\n");
}
+function iz
+meta_lookup_string_slow(s8 *strings, iz string_count, s8 s)
+{
+ // TODO(rnp): obviously this is slow
+ iz result = -1;
+ for (iz i = 0; i < string_count; i++) {
+ if (s8_equal(s, strings[i])) {
+ result = i;
+ break;
+ }
+ }
+ return result;
+}
+
function MetaEntryKind
meta_entry_kind_from_string(s8 s)
{
@@ -1139,12 +1177,8 @@ meta_entry_kind_from_string(s8 s)
read_only local_persist s8 kinds[] = {META_ENTRY_KIND_LIST};
#undef X
MetaEntryKind result = MetaEntryKind_Invalid;
- for EachNonZeroEnumValue(MetaEntryKind, kind) {
- if (s8_equal(kinds[kind], s)) {
- result = kind;
- break;
- }
- }
+ iz id = meta_lookup_string_slow(kinds + 1, countof(kinds) - 1, s);
+ if (id > 0) result = (MetaEntryKind)(id + 1);
return result;
}
@@ -1162,7 +1196,8 @@ meta_parser_trim(MetaParser *p)
}break;
case '\n':{ p->p.location.line++; p->p.location.column = 0; comment = 0; }break;
case '/':{
- comment = ((s + 1) != end && s[1] == '/');
+ comment = ((s + 1) != end && s[1] == '/');
+ if (comment) s++;
} /* FALLTHROUGH */
default:{done = !comment;}break;
}
@@ -1536,14 +1571,48 @@ DA_STRUCT(MetaMUnion, MetaMUnion);
typedef enum {
MetaExpansionPartKind_Alignment,
+ MetaExpansionPartKind_Conditional,
MetaExpansionPartKind_EvalKind,
+ MetaExpansionPartKind_EvalKindCount,
MetaExpansionPartKind_Reference,
MetaExpansionPartKind_String,
} MetaExpansionPartKind;
+typedef enum {
+ MetaExpansionConditionalArgumentKind_Invalid,
+ MetaExpansionConditionalArgumentKind_Number,
+ MetaExpansionConditionalArgumentKind_Evaluation,
+ MetaExpansionConditionalArgumentKind_Reference,
+} MetaExpansionConditionalArgumentKind;
+
+typedef struct {
+ MetaExpansionConditionalArgumentKind kind;
+ union {
+ s8 *strings;
+ i64 number;
+ };
+} MetaExpansionConditionalArgument;
+
+typedef enum {
+ MetaExpansionOperation_Invalid,
+ MetaExpansionOperation_LessThan,
+ MetaExpansionOperation_GreaterThan,
+} MetaExpansionOperation;
+
+typedef struct {
+ MetaExpansionConditionalArgument lhs;
+ MetaExpansionConditionalArgument rhs;
+ MetaExpansionOperation op;
+ u32 instruction_skip;
+} MetaExpansionConditional;
+
typedef struct {
- s8 *strings;
MetaExpansionPartKind kind;
+ union {
+ s8 string;
+ s8 *strings;
+ MetaExpansionConditional conditional;
+ };
} MetaExpansionPart;
DA_STRUCT(MetaExpansionPart, MetaExpansionPart);
@@ -1567,7 +1636,14 @@ typedef struct {
MetaEmitOperationKind kind;
MetaLocation location;
} MetaEmitOperation;
-DA_STRUCT(MetaEmitOperation, MetaEmitOperation);
+
+typedef struct {
+ MetaEmitOperation *data;
+ iz count;
+ iz capacity;
+
+ s8 filename;
+} MetaEmitOperationList;
typedef struct {
MetaEmitOperationList *data;
@@ -1592,7 +1668,7 @@ typedef struct {
s8_list munion_namespaces;
MetaMUnionList munions;
- MetaEmitOperationListSet emit_sets;
+ MetaEmitOperationListSet emit_sets[MetaEmitLang_Count];
MetaShaderBakeParametersList shader_bake_parameters;
MetaIDList shader_enumerations;
@@ -1602,21 +1678,6 @@ typedef struct {
s8_list shader_names;
} MetaContext;
-
-function iz
-meta_lookup_string_slow(s8 *strings, iz string_count, s8 s)
-{
- // TODO(rnp): obviously this is slow
- iz result = -1;
- for (iz i = 0; i < string_count; i++) {
- if (s8_equal(s, strings[i])) {
- result = i;
- break;
- }
- }
- return result;
-}
-
function iz
meta_lookup_id_slow(MetaIDList *v, u32 id)
{
@@ -1893,8 +1954,11 @@ meta_push_expansion_part(MetaContext *ctx, Arena *arena, MetaExpansionPartList *
MetaExpansionPart *result = da_push(arena, parts);
result->kind = kind;
switch (kind) {
- case MetaExpansionPartKind_Alignment:{}break;
+ case MetaExpansionPartKind_Alignment:
+ case MetaExpansionPartKind_Conditional:
+ {}break;
case MetaExpansionPartKind_EvalKind:
+ case MetaExpansionPartKind_EvalKindCount:
case MetaExpansionPartKind_Reference:
{
iz index = meta_lookup_string_slow(t->fields, t->field_count, string);
@@ -1906,14 +1970,154 @@ meta_push_expansion_part(MetaContext *ctx, Arena *arena, MetaExpansionPartList *
(i32)table_name.len, table_name.data, (i32)string.len, string.data);
}
}break;
- case MetaExpansionPartKind_String:{
- result->strings = push_struct(arena, s8);
- result->strings[0] = string;
+ case MetaExpansionPartKind_String:{ result->string = string; }break;
+ InvalidDefaultCase;
+ }
+ return result;
+}
+
+#define META_EXPANSION_TOKEN_LIST \
+ X('|', Alignment) \
+ X('%', TypeEval) \
+ X('#', TypeEvalElements) \
+ X('"', Quote) \
+ X('-', Dash) \
+ X('>', GreaterThan) \
+ X('<', LessThan) \
+
+typedef enum {
+ MetaExpansionToken_EOF,
+ MetaExpansionToken_Identifier,
+ MetaExpansionToken_Number,
+ MetaExpansionToken_String,
+ #define X(__1, kind, ...) MetaExpansionToken_## kind,
+ META_EXPANSION_TOKEN_LIST
+ #undef X
+ MetaExpansionToken_Count,
+} MetaExpansionToken;
+
+read_only global s8 meta_expansion_token_strings[] = {
+ s8_comp("EOF"),
+ s8_comp("Indentifier"),
+ s8_comp("Number"),
+ s8_comp("String"),
+ #define X(s, kind, ...) s8_comp(#s),
+ META_EXPANSION_TOKEN_LIST
+ #undef X
+};
+
+typedef struct {
+ s8 s;
+ union {
+ i64 number;
+ s8 string;
+ };
+ s8 save;
+ MetaLocation loc;
+} MetaExpansionParser;
+
+#define meta_expansion_save(v) (v)->save = (v)->s
+#define meta_expansion_restore(v) swap((v)->s, (v)->save)
+#define meta_expansion_commit(v) meta_expansion_restore(v)
+
+#define meta_expansion_expected(loc, e, g) \
+ meta_compiler_error(loc, "invalid expansion string: expected %.*s after %.*s\n", \
+ (i32)meta_expansion_token_strings[e].len, meta_expansion_token_strings[e].data, \
+ (i32)meta_expansion_token_strings[g].len, meta_expansion_token_strings[g].data)
+
+function s8
+meta_expansion_extract_string(MetaExpansionParser *p)
+{
+ s8 result = {.data = p->s.data};
+ for (; result.len < p->s.len; result.len++) {
+ b32 done = 0;
+ switch (p->s.data[result.len]) {
+ #define X(t, ...) case t:
+ META_EXPANSION_TOKEN_LIST
+ #undef X
+ case ' ':
+ {done = 1;}break;
+ default:{}break;
+ }
+ if (done) break;
+ }
+ p->s.data += result.len;
+ p->s.len -= result.len;
+ return result;
+}
+
+function MetaExpansionToken
+meta_expansion_token(MetaExpansionParser *p)
+{
+ MetaExpansionToken result = MetaExpansionToken_EOF;
+ meta_expansion_save(p);
+ if (p->s.len > 0) {
+ b32 chop = 1;
+ switch (p->s.data[0]) {
+ #define X(t, kind, ...) case t:{ result = MetaExpansionToken_## kind; }break;
+ META_EXPANSION_TOKEN_LIST
+ #undef X
+ default:{
+ chop = 0;
+ if (BETWEEN(p->s.data[0], '0', '9')) result = MetaExpansionToken_Number;
+ else result = MetaExpansionToken_Identifier;
+ }break;
+ }
+ if (chop) {
+ s8_chop(&p->s, 1);
+ p->s = s8_trim(p->s);
+ }
+
+ switch (result) {
+ case MetaExpansionToken_Number:{
+ IntegerConversion integer = integer_from_s8(p->s);
+ if (integer.result != IntegerConversionResult_Success) {
+ /* TODO(rnp): point at start */
+ meta_compiler_error(p->loc, "invalid integer in expansion string\n");
+ }
+ p->number = integer.S64;
+ p->s = integer.unparsed;
+ }break;
+ case MetaExpansionToken_Identifier:{ p->string = meta_expansion_extract_string(p); }break;
+ default:{}break;
+ }
+ p->s = s8_trim(p->s);
+ }
+ return result;
+}
+
+function MetaExpansionPart *
+meta_expansion_start_conditional(MetaContext *ctx, Arena *arena, MetaExpansionPartList *ops,
+ MetaExpansionParser *p, MetaExpansionToken token, b32 negate)
+{
+ MetaExpansionPart *result = meta_push_expansion_part(ctx, arena, ops, MetaExpansionPartKind_Conditional,
+ s8(""), 0, p->loc);
+ switch (token) {
+ case MetaExpansionToken_Number:{
+ result->conditional.lhs.kind = MetaExpansionConditionalArgumentKind_Number;
+ result->conditional.lhs.number = negate ? -p->number : p->number;
}break;
+ default:{}break;
}
return result;
}
+function void
+meta_expansion_end_conditional(MetaExpansionPart *ep, MetaExpansionParser *p, MetaExpansionToken token, b32 negate)
+{
+ if (ep->conditional.rhs.kind != MetaExpansionConditionalArgumentKind_Invalid) {
+ meta_compiler_error(p->loc, "invalid expansion conditional: duplicate right hand expression: '%.*s'\n",
+ (i32)p->save.len, p->save.data);
+ }
+ switch (token) {
+ case MetaExpansionToken_Number:{
+ ep->conditional.rhs.kind = MetaExpansionConditionalArgumentKind_Number;
+ ep->conditional.rhs.number = negate ? -p->number : p->number;
+ }break;
+ default:{}break;
+ }
+}
+
function MetaExpansionPartList
meta_generate_expansion_set(MetaContext *ctx, Arena *arena, s8 expansion_string, MetaTable *t, MetaLocation loc)
{
@@ -1923,14 +2127,118 @@ meta_generate_expansion_set(MetaContext *ctx, Arena *arena, s8 expansion_string,
meta_expansion_string_split(remainder, &left, &inner, &remainder, loc);
if (left.len) meta_push_expansion_part(ctx, arena, &result, MetaExpansionPartKind_String, left, t, loc);
if (inner.len) {
- s8 test = inner;
- s8 opcode = s8_chop(&test, 1);
- if (test.len == 0 && opcode.data[0] == '|') {
- meta_push_expansion_part(ctx, arena, &result, MetaExpansionPartKind_Alignment, inner, t, loc);
- } else if (test.len && opcode.data[0] == '%') {
- meta_push_expansion_part(ctx, arena, &result, MetaExpansionPartKind_EvalKind, test, t, loc);
- } else {
- meta_push_expansion_part(ctx, arena, &result, MetaExpansionPartKind_Reference, inner, t, loc);
+ MetaExpansionParser p[1] = {{.s = inner, .loc = loc}};
+
+ MetaExpansionPart *test_part = 0;
+ b32 count_test_parts = 0;
+
+ for (MetaExpansionToken token = meta_expansion_token(p);
+ token != MetaExpansionToken_EOF;
+ token = meta_expansion_token(p))
+ {
+ if (count_test_parts) test_part->conditional.instruction_skip++;
+ switch (token) {
+ case MetaExpansionToken_Alignment:{
+ meta_push_expansion_part(ctx, arena, &result, MetaExpansionPartKind_Alignment, p->s, t, loc);
+ }break;
+
+ case MetaExpansionToken_Identifier:{
+ meta_push_expansion_part(ctx, arena, &result, MetaExpansionPartKind_Reference, p->string, t, loc);
+ }break;
+
+ case MetaExpansionToken_TypeEval:
+ case MetaExpansionToken_TypeEvalElements:
+ {
+ if (meta_expansion_token(p) != MetaExpansionToken_Identifier) {
+ loc.column += p->save.data - expansion_string.data;
+ meta_expansion_expected(loc, MetaExpansionToken_Identifier, token);
+ }
+ MetaExpansionPartKind kind = token == MetaExpansionToken_TypeEval ?
+ MetaExpansionPartKind_EvalKind :
+ MetaExpansionPartKind_EvalKindCount;
+ meta_push_expansion_part(ctx, arena, &result, kind, p->string, t, loc);
+ }break;
+
+ case MetaExpansionToken_Quote:{
+ u8 *point = p->s.data;
+ s8 string = meta_expansion_extract_string(p);
+ token = meta_expansion_token(p);
+ if (token != MetaExpansionToken_Quote) {
+ loc.column += point - expansion_string.data;
+ /* TODO(rnp): point at start */
+ meta_compiler_error(loc, "unterminated string in expansion\n");
+ }
+ meta_push_expansion_part(ctx, arena, &result, MetaExpansionPartKind_String, string, t, loc);
+ }break;
+
+ case MetaExpansionToken_Dash:{
+ token = meta_expansion_token(p);
+ switch (token) {
+ case MetaExpansionToken_GreaterThan:{
+ if (!test_part) goto error;
+ if (test_part->conditional.lhs.kind == MetaExpansionConditionalArgumentKind_Invalid ||
+ test_part->conditional.rhs.kind == MetaExpansionConditionalArgumentKind_Invalid)
+ {
+ b32 lhs = test_part->conditional.lhs.kind == MetaExpansionConditionalArgumentKind_Invalid;
+ b32 rhs = test_part->conditional.rhs.kind == MetaExpansionConditionalArgumentKind_Invalid;
+ if (lhs && rhs)
+ meta_compiler_error(loc, "expansion string test terminated without arguments\n");
+ meta_compiler_error(loc, "expansion string test terminated without %s argument\n",
+ lhs? "left" : "right");
+ }
+ count_test_parts = 1;
+ }break;
+ case MetaExpansionToken_Number:{
+ if (test_part) meta_expansion_end_conditional(test_part, p, token, 1);
+ else test_part = meta_expansion_start_conditional(ctx, arena, &result, p, token, 1);
+ }break;
+ default:{ goto error; }break;
+ }
+ }break;
+
+ case MetaExpansionToken_Number:{
+ if (test_part) meta_expansion_end_conditional(test_part, p, token, 0);
+ else test_part = meta_expansion_start_conditional(ctx, arena, &result, p, token, 0);
+ }break;
+
+ case MetaExpansionToken_GreaterThan:
+ case MetaExpansionToken_LessThan:
+ {
+ if (test_part && test_part->conditional.op != MetaExpansionOperation_Invalid) goto error;
+ if (!test_part) {
+ if (result.count == 0) {
+ meta_compiler_error(p->loc, "invalid expansion conditional: missing left hand side\n");
+ }
+
+ s8 *strings = result.data[result.count - 1].strings;
+ MetaExpansionPartKind last_kind = result.data[result.count - 1].kind;
+ if (last_kind != MetaExpansionPartKind_EvalKindCount &&
+ last_kind != MetaExpansionPartKind_Reference)
+ {
+ meta_compiler_error(p->loc, "invalid expansion conditional: left hand side not numeric\n");
+ }
+ result.count--;
+ test_part = meta_expansion_start_conditional(ctx, arena, &result, p, token, 0);
+ if (last_kind == MetaExpansionPartKind_EvalKindCount) {
+ test_part->conditional.lhs.kind = MetaExpansionConditionalArgumentKind_Evaluation;
+ } else {
+ test_part->conditional.lhs.kind = MetaExpansionConditionalArgumentKind_Reference;
+ }
+ test_part->conditional.lhs.strings = strings;
+ }
+ test_part->conditional.op = token == MetaExpansionToken_LessThan ?
+ MetaExpansionOperation_LessThan :
+ MetaExpansionOperation_GreaterThan;
+ }break;
+
+ error:
+ default:
+ {
+ meta_compiler_error(loc, "invalid nested %.*s in expansion string\n",
+ (i32)meta_expansion_token_strings[token].len,
+ meta_expansion_token_strings[token].data);
+ }break;
+ }
}
}
} while (remainder.len);
@@ -2005,7 +2313,7 @@ meta_embed(MetaContext *ctx, Arena scratch, MetaEntry *e, iz entry_count)
meta_entry_argument_expected(e, s8("filename"));
s8 filename = meta_entry_argument_expect(e, 0, MetaEntryArgumentKind_String).string;
- MetaEmitOperationList *ops = da_push(ctx->arena, &ctx->emit_sets);
+ MetaEmitOperationList *ops = da_push(ctx->arena, ctx->emit_sets + MetaEmitLang_C);
if (e->name.len == 0) meta_entry_error(e, "name must be provided for output array");
MetaEmitOperation *op;
@@ -2022,12 +2330,52 @@ meta_embed(MetaContext *ctx, Arena scratch, MetaEntry *e, iz entry_count)
op->string = s8("};");
}
+function MetaKind
+meta_map_kind(s8 kind, s8 table_name, MetaLocation location)
+{
+ iz id = meta_lookup_string_slow(meta_kind_meta_types, MetaKind_Count, kind);
+ if (id < 0) {
+ meta_compiler_error(location, "Invalid Kind in '%.*s' table expansion: %.*s\n",
+ (i32)table_name.len, table_name.data, (i32)kind.len, kind.data);
+ }
+ MetaKind result = (MetaKind)id;
+ return result;
+}
+
+function MetaEmitLang
+meta_map_emit_lang(s8 lang, MetaEntry *e)
+{
+ #define X(k, ...) s8_comp(#k),
+ read_only local_persist s8 meta_lang_strings[] = {META_EMIT_LANG_LIST};
+ #undef X
+
+ iz id = meta_lookup_string_slow(meta_lang_strings, MetaEmitLang_Count, lang);
+ if (id < 0) {
+ #define X(k, ...) #k ", "
+ meta_entry_error(e, "Unknown Emit Language: '%.*s'\nPossible Values: "
+ META_EMIT_LANG_LIST "\n", (i32)lang.len, lang.data);
+ #undef X
+ }
+ MetaEmitLang result = (MetaEmitLang)id;
+ return result;
+}
+
function iz
meta_pack_emit(MetaContext *ctx, Arena scratch, MetaEntry *e, iz entry_count)
{
assert(e->kind == MetaEntryKind_Emit);
- MetaEmitOperationList *ops = da_push(ctx->arena, &ctx->emit_sets);
+ MetaEmitLang lang = MetaEmitLang_C;
+ if (e->argument_count) {
+ meta_entry_argument_expected(e, s8("emit_language"));
+ s8 name = meta_entry_argument_expect(e, 0, MetaEntryArgumentKind_String).string;
+ lang = meta_map_emit_lang(name, e);
+ }
+
+ MetaEmitOperationList *ops = da_push(ctx->arena, ctx->emit_sets + lang);
+ /* TODO(rnp): probably we should check this is unique */
+ ops->filename = e->name;
+
MetaEntryScope scope = meta_entry_extract_scope(e, entry_count);
for (MetaEntry *row = scope.start; row != scope.one_past_last; row++) {
switch (row->kind) {
@@ -2129,8 +2477,8 @@ function CommandList
meta_extract_emit_file_dependencies(MetaContext *ctx, Arena *arena)
{
CommandList result = {0};
- for (iz set = 0; set < ctx->emit_sets.count; set++) {
- MetaEmitOperationList *ops = ctx->emit_sets.data + set;
+ for (iz set = 0; set < ctx->emit_sets[MetaEmitLang_C].count; set++) {
+ MetaEmitOperationList *ops = ctx->emit_sets[MetaEmitLang_C].data + set;
for (iz opcode = 0; opcode < ops->count; opcode++) {
MetaEmitOperation *op = ops->data + opcode;
switch (op->kind) {
@@ -2145,18 +2493,6 @@ meta_extract_emit_file_dependencies(MetaContext *ctx, Arena *arena)
return result;
}
-function MetaKind
-meta_map_kind(s8 kind, s8 table_name, MetaLocation location)
-{
- iz id = meta_lookup_string_slow(meta_kind_meta_types, MetaKind_Count, kind);
- if (id < 0) {
- meta_compiler_error(location, "Invalid Kind in '%.*s' table expansion: %.*s\n",
- (i32)table_name.len, table_name.data, (i32)kind.len, kind.data);
- }
- MetaKind result = (MetaKind)id;
- return result;
-}
-
function void
metagen_push_byte_array(MetaprogramContext *m, s8 bytes)
{
@@ -2199,90 +2535,159 @@ metagen_push_table(MetaprogramContext *m, Arena scratch, s8 row_start, s8 row_en
}
}
-function void
-metagen_run_emit(MetaprogramContext *m, MetaContext *ctx, s8 *evaluation_table)
+function i64
+meta_expansion_part_conditional_argument(MetaExpansionConditionalArgument a, u32 entry,
+ s8 table_name, MetaLocation loc)
{
- for (iz set = 0; set < ctx->emit_sets.count; set++) {
- MetaEmitOperationList *ops = ctx->emit_sets.data + set;
- for (iz opcode = 0; opcode < ops->count; opcode++) {
- MetaEmitOperation *op = ops->data + opcode;
- switch (op->kind) {
- case MetaEmitOperationKind_String:{ meta_push_line(m, op->string); }break;
- case MetaEmitOperationKind_FileBytes:{
- Arena scratch = m->scratch;
- s8 filename = push_s8_from_parts(&scratch, s8(OS_PATH_SEPARATOR), ctx->directory, op->string);
- s8 file = os_read_whole_file(&scratch, (c8 *)filename.data);
- m->indentation_level++;
- metagen_push_byte_array(m, file);
- m->indentation_level--;
- }break;
- case MetaEmitOperationKind_Expand:{
- Arena scratch = m->scratch;
+ i64 result = 0;
+ switch (a.kind) {
+ case MetaExpansionConditionalArgumentKind_Number:{
+ result = a.number;
+ }break;
+
+ case MetaExpansionConditionalArgumentKind_Evaluation:
+ {
+ s8 string = a.strings[entry];
+ MetaKind kind = meta_map_kind(string, table_name, loc);
+ result = meta_kind_elements[kind];
+ }break;
+
+ case MetaExpansionConditionalArgumentKind_Reference:{
+ s8 string = a.strings[entry];
+ IntegerConversion integer = integer_from_s8(string);
+ if (integer.result != IntegerConversionResult_Success) {
+ meta_compiler_error(loc, "Invalid integer in '%.*s' table expansion: %.*s\n",
+ (i32)table_name.len, table_name.data, (i32)string.len, string.data);
+ }
+ result = integer.S64;
+ }break;
- MetaEmitOperationExpansion *eop = &op->expansion_operation;
- MetaTable *t = ctx->tables.data + eop->table_id;
- s8 table_name = ctx->table_names.data[t->table_name_id];
+ InvalidDefaultCase;
+ }
- u32 alignment_count = 1;
- u32 evaluation_count = 0;
- for (u32 part = 0; part < eop->part_count; part++) {
- if (eop->parts[part].kind == MetaExpansionPartKind_Alignment)
- alignment_count++;
- if (eop->parts[part].kind == MetaExpansionPartKind_EvalKind)
- evaluation_count++;
+ return result;
+}
+
+function b32
+meta_expansion_part_conditional(MetaExpansionPart *p, u32 entry, s8 table_name, MetaLocation loc)
+{
+ assert(p->kind == MetaExpansionPartKind_Conditional);
+ b32 result = 0;
+ i64 lhs = meta_expansion_part_conditional_argument(p->conditional.lhs, entry, table_name, loc);
+ i64 rhs = meta_expansion_part_conditional_argument(p->conditional.rhs, entry, table_name, loc);
+ switch (p->conditional.op) {
+ case MetaExpansionOperation_LessThan:{ result = lhs < rhs; }break;
+ case MetaExpansionOperation_GreaterThan:{ result = lhs > rhs; }break;
+ InvalidDefaultCase;
+ }
+ return result;
+}
+
+function void
+metagen_run_emit(MetaprogramContext *m, MetaContext *ctx, MetaEmitOperationList *ops,
+ s8 *evaluation_table)
+{
+ for (iz opcode = 0; opcode < ops->count; opcode++) {
+ MetaEmitOperation *op = ops->data + opcode;
+ switch (op->kind) {
+ case MetaEmitOperationKind_String:{ meta_push_line(m, op->string); }break;
+ case MetaEmitOperationKind_FileBytes:{
+ Arena scratch = m->scratch;
+ s8 filename = push_s8_from_parts(&scratch, s8(OS_PATH_SEPARATOR), ctx->directory, op->string);
+ s8 file = os_read_whole_file(&scratch, (c8 *)filename.data);
+ m->indentation_level++;
+ metagen_push_byte_array(m, file);
+ m->indentation_level--;
+ }break;
+ case MetaEmitOperationKind_Expand:{
+ Arena scratch = m->scratch;
+
+ MetaEmitOperationExpansion *eop = &op->expansion_operation;
+ MetaTable *t = ctx->tables.data + eop->table_id;
+ s8 table_name = ctx->table_names.data[t->table_name_id];
+
+ u32 alignment_count = 1;
+ u32 evaluation_count = 0;
+ for (u32 part = 0; part < eop->part_count; part++) {
+ if (eop->parts[part].kind == MetaExpansionPartKind_Alignment)
+ alignment_count++;
+ if (eop->parts[part].kind == MetaExpansionPartKind_EvalKind ||
+ eop->parts[part].kind == MetaExpansionPartKind_EvalKindCount)
+ evaluation_count++;
+ }
+
+ MetaKind **evaluation_columns = push_array(&scratch, MetaKind *, evaluation_count);
+ for (u32 column = 0; column < evaluation_count; column++)
+ evaluation_columns[column] = push_array(&scratch, MetaKind, t->entry_count);
+
+ for (u32 part = 0; part < eop->part_count; part++) {
+ u32 eval_column = 0;
+ MetaExpansionPart *p = eop->parts + part;
+ if (p->kind == MetaExpansionPartKind_EvalKind) {
+ for (u32 entry = 0; entry < t->entry_count; entry++) {
+ evaluation_columns[eval_column][entry] = meta_map_kind(p->strings[entry],
+ table_name, op->location);
+ }
+ eval_column++;
}
+ }
- MetaKind **evaluation_columns = push_array(&scratch, MetaKind *, evaluation_count);
- for (u32 column = 0; column < evaluation_count; column++)
- evaluation_columns[column] = push_array(&scratch, MetaKind, t->entry_count);
+ s8 **columns = push_array(&scratch, s8 *, alignment_count);
+ for (u32 column = 0; column < alignment_count; column++)
+ columns[column] = push_array(&scratch, s8, t->entry_count);
+ Stream sb = arena_stream(scratch);
+ for (u32 entry = 0; entry < t->entry_count; entry++) {
+ u32 column = 0;
+ u32 eval_column = 0;
for (u32 part = 0; part < eop->part_count; part++) {
- u32 eval_column = 0;
MetaExpansionPart *p = eop->parts + part;
- if (p->kind == MetaExpansionPartKind_EvalKind) {
- for (u32 entry = 0; entry < t->entry_count; entry++) {
- evaluation_columns[eval_column][entry] = meta_map_kind(p->strings[entry],
- table_name, op->location);
- }
- eval_column++;
+ switch (p->kind) {
+ case MetaExpansionPartKind_Alignment:{
+ columns[column][entry] = arena_stream_commit_and_reset(&scratch, &sb);
+ column++;
+ }break;
+
+ case MetaExpansionPartKind_Conditional:{
+ if (!meta_expansion_part_conditional(p, entry, table_name, op->location))
+ part += p->conditional.instruction_skip;
+ }break;
+
+ case MetaExpansionPartKind_EvalKind:{
+ s8 kind = evaluation_table[evaluation_columns[eval_column][entry]];
+ stream_append_s8(&sb, kind);
+ }break;
+
+ case MetaExpansionPartKind_EvalKindCount:{
+ stream_append_u64(&sb, meta_kind_elements[evaluation_columns[eval_column][entry]]);
+ }break;
+
+ case MetaExpansionPartKind_Reference:
+ case MetaExpansionPartKind_String:
+ {
+ s8 string = p->kind == MetaExpansionPartKind_Reference ? p->strings[entry] : p->string;
+ stream_append_s8(&sb, string);
+ }break;
}
}
- s8 **columns = push_array(&scratch, s8 *, alignment_count);
- for (u32 column = 0; column < alignment_count; column++)
- columns[column] = push_array(&scratch, s8, t->entry_count);
-
- Stream sb = arena_stream(scratch);
- for (u32 entry = 0; entry < t->entry_count; entry++) {
- u32 column = 0;
- u32 eval_column = 0;
- for (u32 part = 0; part < eop->part_count; part++) {
- MetaExpansionPart *p = eop->parts + part;
- switch (p->kind) {
- case MetaExpansionPartKind_Alignment:{
- columns[column][entry] = arena_stream_commit_and_reset(&scratch, &sb);
- column++;
- }break;
- case MetaExpansionPartKind_EvalKind:{
- s8 kind = evaluation_table[evaluation_columns[eval_column][entry]];
- stream_append_s8(&sb, kind);
- }break;
- case MetaExpansionPartKind_Reference:
- case MetaExpansionPartKind_String:
- {
- u32 index = p->kind == MetaExpansionPartKind_Reference ? entry : 0;
- stream_append_s8(&sb, p->strings[index]);
- }break;
- }
- }
- columns[column][entry] = arena_stream_commit_and_reset(&scratch, &sb);
- }
- metagen_push_table(m, scratch, s8(""), s8(""), columns, t->entry_count, alignment_count);
- }break;
- InvalidDefaultCase;
+ columns[column][entry] = arena_stream_commit_and_reset(&scratch, &sb);
}
+ metagen_push_table(m, scratch, s8(""), s8(""), columns, t->entry_count, alignment_count);
+ }break;
+ InvalidDefaultCase;
}
- meta_end_line(m);
+ }
+ meta_end_line(m);
+}
+
+function void
+metagen_run_emit_set(MetaprogramContext *m, MetaContext *ctx, MetaEmitOperationListSet *emit_set,
+ s8 *evaluation_table)
+{
+ for (iz set = 0; set < emit_set->count; set++) {
+ MetaEmitOperationList *ops = emit_set->data + set;
+ metagen_run_emit(m, ctx, ops, evaluation_table);
}
}
@@ -2603,7 +3008,7 @@ metagen_emit_c_code(MetaContext *ctx, Arena arena)
metagen_push_table(m, m->scratch, s8(""), s8(";"), (s8 *[]){types, names}, countof(names), 2);
} meta_end_scope(m, s8("} BeamformerShaderBakeParameters;\n"));
- metagen_run_emit(m, ctx, meta_kind_c_types);
+ metagen_run_emit_set(m, ctx, ctx->emit_sets + MetaEmitLang_C, meta_kind_c_types);
/////////////////////////////////
// NOTE(rnp): shader info tables
@@ -2798,7 +3203,9 @@ metagen_matlab_union(MetaprogramContext *m, MetaContext *ctx, MetaMUnion *mu, s8
{
u32 name_index = table_matches[member].name_index;
for (u32 prop = 0; prop < t->entry_count; prop++) {
- meta_push_line(m, t->entries[name_index][prop], s8("(1,1) "), meta_kind_matlab_types[kinds[prop]]);
+ meta_begin_line(m, t->entries[name_index][prop], s8("(1,"));
+ meta_push_u64(m, meta_kind_elements[kinds[prop]]);
+ meta_end_line(m, s8(") "), meta_kind_matlab_types[kinds[prop]]);
}
} meta_end_scope(m, s8("end"));
} meta_end_scope(m, s8("end"));
@@ -2838,31 +3245,6 @@ metagen_emit_matlab_code(MetaContext *ctx, Arena arena)
result &= meta_end_and_write_matlab(m, OUTPUT("matlab/OGLBeamformerLiveFeedbackFlags.m"));
#undef X
- #define X(name, __t, __s, kind, elements, ...) meta_push_matlab_property(m, s8(#name), (u64)elements, s8(#kind));
- meta_begin_matlab_class(m, "OGLBeamformerParameters");
- meta_begin_scope(m, s8("properties"));
- BEAMFORMER_PARAMS_HEAD
- BEAMFORMER_UI_PARAMS
- result &= meta_end_and_write_matlab(m, OUTPUT("matlab/OGLBeamformerParameters.m"));
-
- meta_begin_matlab_class(m, "OGLBeamformerParametersHead");
- meta_begin_scope(m, s8("properties"));
- BEAMFORMER_PARAMS_HEAD
- result &= meta_end_and_write_matlab(m, OUTPUT("matlab/OGLBeamformerParametersHead.m"));
-
- meta_begin_matlab_class(m, "OGLBeamformerParametersUI");
- meta_begin_scope(m, s8("properties"));
- BEAMFORMER_UI_PARAMS
- result &= meta_end_and_write_matlab(m, OUTPUT("matlab/OGLBeamformerParametersUI.m"));
-
- meta_begin_matlab_class(m, "OGLBeamformerSimpleParameters");
- meta_begin_scope(m, s8("properties"));
- BEAMFORMER_PARAMS_HEAD
- BEAMFORMER_UI_PARAMS
- BEAMFORMER_SIMPLE_PARAMS
- result &= meta_end_and_write_matlab(m, OUTPUT("matlab/OGLBeamformerSimpleParameters.m"));
- #undef X
-
#define X(name, __t, __s, elements, ...) meta_push_matlab_property(m, s8(#name), elements, s8(""));
meta_begin_matlab_class(m, "OGLBeamformerLiveImagingParameters");
meta_begin_scope(m, s8("properties"));
@@ -2905,6 +3287,22 @@ metagen_emit_matlab_code(MetaContext *ctx, Arena arena)
result &= meta_end_and_write_matlab(m, (c8 *)output.data);
}
+ ////////////////////
+ // NOTE: emit files
+ {
+ MetaEmitOperationListSet *emit_set = ctx->emit_sets + MetaEmitLang_MATLAB;
+ for (u32 list = 0; list < emit_set->count; list++) {
+ MetaEmitOperationList *ops = emit_set->data + list;
+ Arena scratch = m->scratch;
+ s8 output = push_s8_from_parts(&m->scratch, s8(""),
+ s8(OUTPUT("matlab") OS_PATH_SEPARATOR "OGLBeamformer"),
+ ops->filename, s8(".m"));
+ meta_push_line(m, s8("% GENERATED CODE"));
+ metagen_run_emit(m, ctx, ops, meta_kind_matlab_types);
+ result &= meta_write_and_reset(m, (c8 *)output.data);
+ m->scratch = scratch;
+ }
+ }
//////////////////
// NOTE: MUnions
@@ -2939,6 +3337,7 @@ metagen_emit_helper_library_header(MetaContext *ctx, Arena arena)
meta_push_line(m, s8("/* See LICENSE for license details. */\n"));
meta_push_line(m, s8("// GENERATED CODE\n"));
+ meta_push_line(m, s8("#include <stdint.h>\n"));
{
iz index = meta_lookup_string_slow(ctx->enumeration_kinds.data, ctx->enumeration_kinds.count,
@@ -2971,6 +3370,8 @@ metagen_emit_helper_library_header(MetaContext *ctx, Arena arena)
}
}
+ metagen_run_emit_set(m, ctx, ctx->emit_sets + MetaEmitLang_CLibrary, meta_kind_base_c_types);
+
meta_push(m, parameters_header, base_header);
result &= meta_write_and_reset(m, out);
@@ -3097,7 +3498,7 @@ metagen_file_direct(Arena arena, char *filename)
if (needs_rebuild_(out, deps.data, deps.count)) {
build_log_generate(out);
meta_push(m, c_file_header);
- metagen_run_emit(m, ctx, meta_kind_c_types);
+ metagen_run_emit_set(m, ctx, ctx->emit_sets + MetaEmitLang_C, meta_kind_c_types);
result &= meta_write_and_reset(m, out);
}
diff --git a/generated/beamformer.meta.c b/generated/beamformer.meta.c
@@ -157,6 +157,105 @@ typedef struct {
} BeamformerShaderBakeParameters;
typedef struct {
+ m4 xdc_transform;
+ v2 xdc_element_pitch;
+ uv2 raw_data_dimensions;
+ v2 focal_vector;
+ u32 transmit_receive_orientation;
+ u32 sample_count;
+ u32 channel_count;
+ u32 acquisition_count;
+ u32 das_shader_id;
+ f32 time_offset;
+ u8 single_focus;
+ u8 single_orientation;
+ u8 decode_mode;
+ u8 sampling_mode;
+ v3 output_min_coordinate;
+ v3 output_max_coordinate;
+ iv4 output_points;
+ f32 sampling_frequency;
+ f32 demodulation_frequency;
+ f32 speed_of_sound;
+ f32 f_number;
+ f32 off_axis_pos;
+ u32 interpolation_mode;
+ u32 coherency_weighting;
+ u32 beamform_plane;
+ u32 decimation_rate;
+} BeamformerParameters;
+
+typedef struct {
+ m4 xdc_transform;
+ v2 xdc_element_pitch;
+ uv2 raw_data_dimensions;
+ v2 focal_vector;
+ u32 transmit_receive_orientation;
+ u32 sample_count;
+ u32 channel_count;
+ u32 acquisition_count;
+ u32 das_shader_id;
+ f32 time_offset;
+ u8 single_focus;
+ u8 single_orientation;
+ u8 decode_mode;
+ u8 sampling_mode;
+} BeamformerParametersHead;
+
+typedef struct {
+ v3 output_min_coordinate;
+ v3 output_max_coordinate;
+ iv4 output_points;
+ f32 sampling_frequency;
+ f32 demodulation_frequency;
+ f32 speed_of_sound;
+ f32 f_number;
+ f32 off_axis_pos;
+ u32 interpolation_mode;
+ u32 coherency_weighting;
+ u32 beamform_plane;
+ u32 decimation_rate;
+} BeamformerUIParameters;
+
+typedef struct {
+ m4 xdc_transform;
+ v2 xdc_element_pitch;
+ uv2 raw_data_dimensions;
+ v2 focal_vector;
+ u32 transmit_receive_orientation;
+ u32 sample_count;
+ u32 channel_count;
+ u32 acquisition_count;
+ u32 das_shader_id;
+ f32 time_offset;
+ u8 single_focus;
+ u8 single_orientation;
+ u8 decode_mode;
+ u8 sampling_mode;
+ v3 output_min_coordinate;
+ v3 output_max_coordinate;
+ iv4 output_points;
+ f32 sampling_frequency;
+ f32 demodulation_frequency;
+ f32 speed_of_sound;
+ f32 f_number;
+ f32 off_axis_pos;
+ u32 interpolation_mode;
+ u32 coherency_weighting;
+ u32 beamform_plane;
+ u32 decimation_rate;
+ i16 channel_mapping[256];
+ i16 sparse_elements[256];
+ u8 transmit_receive_orientations[256];
+ f32 steering_angles[256];
+ f32 focal_depths[256];
+ i32 compute_stages[16];
+ i32 compute_stage_parameters[16];
+ u32 compute_stages_count;
+ i32 data_kind;
+} BeamformerSimpleParameters;
+
+typedef struct {
BeamformerEmissionKind kind;
union {
struct {
diff --git a/helpers/ogl_beamformer_lib.c b/helpers/ogl_beamformer_lib.c
@@ -404,7 +404,7 @@ beamformer_push_data_base(void *data, u32 data_size, i32 timeout_ms, u32 block)
BeamformerDataKind data_kind = b->pipeline.data_kind;
u32 size = bp->acquisition_count * bp->sample_count * bp->channel_count * beamformer_data_kind_byte_size[data_kind];
- u32 raw_size = bp->raw_data_dimensions[0] * bp->raw_data_dimensions[1] * beamformer_data_kind_byte_size[data_kind];
+ u32 raw_size = bp->raw_data_dimensions.x * bp->raw_data_dimensions.y * beamformer_data_kind_byte_size[data_kind];
if (lib_error_check(size <= arena_capacity(&scratch, u8), BF_LIB_ERR_KIND_BUFFER_OVERFLOW) &&
lib_error_check(size <= data_size && data_size == raw_size, BF_LIB_ERR_KIND_DATA_SIZE_MISMATCH))
@@ -413,7 +413,7 @@ beamformer_push_data_base(void *data, u32 data_size, i32 timeout_ms, u32 block)
if (lib_try_lock(BeamformerSharedMemoryLockKind_ScratchSpace, 0)) {
u32 channel_count = bp->channel_count;
u32 out_channel_stride = beamformer_data_kind_element_count[data_kind] * bp->sample_count * bp->acquisition_count;
- u32 in_channel_stride = beamformer_data_kind_element_count[data_kind] * bp->raw_data_dimensions[0];
+ u32 in_channel_stride = beamformer_data_kind_element_count[data_kind] * bp->raw_data_dimensions.x;
for (u32 channel = 0; channel < channel_count; channel++) {
u16 data_channel = (u16)b->channel_mapping[channel];
@@ -581,9 +581,9 @@ beamformer_beamform_data(BeamformerSimpleParameters *bp, void *data, uint32_t da
{
b32 result = validate_simple_parameters(bp);
if (result) {
- bp->output_points[0] = MAX(1, bp->output_points[0]);
- bp->output_points[1] = MAX(1, bp->output_points[1]);
- bp->output_points[2] = MAX(1, bp->output_points[2]);
+ bp->output_points.E[0] = MAX(1, bp->output_points.E[0]);
+ bp->output_points.E[1] = MAX(1, bp->output_points.E[1]);
+ bp->output_points.E[2] = MAX(1, bp->output_points.E[2]);
beamformer_push_simple_parameters(bp);
@@ -593,7 +593,7 @@ beamformer_beamform_data(BeamformerSimpleParameters *bp, void *data, uint32_t da
complex |= shader == BeamformerShaderKind_Demodulate || shader == BeamformerShaderKind_CudaHilbert;
}
- iz output_size = bp->output_points[0] * bp->output_points[1] * bp->output_points[2] * (i32)sizeof(f32);
+ iz output_size = bp->output_points.x * bp->output_points.y * bp->output_points.z * (i32)sizeof(f32);
if (complex) output_size *= 2;
Arena scratch = beamformer_shared_memory_scratch_arena(g_beamformer_library_context.bp);
diff --git a/math.c b/math.c
@@ -253,16 +253,6 @@ v3_from_iv3(iv3 v)
}
function v3
-v3_from_f32_array(f32 v[3])
-{
- v3 result;
- result.E[0] = v[0];
- result.E[1] = v[1];
- result.E[2] = v[2];
- return result;
-}
-
-function v3
v3_abs(v3 a)
{
v3 result;
diff --git a/tests/throughput.c b/tests/throughput.c
@@ -14,7 +14,7 @@
#include <stdlib.h>
#include <zstd.h>
-global i32 g_output_points[4] = {512, 1, 1024, 1};
+global iv3 g_output_points = {{512, 1, 1024}};
global v2 g_axial_extent = {{ 10e-3f, 165e-3f}};
global v2 g_lateral_extent = {{-60e-3f, 60e-3f}};
global f32 g_f_number = 0.5f;
@@ -188,9 +188,9 @@ read_zemp_bp_v1(u8 *path)
function void
beamformer_parameters_from_zemp_bp_v1(zemp_bp_v1 *zbp, BeamformerParameters *out)
{
- mem_copy(out->xdc_transform, zbp->xdc_transform, sizeof(out->xdc_transform));
- mem_copy(out->xdc_element_pitch, zbp->xdc_element_pitch, sizeof(out->xdc_element_pitch));
- mem_copy(out->raw_data_dimensions, zbp->raw_data_dim, sizeof(out->raw_data_dimensions));
+ mem_copy(out->xdc_transform.E, zbp->xdc_transform, sizeof(out->xdc_transform));
+ mem_copy(out->xdc_element_pitch.E, zbp->xdc_element_pitch, sizeof(out->xdc_element_pitch));
+ mem_copy(out->raw_data_dimensions.E, zbp->raw_data_dim, sizeof(out->raw_data_dimensions));
out->sample_count = zbp->decoded_data_dim[0];
out->channel_count = zbp->decoded_data_dim[1];
@@ -281,7 +281,7 @@ decompress_data_at_work_index(Stream *path_base, u32 index)
function b32
send_frame(i16 *restrict i16_data, BeamformerParameters *restrict bp)
{
- u32 data_size = bp->raw_data_dimensions[0] * bp->raw_data_dimensions[1] * sizeof(i16);
+ u32 data_size = bp->raw_data_dimensions.E[0] * bp->raw_data_dimensions.E[1] * sizeof(i16);
b32 result = beamformer_push_data_with_compute(i16_data, data_size, BeamformerViewPlaneTag_XZ, 0);
if (!result && !g_should_exit) printf("lib error: %s\n", beamformer_get_last_error_string());
@@ -307,16 +307,16 @@ execute_study(s8 study, Arena arena, Stream path, Options *options)
BeamformerParameters bp = {0};
beamformer_parameters_from_zemp_bp_v1(zbp, &bp);
- mem_copy(bp.output_points, g_output_points, sizeof(bp.output_points));
- bp.output_points[3] = 1;
+ bp.output_points.xyz = g_output_points;
+ bp.output_points.w = 1;
- bp.output_min_coordinate[0] = g_lateral_extent.x;
- bp.output_min_coordinate[1] = 0;
- bp.output_min_coordinate[2] = g_axial_extent.x;
+ bp.output_min_coordinate.E[0] = g_lateral_extent.x;
+ bp.output_min_coordinate.E[1] = 0;
+ bp.output_min_coordinate.E[2] = g_axial_extent.x;
- bp.output_max_coordinate[0] = g_lateral_extent.y;
- bp.output_max_coordinate[1] = 0;
- bp.output_max_coordinate[2] = g_axial_extent.y;
+ bp.output_max_coordinate.E[0] = g_lateral_extent.y;
+ bp.output_max_coordinate.E[1] = 0;
+ bp.output_max_coordinate.E[2] = g_axial_extent.y;
bp.f_number = g_f_number;
bp.beamform_plane = 0;
@@ -373,8 +373,8 @@ execute_study(s8 study, Arena arena, Stream path, Options *options)
bp.single_orientation = 1;
bp.transmit_receive_orientation = packed_tx_rx;
- bp.focal_vector[0] = zbp->transmit_angles[0];
- bp.focal_vector[1] = zbp->focal_depths[0];
+ bp.focal_vector.E[0] = zbp->transmit_angles[0];
+ bp.focal_vector.E[1] = zbp->focal_depths[0];
} else {
alignas(64) v2 focal_vectors[BeamformerMaxChannelCount];
for (u32 i = 0; i < countof(focal_vectors); i++)
@@ -412,7 +412,7 @@ execute_study(s8 study, Arena arena, Stream path, Options *options)
u32 frame = 0;
f32 times[32] = {0};
- f32 data_size = (f32)(bp.raw_data_dimensions[0] * bp.raw_data_dimensions[1] * sizeof(*data));
+ f32 data_size = (f32)(bp.raw_data_dimensions.E[0] * bp.raw_data_dimensions.E[1] * sizeof(*data));
f64 start = os_get_time();
for (;!g_should_exit;) {
if (send_frame(data, &bp)) {
@@ -440,7 +440,7 @@ execute_study(s8 study, Arena arena, Stream path, Options *options)
beamformer_set_live_parameters(&lip);
} else {
for (u32 i = 0; i < zbp->raw_data_dim[2]; i++)
- send_frame(data + i * bp.raw_data_dimensions[0] * bp.raw_data_dimensions[1], &bp);
+ send_frame(data + i * bp.raw_data_dimensions.E[0] * bp.raw_data_dimensions.E[1], &bp);
}
free(zbp);
diff --git a/ui.c b/ui.c
@@ -1184,11 +1184,11 @@ add_beamformer_parameters_view(Variable *parent, BeamformerCtx *ctx)
VariableGroupKind_Vector, ui->font);
{
add_beamformer_variable(ui, group, &ui->arena, s8("Min:"), s8("[mm]"),
- bp->output_min_coordinate + 0, v2_inf, 1e3f, 0.5e-3f,
+ bp->output_min_coordinate.E + 0, v2_inf, 1e3f, 0.5e-3f,
V_INPUT|V_TEXT|V_CAUSES_COMPUTE, ui->font);
add_beamformer_variable(ui, group, &ui->arena, s8("Max:"), s8("[mm]"),
- bp->output_max_coordinate + 0, v2_inf, 1e3f, 0.5e-3f,
+ bp->output_max_coordinate.E + 0, v2_inf, 1e3f, 0.5e-3f,
V_INPUT|V_TEXT|V_CAUSES_COMPUTE, ui->font);
}
group = end_variable_group(group);
@@ -1197,11 +1197,11 @@ add_beamformer_parameters_view(Variable *parent, BeamformerCtx *ctx)
VariableGroupKind_Vector, ui->font);
{
add_beamformer_variable(ui, group, &ui->arena, s8("Min:"), s8("[mm]"),
- bp->output_min_coordinate + 2, v2_inf, 1e3f, 0.5e-3f,
+ bp->output_min_coordinate.E + 2, v2_inf, 1e3f, 0.5e-3f,
V_INPUT|V_TEXT|V_CAUSES_COMPUTE, ui->font);
add_beamformer_variable(ui, group, &ui->arena, s8("Max:"), s8("[mm]"),
- bp->output_max_coordinate + 2, v2_inf, 1e3f, 0.5e-3f,
+ bp->output_max_coordinate.E + 2, v2_inf, 1e3f, 0.5e-3f,
V_INPUT|V_TEXT|V_CAUSES_COMPUTE, ui->font);
}
group = end_variable_group(group);
@@ -1285,10 +1285,10 @@ ui_beamformer_frame_view_convert(BeamformerUI *ui, Arena *arena, Variable *view,
axial->zoom_starting_coord = F32_INFINITY;
b32 copy = kind == BeamformerFrameViewKind_Copy;
- lateral->min_value = copy ? &bv->min_coordinate.x : ui->params.output_min_coordinate + 0;
- lateral->max_value = copy ? &bv->max_coordinate.x : ui->params.output_max_coordinate + 0;
- axial->min_value = copy ? &bv->min_coordinate.z : ui->params.output_min_coordinate + 2;
- axial->max_value = copy ? &bv->max_coordinate.z : ui->params.output_max_coordinate + 2;
+ lateral->min_value = copy ? &bv->min_coordinate.x : &ui->params.output_min_coordinate.x;
+ lateral->max_value = copy ? &bv->max_coordinate.x : &ui->params.output_max_coordinate.x;
+ axial->min_value = copy ? &bv->min_coordinate.z : &ui->params.output_min_coordinate.z;
+ axial->max_value = copy ? &bv->max_coordinate.z : &ui->params.output_max_coordinate.z;
#define X(id, text) add_button(ui, menu, arena, s8(text), UI_BID_ ##id, 0, ui->small_font);
FRAME_VIEW_BUTTONS
@@ -1503,9 +1503,7 @@ beamformer_frame_view_plane_size(BeamformerUI *ui, BeamformerFrameView *view)
{
v3 result;
if (view->kind == BeamformerFrameViewKind_3DXPlane) {
- v3 min = v3_from_f32_array(ui->params.output_min_coordinate);
- v3 max = v3_from_f32_array(ui->params.output_max_coordinate);
- result = v3_sub(max, min);
+ result = v3_sub(ui->params.output_max_coordinate, ui->params.output_min_coordinate);
swap(result.y, result.z);
result.x = MAX(1e-3f, result.x);
result.y = MAX(1e-3f, result.y);
@@ -1538,8 +1536,8 @@ normalized_p_in_rect(Rect r, v2 p, b32 invert_y)
function v3
x_plane_position(BeamformerUI *ui)
{
- f32 y_min = ui->params.output_min_coordinate[2];
- f32 y_max = ui->params.output_max_coordinate[2];
+ f32 y_min = ui->params.output_min_coordinate.E[2];
+ f32 y_max = ui->params.output_max_coordinate.E[2];
v3 result = {.y = y_min + (y_max - y_min) / 2};
return result;
}
@@ -1713,15 +1711,15 @@ view_update(BeamformerUI *ui, BeamformerFrameView *view)
view->dirty |= view->frame != ui->latest_plane[index];
view->frame = ui->latest_plane[index];
if (view->dirty) {
- view->min_coordinate = v3_from_f32_array(ui->params.output_min_coordinate);
- view->max_coordinate = v3_from_f32_array(ui->params.output_max_coordinate);
+ view->min_coordinate = ui->params.output_min_coordinate;
+ view->max_coordinate = ui->params.output_max_coordinate;
}
}
/* TODO(rnp): x-z or y-z */
/* TODO(rnp): add method of setting a target size in frame view */
iv2 current = view->texture_dim;
- iv2 target = {.w = (i32)ui->params.output_points[0], .h = (i32)ui->params.output_points[2]};
+ iv2 target = {.w = ui->params.output_points.E[0], .h = ui->params.output_points.E[2]};
if (view->kind != BeamformerFrameViewKind_Copy &&
view->kind != BeamformerFrameViewKind_3DXPlane &&
!iv2_equal(current, target) && !iv2_equal(target, (iv2){0}))
@@ -3963,10 +3961,10 @@ ui_init(BeamformerCtx *ctx, Arena store)
function void
validate_ui_parameters(BeamformerUIParameters *p)
{
- if (p->output_min_coordinate[0] > p->output_max_coordinate[0])
- swap(p->output_min_coordinate[0], p->output_max_coordinate[0]);
- if (p->output_min_coordinate[2] > p->output_max_coordinate[2])
- swap(p->output_min_coordinate[2], p->output_max_coordinate[2]);
+ if (p->output_min_coordinate.x > p->output_max_coordinate.x)
+ swap(p->output_min_coordinate.x, p->output_max_coordinate.x);
+ if (p->output_min_coordinate.z > p->output_max_coordinate.z)
+ swap(p->output_min_coordinate.z, p->output_max_coordinate.z);
}
function void
diff --git a/util.c b/util.c
@@ -655,32 +655,54 @@ cut_rect_vertical(Rect rect, f32 at, Rect *top, Rect *bot)
}
}
-function f64
-parse_f64(s8 s)
+function IntegerConversion
+integer_from_s8(s8 raw)
{
- f64 integral = 0, fractional = 0, sign = 1;
+ IntegerConversion result = {0};
- if (s.len > 0 && *s.data == '-') {
- sign = -1;
- s.data++;
- s.len--;
+ iz i = 0;
+ i64 scale = 1;
+ if (raw.len && raw.data[0] == '-') {
+ scale = -1;
+ i = 1;
}
- while (s.len > 0 && *s.data != '.') {
- integral *= 10;
- integral += *s.data - '0';
- s.data++;
- s.len--;
+ for (; i < raw.len; i++) {
+ i64 digit = (i64)raw.data[i] - '0';
+ if (BETWEEN(digit, 0, 9)) {
+ if (result.U64 > (U64_MAX - (u64)digit) / 10) {
+ result.result = IntegerConversionResult_OutOfRange;
+ result.U64 = U64_MAX;
+ } else {
+ result.U64 = 10 * result.U64 + (u64)digit;
+ }
+ } else {
+ break;
+ }
}
+ result.unparsed = (s8){.len = raw.len - i, .data = raw.data + i};
+ result.result = IntegerConversionResult_Success;
+ result.S64 = (i64)result.U64 * scale;
+
+ return result;
+}
+function f64
+parse_f64(s8 s)
+{
+ IntegerConversion integral = integer_from_s8(s);
+
+ s = integral.unparsed;
if (*s.data == '.') { s.data++; s.len--; }
+ while (s.len > 0 && s.data[s.len - 1] == '0') s.len--;
- while (s.len > 0) {
- ASSERT(s.data[s.len - 1] != '.');
- fractional /= 10;
- fractional += (f64)(s.data[--s.len] - '0') / 10.0;
- }
- f64 result = sign * (integral + fractional);
+ IntegerConversion fractional = integer_from_s8(s);
+
+ u64 power = (u64)(fractional.unparsed.data - s.data);
+ f64 frac = (f64)fractional.U64;
+ while (power > 0) { frac /= 10.0; power--; }
+
+ f64 result = (f64)integral.S64 + frac;
return result;
}
diff --git a/util.h b/util.h
@@ -151,6 +151,7 @@
#define I32_MAX (0x7FFFFFFFL)
#define U16_MAX (0x0000FFFFUL)
#define U32_MAX (0xFFFFFFFFUL)
+#define U64_MAX (0xFFFFFFFFFFFFFFFFULL)
#define F32_INFINITY (1e+300*1e+300)
#define F32_EPSILON (1e-6f)
#ifndef PI
@@ -195,6 +196,21 @@ typedef struct { iz len; u16 *data; } s16;
typedef struct { u32 cp, consumed; } UnicodeDecode;
+typedef enum {
+ IntegerConversionResult_Invalid,
+ IntegerConversionResult_OutOfRange,
+ IntegerConversionResult_Success,
+} IntegerConversionResult;
+
+typedef struct {
+ IntegerConversionResult result;
+ union {
+ u64 U64;
+ i64 S64;
+ };
+ s8 unparsed;
+} IntegerConversion;
+
/* NOTE: raylib stubs */
#ifndef RAYLIB_H
typedef struct { f32 x, y; } Vector2;
@@ -217,6 +233,12 @@ typedef union {
} iv3;
typedef union {
+ struct { i32 x, y, z, w; };
+ struct { iv3 xyz; i32 _w; };
+ i32 E[4];
+} iv4;
+
+typedef union {
struct { u32 x, y; };
struct { u32 w, h; };
u32 E[2];