v0.1A scripting language for the web

Examples

Real programs that show off what May can do. Each one is a complete, runnable script.

Hello world

The smallest possible May program.

import "io";

print("Hello world!");
colprint("Hello world in red!", "red");

Pipe everything

The pipe operator turns nested calls into a left-to-right pipeline.

import "io";
import "string";

"hello world" | upper | print;

Find a substring

A naive substring search written by hand.

import "io";
import "string";

int find(string str, string pattern) {
    int count = 0;
    int limit = (str | length) - (pattern | length);
    int plen = pattern | length;
    while (count <= limit) {
        int subcount = 0;
        int isfound = 1;
        while (subcount < plen) {
            if (char(str, count + subcount) != char(pattern, subcount)) {
                isfound = 0;
            }
            subcount++;
        }
        if (isfound) { return count; }
        count++;
    }
    return -1;
}

string message = "Dusty May is the greatest basketball coach of all time.";
find(message, "coach") | print;

Calling a public API

Ask the user a question, then call an HTTP endpoint and print the result.

import "io";
import "api";
import "string";

string confirm = input("Want a fact about dogs? [y/n] ");
if ((confirm | upper) == "Y") {
    object res = curl("https://dogapi.dog/api/v2/facts?limit=1", {});
    res->data[0]->attributes->body | print;
} else {
    print("Maybe next time!");
}

HTMX counter

Server-rendered, no JavaScript framework, just a button that pings an endpoint and swaps in new HTML.

import "http";
import "io";
import "html";

int count = 0;

string counter() {
    return div({
        children: [
            h1({ children: [toString(count)],
                 attributes: { class: "text-4xl" } }),
            button({
                children: ["Increment"],
                attributes: {
                    "hx-post": "/increment",
                    "hx-target": "#counter",
                    class: "bg-blue-500 text-white px-4 py-2 rounded"
                }
            })
        ],
        attributes: { id: "counter" }
    });
}

string home() {
    return html({
        children: [
            head({ children: [
                script({ attributes: { src: "https://cdn.tailwindcss.com" } }),
                script({ attributes: { src: "https://unpkg.com/htmx.org@2.0.4" } })
            ] }),
            body({ children: [counter()] })
        ]
    });
}

int server = create_server(8080);
while (1) {
    int client = accept_client(server);
    object req = read_request(client);
    if (req->path == "/increment") {
        count = count + 1;
        send_html(client, counter());
    } else {
        send_html(client, router(req, { "/": home() }));
    }
}

AI chat

Ask a question, hand it to an OpenAI-compatible model, print the answer.

import "io";
import "api";
import "dotenv";

string prompt = input("What would you like to ask AI: ");

curl(envvar("AI_URL"), {
    method: "POST",
    headers: { Authorization: "Bearer " + envvar("AI_API_KEY") },
    body: {
        model: envvar("AI_MODEL"),
        messages: [
            { role: "system", content: "You are a super friendly AI." },
            { role: "user", content: prompt }
        ]
    }
}) | print;
All of these examples live in the examples/ directory of the repository. Clone, edit, and run them to start playing.