Skip to content

Adding Classes to scarpet. #306

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 26 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
c0997d7
Added classes.scl
Ghoulboy78 Apr 3, 2022
f5fce43
Added max heap example
Ghoulboy78 Apr 3, 2022
0d7eb44
renamed heap.sc to min_heap.sc
Ghoulboy78 Apr 3, 2022
9ecdf1c
Renaming max_heap.sc to max_heap.scl
Ghoulboy78 Apr 3, 2022
2e1c112
Adding docs
Ghoulboy78 Apr 3, 2022
010ff89
Making a copy of the class map
Ghoulboy78 Apr 5, 2022
28b87bf
Adding inheritance
Ghoulboy78 Apr 7, 2022
1c7cdc8
Checking for own class when doing instanceof
Ghoulboy78 Apr 7, 2022
90b839a
Updating instance_of to account for inputting non-class values
Ghoulboy78 Apr 8, 2022
aaf504b
Adding .bak files to gitignore
Ghoulboy78 Apr 8, 2022
b063328
Adding object class
Ghoulboy78 Apr 8, 2022
2e56d8f
Adding interface and iterator class
Ghoulboy78 Apr 10, 2022
893e686
Removing unfinished sentence
Ghoulboy78 Apr 11, 2022
26a7c63
Adding anonymous classes
Ghoulboy78 Apr 29, 2022
02cb871
Adding limit to while loop
Ghoulboy78 May 6, 2022
6e2cde1
Checking for a null next() function to break out of loop
Ghoulboy78 May 7, 2022
b8f29f6
Added detailed Readme.md
Ghoulboy78 May 18, 2022
d6e60ce
Adding headings
Ghoulboy78 May 18, 2022
7c3ff11
Adding interface docs
Ghoulboy78 May 18, 2022
35103f0
Updating documentation in max_heap.scl
Ghoulboy78 Jul 17, 2022
0849558
Updating docs in Readme.md
Ghoulboy78 Jul 17, 2022
5730ed7
Merge branch 'classes' of https://github.com/gnembon/scarpet into cla…
Ghoulboy78 Jul 17, 2022
f4cf11e
Update classes.scl
Ghoulboy78 Oct 24, 2022
3f52786
Making docs more verbose
Ghoulboy78 Dec 9, 2022
921dfec
Update max_heap.scl
Ghoulboy78 Dec 9, 2022
38ce6ba
Update README.md
Ghoulboy78 Dec 11, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,8 @@
bin/
.classpath
.project
*.code-workspace
*.code-workspace


# npp
*.bak
93 changes: 92 additions & 1 deletion programs/fundamentals/README.md
Original file line number Diff line number Diff line change
@@ -1 +1,92 @@
Programs for calculating math operations, and examples for min/max interation, heaps and hashes
# Fundamentals
Programs for calculating math and other programming operations.

## `hashmap.sc`
#### By: LucunJi(禄存)
An implementation of hash maps, which predates native scarpet maps and sets. Contains functions to create and
modify maps, which are stored as lists. This implementation onlny works for numbers, however you can add your
own `_hash` function to make it work for all types.

## `hashset.sc`
#### By: LucunJi(禄存)
Same deal as with `hashmap.sc`, except for hash sets. You can currently use native scarpet implementation, but
these also give a good insight into how hash sets and hash maps work.

## `math.scl`
#### By: gnembom
A bunch of useful maths functions, such as `sum()`, `bin()` and `hex()`, as well as the distance functions
from the math.scl built into carpet mod.

## `min_heap.sc`
#### By: LucunJi(禄存)
An implementation of min heaps in scarpet. This approach shows very well how min heaps work. Heaps are stored
as lists, and the functions in the library can be used to manipulate them as if they were hash set objects.

## `max_heap.scl`
#### By: Ghoulboy
An implementation of max heaps using `classes.scl`. This implementation works very differently to
`min_heap.scl`, as here the heap is stored as a map, which represents the object. To use it, you need to
import the `max_heap()` function from here as well as (at the very least `call_function` from `classes.scl`.
You can use it by calling the `'insert'` and `'remove'` methods on the object.
For more information on how these work, see below for `classes.scl`.

## `classes.scl`
#### By: Ghoulboy
This is a library which allows the user to add classes into the game. Much like with Python, it works by
making maps (or dicts) act as classes. Fields are regular map entries, and methods are entries with a string
key and an anonymous function value. The important part is that the function always has at least 1 argument,
which is going to be the object itself, so that the function can handle it (see `self` in Python).

### Declaring a class
To register a new class, use `new_class()`. The first argument is the name of the class, which will later be
stored in the `__name__` field automatically by `classes.scl`. The second argument is the map representing the
class, its fields and its methods. The last argument is a vararg list of the parent classes of this class. If
a parent specifies a field or a method then this class will imherit that unless it specifies that same field
or method. Note, the last argument takes in the parent classes, not their names, because classes.scl does not
store classes, it just allows to manipulate them.

To initialize an object, use the `new_object()` function. The first argument is the class variable (returned from
the `new_class()` function). The next few arguments are the constructor of the class. This must be declared as an
`__init__` method (see below) with any args you wish, except `classes.scl` will throw an error if it is not
present. A class can also inherit an `__init__` method from its parent class. The `__init__` method will be
called when running the `new_object()` function.

If a class is not meant to be initialised, then you can initialise it as an interface. To do this, import
`global_interface_class` from `classes.scl` and put it as a parent class, and don't specify a constructor. If a
class chooses to implement your interface and doesn't implement a method, it will give an error message
describing which method they didn't override and where it came from.

### Declaring a method
When creating a method, as mentioned previously it must take in at least one argument which is the object
(here referred to as `self`). The method can then do whatever it wants with its arguments. It must then return
either the `self` object, or a list with `self` as the first argument and a return value as the second. This
allows a programmer to do `return_value = call_function(object, method, args)` and get a value. Returning the
object (along with any modifications you have made to it) is important because that way the object will remain
modified after you're done with it.

To access a class' fields, you just do the same as with a normal map (`object:'field_name'`). This doesn't
really allow for private fields, however methods will be considered private if they have a `_` prefix. This
will make `call_function` throw an error if the programmer tries to call them. If you need to call a private
method within a class, use `call(object:'private_method_name', object, args)`. Note that unlike with
`call_function`, this will not give neat return values, instead it will either return the object or a list of the object and return value.

### `Object` class
Classes also have some built-in methods. Much like with Java, they all inherit from the `Object` class. This
gives them the following methods:
- `str` : Returns a string representation of the object
- `hash` : Returns a hash of the object (calculated by default using native scarpet `hash()` function)
- `number` : Returns a numeric representation of the object (by default length of `str` representation)
- `class` : Returns the name of this class (also available as `object:'__name__'` field)
- `equals` : Compares two objects to check if they are equal (by default using scarpet `==` operator)
- `clone` : Returns an identical copy of this object which is it's own separate object (using underused scarpet
`copy()` function)
- `bool` : Returns a boolean representation of hte object (by default checking whether object's `number`
representation is not 0)
- `compare` : Takes in another object and returns a positive number if this one is greater, negative if it's
less and 0 if they are equal (by default by subtracting this object's `number` representation with the other
object's `number` representation)
- `length` : Returns the length of this object (by default the length of its `str` representation)
- `nbt` : Returns a nbt representation of this object (by default an nbt of `str` representation)
- `json` : Returns a json representation of this object (by default a json of `str` representation)


140 changes: 140 additions & 0 deletions programs/fundamentals/classes.scl
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
//Library functions which add classes to scarpet
//
//This implementation makes maps as classes, and each map has to have a __init__ function.
//All functions must take at least one parameter (like in Python 'self'), and then any additional parameters.
//If the function should return nothing, then return just the object itself (with any modifications that the function adds)
//If the function should return something, return a list with the object as the first element, and return value as the second
//For fields, just access and set them like you would access and set values in a normal map
//
//When declaring methods in the class, you can optionally add a _ infront of them to make them private.
//This means that they cannot be called using call_function(), telling the programmer that they're potentially dangerous.
//When calling functions within the class methods, just use call(self:function_name, self, args).
//This allows you to access private functions within the object.
//
//When calling a function, use call_function(), with the object as the first parameter, function name as the second
//and then any arguments at the end. If the function has a return value, then call_function() will return it, otherwise
//it will return null.
//
//
//For an example of all this, see max_heap.scl
//By: Ghoulboy

//Initializes a class with its parent classes, which are passed in as their classes, not their names
//We need the classes to complete the declaration of the class by essentially filling in all the missing methods
new_class(name, declarer, ...parents)->(
//Also means a class called Anonymous won't work
declarer:'__name__' = if(name=='', 'Anonymous', name);
declarer:'__parents__' = {}; //Using a set to avoid duplicate parentage
declarer:'__member_data__' = {}; //Used to improve error messages
for(parents,
class=_;
if(class:'__name__'=='Anonymous', throw('Cannot inherit from an anonymous class'));
declarer:'__parents__'+=class:'__name__';
for(class,
member = _;
if(member=='__parents__',//Pushing parents' parents into child's parents
declarer:'__parents__' = declarer:'__parents__' + class:'__parents__',
!has(declarer, member), //stuff which the declarer doesn't override
declarer:member = class:member;
declarer:'__member_data__':member = class:'__name__'
);
)
);
if(name!='Object',
object = global_object_class;
for(object,
member = _;
if(!has(declarer, member),
declarer:member = object:member;
declarer:'__member_data__':member = 'Object'
)
)
);
//Checking for initializer here cos it could have been stolen from a parent class
if(!has(declarer, '__init__') || type(declarer:'__init__')!='function', throw('missing_constructor', 'value_exception', declarer));
declarer
);


//Checks that a given input is a class.
//NB: this only checks that it has a valid constructor and has a __name__ property
is_class(class)->
type(class)=='map' && has(class, '__init__') && type(class:'__init__')=='function' && has(class, '__name__') && has(class, '__parents__');

//This throws an exception if a given input is not a valid class
check_class(class)->
if(!is_class(class), throw('invalid_class', 'value_exception', class));

//the object is literally the same as class, except we have called initializer
new_object(class, ...args)->(
check_class(class);
object = copy(class); //this is to avoid modifying the original map
call(object:'__init__', object, ...args);
);

//Basically java instanceof operator
instance_of(object, class_name)->is_class(object)&&(object:'__name__'==class_name || has(object:'__parents__', class_name));

//This is for calling public functions (which don't begin with _)
//You can use call() directly, but this gives return value of functions neatly and blocks you from using private methods
call_function(object, function, ...args)->(
check_class(object);
if(!has(object, function), throw('unknown_method', 'value_exception', function));
if(split(function):0 == '_', throw('hidden_method', 'value_exception', function));
cb = call(object:function, object, ...args);
if(type(cb)=='map',
object = cb; null,
type(cb)=='list',
object=cb:0; cb:1,
// must either return modified object, or a list pair of the object and the return value of the function
// Just checking here if we forgot to implement an abstract method from an interface
instance_of(object, 'Interface'),
//Copying Java's error message for unimplemented interface functions
throw(str('Class \'%s\' must implement abstract method \'%s\' inherited from class \'%s\'',
object:'__name__',
function,
object:'__member_data__':function
)),
throw('invalid_function_return', 'value_exception', cb)
)
);


//This is a fundamental base class which all classes inherit from.
//It contains base methods which we can guarantee will be in all classes
//A bit of the code is copied from Value.java, and some from Object.java
global_object_class = new_class('Object', {
'__init__'->_(self)->throw('Cannot initialize class of Object'),
'str'->_(self)->[self, self:'__name__' + '@' + call_function(self, 'hash')],
'hash'->_(self)->[self, hash(self)],
'number'->_(self)->[self, length(call_function(self, 'str'))],
'class'->_(self)->[self, self:'__name__'],
'equals'->_(self, obj)->[self, self==obj],
'clone'->_(self)->[self, copy(self)],
'bool'->_(self)->[self, bool(call_function(self, 'number'))],
'compare'->_(self, other)->[self, call_function(self, 'number') - call_function(other, 'number')],
'length'->_(self)->[self, length(call_function(self, 'str'))],
'nbt'->_(self)->[self, nbt(call_function(self, 'str'))],
'json'->_(self)->[self, encode_json(call_function(self, 'str'))],
});

//This will be used to simulate interfaces, essentially.
//Classes which want to be interfaces can inherit from this and not specify a constructor
global_interface_class = new_class('Interface', {
'__init__'->_(self)->throw('Cannot initialize an interface class'),
});

//Some template classes. These may in time be moved to a separate file
global_iterator_class = new_class('Iterator', {
'has_next'->_(self)->null,
'next'->_(self)->null,
'for_each'->_(self, function)->(
while(call_function(self, 'has_next'), 0x7FFFFFFFFFFFFFFF,
next = call(self:'next', self);
if(next==null, break());
call(function, next:1);
);
self
),
}, global_interface_class);

86 changes: 86 additions & 0 deletions programs/fundamentals/max_heap.scl
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
//A library script which contains the class of max heaps, implemented using classes.scl
//If you want to know how a max heap works, see documentation of min_heaps.sc (which works very differently)
//
//To use this, import it into your script, and then import 'call_function' from classes.scl, so you can use the methods
//At the bottom, commented out, is an example script showing how it works and how to use it.
//
//By: Ghoulboy

import('classes', 'new_class', 'new_object', 'call_function');

max_heap_class = new_class('MaxHeap', {
'__init__'->_(self, max_size)->(
self:'max_size' = max_size;
self:'size' = 0;
_Heap = [];
loop(max_size, _Heap+=0);
self:'_Heap' = _Heap;
self:'_FRONT' = 0;
self
),
'_parent'->_(self, pos)->[self, bitwise_shift_right(pos, 1)],
'_leftChild'->_(self, pos)->[self, bitwise_shift_left(pos, 1)],
'_rightChild'->_(self, pos)->[self, bitwise_shift_left(pos, 1) + 1],
'_isLeaf'->_(self, pos)->[self, pos*2 > self:'size'],
'_swap'->_(self, fpos, spos)->(
temp = self:'_Heap':fpos;
self:'_Heap':fpos = self:'_Heap':spos;
self:'_Heap':spos = temp;
self
),
'_maxHeapify'->_(self, pos) -> (
//Getting all this stuff here cos it doesn't change and clutters up the code like crazy
left = call(self:'_leftChild', self, pos):1;
right = call(self:'_rightChild', self, pos):1;
selfpos = self:'_Heap':pos;
leftchild = self:'_Heap':left;
rightchild = self:'_Heap':right;
if(!call(self:'_isLeaf', self, pos):1 && (selfpos < leftchild || selfpos < rightchild),
heapifypos = if(leftchild>rightchild, left, right);
call(self:'_swap', self, pos, heapifypos);
call(self:'_maxHeapify', self, heapifypos)
);
self
),
'insert'->_(self, element) -> (
if(self:'size' < self:'max_size',
self:'size' += 1;
self:'_Heap':(self:'size') = element;
current = self:'size';
while(self:'_Heap':current > self:'_Heap':(call(self:'_parent', self, current):1), self:'max_size',
call(self:'_swap', self, current, call(self:'_parent', self, current):1);
current = call(self:'_parent', self, current):1
);
);
self
),
'remove'->_(self)->(
popped = self:'_Heap':(self:'_FRONT');
self:'_Heap':(self:'_FRONT') = self:'_Heap':(self:'size');
self:'size' += -1;
call(self:'_maxHeapify', self, self:'_FRONT');
[self, popped]
),
});

max_heap(outer(max_heap_class), maxsize) -> new_object(max_heap_class, maxsize);

//This is an example of using a MaxHeap
//heap = new_object(max_heap, 5);
//print(heap:'_Heap'); => [0, 0, 0, 0, 0]
//call_function(heap, 'insert', 1);
//print(heap:'_Heap'); => [1, 0, 0, 0, 0]
//call_function(heap, 'insert', 3);
//print(heap:'_Heap'); => [3, 1, 0, 0, 0]
//call_function(heap, 'insert', 3);
//print(heap:'_Heap'); => [3, 3, 0, 1, 0]
//print(call_function(heap, 'remove')); => 3
//print(heap:'_Heap'); => [3, 1, 0, 1, 0] //The extra 1 here is becase we don't remove it from the list, rather overwrite it later
//call_function(heap, 'insert', 5);
//print(heap:'_Heap'); => [5, 3, 0, 1, 0] //The first 1 from before has overwritten the trailing 1
//call_function(heap, 'insert', 6);
//print(heap:'_Heap'); => [6, 5, 3, 1, 0]
//print(call_function(heap, 'remove'));
//print(heap:'_Heap'); => [5, 3, 0, 1, 0]
//
//print(heap); => {remove: _, __name__: MaxHeap, _Heap: [5, 3, 0, 1, 0], _leftChild: _, _isLeaf: _, _rightChild: _, _maxHeapify: _, __init__: _, _swap: _, insert: _, _parent: _, _FRONT: 0, size: 3, max_size: 5}
File renamed without changes.