openpeeps / tim

This is Tim ⚡️ A high-performance template engine & markup language written in Nim
https://openpeeps.github.io/tim/
GNU Lesser General Public License v3.0
43 stars 1 forks source link

Standard library #5

Open georgelemon opened 1 month ago

georgelemon commented 1 month ago

Tim should borrow some callable utilities from Nim's standard library

Tim's standard library in modules: std/system, std/strings, std/arrays, std/objects, std/math and std/os (read-only functions)

var x = "Hello"
echo len($x)     // 5
echo $x.len      // 5 (UCS style)

echo $x.encode.len   // 8
// equivalent of 
echo len(encode($x)) // 8

@import "std/strings"
echo $x.contains("o")
Uzo2005 commented 3 weeks ago

Hi I have some questions about this:

  1. Is this feature a way to expose some functions from the host language into tim templates?
  2. Or is it a way to make tim become its own programming language by bootstrapping off of Nim's std lib?

To clarify further: if question1's answer is yes, then I think the better approach might be to copy what nimja is doing. Basically nimja templates can invoke any function in the same scope as the template render call(tmplf in the code sample below). Sample code below

$ cat index.nimja
<div>{{printNum1()}}<div/>
#FILE: server.nim
import nimja
proc printNum1(): int = 1

## Route Handlers
proc getLandingPage(): string =
        result = tmplf(getScriptDir() & "/index.nimja")

echo getLandingPage() #<div>1<div/>

This approach can easily scale to all the languages Tim wishes to support although am not sure how the syntax or implementation details will work.

if question2's answer is yes, then if I understand correctly Tim users are provided with some extra built-in functions that they can use within their templates just like liquidjs templating engine's filters. Hence these functions will never have sideffects and will only help to further transform their inputs into strings. In my opinion, the benefit of this approach is that it will be easier for people to find out the expected output of a Tim template because the calleable utilities are finite and can be found by looking in Nim's std lib.

I understand that the answer for both of these questions can be yes. In this case, tim will allow users to call functions/procedures in the same scope as the newEngine invocation, while simultaenously offering builtin helper functions to users.

Thanks for reading and I look forward to getting a better understanding of this proposal

georgelemon commented 3 weeks ago

Well, yes, the main idea around @import is to be able to expose functions from the host language into Tim templates. This is going to be the expected behavior when using the S2S compilation via cli app.

So, @import "std/strings" will be translated into import std/strutils, when translating Tim code to Nim via compilers/nimc.nim. Also, there will be more S2S compilers, like py.nim, js.nim and more.

The current std.nim is pretty experimental, and yes, will make Tim more than just a template engine. This feature is already available in Tim when used as a Nimble package or Node/Bun environment as native .node addon (I need to prepare Tim for publishing the npm package).

In addition to S2S compilation feature, the CLI will also provide the option to use Tim as a standalone microservice in background. So, there will be a run command which basically wraps the current html.nim engine + std.nim + caching + a communication channel powered by ZeroMQ.

georgelemon commented 3 weeks ago

Currently, exposing a function is possible by doing this

import pkg/tim

# initialize tim engine
var timEngine* = newTim("templates", "storage", currentSourcePath())

# initialize the local module
# note that `proc hello` is converted into `fn hello` in Tim.
# Also, this is pretty low-level and requires working with Tim's AST structure.
tim.initModule:
  block:
    proc hello(x: string): string =
      # args[0].value is an ast.Node type of ntLitString
      result = ast.newNode(ntLitString)
      result.sVal = "Hello " & args[0].value.sVal
# ... precompilation and app setup
@import "*" // importing the local module. yes maybe `*` is not very specific
h1: hello("World!")

Obviously this works for Tim when used as a Nimble package.

For the CLI app

Uzo2005 commented 3 weeks ago

Thanks for clarifying, I love the direction you are taking it. I dont fully understand the implications of this

Tim microservice will allow a similar approach via plugins. Basically, we'll be able to expose extra functionality to Tim by compiling Nim code using --app:lib

but I will just observe and learn.