Skip to content

Commit c76a6e6

Browse files
committed
s2s transpilers - update
Signed-off-by: George Lemon <georgelemon@protonmail.com>
1 parent 472b276 commit c76a6e6

12 files changed

Lines changed: 2092 additions & 605 deletions

File tree

src/tim/engine/transpilers/jsgen.nim

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
# A super fast template engine for cool kids
2+
#
3+
# (c) 2025 George Lemon | LGPL-v3 License
4+
# Made by Humans from OpenPeeps
5+
# https://github.com/openpeeps/tim | https://openpeeps.dev/packages/tim
6+
17
include ./private
28

39
#
Lines changed: 151 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
# A super fast template engine for cool kids
2+
#
3+
# (c) 2025 George Lemon | LGPL-v3 License
4+
# Made by Humans from OpenPeeps
5+
# https://github.com/openpeeps/tim | https://openpeeps.dev/packages/tim
6+
17
include ./private
28

39
#
@@ -9,16 +15,36 @@ proc genStmt(node: Node, indent: int = 0) {.codegen.}
915
# Lua Transpiler
1016
#
1117

12-
proc repeatStr(s: string, n: int): string =
13-
for i in 0..<n:
14-
result.add(s)
18+
proc luaOp(op: string): string =
19+
case op
20+
of "&": ".."
21+
of "==": "=="
22+
of "!=": "~="
23+
of "is": "=="
24+
of "isnot": "~="
25+
of "and": "and"
26+
of "or": "or"
27+
of "not": "not"
28+
of "mod": "%"
29+
else: op
30+
31+
proc luaEscapeStr(s: string): string =
32+
result = newStringOfCap(s.len)
33+
for c in s:
34+
case c
35+
of '\n': result.add("\\n")
36+
of '\r': result.add("\\r")
37+
of '\t': result.add("\\t")
38+
of '\\': result.add("\\\\")
39+
of '"': result.add("\\\"")
40+
else: result.add(c)
1541

1642
proc getImplValue(node: Node, unquoted = true): string =
1743
case node.kind
1844
of nkInt: $node.intVal
1945
of nkFloat: $node.floatVal
2046
of nkString:
21-
if unquoted: "\"" & node.stringVal & "\""
47+
if unquoted: "\"" & luaEscapeStr(node.stringVal) & "\""
2248
else: node.stringVal
2349
of nkBool:
2450
if node.boolVal: "true"
@@ -37,42 +63,39 @@ proc getImplValue(node: Node, unquoted = true): string =
3763
else: ""
3864

3965
proc writeVar(node: Node, indent: int = 0): string {.codegen.} =
40-
let ind = repeatStr(" ", indent)
66+
let ind = repeat(" ", indent)
4167
for decl in node[0]:
42-
let varName = decl[0].ident
68+
let varName = if decl[0].kind == nkIdent: decl[0].ident else: decl[0][0].ident
4369
let value = decl[^1].getImplValue
4470
result.add(ind & "local " & varName & " = " & value & "\n")
4571

46-
proc renderHandle(node: Node, unquoted = true): string =
72+
proc exprToString(node: Node): string =
4773
case node.kind
48-
of nkString:
49-
if not unquoted: "\"" & node.stringVal & "\""
50-
else: node.stringVal
51-
of nkInt:
52-
$node.intVal
53-
of nkFloat:
54-
$node.floatVal
74+
of nkString: "\"" & luaEscapeStr(node.stringVal) & "\""
75+
of nkInt: $node.intVal
76+
of nkFloat: $node.floatVal
5577
of nkBool:
5678
if node.boolVal: "true" else: "false"
57-
of nkIdent:
58-
node.ident
59-
of nkPrefix:
60-
node[0].renderHandle & node[1].renderHandle
61-
of nkPostfix:
62-
node[0].renderHandle & node[1].renderHandle
79+
of nkIdent: node.ident
80+
of nkPrefix: luaOp(node[0].ident) & exprToString(node[1])
81+
of nkPostfix: exprToString(node[0]) & luaOp(node[1].ident)
6382
of nkInfix:
64-
node[1].renderHandle & ' ' & node[0].renderHandle & ' ' & node[2].renderHandle
83+
let op = if node[0].kind == nkIdent: luaOp(node[0].ident) else: luaOp(node[0].render)
84+
"(" & exprToString(node[1]) & " " & op & " " & exprToString(node[2]) & ")"
6585
of nkCall:
66-
node[0].renderHandle & '(' & node[1..^1].mapIt(it.renderHandle).join(", ") & ')'
86+
let callee = if node[0].kind == nkIdent: node[0].ident else: exprToString(node[0])
87+
callee & "(" & node[1..^1].mapIt(exprToString(it)).join(", ") & ")"
88+
of nkBracket:
89+
exprToString(node[0]) & "[" & exprToString(node[1]) & "]"
90+
of nkDot:
91+
exprToString(node[0]) & "." & exprToString(node[1])
6792
else:
6893
""
6994

70-
proc writeHtml(node: Node, indent: int = 0): string {.codegen.} =
71-
let tag = node.getTag()
72-
var
73-
classNames: seq[string] = @[]
74-
idVal: string = ""
75-
customAttrs: seq[(string, string)] = @[]
95+
proc attrsToLua(node: Node): (seq[string], string, seq[(string, string)]) =
96+
var classNames: seq[string] = @[]
97+
var idVal: string = ""
98+
var customAttrs: seq[(string, string)] = @[]
7699
for attr in node.attributes:
77100
if attr.kind == nkHtmlAttribute:
78101
case attr.attrType
@@ -82,86 +105,85 @@ proc writeHtml(node: Node, indent: int = 0): string {.codegen.} =
82105
classNames.add(attr.attrNode.stringVal)
83106
of nkIdent:
84107
classNames.add(attr.attrNode.ident)
85-
else:
86-
discard
108+
of nkInfix, nkCall:
109+
classNames.add("\" .. " & exprToString(attr.attrNode) & " .. \"")
110+
else: discard
87111
of htmlAttrId:
88112
case attr.attrNode.kind
89113
of nkString:
90114
idVal = attr.attrNode.stringVal
91115
of nkIdent:
92116
idVal = attr.attrNode.ident
93-
else:
94-
discard
117+
of nkInfix, nkCall:
118+
idVal = "\" .. " & exprToString(attr.attrNode) & " .. \""
119+
else: discard
95120
of htmlAttr:
96-
if attr.attrNode.kind == nkInfix:
97-
if attr.attrNode[2].kind == nkInfix:
98-
if attr.attrNode[2][0].ident == "&":
99-
let left = attr.attrNode[2][1]
100-
let right = attr.attrNode[2][2]
101-
let leftVal =
102-
if left.kind == nkString:
103-
left.stringVal
104-
else:
105-
"\" .. " & left.renderHandle(false) & " .. \""
106-
let rightVal =
107-
if right.kind == nkIdent and right.ident.len > 0 and right.ident[0] == '$':
108-
"\" .. "& right.ident[1..^1] & " .. \""
109-
else:
110-
(if right.kind == nkString: right.stringVal
111-
else: "\" .. " & right.renderHandle(false) & " .. \"")
112-
customAttrs.add((attr.attrNode[1].renderHandle(true), leftVal & rightVal))
113-
else:
114-
let key = attr.attrNode[1].renderHandle
115-
let value =
116-
case attr.attrNode[2].kind
117-
of nkIdent, nkCall:
118-
"\" .. " & attr.attrNode[2].renderHandle & " .. \""
119-
else:
120-
attr.attrNode[2].renderHandle
121-
customAttrs.add((key, value))
121+
if attr.attrNode.kind == nkInfix and attr.attrNode.len >= 3:
122+
let keyNode = attr.attrNode[1]
123+
let valNode = attr.attrNode[2]
124+
let key = if keyNode.kind == nkIdent: keyNode.ident
125+
elif keyNode.kind == nkString: keyNode.stringVal
126+
else: $keyNode.render
127+
let valStr = case valNode.kind
128+
of nkString: luaEscapeStr(valNode.stringVal)
129+
of nkInt, nkFloat, nkBool: $valNode.render
130+
of nkIdent: "\" .. " & valNode.ident & " .. \""
131+
else: "\" .. " & exprToString(valNode) & " .. \""
132+
customAttrs.add((key, valStr))
122133
elif attr.attrNode.kind == nkString:
123134
customAttrs.add((attr.attrNode.stringVal, ""))
124-
else:
125-
discard
126-
let ind = repeatStr(" ", indent)
135+
elif attr.attrNode.kind == nkIdent:
136+
customAttrs.add((attr.attrNode.ident, ""))
137+
else:
138+
discard
139+
else: discard
140+
(classNames, idVal, customAttrs)
141+
142+
proc writeHtml(node: Node, indent: int = 0): string {.codegen.} =
143+
let tag = node.getTag()
144+
let (classNames, idVal, customAttrs) = attrsToLua(node)
145+
let ind = repeat(" ", indent)
146+
127147
result.add(ind & "html = html .. \"<" & tag)
148+
128149
if classNames.len > 0:
129-
result.add(" class=\\\"" & classNames.join(" ") & "\\\"")
150+
result.add(" class=\\\"")
151+
var first = true
152+
for c in classNames:
153+
if not first: result.add(" ")
154+
first = false
155+
result.add(c)
156+
result.add("\\\"")
157+
130158
if idVal.len > 0:
131159
result.add(" id=\\\"" & idVal & "\\\"")
132-
for (name, value) in customAttrs:
160+
161+
for (key, value) in customAttrs:
133162
if value.len > 0:
134-
result.add(" " & name & "=\\\"" & value & "\\\"")
163+
result.add(" " & key & "=\\\"" & value & "\\\"")
135164
else:
136-
result.add(" " & name)
165+
result.add(" " & key)
137166
result.add(">\"\n")
167+
138168
for child in node.childElements:
139169
case child.kind
140-
of nkBool, nkInt, nkFloat:
141-
result.add(ind & "html = html .. " & child.renderHandle & "\n")
142170
of nkString:
143-
if tag == "script":
144-
let js = minifyInlineJs(child.stringVal)
145-
result.add(ind & "html = html .. [[")
146-
result.add(js)
147-
result.add("]]\n")
148-
else:
149-
result.add(ind & "html = html .. " & child.renderHandle(false) & "\n")
171+
result.add(ind & "html = html .. \"" & luaEscapeStr(child.stringVal) & "\"\n")
172+
of nkBool, nkInt, nkFloat:
173+
result.add(ind & "html = html .. tostring(" & $child.render & ")\n")
150174
of nkIdent:
151-
result.add(ind & "html = html .. " & child.renderHandle & "\n")
152-
of nkCall:
153-
if child[0].ident[0] == '@':
154-
discard
155-
else:
156-
result.add(gen.genStmt(child, indent))
175+
result.add(ind & "html = html .. tostring(" & child.ident & ")\n")
176+
of nkInfix, nkPrefix, nkPostfix, nkCall, nkBracket, nkDot:
177+
result.add(ind & "html = html .. tostring(" & exprToString(child) & ")\n")
157178
else:
158179
result.add(gen.genStmt(child, indent))
180+
159181
if node.tag notin voidHtmlElements:
160182
result.add(ind & "html = html .. \"</" & tag & ">\"\n")
161183

162184
proc genStmt(node: Node, indent: int = 0): Rope {.codegen.} =
163185
result = Rope()
164-
let ind = repeatStr(" ", indent)
186+
let ind = repeat(" ", indent)
165187
case node.kind
166188
of nkVar, nkLet, nkConst:
167189
result.add(gen.writeVar(node, indent))
@@ -195,12 +217,12 @@ proc genStmt(node: Node, indent: int = 0): Rope {.codegen.} =
195217
of nkHtmlElement:
196218
result.add(gen.writeHtml(node, indent))
197219
of nkIf:
198-
result.add(ind & "if " & node[0].render & " then\n")
220+
result.add(ind & "if " & exprToString(node[0]) & " then\n")
199221
result.add(gen.genStmt(node[1], indent + 1))
200222
let hasElse = node.children.len mod 2 == 1
201223
let elifBranches = if hasElse: node[2..^2] else: node[2..^1]
202224
for i in countup(0, elifBranches.len - 1, 2):
203-
result.add(ind & "elseif " & elifBranches[i].render & " then\n")
225+
result.add(ind & "elseif " & exprToString(elifBranches[i]) & " then\n")
204226
result.add(gen.genStmt(elifBranches[i + 1], indent + 1))
205227
if hasElse:
206228
result.add(ind & "else\n")
@@ -209,56 +231,80 @@ proc genStmt(node: Node, indent: int = 0): Rope {.codegen.} =
209231
of nkFor:
210232
let varName = node[0].render
211233
let iterable = node[1]
212-
if iterable.kind == nkInfix and iterable[0].kind == nkIdent and iterable[0].ident == "..":
213-
let startVal = iterable[1].render
214-
let endVal = iterable[2].render
234+
if iterable.kind == nkCall and iterable[0].kind == nkIdent and iterable[0].ident == "..":
235+
let startVal = exprToString(iterable[1])
236+
let endVal = exprToString(iterable[2])
215237
result.add(ind & "for " & varName & " = " & startVal & ", " & endVal & " do\n")
216238
result.add(gen.genStmt(node[2], indent + 1))
217239
result.add(ind & "end\n")
218240
else:
219-
if iterable[0].ident == "..":
220-
# a tim range `0..10` in lua shoild be `0, 10`
221-
result.add(ind & "for x, " & varName & " in " & iterable[1].render & ", " & iterable[2].render & " do\n")
222-
else:
223-
result.add(ind & "for _, " & varName & " in ipairs(" & iterable.render & ") do\n")
241+
let iterExpr = exprToString(iterable)
242+
result.add(ind & "for _, " & varName & " in ipairs(" & iterExpr & ") do\n")
224243
result.add(gen.genStmt(node[2], indent + 1))
225244
result.add(ind & "end\n")
226-
of nkCall:
227-
if node[0].ident == "echo":
228-
result.add(ind & "print(" & node[1..^1].mapIt(it.render).join(", ") & ")\n")
229-
else:
230-
result.add(ind & node[0].render & "(" & node[1..^1].mapIt(it.render).join(", ") & ")\n")
245+
of nkWhile:
246+
result.add(ind & "while " & exprToString(node[0]) & " do\n")
247+
result.add(gen.genStmt(node[1], indent + 1))
248+
result.add(ind & "end\n")
231249
of nkBlock:
232250
for i, s in node:
233251
result.add(gen.genStmt(s, indent))
234252
of nkReturn:
235253
if node[0].kind != nkEmpty:
236-
result.add(ind & "return " & node[0].render & "\n")
254+
result.add(ind & "return " & exprToString(node[0]) & "\n")
237255
else:
238256
result.add(ind & "return\n")
239257
of nkBreak:
240258
result.add(ind & "break\n")
241259
of nkContinue:
242260
result.add(ind & "goto continue\n")
243-
of nkWhile:
244-
result.add(ind & "while " & node[0].render & " do\n")
245-
result.add(gen.genStmt(node[1], indent + 1))
246-
result.add(ind & "end\n")
261+
of nkCall:
262+
let callee = if node[0].kind == nkIdent: node[0].ident else: exprToString(node[0])
263+
if callee == "echo":
264+
result.add(ind & "print(" & node[1..^1].mapIt(exprToString(it)).join(", ") & ")\n")
265+
else:
266+
result.add(ind & callee & "(" & node[1..^1].mapIt(exprToString(it)).join(", ") & ")\n")
267+
of nkInfix, nkPrefix, nkPostfix:
268+
let op = if node.kind == nkInfix and node[0].kind == nkIdent: node[0].ident
269+
elif node.kind == nkInfix: node[0].render
270+
else: ""
271+
if op == "=":
272+
result.add(ind & exprToString(node[1]) & " = " & exprToString(node[2]) & "\n")
273+
else:
274+
result.add(ind & exprToString(node) & "\n")
275+
of nkIdent:
276+
result.add(ind & node.ident & "\n")
277+
of nkRawHtml:
278+
result.add(ind & "html = html .. [[" & node.rawHtml & "]]\n")
279+
of nkCssSnippet:
280+
let css = node.snippetCode
281+
result.add(ind & "html = html .. \"<style>\"\n")
282+
result.add(ind & "html = html .. [[" & css & "]]\n")
283+
result.add(ind & "html = html .. \"</style>\"\n")
284+
of nkJavaScriptSnippet:
285+
let js = node.snippetCode
286+
result.add(ind & "html = html .. \"<script>\"\n")
287+
result.add(ind & js & "\n")
288+
result.add(ind & "html = html .. \"</script>\"\n")
289+
of nkDocComment:
290+
if node.comment.len > 0:
291+
result.add(ind & "--[[ " & node.comment & " ]]\n")
247292
else: discard
248293

249294
proc genScript*(program: Ast, includePath: Option[string],
250295
isMainScript: static bool = false,
251296
isSnippet: static bool = false,
252297
withReturnStmt: static bool = true) {.codegen.} =
253-
result.add("local $1 = {}\n" % [gen.module.getModuleName()])
298+
let modName = gen.module.getModuleName()
299+
result.add("local $1 = {}\n" % [modName])
254300
result.add("\n-- @param args Table\n-- @return string The generated HTML.\n")
255-
result.add("function $1.render(args)\n" % [gen.module.getModuleName()])
301+
result.add("function $1.render(args)\n" % [modName])
256302
result.add(" local app = args.app or {}\n")
257-
result.add(" local this = args.app or {}\n")
303+
result.add(" local this = args.this or {}\n")
258304
result.add(" local html = ''\n")
259305
for node in program.nodes:
260306
result.add(gen.genStmt(node, 1))
261307
result.add(" return html\n")
262308
result.add("end\n")
263309
when withReturnStmt == true:
264-
result.add("return $1\n" % [gen.module.getModuleName()])
310+
result.add("return $1\n" % [modName])

src/tim/engine/transpilers/native/darwin.nim

Whitespace-only changes.

0 commit comments

Comments
 (0)