GMLscripts.com

Discuss and collaborate on GML scripts
Invert

You are not logged in.

#1 2021-12-16 05:58:17

maras
Member
Registered: 2021-04-25
Posts: 26
Website

Foreach v2.0.5

A foreach loop for arrays, lists, maps, structs, grids, strings and number ranges.
This foreach was made using macros so you don't have to pass variables like arguments. You can access them inside of the loop directly.
Reserved keywords: foreach, in, in_reversed, exec, as_list, as_grid, as_map, fe + global variable FEDATA

Changes
[v2.0.5] Syntax update
The syntax is much simpler again thanks to Gamer-XP

Syntax

Expandforeach <var> in <data> exec

    <var> - variable
    <data> - any supported data

The only datatypes that require exact specifications are DS types

Expandforeach v in <some_ds_map> as_map exec
foreach v in <some_ds_list> as_map exec
foreach v in <some_ds_grid> as_grid exec

You can use break and continue

The variable fe contains these variables:
- index fe.i (array, list, number range, string)
- key fe.key (map, struct)
- position fe.xpos, fe.ypos (grid)
- write function fe.set(val) (anything but string and number range)

EXAMPLES HERE:
https://github.com/mh-cz/GameMaker-Foreach

Expand/// Foreach v2.0.5
//
// foreach <var> in <data> exec
// 
//     <var> - variable
//     <data> - any supported data
//     
/// GMLscripts.com/license

global.FEDATA = { stack: [], current: undefined };

#macro fe global.FEDATA.current

#macro foreach \
	for(var _DataLoadeD_ = false, _CanLooP_ = false; true; { \
	if _DataLoadeD_ { _CanLooP_ = true; if !fe.next() { fe.done(); break; } var 

#macro in \
	= fe.get(); } else { _DataLoadeD_ = true; fe = _FeDetectType_(false, 

#macro in_reverse \
	= fe.get(); } else { _DataLoadeD_ = true; fe = _FeDetectType_(true, 

#macro exec \
	); array_push(global.FEDATA.stack, fe); }}) if _CanLooP_

#macro as_list ,[ds_type_list]
#macro as_map ,[ds_type_map]
#macro as_grid ,[ds_type_grid]

function _FeDetectType_(reversed, data) {
	
	if is_array(data) return new _FeArray_(reversed, data);
	else if is_string(data) return new _FeString_(reversed, data);
	else if is_struct(data) return new _FeStruct_(data);
	else if argument_count == 3 and is_array(argument[2]) {
		switch(argument[2][0]) {
			case ds_type_list: return new _FeList_(reversed, data);
			case ds_type_map:  return new _FeMap_(data);
			case ds_type_grid: return new _FeGrid_(reversed, data);
			default: throw "Unsupported ds type: " + string(argument[2][0]);
		}
	}
	else if is_numeric(data) {
		if argument_count <= 2 return new _FeRange_(data);
		else if argument_count == 3 return new _FeRange_(data, argument[2])
		else return new _FeRange_(data, argument[2], argument[3]);
	}
	else throw "Unsupported type: " + string(data);
}

function _FeArray_(reversed, data) constructor {

	self.data = data;
	self.i = -1;
	self.len = array_length(data);
	self.step = 1;
		
	if reversed {
		i = len;
		step = -step;
	}

	static set = function(v) {
		data[@ i] = v;
	}

	static next = function() {
		i += step;
		return i < len and i > -1;
	}

	static get = function() {
		return data[i];
	}

	static done = function() {
		array_pop(global.FEDATA.stack);
		var l = array_length(global.FEDATA.stack);
		if l != 0 fe = global.FEDATA.stack[l-1];
	}
}

function _FeList_(reversed, data) constructor {

	self.data = data;
	self.i = -1;
	self.len = ds_list_size(data);
	self.step = 1;

	if reversed {
		i = len;
		step = -step;
	}

	static set = function(v) {
		data[| i] = v;
	}

	static next = function() {
		i += step;
		return i < len and i > -1;
	}

	static get = function() {
		return data[| i];
	}

	static done = function() {
		array_pop(global.FEDATA.stack);
		var l = array_length(global.FEDATA.stack);
		if l != 0 fe = global.FEDATA.stack[l-1];
	}
}

function _FeMap_(data) constructor {

	self.data = data;
	self.i = -1;
	self.key = "";
	self.keys = ds_map_keys_to_array(data);
	self.len = array_length(keys);
	self.step = 1;

	static set = function(v) {
		data[? key] = v;
	}

	static next = function() {
		i += step;
		return i < len;
	}

	static get = function() {
		key = keys[i];
		return data[? key];
	}

	static done = function() {
		array_pop(global.FEDATA.stack);
		var l = array_length(global.FEDATA.stack);
		if l != 0 fe = global.FEDATA.stack[l-1];
	}
}

function _FeStruct_(data) constructor {

	self.data = data;
	self.i = -1;
	self.key = "";
	self.keys = variable_struct_get_names(data);
	self.len = array_length(keys);
	self.step = 1;

	static set = function(v) {
		data[$ key] = v;
	}

	static next = function() {
		i += step;
		return i < len;
	}

	static get = function() {
		key = keys[i];
		return data[$ key];
	}

	static done = function() {
		array_pop(global.FEDATA.stack);
		var l = array_length(global.FEDATA.stack);
		if l != 0 fe = global.FEDATA.stack[l-1];
	}
}

function _FeGrid_(reversed, data) constructor {

	self.data = data;
	self.xpos = 0;
	self.ypos = 0;
	self.w = ds_grid_width(data);
	self.h = ds_grid_height(data);
	self.i = -1;
	self.len = w * h;
	self.step = 1;

	if reversed {
		i = len;
		step = -step;
	}

	static set = function(v) {
		data[# xpos, ypos] = v;
	}

	static next = function() {
		i += step;
		return i < len and i > -1;
	}

	static get = function() {
		xpos = i mod w;
		ypos = i div h;
		return data[# xpos, ypos];
	}

	static done = function() {
		array_pop(global.FEDATA.stack);
		var l = array_length(global.FEDATA.stack);
		if l != 0 fe = global.FEDATA.stack[l-1];
	}
}

function _FeRange_() constructor {

	self.from = -1;
	self.to = 1;
	self.step = 1;
	self.i = 0;

	switch(argument_count) {
		case 1:
			to = argument[0];
			i = from;
			break;
		case 2:
			from = argument[0] - step;
			to = argument[1];
			i = from;
			break;
		case 3:
			step = abs(argument[2]);
			from = argument[0] - step;
			to = argument[1];
			i = from;
			break;
	}

	static next = function() {
		i += step;
		return (from < to and i <= to) or (from > to and i >= to);
	}

	static get = function() {
		return i;
	}

	static done = function() {
		array_pop(global.FEDATA.stack);
		var l = array_length(global.FEDATA.stack);
		if l != 0 fe = global.FEDATA.stack[l-1];
	}
}

function _FeString_(reversed, data) constructor {

	self.data = data;
	self.i = -1;
	self.len = string_length(data);
	self.step = 1;

	if reversed {
		i = len;
		step = -step;
	}

	static next = function() {
		i += step;
		return i < len and i > -1;
	}

	static get = function() {
		return string_char_at(data, i+1);
	}

	static done = function() {
		array_pop(global.FEDATA.stack);
		var l = array_length(global.FEDATA.stack);
		if l != 0 fe = global.FEDATA.stack[l-1];
	}
}

Last edited by maras (2022-09-16 08:35:41)


I'm on the official GM discord > maras#5104

Offline

#2 2022-01-10 16:35:20

xot
Administrator
Registered: 2007-08-18
Posts: 1,239

Re: Foreach v2.0.5

Whoa! I'll have to think about this.


Abusing forum power since 1986.

Offline

#3 2022-06-27 04:37:37

gnysek
Member
Registered: 2011-12-29
Posts: 30

Re: Foreach v2.0.5

Main issue with this script is that it uses macros to make some tricks with GML, and replace things on compiling game. While this is OK for very advanced programmers, I think it's not a style that should be promoted for clean code.

Offline

#4 2022-07-24 03:10:37

maras
Member
Registered: 2021-04-25
Posts: 26
Website

Re: Foreach v2.0.5

gnysek wrote:

Main issue with this script is that it uses macros to make some tricks with GML, and replace things on compiling game. While this is OK for very advanced programmers, I think it's not a style that should be promoted for clean code.

Well it was never really meant to be clean. The main goal was to make it look simple, quick to type and being able to access variables without passing them as arguments first. Dirty macros were the only thing that came to mind

Last edited by maras (2022-07-25 10:18:44)


I'm on the official GM discord > maras#5104

Offline

Board footer

Powered by FluxBB