mirror of
https://github.com/uffejakobsen/acme.git
synced 2024-12-22 18:30:44 +00:00
added "!for VAR in ITERABLE { BLOCK }" possibility.
git-svn-id: https://svn.code.sf.net/p/acme-crossass/code-0/trunk@306 4df02467-bbd4-4a76-a152-e7ce94205b78
This commit is contained in:
parent
cc82e17cda
commit
c9d148d345
@ -457,11 +457,17 @@ Examples: ; this was taken from <6502/std.a>:
|
||||
|
||||
|
||||
Call: !for SYMBOL, START, END { BLOCK }
|
||||
or: !for SYMBOL in ITERABLE { BLOCK }
|
||||
Purpose: Looping assembly. The block of statements will be
|
||||
parsed a fixed number of times, as specified by the
|
||||
values of START and END. For more flexible
|
||||
possibilities, have a look at "!do" and "!while"
|
||||
below.
|
||||
arguments:
|
||||
When using the first syntax, SYMBOL will simply count
|
||||
from START to END.
|
||||
When using the second syntax, SYMBOL will iterate over
|
||||
the contents of the ITERABLE, which must be a string
|
||||
or a list.
|
||||
For more flexible loop constructs, have a look at
|
||||
"!do" and "!while" below.
|
||||
Parameters: SYMBOL: Any valid symbol name.
|
||||
START: Any formula the value parser accepts, but it
|
||||
must be solvable even in the first pass. SYMBOL will
|
||||
@ -469,6 +475,15 @@ Parameters: SYMBOL: Any valid symbol name.
|
||||
END: Any formula the value parser accepts, but it must
|
||||
be solvable even in the first pass. SYMBOL will have
|
||||
this value during the last loop cycle.
|
||||
ITERABLE: This must be a string or a list, but its
|
||||
length must be defined even in the first pass (and of
|
||||
course it should stay the same during all subsequent
|
||||
passes).
|
||||
If ITERABLE is a list, its _items_ are allowed
|
||||
to be undefined.
|
||||
If ITERABLE is a string, SYMBOL will be set to
|
||||
each of its character codes in turn, using the
|
||||
currently chosen conversion table.
|
||||
BLOCK: A block of assembler statements.
|
||||
If START or END are floats, they will be converted to
|
||||
integers (never use floats for loop counters). If
|
||||
@ -508,8 +523,25 @@ Examples:
|
||||
sta $0400 + i
|
||||
}
|
||||
|
||||
Miscellaneous: The old syntax ("!for SYMBOL, END { BLOCK }" where
|
||||
START was always implied to be 1) is still fully
|
||||
split_table_lo ; generate two tables from one list
|
||||
!for h in my_handler_list {
|
||||
!by <h
|
||||
}
|
||||
split_table_hi
|
||||
!for h in my_handler_list {
|
||||
!by >h
|
||||
}
|
||||
|
||||
hidden_string ; "encrypt" a string by XORing with address
|
||||
!ct scr { ; use screen codes
|
||||
!for c in "very secret message" {
|
||||
!by c XOR <*
|
||||
}
|
||||
}
|
||||
|
||||
Miscellaneous: The old syntax
|
||||
!for SYMBOL, END { BLOCK }
|
||||
where START was always implied to be 1 is still fully
|
||||
supported, but gives a warning to get people to change
|
||||
to the new syntax.
|
||||
You can disable this warning using the "--dialect" or
|
||||
@ -778,6 +810,11 @@ or type (call-by-value vs. call-by-reference). So
|
||||
!macro process_bytes b1, b2, ~b3 {...whatever...}
|
||||
can *all* be used at the same time without any name clash.
|
||||
|
||||
Since release 0.97, lists are supported. This is useful for macros if
|
||||
you want an arbitrary number of arguments: Just define the macro with
|
||||
a single argument, then pass a list and have the macro iterate over
|
||||
its contents.
|
||||
|
||||
|
||||
----------------------------------------------------------------------
|
||||
Section: Segment assembly
|
||||
|
@ -264,6 +264,12 @@ File name quotes not found ("" or <>).
|
||||
File names have to be given in quotes. Either "" quoting for files
|
||||
located in the current directory or <> quoting for library files.
|
||||
|
||||
Force bits can only be given to counters, not when iterating over string/list contents.
|
||||
You used a force bit with a "!for" loop counter, but then used the
|
||||
"iterate over string/list contents" syntax. This does not work,
|
||||
because lists could contain other lists, and then a force bit does
|
||||
not make any sense.
|
||||
|
||||
Force bits can only be given to numbers.
|
||||
You tried to give a force bit to a symbol and then assign a string
|
||||
or list to it.
|
||||
@ -276,6 +282,10 @@ Garbage data at end of statement (unexpected 'CHAR').
|
||||
There are still arguments when there should not be any more. The
|
||||
given character is the one where end-of-line was expected.
|
||||
|
||||
Given object is not iterable.
|
||||
You used "!for VAR in ITERABLE", but the iterable was neither a
|
||||
string nor a list (likely a number).
|
||||
|
||||
Hex digits are not given in pairs.
|
||||
The two digits of a hex byte are separated by another character,
|
||||
or there is an odd number of digits.
|
||||
@ -289,6 +299,12 @@ Index is undefined.
|
||||
Index out of range.
|
||||
The value for an indexing operation wasn't in the allowed range.
|
||||
|
||||
Loop var must be followed by either "in" keyword or comma.
|
||||
You made a syntax error when using "!for": After the loop counter
|
||||
symbol there can either be a comma (for a simple counting loop) or
|
||||
the "in" keyword (when iterating over string or list contents).
|
||||
Anything else will give this error.
|
||||
|
||||
Macro already defined.
|
||||
Macros can only be defined once. If you define a macro twice, ACME
|
||||
will help you find the definitions by giving a warning for the
|
||||
@ -554,6 +570,9 @@ IllegalImmediateMode
|
||||
IllegalInputSource
|
||||
Input is taken neither from a file nor from a RAM block.
|
||||
|
||||
IllegalLoopAlgo
|
||||
The "!for" function was told to use an unknown algorithm.
|
||||
|
||||
IllegalNumberTypeX
|
||||
A number was neither INT nor FLOAT nor UNDEFINED.
|
||||
|
||||
|
74
src/flow.c
74
src/flow.c
@ -63,24 +63,11 @@ static void parse_ram_block(struct block *block)
|
||||
}
|
||||
|
||||
|
||||
// back end function for "!for" pseudo opcode
|
||||
void flow_forloop(struct for_loop *loop)
|
||||
// function for "!for" with counter variable
|
||||
static void counting_for(struct for_loop *loop)
|
||||
{
|
||||
struct input loop_input,
|
||||
*outer_input;
|
||||
struct object loop_var;
|
||||
|
||||
// switching input makes us lose GotByte. But we know it's '}' anyway!
|
||||
// set up new input
|
||||
loop_input = *Input_now; // copy current input structure into new
|
||||
loop_input.source = INPUTSRC_RAM; // set new byte source
|
||||
// remember old input
|
||||
outer_input = Input_now;
|
||||
// activate new input
|
||||
// (not yet useable; pointer and line number are still missing)
|
||||
Input_now = &loop_input;
|
||||
// fix line number (not for block, but in case symbol handling throws errors)
|
||||
Input_now->line_number = loop->block.start;
|
||||
// init counter
|
||||
loop_var.type = &type_number;
|
||||
loop_var.u.number.ntype = NUMTYPE_INT;
|
||||
@ -98,12 +85,11 @@ void flow_forloop(struct for_loop *loop)
|
||||
// cases like !set N=N+1 worked, because the force bit was
|
||||
// taken from result.
|
||||
// maybe support this behaviour via --dialect?
|
||||
if (loop->force_bit)
|
||||
symbol_set_force_bit(loop->symbol, loop->force_bit);
|
||||
if (loop->u.counter.force_bit)
|
||||
symbol_set_force_bit(loop->symbol, loop->u.counter.force_bit);
|
||||
loop_var = loop->symbol->object; // update local copy with force bit
|
||||
loop->symbol->has_been_read = TRUE; // lock force bit
|
||||
loop_var.u.number.val.intval = loop->u.counter.first; // SEE ABOVE - this may be nonzero, but has not yet been copied to user symbol!
|
||||
|
||||
while (loop->iterations_left) {
|
||||
loop->symbol->object = loop_var; // overwrite whole struct, in case some joker has re-assigned loop counter var
|
||||
parse_ram_block(&loop->block);
|
||||
@ -111,15 +97,53 @@ void flow_forloop(struct for_loop *loop)
|
||||
loop->iterations_left--;
|
||||
}
|
||||
// new algo wants illegal value in loop counter after block:
|
||||
if (loop->algorithm == FORALGO_NEW)
|
||||
if (loop->algorithm == FORALGO_NEWCOUNT)
|
||||
loop->symbol->object = loop_var; // overwrite whole struct, in case some joker has re-assigned loop counter var
|
||||
}
|
||||
|
||||
// case FORALGO_ITER: // iterate over string/list contents:
|
||||
// FIXME
|
||||
// break;
|
||||
// default:
|
||||
// Bug_found("IllegalLoopAlgo", loop->algorithm); // FIXME - add to docs!
|
||||
// }
|
||||
// function for "!for" with iterating variable
|
||||
static void iterating_for(struct for_loop *loop)
|
||||
{
|
||||
intval_t index = 0;
|
||||
struct object obj;
|
||||
|
||||
while (loop->iterations_left) {
|
||||
loop->u.iter.obj.type->at(&loop->u.iter.obj, &obj, index++);
|
||||
symbol_set_object(loop->symbol, &obj, POWER_CHANGE_VALUE | POWER_CHANGE_OBJTYPE);
|
||||
parse_ram_block(&loop->block);
|
||||
loop->iterations_left--;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// back end function for "!for" pseudo opcode
|
||||
void flow_forloop(struct for_loop *loop)
|
||||
{
|
||||
struct input loop_input,
|
||||
*outer_input;
|
||||
|
||||
// switching input makes us lose GotByte. But we know it's '}' anyway!
|
||||
// set up new input
|
||||
loop_input = *Input_now; // copy current input structure into new
|
||||
loop_input.source = INPUTSRC_RAM; // set new byte source
|
||||
// remember old input
|
||||
outer_input = Input_now;
|
||||
// activate new input
|
||||
// (not yet useable; pointer and line number are still missing)
|
||||
Input_now = &loop_input;
|
||||
// fix line number (not for block, but in case symbol handling throws errors)
|
||||
Input_now->line_number = loop->block.start;
|
||||
switch (loop->algorithm) {
|
||||
case FORALGO_OLDCOUNT:
|
||||
case FORALGO_NEWCOUNT:
|
||||
counting_for(loop);
|
||||
break;
|
||||
case FORALGO_ITERATE:
|
||||
iterating_for(loop);
|
||||
break;
|
||||
default:
|
||||
Bug_found("IllegalLoopAlgo", loop->algorithm);
|
||||
}
|
||||
// restore previous input:
|
||||
Input_now = outer_input;
|
||||
}
|
||||
|
16
src/flow.h
16
src/flow.h
@ -18,26 +18,24 @@ struct block {
|
||||
|
||||
// struct to pass "!for" loop stuff from pseudoopcodes.c to flow.c
|
||||
enum foralgo {
|
||||
FORALGO_OLD, // block can be skipped by passing zero, counter keeps value after block
|
||||
FORALGO_NEW, // first and last value are given, counter is out of range after block
|
||||
//FORALGO_ITER, // iterate over string/list contents (old algo could be changed to use this!)
|
||||
FORALGO_OLDCOUNT, // block can be skipped by passing zero, counter keeps value after block
|
||||
FORALGO_NEWCOUNT, // first and last value are given, counter is out of range after block
|
||||
FORALGO_ITERATE // iterate over string/list contents
|
||||
};
|
||||
struct for_loop {
|
||||
struct symbol *symbol;
|
||||
enum foralgo algorithm;
|
||||
bits force_bit; // TODO - move to counter struct? illegal for iter algo!
|
||||
intval_t iterations_left;
|
||||
union {
|
||||
struct {
|
||||
intval_t first,
|
||||
increment; // 1 or -1
|
||||
bits force_bit;
|
||||
int addr_refs; // address reference count
|
||||
} counter;
|
||||
/* struct {
|
||||
struct symbol *iterable;
|
||||
int index;
|
||||
add a "last" value here? or check len() in every iteration?
|
||||
} iter;*/
|
||||
struct {
|
||||
struct object obj; // string or list
|
||||
} iter;
|
||||
} u;
|
||||
struct block block;
|
||||
};
|
||||
|
@ -1009,12 +1009,13 @@ static enum eos po_ifndef(void) // now GotByte = illegal char
|
||||
|
||||
|
||||
// looping assembly ("!for"). has to be re-entrant.
|
||||
// old syntax: !for VAR, END { BLOCK } VAR counts from 1 to END
|
||||
// new syntax: !for VAR, START, END { BLOCK } VAR counts from START to END
|
||||
// maybe future alternative: !for VAR in ITERABLE { BLOCK } VAR iterates over string/list contents
|
||||
// old counter syntax: !for VAR, END { BLOCK } VAR counts from 1 to END
|
||||
// new counter syntax: !for VAR, START, END { BLOCK } VAR counts from START to END
|
||||
// iterating syntax: !for VAR in ITERABLE { BLOCK } VAR iterates over string/list contents
|
||||
static enum eos po_for(void) // now GotByte = illegal char
|
||||
{
|
||||
scope_t scope;
|
||||
bits force_bit;
|
||||
struct for_loop loop;
|
||||
struct number intresult;
|
||||
|
||||
@ -1022,63 +1023,80 @@ static enum eos po_for(void) // now GotByte = illegal char
|
||||
return SKIP_REMAINDER; // zero length
|
||||
|
||||
// now GotByte = illegal char
|
||||
loop.force_bit = Input_get_force_bit(); // skips spaces after
|
||||
force_bit = Input_get_force_bit(); // skips spaces after
|
||||
loop.symbol = symbol_find(scope); // if not number, error will be reported on first assignment
|
||||
if (!Input_accept_comma()) {
|
||||
#if 1
|
||||
Throw_error(exception_syntax);
|
||||
return SKIP_REMAINDER;
|
||||
#else
|
||||
// check for "in" keyword
|
||||
if (Input_read_and_lower_keyword() == 0)
|
||||
return SKIP_REMAINDER;
|
||||
|
||||
if (strcmp(GlobalDynaBuf->buffer, "in") != 0) {
|
||||
Throw_error("Loop var must be followed by either \"in\" keyword or comma."); // TODO - add to docs!
|
||||
return SKIP_REMAINDER;
|
||||
}
|
||||
if (loop.force_bit) {
|
||||
Throw_error("Force bits can only be given to counters, not when iterating over string/list contents."); // TODO - add to docs!
|
||||
return SKIP_REMAINDER;
|
||||
}
|
||||
loop.algorithm = FORALGO_ITER;
|
||||
FIXME
|
||||
#endif
|
||||
}
|
||||
|
||||
ALU_defined_int(&intresult); // read first argument
|
||||
loop.u.counter.addr_refs = intresult.addr_refs;
|
||||
if (Input_accept_comma()) {
|
||||
// new format - yay!
|
||||
loop.algorithm = FORALGO_NEW;
|
||||
if (config.wanted_version < VER_NEWFORSYNTAX)
|
||||
Throw_first_pass_warning("Found new \"!for\" syntax.");
|
||||
loop.u.counter.first = intresult.val.intval; // use first argument
|
||||
ALU_defined_int(&intresult); // read second argument
|
||||
// compare addr_ref counts and complain if not equal!
|
||||
if (config.warn_on_type_mismatch
|
||||
&& (intresult.addr_refs != loop.u.counter.addr_refs)) {
|
||||
Throw_first_pass_warning("Wrong type for loop's END value - must match type of START value.");
|
||||
}
|
||||
// setup direction and total
|
||||
if (loop.u.counter.first <= intresult.val.intval) {
|
||||
loop.iterations_left = 1 + intresult.val.intval - loop.u.counter.first;
|
||||
loop.u.counter.increment = 1;
|
||||
// counter syntax (old or new)
|
||||
loop.u.counter.force_bit = force_bit;
|
||||
ALU_defined_int(&intresult); // read first argument
|
||||
loop.u.counter.addr_refs = intresult.addr_refs;
|
||||
if (Input_accept_comma()) {
|
||||
// new counter syntax
|
||||
loop.algorithm = FORALGO_NEWCOUNT;
|
||||
if (config.wanted_version < VER_NEWFORSYNTAX)
|
||||
Throw_first_pass_warning("Found new \"!for\" syntax.");
|
||||
loop.u.counter.first = intresult.val.intval; // use first argument
|
||||
ALU_defined_int(&intresult); // read second argument
|
||||
// compare addr_ref counts and complain if not equal!
|
||||
if (config.warn_on_type_mismatch
|
||||
&& (intresult.addr_refs != loop.u.counter.addr_refs)) {
|
||||
Throw_first_pass_warning("Wrong type for loop's END value - must match type of START value.");
|
||||
}
|
||||
// setup direction and total
|
||||
if (loop.u.counter.first <= intresult.val.intval) {
|
||||
// count up
|
||||
loop.iterations_left = 1 + intresult.val.intval - loop.u.counter.first;
|
||||
loop.u.counter.increment = 1;
|
||||
} else {
|
||||
// count down
|
||||
loop.iterations_left = 1 + loop.u.counter.first - intresult.val.intval;
|
||||
loop.u.counter.increment = -1;
|
||||
}
|
||||
} else {
|
||||
loop.iterations_left = 1 + loop.u.counter.first - intresult.val.intval;
|
||||
loop.u.counter.increment = -1;
|
||||
// old counter syntax
|
||||
loop.algorithm = FORALGO_OLDCOUNT;
|
||||
if (config.wanted_version >= VER_NEWFORSYNTAX)
|
||||
Throw_first_pass_warning("Found old \"!for\" syntax.");
|
||||
if (intresult.val.intval < 0)
|
||||
Throw_serious_error("Loop count is negative.");
|
||||
// count up
|
||||
loop.u.counter.first = 1;
|
||||
loop.iterations_left = intresult.val.intval; // use given argument
|
||||
loop.u.counter.increment = 1;
|
||||
}
|
||||
} else {
|
||||
// old format - booo!
|
||||
loop.algorithm = FORALGO_OLD;
|
||||
if (config.wanted_version >= VER_NEWFORSYNTAX)
|
||||
Throw_first_pass_warning("Found old \"!for\" syntax.");
|
||||
if (intresult.val.intval < 0)
|
||||
Throw_serious_error("Loop count is negative.");
|
||||
loop.u.counter.first = 1;
|
||||
loop.iterations_left = intresult.val.intval; // use given argument
|
||||
loop.u.counter.increment = 1;
|
||||
// iterator syntax
|
||||
loop.algorithm = FORALGO_ITERATE;
|
||||
// check for "in" keyword
|
||||
if ((GotByte != 'i') && (GotByte != 'I')) {
|
||||
Throw_error(exception_syntax);
|
||||
return SKIP_REMAINDER; // FIXME - this ignores '{' and will then complain about '}'
|
||||
}
|
||||
/* checking for the first character explicitly here looks dumb, but actually
|
||||
solves a purpose: we're here because the check for comma failed, but maybe that
|
||||
was just a typo. if the current byte is '.' or '-' or whatever, then trying to
|
||||
read a keyword will result in "No string given" - which is confusing for the
|
||||
user if they did not even want to put a string there.
|
||||
so if the current byte is not the start of "in" we just throw a syntax error.
|
||||
knowing there is an "i" also makes sure that Input_read_and_lower_keyword()
|
||||
does not fail. */
|
||||
Input_read_and_lower_keyword();
|
||||
if (strcmp(GlobalDynaBuf->buffer, "in") != 0) {
|
||||
Throw_error("Loop var must be followed by either \"in\" keyword or comma.");
|
||||
return SKIP_REMAINDER; // FIXME - this ignores '{' and will then complain about '}'
|
||||
}
|
||||
if (force_bit) {
|
||||
Throw_error("Force bits can only be given to counters, not when iterating over string/list contents.");
|
||||
return SKIP_REMAINDER; // FIXME - this ignores '{' and will then complain about '}'
|
||||
}
|
||||
ALU_any_result(&loop.u.iter.obj); // get iterable
|
||||
loop.iterations_left = loop.u.iter.obj.type->length(&loop.u.iter.obj);
|
||||
if (loop.iterations_left < 0) {
|
||||
Throw_error("Given object is not iterable.");
|
||||
return SKIP_REMAINDER; // FIXME - this ignores '{' and will then complain about '}'
|
||||
}
|
||||
}
|
||||
|
||||
if (GotByte != CHAR_SOB)
|
||||
Throw_serious_error(exception_no_left_brace);
|
||||
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
#define RELEASE "0.97" // update before release FIXME
|
||||
#define CODENAME "Zem" // update before release
|
||||
#define CHANGE_DATE "23 Oct" // update before release FIXME
|
||||
#define CHANGE_DATE "24 Oct" // update before release FIXME
|
||||
#define CHANGE_YEAR "2020" // update before release
|
||||
//#define HOME_PAGE "http://home.pages.de/~mac_bacon/smorbrod/acme/"
|
||||
#define HOME_PAGE "http://sourceforge.net/p/acme-crossass/" // FIXME
|
||||
|
Loading…
Reference in New Issue
Block a user