<commit_before>#include "runtime.hh"
#include <stdio.h>
#include <string.h>
#include <unordered_map>
#include <vector>
#include <gc.h>

using namespace MysoreScript;

namespace
{

/**
 * Global vector of selector names.  This is used to map from a selector to a
 * string value.
 */
std::vector<std::string> selNames;

/**
 * Invalid method function.  Returned when method lookup fails.  This logs a
 * message indicating the error.
 */
Obj invalidMethod(Obj obj, Selector sel)
{
	auto selName = selNames[sel];
	if (!obj)
	{
		fprintf(stderr, "\nERROR: method %s called on null object\n",
				selName.c_str());
		return nullptr;
	}
	Class *cls = isInteger(obj) ? &SmallIntClass : obj->isa;
	fprintf(stderr, "\nERROR: %s does not respond to selector %s\n",
			cls->className, selName.c_str());
	return nullptr;
}

/**
 * The `.length()` method for `String` objects.
 */
Obj StringLength(String *str, Selector sel)
{
	return str->length;
}

/**
 * The `.charAt(idx)` method for `String` objects.
 */
Obj StringCharAt(String *str, Selector sel, Obj idx)
{
	// If the index isn't a small integer, then return 0.
	if (!isInteger(idx))
	{
		return nullptr;
	}
	intptr_t i = getInteger(idx);
	intptr_t len = getInteger(str->length);
	// Check that the access is in bounds.
	if (i >= len && (i > 0))
	{
		return nullptr;
	}
	// Turn the small integer object into a primitive integer and use it to
	// dereference the character array, then turn the result into an integer
	// value.
	return createSmallInteger(str->characters[i]);
}
/**
 * The `.length()` method for `Array` objects.
 */
Obj ArrayLength(Array *arr, Selector sel)
{
	return arr->length;
}
/**
 * The `.at(idx)` method for `Array` objects.
 */
Obj ArrayAt(Array *arr, Selector sel, Obj idx)
{
	if (!isInteger(idx))
	{
		return nullptr;
	}
	intptr_t i = getInteger(idx);
	intptr_t len = arr->length ? getInteger(arr->length) : 0;
	// Check that the access is in bounds.
	if (i >= len && (i > 0))
	{
		return nullptr;
	}
	return arr->buffer[i];
}
/**
 * The `.atPut(idx, obj)` method for `Array` objects.
 */
Obj ArrayAtPut(Array *arr, Selector sel, Obj idx, Obj obj)
{
	// If the index isn't an integer, return null
	if (!isInteger(idx))
	{
		return nullptr;
	}
	intptr_t i = getInteger(idx);
	intptr_t len = arr->length ? getInteger(arr->length) : 0;
	// Check that the index is positive.
	if (i < 0)
	{
		return nullptr;
	}
	intptr_t bufferSize = arr->bufferSize ? getInteger(arr->bufferSize) : 0;
	if (i >= bufferSize)
	{
		// Note that this is really inefficient, but will stress the GC a
		// little bit, which should make life a bit more interesting for
		// students...
		size_t newSize = i + 1;
		Obj *buffer = (Obj*)GC_MALLOC(newSize * sizeof(Obj));
		memcpy(buffer, arr->buffer, len * sizeof(Obj));
		arr->buffer = buffer;
		arr->bufferSize = createSmallInteger(newSize);
	}
	if (i >= len)
	{
		arr->length = createSmallInteger(i+1);
	}
	arr->buffer[i] = obj;
	return obj;
}

/**
 * The `.dump()` method for `Number` objects.
 */
Obj NumberDump(Obj str, Selector sel)
{
	fprintf(stderr, "%lld\n", (long long)getInteger(str));
	return nullptr;
}
/**
 * The `.dump()` method for `String` objects.
 */
Obj StringDump(String *str, Selector sel)
{
	fwrite(str->characters, getInteger(str->length), 1, stderr);
	return nullptr;
}

/**
 * The + method on a string, allocates a new string with the specified length.
 */
Obj StringAdd(String *str, Selector sel, String *other)
{
	// If we are trying to concatenate something that's not a string, return
	// null
	if (isInteger((Obj)other) || other->isa != &StringClass)
	{
		return nullptr;
	}
	uintptr_t len1 = getInteger(str->length);
	uintptr_t len2 = getInteger(other->length);
	uintptr_t lenTotal = len1+len2;
	String *newStr = gcAlloc<String>(lenTotal);
	newStr->isa = &StringClass;
	newStr->length = createSmallInteger(lenTotal);
	memcpy(newStr->characters, str->characters, len1);
	memcpy(newStr->characters+len1, other->characters, len2);
	return (Obj)newStr;
}

/**
 * Compare two strings, returning an integer representing the ordering.
 */
Obj StringCmp(String *str, Selector sel, String *other)
{
	// If we are trying to compare something that's not a string, return null
	if (isInteger((Obj)other) || other->isa != &StringClass)
	{
		return nullptr;
	}
	uintptr_t len1 = getInteger(str->length);
	uintptr_t len2 = getInteger(other->length);
	uintptr_t len = std::min(len1, len2);
	int result = memcmp(str->characters, other->characters, len);
	if (result == 0)
	{
		if (len1 > len2)
		{
			result = str->characters[len2];
		}
		else if (len1 < len2)
		{
			result = 0 - other->characters[len1];
		}
	}
	return createSmallInteger(result);
}

/**
 * Selectors for methods that are defined as part of the runtime.<commit_msg>Allow the string methods to handle null arguments.<commit_after>#include "runtime.hh"
#include <stdio.h>
#include <string.h>
#include <unordered_map>
#include <vector>
#include <gc.h>

using namespace MysoreScript;

namespace
{

/**
 * Global vector of selector names.  This is used to map from a selector to a
 * string value.
 */
std::vector<std::string> selNames;

/**
 * Invalid method function.  Returned when method lookup fails.  This logs a
 * message indicating the error.
 */
Obj invalidMethod(Obj obj, Selector sel)
{
	auto selName = selNames[sel];
	if (!obj)
	{
		fprintf(stderr, "\nERROR: method %s called on null object\n",
				selName.c_str());
		return nullptr;
	}
	Class *cls = isInteger(obj) ? &SmallIntClass : obj->isa;
	fprintf(stderr, "\nERROR: %s does not respond to selector %s\n",
			cls->className, selName.c_str());
	return nullptr;
}

/**
 * The `.length()` method for `String` objects.
 */
Obj StringLength(String *str, Selector sel)
{
	return str->length;
}

/**
 * The `.charAt(idx)` method for `String` objects.
 */
Obj StringCharAt(String *str, Selector sel, Obj idx)
{
	// If the index isn't a small integer, then return 0.
	if (!isInteger(idx))
	{
		return nullptr;
	}
	intptr_t i = getInteger(idx);
	intptr_t len = getInteger(str->length);
	// Check that the access is in bounds.
	if (i >= len && (i > 0))
	{
		return nullptr;
	}
	// Turn the small integer object into a primitive integer and use it to
	// dereference the character array, then turn the result into an integer
	// value.
	return createSmallInteger(str->characters[i]);
}
/**
 * The `.length()` method for `Array` objects.
 */
Obj ArrayLength(Array *arr, Selector sel)
{
	return arr->length;
}
/**
 * The `.at(idx)` method for `Array` objects.
 */
Obj ArrayAt(Array *arr, Selector sel, Obj idx)
{
	if (!isInteger(idx))
	{
		return nullptr;
	}
	intptr_t i = getInteger(idx);
	intptr_t len = arr->length ? getInteger(arr->length) : 0;
	// Check that the access is in bounds.
	if (i >= len && (i > 0))
	{
		return nullptr;
	}
	return arr->buffer[i];
}
/**
 * The `.atPut(idx, obj)` method for `Array` objects.
 */
Obj ArrayAtPut(Array *arr, Selector sel, Obj idx, Obj obj)
{
	// If the index isn't an integer, return null
	if (!isInteger(idx))
	{
		return nullptr;
	}
	intptr_t i = getInteger(idx);
	intptr_t len = arr->length ? getInteger(arr->length) : 0;
	// Check that the index is positive.
	if (i < 0)
	{
		return nullptr;
	}
	intptr_t bufferSize = arr->bufferSize ? getInteger(arr->bufferSize) : 0;
	if (i >= bufferSize)
	{
		// Note that this is really inefficient, but will stress the GC a
		// little bit, which should make life a bit more interesting for
		// students...
		size_t newSize = i + 1;
		Obj *buffer = (Obj*)GC_MALLOC(newSize * sizeof(Obj));
		memcpy(buffer, arr->buffer, len * sizeof(Obj));
		arr->buffer = buffer;
		arr->bufferSize = createSmallInteger(newSize);
	}
	if (i >= len)
	{
		arr->length = createSmallInteger(i+1);
	}
	arr->buffer[i] = obj;
	return obj;
}

/**
 * The `.dump()` method for `Number` objects.
 */
Obj NumberDump(Obj str, Selector sel)
{
	fprintf(stderr, "%lld\n", (long long)getInteger(str));
	return nullptr;
}
/**
 * The `.dump()` method for `String` objects.
 */
Obj StringDump(String *str, Selector sel)
{
	fwrite(str->characters, getInteger(str->length), 1, stderr);
	return nullptr;
}

/**
 * The + method on a string, allocates a new string with the specified length.
 */
Obj StringAdd(String *str, Selector sel, String *other)
{
	// If we are trying to concatenate something that's not a string, return
	// null
	if (other == nullptr || isInteger((Obj)other) || other->isa != &StringClass)
	{
		return nullptr;
	}
	uintptr_t len1 = getInteger(str->length);
	uintptr_t len2 = getInteger(other->length);
	uintptr_t lenTotal = len1+len2;
	String *newStr = gcAlloc<String>(lenTotal);
	newStr->isa = &StringClass;
	newStr->length = createSmallInteger(lenTotal);
	memcpy(newStr->characters, str->characters, len1);
	memcpy(newStr->characters+len1, other->characters, len2);
	return (Obj)newStr;
}

/**
 * Compare two strings, returning an integer representing the ordering.
 */
Obj StringCmp(String *str, Selector sel, String *other)
{
	// If we are trying to compare something that's not a string, return null
	if (other == nullptr || isInteger((Obj)other) || other->isa != &StringClass)
	{
		return nullptr;
	}
	uintptr_t len1 = getInteger(str->length);
	uintptr_t len2 = getInteger(other->length);
	uintptr_t len = std::min(len1, len2);
	int result = memcmp(str->characters, other->characters, len);
	if (result == 0)
	{
		if (len1 > len2)
		{
			result = str->characters[len2];
		}
		else if (len1 < len2)
		{
			result = 0 - other->characters[len1];
		}
	}
	return createSmallInteger(result);
}

/**
 * Selectors for methods that are defined as part of the runtime.
{'fraction_non_alphanumeric': 0.14632360631733082, 'fraction_numerical': 0.006066311055328941, 'mean_word_length': 3.856272219400711, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 103, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3972410', 'n_tokens_mistral': 3429, 'n_tokens_neox': 3318, 'n_words': 1028}