-
Notifications
You must be signed in to change notification settings - Fork 0
Pomme syntax
Pomme consists of classes and functions. All code must be declared inside a function.
class TestClass
{
void HelloWorld()
{
Print("Hello World"); // ok
}
native void NativeFunction(int a);
}
void HelloWorld()
{
Print("Hello World"); // ok
}
Print("Hello World"); // this code will be never executed and should be caught by the compiler as an unexpected statement
Variables are defined by type and name.
void TestFunc()
{
// declare
int a;
// assign
a = 5;
// initialize
int b = 9;
}
Functions are a basic feature of Pomme. Function declaration consists of return value type, function name, and list of parameters.
- Function can be declared in the global scope or inside a class declaration
- Function parameters are fixed and typed (cannot be changed during run-time, no variadic parameters)
- Function can be overloaded
void MethodA() // function with no parameters and no return value
{
}
int GiveMeTen() // function with no parameters which returns integer value
{
return 10;
}
float Sum(float a, float b) // function with two float parameters which return float value
{
return a + b;
}
/*
Multi
line
comment
*/
void Test()
{
int a; // single line comment
}
Control structures work very similar to C# or C/C++ languages.
void Method()
{
int a = 4;
int b = 5;
if (a > 0)
{
Print("A is greater than zero!");
}
else
{
Print("A is not greater than zero!");
}
if (a > 0 && b > 0)
{
Print("A and B are greater than zero!");
}
if (a > 0 || b > 0)
{
Print("A or B are greater than zero!");
}
// 'else if' example
if (a > 10)
{
Print("a is bigger then 10");
}
else if (a > 5)
{
Print("a is bigger then 5 but smaller than 10");
}
else
{
Print("a is smaller then 5");
}
}
The for loop consists of three parts: declaration, condition and increment.
void Method()
{
// this code prints
// "i = 0"
// "i = 1"
// "i = 2"
for (int i = 0; i < 3; i++)
{
Print(i);
}
}
void Method()
{
int i = 0;
// this code prints
// "i = 0"
// "i = 1"
// "i = 2"
while (i < 3)
{
Print(i);
i++;
}
}
Constants are like variables but read-only. They are declared by const keyword.
const int MONTHS_COUNT = 12;
void Test()
{
int a = MONTHS_COUNT; // ok
MONTHS_COUNT = 7; // err! you cannot change constant!
}
Operation | Symbol | Prototype |
---|---|---|
Add | + | Class operator+(Class lhs, Class rhs) |
Subtract | - | Class operator-(Class lhs, Class rhs) |
Multiply | * | Class operator*(Class lhs, Class rhs) |
Divide | / | Class operator/(Class lhs, Class rhs) |
Modulo | % | Class operator%(Class lhs, Class rhs) |
Operation | Symbol | Prototype |
---|---|---|
Assign a value to variable | = | Can't be overloaded |
Increment variable by value | += | Class operator+=(Class rhs) |
Decrement variable by value | -= | Class operator-=(Class rhs) |
Multiply variable by value | *= | Class operator*=(Class rhs) |
Divide variable by value | /= | Class operator/=(Class rhs) |
Bitwise-OR by value | |= | Class operator|=(Class rhs) |
Bitwise-AND by value | &= | Class operator&=(Class rhs) |
Left-shift variable by value | <<= | Class operator<<=(Class rhs) |
Right-shift variable by value | >>= | Class operator>>=(Class rhs) |
Increment variable by 1 (Prefix) | ++ | Class operator++() |
Decrement variable by 1 (Prefix) | -- | Class operator--() |
Increment variable by 1 (Postfix) | ++ | Class operator++(int ignore) |
Decrement variable by 1 (Postfix) | -- | Class operator--(int ignore) |
Operation | Symbol | Prototype |
---|---|---|
More than value | > | bool operator>(Class rhs) |
Less than value | < | bool operator<(Class rhs) |
More or equal to the value | >= | bool operator>=(Class rhs) |
Less or equal to the value | <= | bool operator<=(Class rhs) |
Equal | == | bool operator==(Class rhs) |
Not equal | != | bool operator!=(Class rhs) |
Bool | - | bool operatorbool() |
Category | Operator(s) | Prototype |
---|---|---|
Logical | &&, || | - |
Bitwise | &, |, ~ | - |
Shift | <<, >> | - |
Indexing | [, ] | Class operator[](Class rhs) |
Negation | ! | - |
Customizes the Pomme operators for operands of user-defined types.
Overloaded operators are functions with special function name (defined in "Prototype" column in the above tables)
class Test
{
int m_A;
Test copy()
{
Test newTest = new Test();
newTest.m_A = m_A;
return newTest;
}
//prefix increment
Test operator++()
{
m_A++;
return this;
}
//postfix increment
Test operator++(int ignore)
{
Test old = copy();
operator++();
return old;
}
Test operator+(Test lhs, Test rhs)
{
Test c = new Test();
c.m_A = lhs.m_A + rhs.m_A;
return c;
}
Test operator+=(Test rhs)
{
m_A += rhs.m_A;
return this;
}
Test operator+=(int rhs)
{
m_A += rhs;
return this;
}
}
void Method1()
{
Test testa = new Test();
Test testb = new Test();
testb.m_A = 1;
Print(testa.m_A); // prints '0'
Print(testb.m_A); // prints '1'
testa++; //ok
Print(testa.m_A); // prints '1'
++testa; //ok
Print(testa.m_A); // prints '2'
Test testc = testa + testb;
Print(testc.m_A); // prints '3'
testa--; //not ok, should be caught by the compiler (not defined in class Test)
}
void Method2()
{
Test testa = new Test();
Test testb = new Test();
testb.m_A = 2;
Print(testa.m_A); // prints '0'
testa += testb;
Print(testa.m_A); // prints '2'
testa += 2;
Print(testa.m_A); // prints '4'
}
Keyword | Description |
---|---|
private | The method can be called only from the inside of the same class method |
protected | The method can be called only from the inside of class method or methods of its extended classes |
static | The method can be called without object pointer, just by className.methodName(), only static members of the same class can be accessed from inside of a static method |
override | Compiler checks if is method present in the base class and if method signature match |
native | Native call convention of internal function (C++ side) |
Keyword | Description |
---|---|
private | Variable can be accessed only from the inside of class methods. Mutually exclusive with "protected" |
protected | Variable can be accessed only from the inside of class methods or methods of its extended classes. Mutually exclusive with "private" |
static | Variable can be accessed without object pointer, using className.variable |
const | Constant, cannot be modified |
Keyword | Description |
---|---|
modded | Inheritance-like behavior for modding |
Keyword | Description |
---|---|
modded | Inheritance-like behavior for modding |
Keyword | Description |
---|---|
new | Create a new object instance |
class | Class declaration |
extends | Class inheritance |
return | Terminates function & returns value (if specified) |
null | null value |
this | Address of the object, on which the member function is being called |
super | Refers to the base class for the requested variable/function |
Type name | Range | Default Value |
---|---|---|
int64_t | from -9'223'372'036'854'775'808 to +9'223'372'036'854'775'807 | 0 |
uint64_t | from 0 to +18'446'744'073'709'551'615 | 0 |
int32_t | from -2'147'483'648 to +2'147'483'647 | 0 |
uint32_t | from 0 to +4'294'967'295 | 0 |
int8_t | from –128 to +127 | 0 |
uint8_t | from 0 to +255 | 0 |
float | from ±1.401298E−45 to ±3.402823E+38 | 0.0 |
bool | true or false | false |
string | - | "" |
Class | - | null |
void | - | - |
- Classes in Pomme are references and are passed by reference
- All member functions and variables are public by default. Use the 'private' keyword to make them private
class MyClass
{
int Say()
{
return 2;
}
}
void MethodA()
{
MyClass o; // o == null
o = new MyClass; // creates a new instance of MyClass class
o.Say(); // calls Say() function on instance 'o'
}
void UnsafeMethod(MyClass o) // Method not checking for existence of the input argument
{
o.Say();
}
void SafeMethod(MyClass o)
{
if (o)
{
o.Say();
}
}
void MethodD()
{
MyClass o = new MyClass;
SafeMethod(o); // ok
UnsafeMethod(o); // ok
SafeMethod(null); // ok
UnsafeMethod(null); // Crash! Object 'o' is not initialised and UnsafeMethod accessed it!
}
Example of this & super:
class A
{
void Hello()
{
Print("A.Hello()");
}
};
class B extends A
{
override void Hello()
{
Print("B.Hello()");
}
void Test()
{
Hello(); // prints "B.Hello()"
this.Hello(); // 'this' refers to this instance of object, so same as line above, prints "B.Hello()"
super.Hello(); // refers to base(super) class members, prints "A.Hello()"
}
}
Enumerators are set of named constant identifiers.
- Enums have int64_t type
- Enum item value can be assigned in definition, otherwise, it is computed automatically (previous item value plus one)
- Enum can inherit from another Enum (item value continues from last parent item value)
- Enum could be modded
- Enum name used as type behaves like ordinary int64_t (no enum value checking on assign)
enum MyEnumBase
{
Alfa = 5, // has value 5
Beta, // has value 6
Gamma // has value 7
};
modded enum MyEnumBase
{
Mam, // has value 8
Pam, // has value 9
};
enum MyEnum extends MyEnumBase
{
Blue, // has value 10
Yellow, // has value 11
Green = 20, // has value 20
Orange // has value 21
};
void Test()
{
int a = MyEnum.Beta;
MyEnum b = MyEnum.Green;
int c = b;
MyEnumBase d = MyEnumBase.Pam;
Print(a); // prints '6'
Print(b); // prints '20'
Print(c); // prints '20'
Print(d); // prints '9'
Print(MyEnum.Alfa); // prints '5'
}
Pomme has a template feature similar to C++ Templates, which allows classes to operate with generic types.
- Generic type declaration is placed inside <, > (e.g. "class TemplateClass" )operators after template class name identifier
- Pomme supports any number of generic types per template class
class Item<Class T>
{
private T m_data;
void Item(T data)
{
m_data = data;
}
void SetData(T data)
{
m_data = data;
}
T GetData()
{
return m_data;
}
void PrintData()
{
Print(m_data);
}
};
void Method()
{
Item<string> string_item = new Item<string>("Hello!"); // template class Item declared with type "string". In Item<string> class, all "T"s are substituted with 'string'
Item<int> int_item = new Item<int>(72); // template class Item declared with type "int". In Item<int> class, all "T"s are substituted with 'int'
string_item.PrintData(); // prints "m_data = 'Hello!'"
int_item.PrintData(); // prints "m_data = 72"
}
- All member functions and variables are public by default. You can use the 'private' or 'protected' keyword to control access
- Class member functions are virtual and can be overridden by child class
- Use override keyword for overriding base class methods
- Class can inherit from one parent class using the keyword 'extends'
- Class variables are cleared to default values upon creation
class AnimalClass
{
void MakeSound()
{
}
};
class Dog extends AnimalClass
{
override void MakeSound()
{
Print("Wof! Wof!");
}
void Aport()
{
// do something
}
};
class Cat extends AnimalClass
{
override void MakeSound()
{
Print("Meow!");
}
void Scratch()
{
// do something
}
};
void LetAnimalMakeSound(AnimalClass pet)
{
if (pet) // check if pet is not null
{
pet.MakeSound();
}
}
void Method()
{
Cat nyan = new Cat;
Dog pluto = new Dog;
nyan.MakeSound(); // prints "Meow!"
pluto.MakeSound(); // prints "Wof! Wof!"
LetAnimalMakeSound(nyan); // prints "Meow!"
LetAnimalMakeSound(pluto); // prints "Wof! Wof!"
}
Constructor and destructor are special member functions
- Every class can have one constructor and one destructor
- Constructor is function called when object is created(by 'new') and has same name as class ( e.g. 'void ClassName()' )
- Destructor is called when the object is going to be destroyed and has the same name as the class with tilde character at the beginning ( e.g. 'void ~ClassName()' )
- Constructor can have initialization parameters, destructor cannot have any parameters
- Both constructor and destructor do not return any value (returns void)
- Constructor and destructor are called even when the object is created or destroyed from C++
class MyClassA
{
void MyClassA() // constructor declaration of class MyClassA
{
Print("Instance of MyClassA is created!");
}
void ~MyClassA() // destructor declaration of class MyClassA
{
Print("Instance of MyClassA is destroyed!");
}
};
class MyClassB
{
string m_name;
void MyClassB(string name) // constructor declaration of class MyClassB
{
m_name = name;
Print("Instance of MyClassB is created!");
}
void ~MyClassB() // destructor declaration of class MyClassB
{
Print("Instance of MyClassB is destroyed!");
}
};
void Method()
{
MyClassA a = new MyClassA; // prints "Instance of MyClassA is created!"
MyClassB b = new MyClassB("Michal"); // prints "Instance of MyClassB is created!"
} // here at the end of the scope, Pomme automatically destroys 'a' and 'b' object and it prints "Instance of MyClassA is destroyed!" and "Instance of MyClassB is destroyed!"
Pomme has the support of automatic reference counting for classes. They are internally ref-counted and automatically deleted when not needed.
Modded class is used to inject inherited class into class hierarchy without modifying other scripts, which is required for proper modding:
- Modded class behaves like class inherited from original class (you can use super to access original class)
- When modded class is declared, it will be instanced instead of original class everywhere in the script
- When several modded classes are modding the same vanilla class, the next modded class will instead inherit the latest modded class, which enables mod compatibility
// original
class ModMe
{
void Say()
{
Print("Hello original");
}
};
// First mod
modded class ModMe // this class automatically inherits from original class ModMe
{
override void Say()
{
Print("Hello modded One");
super.Say();
}
};
// Second mod
modded class ModMe // this class automatically inherits from the first mod's ModMe
{
override void Say()
{
Print("Hello modded Two");
super.Say();
}
};
void Test()
{
ModMe a = new ModMe(); // modded class ModMe is instanced
a.Say(); // prints 'Hello modded Two' , 'Hello modded One' and 'Hello original'
}
- Constants can be overridden on the compilation by the last loaded mod (be mindful of the mod load order)
- Allows multiple mods to change different constants in a single class
class BaseTest
{
const int CONST_BASE = 4;
}
class TestConst: BaseTest
{
const int CONST_TEST = 5;
}
modded class TestConst
{
const int CONST_BASE = 1;
const int CONST_TEST = 2;
const int CONST_MOD = 3;
}
void TestPrint()
{
Print(TestConst.CONST_BASE); // 1
Print(TestConst.CONST_TEST); // 2
Print(TestConst.CONST_MOD); // 3
}
- Even though modded class behaves similar to an inherited one, it can still access private members of the vanilla class
// original
class VanillaClass
{
private bool imPrivate = 0;
private void DoSomething()
{
Print("Vanilla method");
}
}
// accesss
modded class VanillaClass
{
void AccessPvt()
{
Print(imPrivate);
DoSomething();
}
}
// override
modded class VanillaClass
{
override void DoSomething()
{
super.DoSomething();
Print("Modded method");
}
}