Translates the SplashKit C++ source into another language.
This project can be setup on any platform through the use of Docker containers. A docker file can be found in the root of the repository, information on how to get setup and running with can be found in Docker_README.md
Ensure you have HeaderDoc installed:
- Under macOS, you will need to have Xcode with Developer Tools installed.
- Under Ubuntu, you can download HeaderDoc at Apple's OpenSource Developer Tools here.
Install dependencies using bundle:
bundle installThen run using translate.
-
If you get the following error when running
bundle install:can't find gem bundler (>= 0.a) with executable bundle (Gem::GemNotFoundException)You need to use a Ruby Version Manager to install Ruby 2.7.5, rather than using the default version of Ruby that comes with macOS.
To validate a single file or files, supply the --validate or -v switch and
the --input or -i switch with the header file you wish to validate:
./translate --validate --input /path/to/splashkit/coresdk/src/coresdk/audio.hThis will only validate input that it can be correctly parsed, but will not generate any translated code.
Alternatively, you can validate all header files by supplying just the
SplashKit coresdk/src/coresdk directory instead:
./translate -v -i /path/to/splashkit/coresdk/src/coresdkTo convert, follow the same as the above, removing the --validate/-v switch
and supplying the translated language you would like to generate using a
comma-separated list under the --generate or -g switch and specifying the
output directory using the --output or -o switch:
./translate -i /path/to/splashkit -o ~/Desktop/translated -g YAML,SKLIBC,CPPIf no output directory is used, then it will default to an out/translated
directory inside the input directory specified.
To see a full list of each translator available, use the --help switch.
SplashKit uses HeaderDoc to parse documentation. A guide on HeaderDoc can be found here.
Ensure that snake_case is consistently used throughout documentation.
A header file should begin with a docblock consisting of:
@header [name]- The name of the 'module' of functions and types defined in the header. E.g.,audio.hwould be listed asAudio.@author [name]- One or many author names who have contributed to the header and/or implementation@brief [description]- A brief, one sentence description of what functionality is added in this 'module'.- A longer description of the functionality. The description accepts Markdown.
@attribute group [group]- Groups the contents of the file under a specific group name. Refer to group for more.
Example, audio.h:
/**
* @header Audio
* @author Andrew Cain
* @brief SplashKit Audio allows you to load and play music and sound effects.
*
* The SplashKit's audio library allows you to easily load and play music and
* sound effects within your programs. To get started with audio the first
* thing you need to do is load a sound effect or music file. You can do this
* by calling the `load_sound_effect(string name)` function to the
* `load_music(string name)` function.
*
* @attribute static audio
*/Any attributes set in a header file docblock will apply those attributes to all
docblocks inside that file. In the example above, all docblocks will have
@attribute static audio added to them unless @attribute static is already
listed in a docblock.
A function docblock should consist of at least a basic description of the functionality provided by the function in Markdown. Where applicable, it also must have:
@param [name] [description]- The name and description of a parameter. These should be listed in order of the function's signature. All parameters must be listed and be consistent with the correct name(s) in the signature.@returns [description]- A basic description of what is returned from the function. Any non-void function must have an@returns.@brief [description]- A brief, one sentence description of what functionality is added in this function.
Each of the above should be separated with a newline and grouped together where applicable.
Example, load_sound_effect:
/**
* @brief Loads and returns a sound effect.
*
* The supplied `filename` is used to locate the sound effect to load. The
* supplied `name` indicates the name to use to refer to this `sound_effect`.
* The `sound_effect` can then be retrieved by passing this `name` to
* the `sound_effect_named` function.
*
* @param name The name used to refer to the sound effect.
* @param filename The filename used to locate the sound effect to use.
*
* @returns A new `sound_effect` with the initialised values provided.
*/
sound_effect load_sound_effect(string name, string filename);An enum docblock must define every one of its constants using the
@constant tag. For example:
/**
* Defines each of the five weekdays
*
* @constant Monday The day where you want to go sleep
* @constant Tuesday The day where you start to get stuff done
* @constant Wednesday The day where you realise you're only midway through
* @constant Thursday The day where you can smell Friday coming
* @constant Friday The day where you party hard
*/
enum weekdays {
Monday,
Tuesday,
Wednesday,
Thursday,
Friday
};A struct docblock must define each of its field members using a @param tag.
For example:
/**
* Defines basic details for a person
*
* @param name The name of the person
* @param age The age of the person
* @param friend The person's bestest friend in the whole world
*/
struct person {
string name,
int age,
person *friend
};A function docblock should consist of at least a basic description of the functionality provided by the function in Markdown.
Attributes provide options to the language translator. They are applicable to functions and typedef docblocks. Attributes are declared as thus:
/**
* @attribute [key] [value]
*/Here is a list of all accepted attribute keys:
When added to a typedef, the type will be declared as a class:
/**
* ...
*
* @attribute class sound_effect
*/
typedef struct _sound_data *sound_effect;
Note that typedef aliases to pointers must be declared with a class attribute.
Associates the sound_effect type to an object-oriented-translated SplashKit
class instance.
When added to a function, the type will be associated to a class. You must
pair this with method, constructor, destructor, getter, or setter
attribute (see below) to define how the function will be associated to under
that class.
/**
* ...
*
* @attribute class audio
* @attribute method close
*/
void close_audio();This will convert the above to an equivalent OO method:
Audio.Close()Indicates the module or class name to which a global method or function is
applied. Can be associated to a method name to create a static method on a
class or global function on a method, or a static getter or setter.
Refer to method for more.
Associates a function to a class. Requires the class or static attribute to
be set. The name specified by method will be the method name that will be used
as the method on the class. See the above example.
When method is used with the class attribute, an instance method will be
generated on the class whose name is specified by class.
When method is used with the static attribute, a static method will be
generated on the class whose name is specified by static.
Associates a function as the constructor to a class.
Requires the class attribute to be set in order to construct an instance
of that class.
To mark a constructor, simply set the value as true:
/**
* ...
*
* @attribute class sound_effect
* @attribute constructor true
*/
sound_effect load_sound_effect(string name, string filename);This will convert the above to an equivalent OO constructor:
SoundEffect(string name, string filename)
If defining a constructor, then you cannot violate this attribute with a
destructor attribute in the same HeaderDoc block.
Unless static is specified, then a destructor function cannot also act as
a instance getter, setter or method.
Same as constructor, but for a destructor.
Requires the class attribute to be set in order to destruct an instance
of that class.
Similarly, to destruct a particular instance, the instance object will be
passed into the parameter specified by a self attribute. Thus self must
be set on destructors.
For example:
/**
* ...
*
* @attribute class sound_effect
* @attribute self effect
* @attribute destructor true
*/
void delete_sound_effect(sound_effect effect);This would call the delete_sound_effect on the instance, where the instance is
the effect parameter:
delete someSoundEffectInstance;
If defining a destructor, then you cannot violate this attribute with a
constructor attribute in the same HeaderDoc block.
Unless static is specified, then a destructor function cannot also act as
an instance getter, setter or method.
Specifies the name of the parameter which should act as this or self on the
function call.
When the function is converted to a method for an object-oriented language,
the instance calling the method will be passed into parameter specified by
self.
To know which type the self parameter would be, list the class of that
parameter and make sure it matches the name of the parameter indicated by
self.
For example:
/**
* ...
*
* @attribute class sound_effect
* @attribute method play
* @attribute self effect
*/
void play_sound_effect(sound_effect effect, int times, float volume);When translated into OO:
someSoundEffectInstance.play(3, 10.0f)will call
play_sound_effect(someSoundEffectInstance, 3, 10.0f)When using a self attribute, you must ensure that the parameter named by
self has a type which matches the class attribute value. For example, the
following will cause an error since times is an int, which does not match
the class attribute value of type sound_effect:
/**
* ...
*
* @attribute class sound_effect <-------------------------------
* @attribute method play \
* @attribute self times <--- times is an int, not a sound_effect!!
*/
void play_sound_effect(sound_effect effect, int times, float volume);For translated languages that do not support overloaded function names, the
name specified by suffix name will be used as a suffix appended to the global
and instance name of the function.
If class is specified, then the method name appended with suffix must be
unique within that class.
If static is specified, then function name appended with suffix must be
unique within that static namespace.
If neither are specified, then function name appended with suffix must be
unique globally.
For example:
/**
* ...
*
* @attribute static audio
* @attribute class sound_effect
* @attribute method play
* @attribute suffix with_loops_and_volume
* @attribute self effect
*/
void play_sound_effect(sound_effect effect, int times, float volume);will be translated into Python as:
some_sound_effect_instance.play_with_loops_and_volume(3, 10.0)
play_sound_effect_with_loops_and_volume(effect, 3, 10.0)whereas in C# it would look like:
someSoundEffectInstance.play(3, 10.0f)
Audio.PlaySoundEffect(effect, 3, 10.0f)Creates a getter method to the class or static module specified. Requires
either:
classandselfto make an instance getter on the an instance whose class is specified byclass, orstaticto make a static getter on the class specifiedstatic.
Must be set on a function that:
- has exactly zero or one parameters, depending on if you are using
classorstatic, and - is non-void.
If you are writing a static getter, then there must be no parameters.
If you are writing a class setter, then you will need exactly one
parameter, that being the parameter which will be used as self. You must
not specify that the function also a method (unless static is also supplied)
or a constructor or destructor.
For example, the following:
/**
* ...
*
* @attribute static audio
* @attribute getter is_open
*/
bool audio_is_open();
/**
* ...
*
* @attribute class query_result
* @attribute self effect
* @attribyte getter is_empty
*/
bool query_result_empty(query_result result);generates usage for the following in C#:
if (Audio.IsOpen) { ... }
if (myDatabase.queryResult.IsEmpty) { ... };Creates a setter method on the class instance of static module. Requires
either:
classandselfto make an instance setter on the an instance whose class is specified byclass, orstaticto make a static setter on the class specifiedstatic.
Must be set on a function that has exactly one or two parameters, which
depends on if you are using class or static.
If you are writing a static setter, then you will need exactly one
parameter, being the second must the value that is to be set.
If you are writing a class setter, then you will need exactly two
parameters, where:
- the first must be the parameter which will be used as
self, and - the second must the value that is to be set.
You must not specify that this is also a method (unless static is also
supplied) or a constructor or destructor.
For example, the following:
/**
* ...
*
* @attribute static audio
* @attribute setter is_open
*/
void audio_status(bool open);
/**
* ...
*
* @attribute class database
* @attribute self db
* @attribyte setter last_query
*/
void database_set_query_result(database db, query_result result);generates usage for the following in C#:
Audio.IsOpen = false;
myDatabase.LastQuery = myQueryResult;Applicable only to header file HeaderDoc blocks. The value associated to group
means that this particular header file is grouped under the group specified
by this attribute. Related header files may be applicable to just the one, e.g.:
audio.h,sound_effect.h, andmusic.h
could all be grouped under the Audio group.
Use this to add an arbitrary note to any HeaderDoc block.
It is important to place the contents of your note on a new line.
Not placing your content of your note over two lines will cause issues. Refer to the example below:
/**
* @attribute note This is a really cool function
* that goes over two lines woohoo!
*/
int my_func();The above will cause the following parser issue:
Unknown attribute keys are present: `note This is a really cool function`.
To fix this, you change the example to:
/**
* @attribute note
* This is a really cool function
* that goes over two lines woohoo!
*/
int my_func();Unfortunately, multi-line markdown parsing will not work, such as lists, code blocks and multiple paragraphs.
If any of these basic rules are violated, then the parser will throw a fatal error and stop parsing along with its respective parser rule (PR) number for reference to this list.
-
Attributes marked with
self,destructorconstructormust have aclassattribute specified. -
Attributes marked with
method,getterorsettermust be marked with either:(i)
classto make it an instance method, instance getter or instance setter on instance of that class, or(ii)
staticto make it a static method, static getter or static setter on a class or module indicated by static. -
There can never be both
constructoranddestructorattributes marked together in the same HeaderDoc block. -
If you do not supply
static, then you cannot have aconstructorordestructorwith agetterorsetterin the same block. This would imply that there is an instance getter or instance setter along with a constructor or destructor of that instance by the one function. -
You cannot supply
constructorordestructorandmethodunlessstaticis also supplied. This will make a static method on the class or module specified by static but a destructor/constructor on the class indicated byclass. -
A
selfattribute should always have a value that matches the name of a parameter in the function. -
When
selfis specified, then the parameter name it specifies should have the same type as theclassspecified. -
A
gettershould always return something, and must not returnvoidunless it isvoid*. -
When
classis specified with agetter, there should always be one parameter (the parameter forself). -
When
classis specified with asetter, there should always be two parameters:(i) the parameter for
self, and(ii) the value to set.
-
When
staticis specified with agetter, there should always be no parameters. -
When
staticis specified with asetter, there should always be one parameter. That is, the value to set. -
When
suffixis used, the name must be unique to the global namespace. -
When
suffixis used with aclassandmethod, the name must be unique to the class namespace. -
When a
classis used on a type alias, then the type alias must be an alias to a pointer.