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 install
Then 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.h
This 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/coresdk
To 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,CPP
If 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.h
would 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 destructor
s.
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:
class
andself
to make an instance getter on the an instance whose class is specified byclass
, orstatic
to 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
class
orstatic
, 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:
class
andself
to make an instance setter on the an instance whose class is specified byclass
, orstatic
to 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 group
ed 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 group
ed 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
,destructor
constructor
must have aclass
attribute specified. -
Attributes marked with
method
,getter
orsetter
must be marked with either:(i)
class
to make it an instance method, instance getter or instance setter on instance of that class, or(ii)
static
to make it a static method, static getter or static setter on a class or module indicated by static. -
There can never be both
constructor
anddestructor
attributes marked together in the same HeaderDoc block. -
If you do not supply
static
, then you cannot have aconstructor
ordestructor
with agetter
orsetter
in 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
constructor
ordestructor
andmethod
unlessstatic
is 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
self
attribute should always have a value that matches the name of a parameter in the function. -
When
self
is specified, then the parameter name it specifies should have the same type as theclass
specified. -
A
getter
should always return something, and must not returnvoid
unless it isvoid*
. -
When
class
is specified with agetter
, there should always be one parameter (the parameter forself
). -
When
class
is specified with asetter
, there should always be two parameters:(i) the parameter for
self
, and(ii) the value to set.
-
When
static
is specified with agetter
, there should always be no parameters. -
When
static
is specified with asetter
, there should always be one parameter. That is, the value to set. -
When
suffix
is used, the name must be unique to the global namespace. -
When
suffix
is used with aclass
andmethod
, the name must be unique to the class namespace. -
When a
class
is used on a type alias, then the type alias must be an alias to a pointer.