Skip to content

Language Features

Nenkai edited this page Jul 14, 2024 · 5 revisions

Note

Assume we are starting from Javascript/Python, which is the closest to adhoc.

Nulls: null = nil

Every dereferenced value is a nil, instead of a null.

Modules, Classes, Attributes and Statics

Modules & Statics

Modules have a completely different meaning in Adhoc. Think of them as C++ namespaces, they can be navigated to.

Classes also exist, but are only used to define types that inherit from the base Adhoc objects.

Either of them supports functions and methods.

module MyModule
{
   function myFunction() { ... }
   method myMethod() { ... } // Note the keyword 'method'
}

For statics, the keyword static is used. They are accessed in a C++ namespace manner, or through [] indexing:

module StaticModule
{
  static PI = 3.14;
}

// Access static field
var pi = StaticModule::PI;

// Access a static field by name
var pi = StaticModule["PI"];

// Navigating through engine modules to call a function
pdistd::MPjson::Encode(/* ... */);

If you need to use something from the global modules/top-level and ignore current module scope, you can use the :: operator prefix (similar to C++'s scope resolution operator):

static x = 1;

function test_func()
{
    var x = 2;
    print ::x; // would print 1
}

Attributes

Properties are called attributes in adhoc. They are defined with the attribute keyword, just like how you would declare a var or static.

  • Without value: attribute myAttribute - Will be defaulted to nil
  • With value: attribute myAttribute = []
class Dog
{
   attribute name;
}

Attributes can also be defined in modules.

Class Constructors

Class constructors are defined with the __init__ method identifier. Local attributes are accessed using the self keyword.

class Dog
{
   attribute name;
   
   method __init__(name)
   {
      self.name = name;
   }
}

var obj = MyObject("FooBar");

To access an attribute:

var name = obj.name;

Class Inheritance

The : token instead of extends in Javascript or Java.

class BetterObject
{
  ...
}

class EvenBetterObject : BetterObject
{
 ...
}

Module Constructors

Module constructors are completely new in Adhoc. These are mostly used for initializing UI widgets with user data or with UI method events. They allow defining a constructor for any object that will run once the UI system sees a new object registered (i.e appendChild onto a composite)

var myObject = someWidget.doCopy();
module (myObject)
{
   attribute myAttr;
   
   method onCancel()
   {
      // ...
   }
}

myObject.myAttr; // ❌ myAttr is not yet defined!

Strings & Interpolation

There is only one type of string declaration, quotes.

var str = "Hello world!";
var combinedStrings = "hello"
                      "world!";

var interpolated = "hello, %{name}!"; // Notice %, instead of $ in javascript.

Maps

Maps are Key/Value collections, similar to javascript's map or C#'s dictionaries. Adhoc supports them natively.

var myMap = Map();
var myMap2 = [:]; // Shortcut to creation
var myMapWithElements = ["MyKey":"MyValue", "MyKey2": "MyValue2"]; // Creation with 2 pairs

myMap["hello"] = "world!";
myMap.getMapCount(); // 1
myMapWithElements.getMapCount(); // 2

Foreach

Adhoc supports foreach clauses out of the box.

var arr = ["one", "two", "three"];
var combined;
foreach (var i in arr)
  combined += i + " ";

// combined = "one two three "

Also works with maps.

var map = ["Name": "Bob", Age": 18];
foreach (var [key, value] in map) // Pair deconstruction
{
    // ...
}

Macros/Preprocessing

Supported as C supports it

#define ADD(x,y) (x+y)
var val = ADD(5, 6)

Native Number Types

UInt, Long, ULong, Double respectively are all natively built-in types starting from GT PSP Adhoc) ontop of Bool, Int, Float.

Imports

Imports are mostly used python-like.

import main::*; // Imports/copies all modules from the specified module into the current one.
import myModule::myFunction; // Imports/copies a static/function into the current one.
import myModule::myStatic as obj; // Imports a static into an object.

Includes

C-type includes are supported.

#include "projects/gt6/my_project/myinclude.ad"

Function Expression Variable Capture

Variables outside function expressions are captured.

var myVariable = 0;
var myFunc = function (){
  return myVariable + 100;
}

Code allowed everywhere

Top level, in module or class bodies, code is allowed everywhere.

module MyModule
{
    attribute myAttribute = [];
    myAttribute.push("hello world");
}

Module extensions are also allowed within function themselves.

function myFunction()
{
    module main
    {
        // Anything put here will be part of the "main" module.
        // Declaring a static variable will make it belong to the "main" module.
    }
}

Undefs

Undefs let you undefine functions or static symbols.

function myFunction()
{
   ...
}

undef myFunction; // "myFunction" is undefined, now nil if called.

Operator Overloading

Adhoc supports fully overloading operators.

class OperatorOverloadClassTest
{
    attribute value = "";
    
    method __init__(val)
    {
        self.value = val;
    }

    method __add__(val) // Needs to be the internal label for a designated operator, in this case, __add__ = +
    {
       return OperatorOverloadClassTest(value + val);
    }
}
var obj = MyOperatorOverloadingClass();
obj += "hello world!";
// obj.value is now "hello world!"

Static Scopes

Static fields can be accessed from any module or class depth.

module RootModule
{
  static sStaticField;

  module ChildModule
  {
    function setParentField()
    {
      sStaticfield = "hello world!";
    }
  }
}

Async/Await

Note

GT6 and above.

async function myAsyncFunction() // Must mark as async
{
  var result = await getObject();
}

Finalizer Statements

Finalizer statements allows running code once the current module is finalized/cleaned up.

function func(context)
{
  CursorUtil::setCursor(context, "wait"); // Set cursor to waiting mode
  finally
  {
      // This will be executed once the module is finalized.
      // It will not execute immediately.
      CursorUtil::setCursor(context, "cursor_chrome");
  } 
}

Yield Statements

Mostly unknown, may be similar to unity's yield statement where the runtime waits for the next frame.

function func(context)
{
  yield;
}

Requires

Requires allows importing all contents of a script onto the current one.

require "MyScript.adc"

Symbols

Similar to Javascript's Symbols, they are defined with single quotes.

var symbol = 'my cool symbol';

Variadic Function calls

Calling functions with their arguments being represented by an array can be called using the call() keyword:

function sum(arg1, arg2)
{
   return arg1 + arg2;
}

// call(func, argument array) - NOTE: function or method must be script defined.
call(sum, [9, 10]); // 21

Function Rest Parameters

Identical to javascript except the syntax is swapped around.

function myFunction(args...) // Not ...args!
{
    ...
}

Identifier Literals

Identifier literals allow defining identifiers with normally illegal characters incase you have to.

var `my totally valid identifier` = "hello world";

module `my module`
{

}

Delegates

Note

GT Sport and above.

function myFunc()
{
    return "hello world";
}

delegate myDelegate; // It is not possible to directly assign a function to a delegate
myDelegate = myFunc; // This will not override myDelegate with a function, rather assign a function to the delegate
return myDelegate(); // "hello world"

Pass by reference

function getXY(x_ref, y_ref) // Common practice is to add the `_ref` suffix OR (less common) `ref_` prefix.
{
   *x_ref = 1;
   *y_ref = 1;
}

var x, y;
getXY(&x, &y);

// x = 1, y = 1

Note that you can also pass attributes/statics i.e &myobject.myattr or even array elements i.e &MyArray[0].

Object Selectors

Warning

This one is still sort of unclear.

This feature seems to allow calling grabbing method/attribute references using the .* operator.

scripts/gt5/UsedCarList.ad

method __get_elem__(i)
{
    return *(self.*attr_map[i]);
}

method __set_elem__(i, val)
{
    self.*attr_map[i] = val;
}

projects/gt5/arcade/CarRoot.ad

var delay_load_complete = method(context)
{
    self.Info::FadeEffect.start();
}

self.Info::tuner_logo.on_delay_load_complete = *self.*delay_load_complete;

Not supported

  • Anything modern ECMAScript-ish features (arguably not needed).
  • let, const keywords are not implemented.
  • for..in and for..of are replaced by the much more convenient foreach.
  • ===, !== operators
  • Dynamic objects var obj = {}

And more

That have yet to be figured.