Skip to content

Commit e46a4cf

Browse files
committed
Fix nested REPTs.
Closes 17. Geez, this thing could really use a nice object-oriented refactor.
1 parent 53542a8 commit e46a4cf

File tree

2 files changed

+182
-125
lines changed

2 files changed

+182
-125
lines changed

semantic.c

Lines changed: 174 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ typedef enum Mode
9191
typedef struct SemanticState
9292
{
9393
cc_bool success;
94+
const TextInput *input_callbacks;
9495
const BinaryOutput *output_callbacks;
9596
const TextOutput *listing_callbacks;
9697
const TextOutput *error_callbacks;
@@ -155,7 +156,7 @@ typedef struct Macro
155156
} Macro;
156157

157158
/* Some forward declarations that are needed because some functions recurse into each other. */
158-
static void AssembleFile(SemanticState *state, const TextInput *input_callbacks);
159+
static void AssembleFile(SemanticState *state);
159160
static void AssembleLine(SemanticState *state, const char *source_line);
160161

161162
/* Prevent errors when '__attribute__((format(printf, X, X)))' is not supported. */
@@ -1020,35 +1021,8 @@ static cc_bool ResolveExpression(SemanticState *state, Expression *expression, u
10201021

10211022
static void TerminateRept(SemanticState *state)
10221023
{
1023-
unsigned long countdown;
1024-
1025-
/* Back-up some state into local variables, in case a nested REPT statement clobbers it. */
1026-
unsigned long starting_line_number = state->shared.rept.line_number;
1027-
SourceLineListNode* const source_line_list_head = state->shared.rept.source_line_list.head;
1028-
1029-
/* Exit REPT mode before we recurse into the REPT's nested statements. */
1024+
/* Exit REPT mode. */
10301025
state->mode = MODE_NORMAL;
1031-
1032-
/* Repeat the statements as many times as requested. */
1033-
countdown = state->shared.rept.repetitions;
1034-
/* TODO - Nested REPTs! Put the 'state->shared.rept' stuff in local variables! */
1035-
1036-
while (countdown-- != 0)
1037-
{
1038-
SourceLineListNode *source_line_list_node;
1039-
1040-
/* Rewind back to the line number of the start of the REPT. */
1041-
state->location->line_number = starting_line_number;
1042-
1043-
/* Process the REPT's nested statements. */
1044-
for (source_line_list_node = source_line_list_head; source_line_list_node != NULL; source_line_list_node = source_line_list_node->next)
1045-
AssembleLine(state, source_line_list_node->source_line);
1046-
}
1047-
1048-
/* Increment past the ENDR line number. */
1049-
++state->location->line_number;
1050-
1051-
FreeSourceLineList(source_line_list_head);
10521026
}
10531027

10541028
static void TerminateMacro(SemanticState *state)
@@ -4168,7 +4142,9 @@ static void ProcessInclude(SemanticState *state, const StatementInclude *include
41684142
}
41694143
else
41704144
{
4171-
ClownAssembler_TextInput input_callbacks = {
4145+
const TextInput* const previous_input_callbacks = state->input_callbacks;
4146+
4147+
TextInput input_callbacks = {
41724148
input_file,
41734149
ReadCharacter,
41744150
ReadLine
@@ -4183,7 +4159,11 @@ static void ProcessInclude(SemanticState *state, const StatementInclude *include
41834159
location.previous = state->location;
41844160
state->location = &location;
41854161

4186-
AssembleFile(state, &input_callbacks);
4162+
state->input_callbacks = &input_callbacks;
4163+
4164+
AssembleFile(state);
4165+
4166+
state->input_callbacks = previous_input_callbacks;
41874167

41884168
state->location = state->location->previous;
41894169

@@ -4253,8 +4233,114 @@ static void ProcessIncbin(SemanticState *state, StatementIncbin *incbin)
42534233
}
42544234
}
42554235

4236+
static cc_bool ReadSourceLine(SemanticState *state)
4237+
{
4238+
char *line_buffer_write_pointer;
4239+
4240+
line_buffer_write_pointer = state->line_buffer;
4241+
4242+
/* Read lines one at a time, feeding them to the 'AssembleLine' function. */
4243+
while (TextInput_fgets(line_buffer_write_pointer, &state->line_buffer[sizeof(state->line_buffer)] - line_buffer_write_pointer, state->input_callbacks) != NULL)
4244+
{
4245+
size_t newline_index;
4246+
char newline_character;
4247+
4248+
/* Find the end of the line. We terminate on '\0', '\r', '\n', and ';'.
4249+
Note that terminating at ';' prevents a trailing '&' from being recognised
4250+
and causing a line to be continued on the next line.
4251+
This is needed for compatibility with S.N. 68k (asm68k). */
4252+
{
4253+
char quote_character = '\0';
4254+
for (newline_index = 0; ; ++newline_index)
4255+
{
4256+
const char character = state->line_buffer[newline_index];
4257+
4258+
if (character == '\0' || character == '\r' || character == '\n')
4259+
break;
4260+
4261+
if (quote_character == '\0')
4262+
{
4263+
if (character == '"' || character == '\'')
4264+
quote_character = character;
4265+
else if (character == ';')
4266+
break;
4267+
}
4268+
else if (character == quote_character)
4269+
{
4270+
quote_character = '\0';
4271+
}
4272+
}
4273+
}
4274+
4275+
newline_character = state->line_buffer[newline_index];
4276+
4277+
line_buffer_write_pointer = state->line_buffer;
4278+
4279+
/* If there is no newline, then we've either reached the end of the file,
4280+
or the source line was too long to fit in the buffer. */
4281+
/* TODO: Is there no way to remove this limit? */
4282+
if (newline_character == '\0')
4283+
{
4284+
int character = TextInput_fgetc(state->input_callbacks);
4285+
4286+
if (character != -1)
4287+
{
4288+
InternalError(state, "The source line was too long to fit in the internal buffer.");
4289+
4290+
/* Fast-forward through until the end of the line. */
4291+
while (character != '\r' && character != '\n' && character != -1)
4292+
character = TextInput_fgetc(state->input_callbacks);
4293+
}
4294+
}
4295+
else if (newline_index != 0 && state->line_buffer[newline_index - 1] == '&')
4296+
{
4297+
/* An '&' at the end of a line is like a '\' at the end of a line in C:
4298+
it signals that the current line is continued on the next line. */
4299+
4300+
/* Go back and get another line. */
4301+
line_buffer_write_pointer = &state->line_buffer[newline_index - 1];
4302+
continue;
4303+
}
4304+
4305+
/* Remove newlines from the string, so that they don't appear in the error message. */
4306+
state->line_buffer[newline_index] = '\0';
4307+
4308+
return cc_true;
4309+
}
4310+
4311+
return cc_false;
4312+
}
4313+
4314+
static void AssembleAndListLine(SemanticState *state)
4315+
{
4316+
/* Output program counter to listing file. */
4317+
if (TextOutput_exists(state->listing_callbacks))
4318+
{
4319+
state->listing_counter = 0;
4320+
TextOutput_fprintf(state->listing_callbacks, "%08lX", state->program_counter);
4321+
}
4322+
4323+
AssembleLine(state, state->line_buffer);
4324+
4325+
/* Output line to listing file. */
4326+
if (TextOutput_exists(state->listing_callbacks))
4327+
{
4328+
unsigned int i;
4329+
4330+
for (i = state->listing_counter * 2 + state->listing_counter / 2; i < 28; ++i)
4331+
TextOutput_fputc(' ', state->listing_callbacks);
4332+
4333+
TextOutput_fprintf(state->listing_callbacks, "%s\n", state->line_buffer);
4334+
}
4335+
}
4336+
42564337
static void ProcessRept(SemanticState *state, StatementRept *rept)
42574338
{
4339+
const Mode previous_mode = state->mode;
4340+
const unsigned long previous_repetitions = state->shared.rept.repetitions;
4341+
const unsigned long previous_line_number = state->location->line_number;
4342+
const SourceLineList previous_source_line_list = state->shared.rept.source_line_list;
4343+
42584344
/* Enter REPT mode. */
42594345
state->mode = MODE_REPT;
42604346

@@ -4268,6 +4354,57 @@ static void ProcessRept(SemanticState *state, StatementRept *rept)
42684354

42694355
state->shared.rept.source_line_list.head = NULL;
42704356
state->shared.rept.source_line_list.tail = NULL;
4357+
4358+
for (;;)
4359+
{
4360+
if (!ReadSourceLine(state))
4361+
{
4362+
/* The file ended before an 'ENDR' could be found! */
4363+
4364+
/* Terminate the REPT to hopefully avoid future complications. */
4365+
TerminateRept(state);
4366+
4367+
SemanticError(state, "REPT statement beginning at line %lu is missing its ENDR.", state->shared.rept.line_number);
4368+
}
4369+
else
4370+
{
4371+
AssembleLine(state, state->line_buffer);
4372+
}
4373+
4374+
if (state->mode != MODE_REPT)
4375+
{
4376+
/* An 'ENDR' must have been encountered. */
4377+
4378+
/* Revert back to the previous state. */
4379+
unsigned long repetitions = state->shared.rept.repetitions;
4380+
const unsigned long line_number = state->location->line_number;
4381+
const SourceLineList source_line_list = state->shared.rept.source_line_list;
4382+
4383+
state->mode = previous_mode;
4384+
state->shared.rept.repetitions = previous_repetitions;
4385+
state->location->line_number = previous_line_number;
4386+
state->shared.rept.source_line_list = previous_source_line_list;
4387+
4388+
while (repetitions-- != 0)
4389+
{
4390+
SourceLineListNode *source_line_list_node;
4391+
4392+
/* Rewind back to the line number of the start of the REPT. */
4393+
state->location->line_number = line_number;
4394+
4395+
/* Process the REPT's nested statements. */
4396+
for (source_line_list_node = source_line_list.head; source_line_list_node != NULL; source_line_list_node = source_line_list_node->next)
4397+
AssembleLine(state, source_line_list_node->source_line);
4398+
}
4399+
4400+
/* Increment past the ENDR line number. */
4401+
++state->location->line_number;
4402+
4403+
FreeSourceLineList(source_line_list.head);
4404+
4405+
break;
4406+
}
4407+
}
42714408
}
42724409

42734410
static void ProcessMacro(SemanticState *state, StatementMacro *macro, const char *label, cc_bool is_short)
@@ -5323,7 +5460,7 @@ static void AssembleLine(SemanticState *state, const char *source_line)
53235460

53245461
case MODE_REPT:
53255462
/* If this line is an 'ENDR' directive, then exit REPT mode. Otherwise, add the line to the REPT. */
5326-
if (directive_length != 0 && strncmpci(source_line_pointer, "endr", directive_length) == 0)
5463+
if (directive_length != 0 && (strncmpci(source_line_pointer, "rept", directive_length) == 0 || strncmpci(source_line_pointer, "endr", directive_length) == 0))
53275464
{
53285465
/* TODO - Detect code after the keyword and error if any is found. */
53295466
ParseLine(state, source_line, label, source_line_pointer);
@@ -5387,98 +5524,10 @@ static void AssembleLine(SemanticState *state, const char *source_line)
53875524
free(label);
53885525
}
53895526

5390-
static void AssembleFile(SemanticState *state, const TextInput* const input_callbacks)
5527+
static void AssembleFile(SemanticState *state)
53915528
{
5392-
char *line_buffer_write_pointer;
5393-
5394-
line_buffer_write_pointer = state->line_buffer;
5395-
5396-
/* Read lines one at a time, feeding them to the 'AssembleLine' function. */
5397-
while (!state->end && TextInput_fgets(line_buffer_write_pointer, &state->line_buffer[sizeof(state->line_buffer)] - line_buffer_write_pointer, input_callbacks) != NULL)
5398-
{
5399-
size_t newline_index;
5400-
char newline_character;
5401-
5402-
/* Find the end of the line. We terminate on '\0', '\r', '\n', and ';'.
5403-
Note that terminating at ';' prevents a trailing '&' from being recognised
5404-
and causing a line to be continued on the next line.
5405-
This is needed for compatibility with S.N. 68k (asm68k). */
5406-
{
5407-
char quote_character = '\0';
5408-
for (newline_index = 0; ; ++newline_index)
5409-
{
5410-
const char character = state->line_buffer[newline_index];
5411-
5412-
if (character == '\0' || character == '\r' || character == '\n')
5413-
break;
5414-
5415-
if (quote_character == '\0')
5416-
{
5417-
if (character == '"' || character == '\'')
5418-
quote_character = character;
5419-
else if (character == ';')
5420-
break;
5421-
}
5422-
else if (character == quote_character)
5423-
{
5424-
quote_character = '\0';
5425-
}
5426-
}
5427-
}
5428-
5429-
newline_character = state->line_buffer[newline_index];
5430-
5431-
line_buffer_write_pointer = state->line_buffer;
5432-
5433-
/* If there is no newline, then we've either reached the end of the file,
5434-
or the source line was too long to fit in the buffer. */
5435-
/* TODO: Is there no way to remove this limit? */
5436-
if (newline_character == '\0')
5437-
{
5438-
int character = TextInput_fgetc(input_callbacks);
5439-
5440-
if (character != -1)
5441-
{
5442-
InternalError(state, "The source line was too long to fit in the internal buffer.");
5443-
5444-
/* Fast-forward through until the end of the line. */
5445-
while (character != '\r' && character != '\n' && character != -1)
5446-
character = TextInput_fgetc(input_callbacks);
5447-
}
5448-
}
5449-
else if (newline_index != 0 && state->line_buffer[newline_index - 1] == '&')
5450-
{
5451-
/* An '&' at the end of a line is like a '\' at the end of a line in C:
5452-
it signals that the current line is continued on the next line. */
5453-
5454-
/* Go back and get another line. */
5455-
line_buffer_write_pointer = &state->line_buffer[newline_index - 1];
5456-
continue;
5457-
}
5458-
5459-
/* Remove newlines from the string, so that they don't appear in the error message. */
5460-
state->line_buffer[newline_index] = '\0';
5461-
5462-
/* Output program counter to listing file. */
5463-
if (TextOutput_exists(state->listing_callbacks))
5464-
{
5465-
state->listing_counter = 0;
5466-
TextOutput_fprintf(state->listing_callbacks, "%08lX", state->program_counter);
5467-
}
5468-
5469-
AssembleLine(state, state->line_buffer);
5470-
5471-
/* Output line to listing file. */
5472-
if (TextOutput_exists(state->listing_callbacks))
5473-
{
5474-
unsigned int i;
5475-
5476-
for (i = state->listing_counter * 2 + state->listing_counter / 2; i < 28; ++i)
5477-
TextOutput_fputc(' ', state->listing_callbacks);
5478-
5479-
TextOutput_fprintf(state->listing_callbacks, "%s\n", state->line_buffer);
5480-
}
5481-
}
5529+
while (!state->end && ReadSourceLine(state))
5530+
AssembleAndListLine(state);
54825531

54835532
/* If we're not in normal mode when a file ends, then something is wrong. */
54845533
switch (state->mode)
@@ -5614,6 +5663,7 @@ cc_bool ClownAssembler_Assemble(
56145663

56155664
/* Initialise the state's non-default values. */
56165665
state.success = cc_true;
5666+
state.input_callbacks = input_callbacks;
56175667
state.output_callbacks = output_callbacks;
56185668
state.error_callbacks = error_callbacks;
56195669
state.listing_callbacks = listing_callbacks;
@@ -5662,7 +5712,7 @@ cc_bool ClownAssembler_Assemble(
56625712
#endif
56635713

56645714
/* Perform first pass of assembly, creating a list of fix-ups. */
5665-
AssembleFile(&state, input_callbacks);
5715+
AssembleFile(&state);
56665716

56675717
/* Destroy the lexer, as we no longer need it. */
56685718
if (m68kasm_lex_destroy(state.flex_state) != 0)

0 commit comments

Comments
 (0)