Despite writing OpoLua, a modern OPL runtime for iOS and macOS, it’s a long time since I’ve actually written anything in OPL. So what better for a Psion-themed December Adventure than to return to one of the languages that started it all for me?

Remembering how to write OPL using nOPL+

Earlier this year, I published Thoughts, a lightweight Markdown-based note taking app for macOS which I use primarily for journaling and I thought it would be nice to bring a couple of the main features to my writing experience on EPOC32—a kind of ‘Thoughts-lite’ if you will.

Thoughts is a simple app offering the following functionality:

  • global hotkey for starting new notes
  • automatic date and location tagging (stored in Frontmatter)
  • Markdown syntax highlighting
  • tag editor

It generates Markdown files that look something like this:

---
date: '2024-11-26T09:23:55-10:00'
tags: []
location:
  latitude: 2.15791762399229e+1
  longitude: -1.58105437871125e+2
  name: 61-535 Kamehameha Hwy
  locality: "Hale\u02BBiwa"
---

Selecting multiple matching words in Helix can be done using:

miw*vn

While it might disappoint some members of the Psion community1, I’ve no plans to tackle a dedicated Markdown editor—I find the Symbian-published Editor just great for writing—but I would like to automatically create and timestamp new notes, and pre-populate them with some Frontmatter for filling in tags.

Starting simple, my initial focus is on generating an IS0 8601-formatted timestamp as used by Thoughts. Unfortunately OPL doesn’t make this easy as it’s lacking some basics—I first had to write a way to zero-pad numbers:

PROC ZPAD$:(value&,length%)
    REM Left-pad numbers with '0' up to a length, length%, maximum 10.
    LOCAL result$(10)
    result$=NUM$(value&,length%)
    IF LEN(result$) < length%
        result$ = "0" + result$
    ENDIF
    RETURN result$
ENDP

With that in place, I was able to approach a procedure to format a datetime as ISO 8601:

PROC ISO8601$:(datetime&)
    LOCAL year&, month&, day&, hour&, minute&, second&, result$(25)

    REM Extract the relevant components.
    year&=DTYEAR&:(datetime&)
    month&=DTMONTH&:(datetime&)
    day&=DTDAY&:(datetime&)
    hour&=DTHOUR&:(datetime&)
    minute&=DTMINUTE&:(datetime&)
    second&=DTSECOND&:(datetime&)

    REM Construct the ISO8601 string.
    result$=NUM$(year&,4) + "-" + ZPAD$:(month&,2) + "-" + ZPAD$:(day&,2) + "T" + ZPAD$:(hour&,2) + ":" + ZPAD$:(minute&,2) + ":" + ZPAD$:(second&,2)

    RETURN result$
ENDP

During this process, I found myself relying heavily on nOPL+, a minimal OPL IDE that, crucially, includes a built-in OPL reference:

With my newly-crafted procedure, testing it is simply a matter of getting the current date, calling it with OPL’s weird syntax, and printing the output (not forgetting to use GET to wait for the user to press a key lest my program exits before I see what it’s done):

INCLUDE "date.opx"

PROC main:
    LOCAL now&, date$(25)
    now&=DTNOW&:
    date$=ISO8601$:(now&)
    PRINT date$
    GET
ENDP

Success:

2024-12-03T03:23:25

You might notice I’ve made no attempt to tackle time zones in this implementation, but that’s enough for day 2—remembering OPL’s many quirks took quite a bit longer than I’d expected and the seasonal festivities are already underway. 🎉


  1. You know who you are.