User-defined DSL: routes { ... }

A small grammar extension declared entirely in PatLang source (syntax RouterDSL { trigger; tokens { ... }; rule { ... } }), matching HTTP-verb/URL-path tokens with a general-purpose regex engine also written in PatLang (lib/regex_dsl.patlang). A source-to-source preprocessing pass (lib/syntax_dsl.patlang) expands every routes { ... } block into plain register_route(...) calls before the ordinary compiler ever sees them โ€” run here via the same in-browser WASM playground engine as the demos above (no rustc at all), or compiled natively below.

PatLang source
# Portfolio demo: a user-defined DSL for declaring HTTP routes, entirely
# authored in PatLang source (no change to the core lexer/parser). The
# `syntax RouterDSL { ... }` block below declares a small grammar extension
# -- its own token patterns (matched by a general-purpose regex engine, also
# written in PatLang: self_hosting/lib/regex_dsl.patlang) and a rule
# describing how to expand each triggered line. Every `routes { ... }` block
# that follows is then expanded, line by line, into ordinary `register_route`
# calls by self_hosting/lib/syntax_dsl.patlang -- a source-to-source
# preprocessing pass run once at build time (see build_router_dsl_demo in
# self_hosting/tools/build_portfolio.patlang), BEFORE this file is handed to
# the ordinary tokenizer/parser/lowerer. By the time the compiler proper
# looks at this program, the DSL blocks below no longer exist -- they've
# already become the register_route(...) calls printed at the bottom.

make a function called register_route takes verb, path, controller, action returns done
  print(verb + " " + path + " -> " + controller + "." + action)
  return true
end

syntax RouterDSL {
    trigger: Keyword("routes");

    tokens {
        HttpVerb(verb) = regex("\b(GET|POST|PUT|DELETE)\b", verb);
        UrlPath(path)   = regex("/[a-zA-Z0-9_/:-]*", path);
        Arrow           = "->";
    }

    rule RouteLine {
        let verb = expect HttpVerb;
        let path = expect UrlPath;
        expect Arrow;
        let controller = expect Identifier;
        expect Symbol(".");
        let action = expect Identifier;

        return register_route(verb, path, controller, action);
    }
}

routes {
    GET    /users            -> UserController.index
    POST   /users/:id/edit   -> UserController.update
    DELETE /sessions         -> AuthController.logout
}

print("3 routes registered.")

(not run yet)

Native run on the build machine:

GET /users -> UserController.index
POST /users/:id/edit -> UserController.update
DELETE /sessions -> AuthController.logout
3 routes registered.