Skip to content

Pomme syntax

NiiRoZz edited this page May 28, 2021 · 20 revisions

Syntax

Program structure

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

Variables are defined by type and name.

void TestFunc()
{
	// declare
	int a;

	// assign
	a = 5;

	// initialize
	int b = 9;
}

Functions

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;
}

Comments

/*
Multi
line
comment
*/

void Test()
{
	int a; // single line comment
}

Control Structures

Control structures work very similar to C# or C/C++ languages.

Conditional structures

If statement

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");
	}
}

Iteration structures

For

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);
	}
}

While

void Method()
{
	int i = 0;

	// this code prints
	// "i = 0"
	// "i = 1"
	// "i = 2"

	while (i < 3)
	{
		Print(i);
		i++;
	}
}

Constants

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!
}

Operators

Arithmetic Operators

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)

Assignments

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)

Relational (conditional)

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()

Others

Category Operator(s) Prototype
Logical &&, || -
Bitwise &, |, ~ -
Shift <<, >> -
Indexing [, ] Class operator[](Class rhs)
Negation ! -

Operator overloading

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'
}

Keywords

Function/method modifiers

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)

Variable modifiers

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

Class modifiers

Keyword Description
modded Inheritance-like behavior for modding

Enum modifiers

Keyword Description
modded Inheritance-like behavior for modding

Other keywords

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

Types

Primitive Types

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

  • 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()"
	}
}

Enums

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'
}

Templates

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"
}

Object-oriented programming specifics

  • 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

Inheritance

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 & Destructor

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!"

Automatic Reference Counting

Pomme has the support of automatic reference counting for classes. They are internally ref-counted and automatically deleted when not needed.

Modding

Modded class

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'
}

Modded constants

  • 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
}

Modded private members

  • 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");
	}
}