Jancy Scripting

Jancy Scripting

Jancy is a new language heavily influenced and inspired by both Java and C/C++ (hence, the name). Jancy takes the best from the two worlds and blends it together, adding its own share of innovative features. It's a next-generation scripting language perfectly suited for the IO and UI domains.

ABI compatibility with C

Jancy has high binary- and source-level compatibility with C. This brings two major benefits.

Firstly, it simplifies and at the same time increases effectiveness of the application-to-script interaction. There is no need to pack data into variant-like containers and push them on the VM stack before calling a user script function. Instead, you call Jancy functions directly, just like you call any other function in your program — Jancy understands all major calling conventions.

Secondly, it allows copy-pasting C definitions of protocol headers (and even the whole algorithms!) from publicly available C sources.

C is the de-facto standard language for system programming; it’s possible to find the C language implementation of virtually any protocol or algorithm in existence. Need to use it from Jancy? Simply copy-paste it, and Jancy will most likely have no problems compiling it. Even when not (of course, there are differences in syntax between C and Jancy), the required changes should be minimal.

Pointers

Jancy provides a feature that no other scripting language has — safe pointer arithmetic. It gives you an opportunity to safely “walk” across data buffers, analyzing (or generating) a packet:

io.IpHdr const* ipHdr = (io.IpHdr const*)buffer;
switch (ipHdr.m_protocol)
{
case io.IpProtocol.Icmp:
    processIcmpHdr(ipHdr + 1);
    break;

case io.IpProtocol.Tcp:
    processTcpHdr(ipHdr + 1);
    break;

case io.IpProtocol.Udp:
    processUdpHdr(ipHdr + 1);
    break;
}

Pointers and pointer arithmetic are simply the best when it comes to the binary data processing. This is both the cleanest and the most efficient way of handling binaries.

Another pointer-related feature of Jancy that comes handy in IO programming is the support for partial application and scheduling.

Partial application means that you can capture "context" arguments for your callback (e.g., event handler, completion routine, etc.)

Scheduling means that you can assign a user-defined “scheduler” which will ensure execution of your callback in the correct environment (in a specific worker thread, under a lock, as a window message handler, and so on).

EventContext* context = new EventContext;
context.m_socket = socket;
context.m_requestId = requestId;

// capture context and schedule it to be run in worker thread

socket.m_onSocketEvent += onSocketEvent~(context) @ m_workerThreadScheduler;

// ...

void m_onSocketEvent(
    EventContext* context,
    SocketEventParams const* params
    )
{
    // we are in our worker thread; handle socket event
}

Properties

Property support is pretty common for modern programming languages. But we are sure no other language implements properties as thorough as Jancy does.

Jancy provides all colors and flavor of properties you could imagine:

  • Indexed properties behave like arrays;
  • Bindable properties notify subscribers when changed;
  • Bindable data auto-generate properties for tracking data changes;
  • Autoget properties bypass the getter and access the “value” field directly;
  • Property pointers allow for runtime dynamism when working with properties.

Properties are generally useful, as they make code much cleaner and more natural-looking. But they come especially handy in UI programming.

opaque class ComboBox
{
    // ...

    bool autoget property m_isEditable;

    property m_editText
    {
        char const* autoget m_value;
        set(char const* value);
        alias bindable event m_onPropChanged() = m_onChanged;
    }

    char const* autoget property m_toolTipText;
    uint_t autoget property m_backgroundColor;

    char const* indexed property m_itemText(size_t index);

    property m_currentText
    {
        char const* get();
        alias bindable event m_onPropChanged() = m_onChanged;
    }

    event m_onChanged();
    event m_onEnter();

    // ...
}

Reactive Programming

Jancy is one of the few imperative languages with the support of reactive programming. When explaining what reactive programming is, the best example would be Microsoft Excel.

Everybody used Excel. Everybody knows when you write a “formula” in cell A and refer to other cells B and C, the dependencies are being built automatically. You change B or C, and A gets updated. You do not need to write event handlers that would be invoked when B or C changes, and in these handlers update all the dependent cells. Sounds ridiculous, right? Who would do that?

Well, that’s what UI programmers do. UI widgets provide events that fire when certain properties change, and if you need to track these changes and do something in response – you write an event handler, subscribe for the event, and update dependent controls/value from within that handler.

Jancy brings that Excel-like automatic execution of “formulas” when values referred from that formula change. How does Jancy know when to use Excel-like execution and when to use the traditional imperative approach? Reactors.

Reactors are dedicated sections of reactive code. Expressions within reactors behave like formulas in Excel and get automatically re-evaluated when bindable properties, referred from the given expression change. All the dependency building/subscribing/unsubscribing is happening automatically behind the scene.

reactor TcpConnectionSession.m_uiReactor
{
    m_title = $"TCP $(m_addressCombo.m_editText)";
    m_isTransmitEnabled = m_state == State.Connected;
    m_actionTable[ActionId.Disconnect].m_isEnabled = m_state != State.Closed;
    m_adapterProp.m_isEnabled = m_useLocalAddressProp.m_value;
    m_localPortProp.m_isEnabled = m_useLocalAddressProp.m_value;
}

construct()
{
    // ...

    m_uiReactor.start();
}

Of course, sometimes the reactive approach doesn’t quite cut it, so you always have the traditional event handler approach at your disposal. Reactors help here, too — Jancy provides a natural syntax of declaring in-reactor event handlers which will subscribe/unsubscribe automatically when reactor starts/stops.

int bindable m_state;
ComboBox* m_comboBox;

reactor m_reactor
{
    onevent bindingof(m_state)()
    {
        // handle state change
    }

    onevent m_comboBox.m_onEnter()
    {
        // handle enter hit in combobox
    }
}

More

Above we tried to outline the Jancy features that are especially important for IO Ninja — all of the aforementioned features are heavily used there and you are more than welcome to explore the script sources and see for yourself how it makes code so much more natural and elegant.

But of course, that doesn’t wrap up the list of Jancy innovations. If you want to learn more, please visit the dedicated Jancy website and the Jancy Github page.